Saturn builds
This commit is contained in:
+1
-1
@@ -23,6 +23,6 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "wii" OR DUSK_TARGET_SYSTEM STREQUAL "gamecub
|
||||
add_subdirectory(duskdolphin)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "saturn")
|
||||
add_subdirectory(dusksat)
|
||||
add_subdirectory(dusksaturn)
|
||||
|
||||
endif()
|
||||
@@ -29,4 +29,5 @@
|
||||
typedef bool bool_t;
|
||||
typedef int int_t;
|
||||
typedef float float_t;
|
||||
typedef double double_t;
|
||||
typedef char char_t;
|
||||
+24
-24
@@ -20,8 +20,8 @@ errorret_t inputInit(void) {
|
||||
|
||||
for(uint8_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
INPUT.actions[i].action = (inputaction_t)i;
|
||||
INPUT.actions[i].lastValue = 0.0f;
|
||||
INPUT.actions[i].currentValue = 0.0f;
|
||||
INPUT.actions[i].lastValue = 0;
|
||||
INPUT.actions[i].currentValue = 0;
|
||||
|
||||
eventInit(
|
||||
&INPUT.actions[i].onPressed,
|
||||
@@ -55,14 +55,14 @@ void inputUpdate(void) {
|
||||
do {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
action->lastDynamicValue = action->currentDynamicValue;
|
||||
action->currentDynamicValue = 0.0f;
|
||||
action->currentDynamicValue = 0;
|
||||
if(!TIME.dynamicUpdate) {
|
||||
action->lastValue = action->currentValue;
|
||||
action->currentValue = 0.0f;
|
||||
action->currentValue = 0;
|
||||
}
|
||||
#else
|
||||
action->lastValue = action->currentValue;
|
||||
action->currentValue = 0.0f;
|
||||
action->currentValue = 0;
|
||||
#endif
|
||||
|
||||
action++;
|
||||
@@ -74,7 +74,7 @@ void inputUpdate(void) {
|
||||
cur->lastVal = cur->curVal;
|
||||
cur->curVal = inputButtonGetValue(cur->button);
|
||||
|
||||
if(cur->curVal == 0.0f) {
|
||||
if(cur->curVal == 0) {
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
@@ -104,14 +104,14 @@ void inputUpdate(void) {
|
||||
|
||||
for(uint8_t i = INPUT_ACTION_NULL + 1; i < INPUT_ACTION_COUNT; i++) {
|
||||
inputactiondata_t *act = &INPUT.actions[i];
|
||||
bool_t isDown = act->currentValue > 0.0f;
|
||||
bool_t wasDown = act->lastValue > 0.0f;
|
||||
bool_t isDown = act->currentValue > 0;
|
||||
bool_t wasDown = act->lastValue > 0;
|
||||
if(isDown && !wasDown) eventInvoke(&act->onPressed, act);
|
||||
if(!isDown && wasDown) eventInvoke(&act->onReleased, act);
|
||||
}
|
||||
}
|
||||
|
||||
float_t inputGetCurrentValue(const inputaction_t action) {
|
||||
int16_t inputGetCurrentValue(const inputaction_t action) {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) return inputGetCurrentValueDynamic(action);
|
||||
#endif
|
||||
@@ -120,33 +120,33 @@ float_t inputGetCurrentValue(const inputaction_t action) {
|
||||
return INPUT.actions[action].currentValue;
|
||||
}
|
||||
|
||||
float_t inputGetLastValue(const inputaction_t action) {
|
||||
int16_t inputGetLastValue(const inputaction_t action) {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) return inputGetLastValueDynamic(action);
|
||||
#endif
|
||||
|
||||
|
||||
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
|
||||
return INPUT.actions[action].lastValue;
|
||||
}
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
float_t inputGetCurrentValueDynamic(const inputaction_t action) {
|
||||
int16_t inputGetCurrentValueDynamic(const inputaction_t action) {
|
||||
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
|
||||
return INPUT.actions[action].currentDynamicValue;
|
||||
}
|
||||
|
||||
float_t inputGetLastValueDynamic(const inputaction_t action) {
|
||||
int16_t inputGetLastValueDynamic(const inputaction_t action) {
|
||||
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
|
||||
return INPUT.actions[action].lastDynamicValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool_t inputIsDown(const inputaction_t action) {
|
||||
return inputGetCurrentValue(action) > 0.0f;
|
||||
return inputGetCurrentValue(action) > 0;
|
||||
}
|
||||
|
||||
bool_t inputWasDown(const inputaction_t action) {
|
||||
return inputGetLastValue(action) > 0.0f;
|
||||
return inputGetLastValue(action) > 0;
|
||||
}
|
||||
|
||||
bool_t inputPressed(const inputaction_t action) {
|
||||
@@ -157,11 +157,11 @@ bool_t inputReleased(const inputaction_t action) {
|
||||
return !inputIsDown(action) && inputWasDown(action);
|
||||
}
|
||||
|
||||
float_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
||||
int16_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
||||
assertTrue(neg < INPUT_ACTION_COUNT, "Negative input action out of bounds");
|
||||
assertTrue(pos < INPUT_ACTION_COUNT, "Positive input action out of bounds");
|
||||
|
||||
return inputGetCurrentValue(pos) - inputGetCurrentValue(neg);
|
||||
return (int16_t)(inputGetCurrentValue(pos) - inputGetCurrentValue(neg));
|
||||
}
|
||||
|
||||
void inputAxis2D(
|
||||
@@ -170,8 +170,8 @@ void inputAxis2D(
|
||||
vec2 result
|
||||
) {
|
||||
assertNotNull(result, "Result vector cannot be null");
|
||||
result[0] = inputAxis(negX, posX);
|
||||
result[1] = inputAxis(negY, posY);
|
||||
result[0] = (float_t)inputAxis(negX, posX) / (float_t)INT16_MAX;
|
||||
result[1] = (float_t)inputAxis(negY, posY) / (float_t)INT16_MAX;
|
||||
}
|
||||
|
||||
void inputAngle2D(
|
||||
@@ -180,8 +180,8 @@ void inputAngle2D(
|
||||
vec2 result
|
||||
) {
|
||||
assertNotNull(result, "Result vector cannot be null");
|
||||
float_t x = inputAxis(negX, posX);
|
||||
float_t y = inputAxis(negY, posY);
|
||||
float_t x = (float_t)inputAxis(negX, posX) / (float_t)INT16_MAX;
|
||||
float_t y = (float_t)inputAxis(negY, posY) / (float_t)INT16_MAX;
|
||||
float_t mag = sqrtf(x * x + y * y);
|
||||
if(mag <= 0.0f) {
|
||||
result[0] = 0.0f;
|
||||
@@ -211,7 +211,7 @@ void inputBind(const inputbutton_t button, const inputaction_t act) {
|
||||
data->action = act;
|
||||
}
|
||||
|
||||
float_t inputDeadzone(const float_t rawValue, const float_t deadzone) {
|
||||
if(rawValue < deadzone) return 0.0f;
|
||||
return (rawValue - deadzone) / (1.0f - deadzone);
|
||||
int16_t inputDeadzone(const int16_t rawValue, const int16_t deadzone) {
|
||||
if(rawValue < deadzone) return 0;
|
||||
return (int16_t)(((int32_t)(rawValue - deadzone) * INT16_MAX) / (INT16_MAX - deadzone));
|
||||
}
|
||||
+23
-23
@@ -35,36 +35,36 @@ void inputUpdate(void);
|
||||
|
||||
/**
|
||||
* Gets the current value of a specific input action.
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The current value of the action (0.0f to 1.0f).
|
||||
* @return The current value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetCurrentValue(const inputaction_t action);
|
||||
int16_t inputGetCurrentValue(const inputaction_t action);
|
||||
|
||||
/**
|
||||
* Gets the last value of a specific input action.
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The last value of the action (0.0f to 1.0f).
|
||||
* @return The last value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetLastValue(const inputaction_t action);
|
||||
int16_t inputGetLastValue(const inputaction_t action);
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
/**
|
||||
* Gets the current value of a specific input action (dynamic timestep).
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The current value of the action (0.0f to 1.0f).
|
||||
* @return The current value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetCurrentValueDynamic(const inputaction_t action);
|
||||
int16_t inputGetCurrentValueDynamic(const inputaction_t action);
|
||||
|
||||
/**
|
||||
* Gets the last value of a specific input action (dynamic timestep).
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The last value of the action (0.0f to 1.0f).
|
||||
* @return The last value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetLastValueDynamic(const inputaction_t action);
|
||||
int16_t inputGetLastValueDynamic(const inputaction_t action);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -103,12 +103,12 @@ bool_t inputReleased(const inputaction_t action);
|
||||
/**
|
||||
* Gets the value of an input axis, defined by two actions (negative and
|
||||
* positive).
|
||||
*
|
||||
*
|
||||
* @param neg The action representing the negative direction of the axis.
|
||||
* @param pos The action representing the positive direction of the axis.
|
||||
* @return The current value of the axis (-1.0f to 1.0f).
|
||||
* @return The current value of the axis (-INT16_MAX to INT16_MAX).
|
||||
*/
|
||||
float_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
||||
int16_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
||||
|
||||
/**
|
||||
* Gets the values of a 2D input axis, defined by two pairs of actions (negative
|
||||
@@ -151,12 +151,12 @@ void inputAngle2D(
|
||||
void inputBind(const inputbutton_t button, const inputaction_t act);
|
||||
|
||||
/**
|
||||
* Applies deadzone formula to a raw input value. This will;
|
||||
* return 0 if input is less than the deadzone.
|
||||
* re-map the range between 0 and 1 in deadzone-space.
|
||||
*
|
||||
* @param rawValue The raw input value to apply the deadzone to.
|
||||
* @param deadzone The deadzone threshold (0.0f to 1.0f).
|
||||
* @return The input value after applying the deadzone.
|
||||
* Applies deadzone formula to a raw input value. Returns 0 if the value is
|
||||
* below the deadzone threshold, otherwise remaps to the full 0..INT16_MAX
|
||||
* range.
|
||||
*
|
||||
* @param rawValue The raw input value (0 to INT16_MAX).
|
||||
* @param deadzone The deadzone threshold (0 to INT16_MAX).
|
||||
* @return The input value after applying the deadzone (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputDeadzone(const float_t rawValue, const float_t deadzone);
|
||||
int16_t inputDeadzone(const int16_t rawValue, const int16_t deadzone);
|
||||
@@ -14,12 +14,12 @@
|
||||
|
||||
typedef struct {
|
||||
inputaction_t action;
|
||||
float_t lastValue;
|
||||
float_t currentValue;
|
||||
int16_t lastValue;
|
||||
int16_t currentValue;
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
float_t lastDynamicValue;
|
||||
float_t currentDynamicValue;
|
||||
int16_t lastDynamicValue;
|
||||
int16_t currentDynamicValue;
|
||||
#endif
|
||||
|
||||
eventcallback_t onPressedCallbacks[INPUT_ACTION_CALLBACK_COUNT_MAX];
|
||||
|
||||
@@ -25,7 +25,7 @@ inputbutton_t inputButtonGetByName(const char_t *name) {
|
||||
return (inputbutton_t){ .type = INPUT_BUTTON_TYPE_NONE };
|
||||
}
|
||||
|
||||
float_t inputButtonGetValue(const inputbutton_t button) {
|
||||
int16_t inputButtonGetValue(const inputbutton_t button) {
|
||||
return inputButtonGetValuePlatform(button);
|
||||
}
|
||||
inputbuttondata_t * inputButtonGetData(const inputbutton_t button) {
|
||||
|
||||
@@ -75,8 +75,8 @@ typedef struct inputbutton_s {
|
||||
typedef struct {
|
||||
const char_t *name;
|
||||
inputbutton_t button;
|
||||
float_t curVal;
|
||||
float_t lastVal;
|
||||
int16_t curVal;
|
||||
int16_t lastVal;
|
||||
inputaction_t action;
|
||||
} inputbuttondata_t;
|
||||
|
||||
@@ -92,11 +92,11 @@ inputbutton_t inputButtonGetByName(const char_t *name);
|
||||
|
||||
/**
|
||||
* Gets the current value of an input button.
|
||||
*
|
||||
*
|
||||
* @param button The input button.
|
||||
* @return The current value of the input button (0.0f to 1.0f).
|
||||
* @return The current value of the input button (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputButtonGetValue(const inputbutton_t button);
|
||||
int16_t inputButtonGetValue(const inputbutton_t button);
|
||||
|
||||
/**
|
||||
* Gets the button data for a specific input button.
|
||||
|
||||
@@ -44,7 +44,7 @@ void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
|
||||
break;
|
||||
|
||||
case CUTSCENE_ITEM_TYPE_WAIT:
|
||||
data->wait = fixedSub(data->wait, TIME.delta);
|
||||
data->wait -= TIME.delta;
|
||||
if(data->wait <= 0) cutsceneSystemNext();
|
||||
break;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
typedef fixed_t cutscenewait_t;
|
||||
typedef fixed_t cutscenewaitdata_t;
|
||||
typedef float_t cutscenewait_t;
|
||||
typedef float_t cutscenewaitdata_t;
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = {
|
||||
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = FIXED(2.0f) },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } },
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ static const cutscene_t TEST_CUTSCENE_ONE = {
|
||||
};
|
||||
|
||||
static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = {
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = FIXED(1.0f) },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE },
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct entity_s {
|
||||
fixed_t lastPosition[3];
|
||||
|
||||
entityanim_t animation;
|
||||
fixed_t animTime;
|
||||
float_t animTime;
|
||||
|
||||
entityinteract_t interact;
|
||||
} entity_t;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
void entityAnimUpdate(entity_t *entity) {
|
||||
if(entity->animation == ENTITY_ANIM_IDLE) return;
|
||||
|
||||
entity->animTime = fixedSub(entity->animTime, TIME.delta);
|
||||
entity->animTime -= TIME.delta;
|
||||
if(entity->animTime <= 0) {
|
||||
entity->animation = ENTITY_ANIM_IDLE;
|
||||
entity->animTime = 0;
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
#define ENTITY_ANIM_TURN_DURATION FIXED(0.06f)
|
||||
#define ENTITY_ANIM_WALK_DURATION FIXED(0.1f)
|
||||
#define ENTITY_ANIM_TURN_DURATION 0.06f
|
||||
#define ENTITY_ANIM_WALK_DURATION 0.1f
|
||||
|
||||
typedef struct entity_s entity_t;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "display/screen.h"
|
||||
#include "display/color.h"
|
||||
#include "time/time.h"
|
||||
#include "util/fixed.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
static const uint8_t initIndices[3 * 3] = {
|
||||
@@ -64,7 +63,7 @@ static void renderTest2DZOrder(void) {
|
||||
}
|
||||
|
||||
static void renderTest3DQuad(void) {
|
||||
float_t angle = fixedToFloat(TIME.time);
|
||||
float_t angle = TIME.time;
|
||||
renderQuad3D(
|
||||
-45, 0, 0,
|
||||
(int16_t)(cosf(angle) * 30.0f), 0, (int16_t)(sinf(angle) * 30.0f),
|
||||
@@ -89,7 +88,7 @@ static void renderTest3DOverlap(void) {
|
||||
}
|
||||
|
||||
static void renderTestTilemap(void) {
|
||||
float_t t = fixedToFloat(TIME.time);
|
||||
float_t t = TIME.time;
|
||||
float_t scrollPx = fmodf(t * TILEMAP_SCROLL_SPEED, TILEMAP_SCROLL_RANGE);
|
||||
int16_t x = -(int16_t)(int)scrollPx;
|
||||
renderTilemapChunk(x, TILEMAP_Y, 8000, tilemapChunk);
|
||||
@@ -129,7 +128,7 @@ errorret_t sceneFullInit(scenedata_t *data) {
|
||||
}
|
||||
|
||||
errorret_t sceneFullUpdate(scenedata_t *data) {
|
||||
float_t t = fixedToFloat(TIME.time);
|
||||
float_t t = TIME.time;
|
||||
|
||||
renderTextureGetPalette(testTex)[8] = hueColor(t * 2.0f);
|
||||
renderTextureGetIndices(testTex)[1 * 3 + 1] = (uint8_t)((uint32_t)t % 9u);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "display/screen.h"
|
||||
#include "display/color.h"
|
||||
#include "time/time.h"
|
||||
#include "util/fixed.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static const uint8_t BOX_INDICES[3 * 3] = {
|
||||
@@ -59,13 +58,13 @@ errorret_t sceneSpinningBoxRender(scenedata_t *data) {
|
||||
renderClear(color(16, 16, 24, 255));
|
||||
|
||||
renderSetProjection(FOV_Y, ASPECT, FIXED(10), FIXED(10000));
|
||||
renderSetView(0, 0, 150, 0, 0, 0);
|
||||
renderSetView(0, 0, 270, 0, 0, 0);
|
||||
|
||||
float_t angle = fixedToFloat(TIME.time);
|
||||
float_t angle = TIME.time;
|
||||
renderQuad3D(
|
||||
0, 0, 0,
|
||||
(int16_t)(cosf(angle) * 40.0f), 0, (int16_t)(sinf(angle) * 40.0f),
|
||||
0, 40, 0,
|
||||
(int16_t)(cosf(angle) * 72.0f), 0, (int16_t)(sinf(angle) * 72.0f),
|
||||
0, 72, 0,
|
||||
0, spinTex, COLOR_WHITE
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "time.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
#include "console/console.h"
|
||||
|
||||
dusktime_t TIME;
|
||||
@@ -25,6 +24,8 @@ void timeInit(void) {
|
||||
TIME.dynamicDelta = 0.0f;
|
||||
TIME.dynamicUpdate = false;
|
||||
TIME.lastNonDynamic = 0.0f;
|
||||
// Prime the tick so the first-frame delta isn't the full SDL uptime.
|
||||
timeTickPlatform();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -37,15 +38,15 @@ void timeUpdate(void) {
|
||||
|
||||
assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative");
|
||||
|
||||
if((TIME.dynamicTime - TIME.lastNonDynamic) >= DUSK_TIME_STEP_F) {
|
||||
if((TIME.dynamicTime - TIME.lastNonDynamic) >= DUSK_TIME_STEP) {
|
||||
TIME.dynamicUpdate = false;
|
||||
TIME.lastNonDynamic = TIME.dynamicTime;
|
||||
TIME.delta = DUSK_TIME_STEP;
|
||||
TIME.time = fixedAdd(TIME.time, DUSK_TIME_STEP);
|
||||
TIME.time += DUSK_TIME_STEP;
|
||||
}
|
||||
#else
|
||||
TIME.delta = DUSK_TIME_STEP;
|
||||
TIME.time = fixedAdd(TIME.time, DUSK_TIME_STEP);
|
||||
TIME.time += DUSK_TIME_STEP;
|
||||
#endif
|
||||
|
||||
// Print time in UTC style string
|
||||
|
||||
+3
-11
@@ -8,16 +8,9 @@
|
||||
#pragma once
|
||||
#include "timeepoch.h"
|
||||
#include "time/timeplatform.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
#ifndef DUSK_TIME_STEP
|
||||
#define DUSK_TIME_STEP FIXED(1.0f / 60.0f)
|
||||
#endif
|
||||
|
||||
/* Float-space step used by the dynamic accumulator to avoid fixed-point
|
||||
* truncation error in the trigger comparison. */
|
||||
#ifndef DUSK_TIME_STEP_F
|
||||
#define DUSK_TIME_STEP_F (1.0f / 60.0f)
|
||||
#define DUSK_TIME_STEP (1.0f / 60.0f)
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
@@ -30,11 +23,10 @@
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
fixed_t delta;
|
||||
fixed_t time;
|
||||
float_t delta;
|
||||
float_t time;
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
/* Float accumulator avoids fixed-point truncation drift. */
|
||||
float_t lastNonDynamic;
|
||||
bool_t dynamicUpdate;
|
||||
float_t dynamicDelta;
|
||||
|
||||
@@ -27,11 +27,11 @@ void uiFullboxInit(uifullbox_t *fullbox) {
|
||||
);
|
||||
}
|
||||
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, fixed_t delta) {
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
if(fullbox->duration <= 0 || fullbox->time >= fullbox->duration) return;
|
||||
|
||||
fullbox->time = fixedAdd(fullbox->time, delta);
|
||||
fullbox->time += delta;
|
||||
if(fullbox->time >= fullbox->duration) {
|
||||
fullbox->time = fullbox->duration;
|
||||
eventInvoke(&fullbox->onTransitionEnd, fullbox);
|
||||
@@ -44,7 +44,7 @@ static color_t uiFullboxGetColor(const uifullbox_t *fullbox) {
|
||||
}
|
||||
fixed_t t = easingApply(
|
||||
fullbox->easing,
|
||||
fixedDiv(fullbox->time, fullbox->duration)
|
||||
fixedFromFloat(fullbox->time / fullbox->duration)
|
||||
);
|
||||
return color4b(
|
||||
fixedToU8(fixedLerp(
|
||||
@@ -92,7 +92,7 @@ void uiFullboxTransition(
|
||||
uifullbox_t *fullbox,
|
||||
color_t from,
|
||||
color_t to,
|
||||
fixed_t duration,
|
||||
float_t duration,
|
||||
easingtype_t easing
|
||||
) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
#include "display/color.h"
|
||||
#include "animation/easing.h"
|
||||
#include "event/event.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
typedef struct {
|
||||
color_t fromColor;
|
||||
color_t toColor;
|
||||
fixed_t duration;
|
||||
fixed_t time;
|
||||
float_t duration;
|
||||
float_t time;
|
||||
easingtype_t easing;
|
||||
eventcallback_t onTransitionEndCallbacks[4];
|
||||
void *onTransitionEndUsers[4];
|
||||
@@ -40,7 +39,7 @@ void uiFullboxInit(uifullbox_t *fullbox);
|
||||
* @param fullbox The fullbox to update.
|
||||
* @param delta Seconds elapsed since last update.
|
||||
*/
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, fixed_t delta);
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta);
|
||||
|
||||
/**
|
||||
* Renders the fullbox. Skipped entirely when the current alpha is zero.
|
||||
@@ -63,7 +62,7 @@ void uiFullboxTransition(
|
||||
uifullbox_t *fullbox,
|
||||
color_t from,
|
||||
color_t to,
|
||||
fixed_t duration,
|
||||
float_t duration,
|
||||
easingtype_t easing
|
||||
);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "uiloading.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/fixed.h"
|
||||
#include "util/memory.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/screen/screen.h"
|
||||
@@ -28,10 +27,10 @@ void uiLoadingInit(void) {
|
||||
);
|
||||
}
|
||||
|
||||
void uiLoadingUpdate(fixed_t delta) {
|
||||
void uiLoadingUpdate(float_t delta) {
|
||||
if(UI_LOADING.duration <= 0 || UI_LOADING.time >= UI_LOADING.duration)
|
||||
return;
|
||||
UI_LOADING.time = fixedAdd(UI_LOADING.time, delta);
|
||||
UI_LOADING.time += delta;
|
||||
if(UI_LOADING.time >= UI_LOADING.duration) {
|
||||
UI_LOADING.time = UI_LOADING.duration;
|
||||
eventInvoke(&UI_LOADING.onTransitionEnd, &UI_LOADING);
|
||||
@@ -43,7 +42,7 @@ errorret_t uiLoadingDraw(void) {
|
||||
if(UI_LOADING.duration <= 0 || UI_LOADING.time >= UI_LOADING.duration) {
|
||||
alpha = UI_LOADING.toAlpha;
|
||||
} else {
|
||||
float_t t = fixedToFloat(fixedDiv(UI_LOADING.time, UI_LOADING.duration));
|
||||
float_t t = UI_LOADING.time / UI_LOADING.duration;
|
||||
alpha = UI_LOADING.fromAlpha +
|
||||
(UI_LOADING.toAlpha - UI_LOADING.fromAlpha) * t;
|
||||
}
|
||||
|
||||
@@ -8,16 +8,15 @@
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "event/event.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
#define UI_LOADING_FADE_DURATION FIXED(0.5f)
|
||||
#define UI_LOADING_FADE_DURATION 0.5f
|
||||
#define UI_LOADING_MARGIN 8.0f
|
||||
|
||||
typedef struct {
|
||||
float_t fromAlpha;
|
||||
float_t toAlpha;
|
||||
fixed_t duration;
|
||||
fixed_t time;
|
||||
float_t duration;
|
||||
float_t time;
|
||||
eventcallback_t onTransitionEndCallbacks[4];
|
||||
void *onTransitionEndUsers[4];
|
||||
event_t onTransitionEnd;
|
||||
@@ -36,7 +35,7 @@ void uiLoadingInit(void);
|
||||
*
|
||||
* @param delta Seconds elapsed since last update.
|
||||
*/
|
||||
void uiLoadingUpdate(fixed_t delta);
|
||||
void uiLoadingUpdate(float_t delta);
|
||||
|
||||
/**
|
||||
* Draws the loading indicator. No-op when fully transparent.
|
||||
|
||||
@@ -29,7 +29,9 @@ void * memoryAllocate(const size_t size) {
|
||||
void * memoryAlign(size_t alignment, size_t size) {
|
||||
assertTrue(alignment > 0, "Alignment must be greater than 0.");
|
||||
assertTrue(size > 0, "Cannot allocate 0 bytes of memory.");
|
||||
void *ptr = memalign(alignment, size);
|
||||
// aligned_alloc (C11, <stdlib.h>) requires size to be a multiple of alignment
|
||||
size_t alignedSize = (size + alignment - 1) & ~(alignment - 1);
|
||||
void *ptr = aligned_alloc(alignment, alignedSize);
|
||||
assertNotNull(ptr, "Aligned memory allocation failed.");
|
||||
memoryTrack(ptr);
|
||||
return ptr;
|
||||
|
||||
@@ -160,30 +160,29 @@ void inputUpdateDolphin(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
float_t inputButtonGetValueDolphin(const inputbutton_t button) {
|
||||
int16_t inputButtonGetValueDolphin(const inputbutton_t button) {
|
||||
switch(button.type) {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD: {
|
||||
if(INPUT.platform.padState[0] & button.gpButton) return 1.0f;
|
||||
return 0.0f;
|
||||
return (INPUT.platform.padState[0] & button.gpButton) ? INT16_MAX : 0;
|
||||
}
|
||||
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD_AXIS: {
|
||||
float_t axis = INPUT.platform.pads[0][button.gpAxis.axis];
|
||||
float_t value = axis / 128.0f;
|
||||
if(!button.gpAxis.positive) value = -value;
|
||||
value = inputDeadzone(value, inputGetDeadzoneDolphin(button));
|
||||
return value;
|
||||
float_t f = INPUT.platform.pads[0][button.gpAxis.axis];
|
||||
if(!button.gpAxis.positive) f = -f;
|
||||
if(f < 0.0f) return 0;
|
||||
int16_t value = (int16_t)(f * INT16_MAX);
|
||||
return inputDeadzone(value, inputGetDeadzoneDolphin(button));
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
assertUnreachable("Unknown input button type");
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneDolphin(const inputbutton_t button) {
|
||||
return 0.2f;
|
||||
int16_t inputGetDeadzoneDolphin(const inputbutton_t button) {
|
||||
return (int16_t)(0.2f * INT16_MAX);
|
||||
}
|
||||
@@ -58,17 +58,17 @@ errorret_t inputInitDolphin(void);
|
||||
void inputUpdateDolphin(void);
|
||||
|
||||
/**
|
||||
* Returns the input value (between 0 and 1) of the given button.
|
||||
*
|
||||
* Returns the input value of the given button (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the value of.
|
||||
* @return The value of the button, between 0 and 1.
|
||||
* @return The value of the button (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputButtonGetValueDolphin(const inputbutton_t button);
|
||||
int16_t inputButtonGetValueDolphin(const inputbutton_t button);
|
||||
|
||||
/**
|
||||
* Returns the deadzone for the given button.
|
||||
*
|
||||
* Returns the deadzone for the given button (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the deadzone of.
|
||||
* @return The deadzone for the button, between 0 and 1.
|
||||
* @return The deadzone threshold (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetDeadzoneDolphin(const inputbutton_t button);
|
||||
int16_t inputGetDeadzoneDolphin(const inputbutton_t button);
|
||||
@@ -544,6 +544,6 @@ errorret_t inputInitLinux(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.17f;
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return (int16_t)(0.17f * INT16_MAX);
|
||||
}
|
||||
@@ -92,6 +92,6 @@ errorret_t inputInitPSP(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.2f;
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return (int16_t)(0.2f * INT16_MAX);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/displaysat.h"
|
||||
#include "display/render/rendersat.h"
|
||||
#include "display/display.h"
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
#include <vdp1/cmdt.h>
|
||||
#include <vdp2/cram.h>
|
||||
#include <vdp2/tvmd.h>
|
||||
#include <vdp2/scrn.h>
|
||||
#include <vdp2/vram.h>
|
||||
|
||||
errorret_t displaySaturnInit(void) {
|
||||
logDebug("[Saturn] displaySaturnInit: start\n");
|
||||
DISPLAY.whichBuffer = 0;
|
||||
|
||||
/*
|
||||
* TV mode: NTSC, 320×224, non-interlaced.
|
||||
* Yaul's vdp2_tvmd_display_res_set() configures the sync standard and
|
||||
* horizontal/vertical resolution.
|
||||
*
|
||||
* TODO: replace with the Yaul typed call when integrating the full SDK:
|
||||
* vdp2_tvmd_display_res_set(VDP2_TVMD_INTERLACE_NONE,
|
||||
* VDP2_TVMD_HORZ_NORMAL_A,
|
||||
* VDP2_TVMD_VERT_224);
|
||||
* vdp2_tvmd_display_set();
|
||||
*/
|
||||
|
||||
/*
|
||||
* VDP2 scroll planes: disable all NBG/RBG planes for now; game content is
|
||||
* drawn entirely via VDP1 sprites. Tilemap chunks will re-enable NBG0
|
||||
* when the VDP2 tilemap backend is implemented.
|
||||
*
|
||||
* TODO:
|
||||
* vdp2_scrn_display_set(VDP2_SCRN_DISP_NBG0, false);
|
||||
* vdp2_scrn_display_set(VDP2_SCRN_DISP_NBG1, false);
|
||||
* ...
|
||||
*/
|
||||
|
||||
/*
|
||||
* VDP1 initialisation: the hardware starts drawing from VRAM offset 0.
|
||||
* We place our command table there and texture data afterward.
|
||||
*
|
||||
* TODO:
|
||||
* vdp1_vram_partitions_set(
|
||||
* VDP1_VRAM_CYCP_..., // cycle patterns
|
||||
* ...
|
||||
* );
|
||||
*/
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: calling renderSaturnInit\n");
|
||||
errorChain(renderSaturnInit());
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: done\n");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnFlush(ropbuffer_t *buf) {
|
||||
assertNotNull(buf, "Saturn flush: null ropbuffer");
|
||||
errorChain(renderSaturnFlush(buf));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnSwap(void) {
|
||||
logDebug("[Saturn] displaySaturnSwap\n");
|
||||
/*
|
||||
* Wait for VDP1 to finish rendering the current frame then swap buffers.
|
||||
*
|
||||
* TODO:
|
||||
* vdp1_sync_render();
|
||||
* vdp1_sync();
|
||||
* vdp2_sync();
|
||||
* vdp2_sync_wait();
|
||||
*/
|
||||
DISPLAY.whichBuffer ^= 1;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void displaySaturnDispose(void) {
|
||||
logDebug("[Saturn] displaySaturnDispose\n");
|
||||
renderSaturnDispose();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "input/inputsat.h"
|
||||
|
||||
#define inputInitPlatform inputInitSaturn
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
/*
|
||||
* Saturn standard digital pad buttons (smpc_peripheral_digital_t).
|
||||
* Yaul exposes them via smpc_peripheral_digital_port() and
|
||||
* smpc_peripheral_digital_get().
|
||||
*
|
||||
* Button bitmask in the SMPC peripheral data word:
|
||||
* bit 11 = Right bit 10 = Left bit 9 = Down bit 8 = Up
|
||||
* bit 7 = Start bit 6 = A bit 5 = C bit 4 = B
|
||||
* bit 3 = R bit 2 = X bit 1 = Y bit 0 = Z
|
||||
*
|
||||
* We use Yaul's SMPC_PERIPHERAL_DIGITAL_* macros where available.
|
||||
*/
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } },
|
||||
{ .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } },
|
||||
{ .name = "c", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 5 } },
|
||||
{ .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 2 } },
|
||||
{ .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 1 } },
|
||||
{ .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 0 } },
|
||||
{ .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 7 } },
|
||||
{ .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 8 } },
|
||||
{ .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 9 } },
|
||||
{ .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 10 } },
|
||||
{ .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 11 } },
|
||||
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 15 } },
|
||||
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 3 } },
|
||||
{ .name = "accept", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } }, /* A */
|
||||
{ .name = "cancel", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } }, /* B */
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
errorret_t inputInitSaturn(void) {
|
||||
#define X(buttonName, buttonAction) \
|
||||
inputBind(inputButtonGetByName(buttonName), buttonAction);
|
||||
X("up", INPUT_ACTION_UP);
|
||||
X("down", INPUT_ACTION_DOWN);
|
||||
X("left", INPUT_ACTION_LEFT);
|
||||
X("right", INPUT_ACTION_RIGHT);
|
||||
X("accept", INPUT_ACTION_ACCEPT);
|
||||
X("cancel", INPUT_ACTION_CANCEL);
|
||||
X("start", INPUT_ACTION_RAGEQUIT);
|
||||
#undef X
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
|
||||
errorret_t inputInitSaturn(void);
|
||||
@@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/savesat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/savestreamsat.c
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savesat.h"
|
||||
#include "log/log.h"
|
||||
|
||||
/*
|
||||
* TODO: use Yaul's bup_* API for backup RAM access.
|
||||
* Reference: <bup/bup.h> in the Yaul SDK.
|
||||
*
|
||||
* bup_init(BUP_DEV_INTERNAL); // or BUP_DEV_EXTERNAL for cart
|
||||
* bup_stat_t stat;
|
||||
* bup_stat(BUP_DEV_INTERNAL, &stat);
|
||||
*
|
||||
* Write: bup_write(BUP_DEV_INTERNAL, &dir, data, size, BUP_MODE_NEW);
|
||||
* Read: bup_read(BUP_DEV_INTERNAL, filename, data, size);
|
||||
* Del: bup_delete(BUP_DEV_INTERNAL, filename);
|
||||
*/
|
||||
|
||||
errorret_t saveInitSaturn(void) {
|
||||
logDebug("[Saturn] saveInitSaturn\n");
|
||||
/* TODO: bup_init(BUP_DEV_INTERNAL); */
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDisposeSaturn(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeleteSaturn(const uint8_t slot) {
|
||||
logDebug("[Saturn] saveDeleteSaturn: slot=%u\n", (unsigned)slot);
|
||||
/* TODO: bup_delete(BUP_DEV_INTERNAL, filename_for_slot(slot)); */
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/systemsat.c
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/timesat.c
|
||||
)
|
||||
@@ -10,6 +10,7 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/compat.c
|
||||
)
|
||||
|
||||
add_subdirectory(asset)
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/assetsat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/assetsaturn.c
|
||||
)
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "assetsat.h"
|
||||
#include "assetsaturn.h"
|
||||
|
||||
typedef assetsat_t assetplatform_t;
|
||||
typedef assetsaturn_t assetplatform_t;
|
||||
|
||||
#define assetInitPlatform assetInitSaturn
|
||||
#define assetDisposePlatform assetDisposeSaturn
|
||||
@@ -5,16 +5,15 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetsat.h"
|
||||
#include "assetsaturn.h"
|
||||
#include "log/log.h"
|
||||
|
||||
errorret_t assetInitSaturn(void) {
|
||||
logDebug("[Saturn] assetInitSaturn: initializing CD-Block\n");
|
||||
/* TODO: cd_block_init() */
|
||||
cd_block_init();
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetDisposeSaturn(void) {
|
||||
/* TODO: cd_block_deinit() */
|
||||
errorOk();
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
typedef struct {
|
||||
uint8_t unused;
|
||||
} assetsat_t;
|
||||
} assetsaturn_t;
|
||||
|
||||
errorret_t assetInitSaturn(void);
|
||||
errorret_t assetDisposeSaturn(void);
|
||||
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
* Bare-metal libc compatibility stubs for Saturn/Yaul.
|
||||
*
|
||||
* Yaul's sysroot is a minimal bare-metal environment that lacks many
|
||||
* standard C library functions used by third-party code (libzip, yyjson)
|
||||
* and some Dusk utility modules. Provide minimal correct implementations.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* =========================================================================
|
||||
* Memory allocation
|
||||
* ========================================================================= */
|
||||
|
||||
/* realloc: libyaul provides a weak _realloc but it is not pulled from the
|
||||
* archive in time for libzip/yyjson (link-order issue). Provide a strong
|
||||
* definition using Yaul's __realloc (TLSF allocator). */
|
||||
extern void *__realloc(void *ptr, size_t size);
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
return __realloc(ptr, size);
|
||||
}
|
||||
|
||||
/* calloc: zero-initialised allocation. */
|
||||
void *calloc(size_t nmemb, size_t size) {
|
||||
size_t total = nmemb * size;
|
||||
void *p = malloc(total);
|
||||
if (p) memset(p, 0, total);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* aligned_alloc: C11 aligned allocation.
|
||||
* Yaul's TLSF returns naturally aligned (≥8-byte) blocks.
|
||||
* For Saturn-specific hardware requirements (VDP command lists need 32-byte
|
||||
* alignment) this trivial wrapper is sufficient because Yaul's cmt_pool
|
||||
* handles its own aligned allocation internally; memoryAlign() is only used
|
||||
* for higher-level game objects where natural alignment is enough. */
|
||||
void *aligned_alloc(size_t alignment, size_t size) {
|
||||
(void)alignment;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Unix file-descriptor I/O
|
||||
* libzip's zip_random_unix.c opens /dev/urandom to seed its PRNG.
|
||||
* Saturn has no filesystem; return -1 (ENOENT / error) unconditionally.
|
||||
* ========================================================================= */
|
||||
|
||||
int open(const char *path, int flags, ...) {
|
||||
(void)path; (void)flags;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read(int fd, void *buf, size_t count) {
|
||||
(void)fd; (void)buf; (void)count;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int close(int fd) {
|
||||
(void)fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* memcpy_s — Annex K bounds-checking variant; not in Yaul's bare-metal libc.
|
||||
* libzip uses it; forward to plain memcpy.
|
||||
* ========================================================================= */
|
||||
|
||||
int memcpy_s(void *dest, size_t destsz, const void *src, size_t count) {
|
||||
if (!dest || !src || count > destsz) return 1;
|
||||
memcpy(dest, src, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Timing
|
||||
* ========================================================================= */
|
||||
|
||||
/* usleep: asset.c calls usleep() in async-load paths. Saturn has no threading;
|
||||
* busy-wait is not useful without a timer, so simply return. */
|
||||
int usleep(unsigned int usecs) {
|
||||
(void)usecs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* String-to-number conversion
|
||||
* Yaul provides strtol but not strtoul / strtoll / strtof.
|
||||
* ========================================================================= */
|
||||
|
||||
static unsigned long long _parse_ull(
|
||||
const char *s, char **endptr, int base, int issigned
|
||||
) {
|
||||
unsigned long long val = 0;
|
||||
int neg = 0;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == '+') { s++; }
|
||||
else if (*s == '-') { neg = 1; s++; }
|
||||
if (base == 0) {
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
|
||||
else if (s[0] == '0') { base = 8; s++; }
|
||||
else { base = 10; }
|
||||
} else if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||
s += 2;
|
||||
}
|
||||
const char *start = s;
|
||||
while (*s) {
|
||||
int digit;
|
||||
if (*s >= '0' && *s <= '9') digit = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'f') digit = *s - 'a' + 10;
|
||||
else if (*s >= 'A' && *s <= 'F') digit = *s - 'A' + 10;
|
||||
else break;
|
||||
if (digit >= base) break;
|
||||
val = val * (unsigned long long)base + (unsigned long long)digit;
|
||||
s++;
|
||||
}
|
||||
if (s == start) { if (endptr) *endptr = (char *)start; return 0; }
|
||||
if (endptr) *endptr = (char *)s;
|
||||
if (neg && issigned) return (unsigned long long)(-(long long)val);
|
||||
if (neg) return (unsigned long long)(0ULL - val);
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned long strtoul(const char *s, char **endptr, int base) {
|
||||
return (unsigned long)_parse_ull(s, endptr, base, 0);
|
||||
}
|
||||
|
||||
long long strtoll(const char *s, char **endptr, int base) {
|
||||
return (long long)_parse_ull(s, endptr, base, 1);
|
||||
}
|
||||
|
||||
float strtof(const char *s, char **endptr) {
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
int neg = 0;
|
||||
if (*s == '-') { neg = 1; s++; } else if (*s == '+') s++;
|
||||
float val = 0.0f, frac = 1.0f;
|
||||
int hasDot = 0;
|
||||
const char *start = s;
|
||||
while (*s >= '0' && *s <= '9') { val = val * 10.0f + (float)(*s - '0'); s++; }
|
||||
if (*s == '.') {
|
||||
hasDot = 1; s++;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
frac /= 10.0f; val += frac * (float)(*s - '0'); s++;
|
||||
}
|
||||
}
|
||||
(void)hasDot;
|
||||
int exp = 0; int expneg = 0;
|
||||
if (*s == 'e' || *s == 'E') {
|
||||
s++;
|
||||
if (*s == '-') { expneg = 1; s++; } else if (*s == '+') s++;
|
||||
while (*s >= '0' && *s <= '9') { exp = exp * 10 + (*s - '0'); s++; }
|
||||
float p = 1.0f;
|
||||
for (int i = 0; i < exp; i++) p *= 10.0f;
|
||||
if (expneg) val /= p; else val *= p;
|
||||
}
|
||||
if (endptr) *endptr = (char *)(s == start ? start : s);
|
||||
return neg ? -val : val;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* String utilities missing from Yaul's libc
|
||||
* ========================================================================= */
|
||||
|
||||
/* strdup: libzip pkware code uses strdup() for password strings.
|
||||
* Yaul's bare-metal libc does not provide it. */
|
||||
char *strdup(const char *s) {
|
||||
if (!s) return NULL;
|
||||
size_t len = 0;
|
||||
while (s[len]) len++;
|
||||
char *p = (char *)malloc(len + 1);
|
||||
if (p) memcpy(p, s, len + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* stricmp / strcasecmp: Yaul has strcasecmp (_strcasecmp) but libzip uses
|
||||
* _stricmp (Windows name). Map to the same logic. */
|
||||
int stricmp(const char *a, const char *b) {
|
||||
while (*a && *b) {
|
||||
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
if (d) return d;
|
||||
a++; b++;
|
||||
}
|
||||
return tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Sorting
|
||||
* libzip uses qsort() for sorted-entry zip files (torrentzip).
|
||||
* ========================================================================= */
|
||||
|
||||
void qsort(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *)) {
|
||||
if (nmemb < 2) return;
|
||||
uint8_t *arr = (uint8_t *)base;
|
||||
/* Insertion sort — O(n²) but avoids recursion and is fine for small n. */
|
||||
uint8_t tmp[256];
|
||||
for (size_t i = 1; i < nmemb; i++) {
|
||||
size_t j = i;
|
||||
memcpy(tmp, arr + j * size, size);
|
||||
while (j > 0 && compar(arr + (j-1) * size, tmp) > 0) {
|
||||
memcpy(arr + j * size, arr + (j-1) * size, size);
|
||||
j--;
|
||||
}
|
||||
memcpy(arr + j * size, tmp, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Floating-point math (SH-2 has no FPU; all operations are soft-float)
|
||||
* Used by test scenes and input helpers.
|
||||
* ========================================================================= */
|
||||
|
||||
/* sqrtf — Newton-Raphson, 4 iterations (enough for single precision). */
|
||||
float sqrtf(float x) {
|
||||
if (x <= 0.0f) return 0.0f;
|
||||
float r = x;
|
||||
r = (r + x / r) * 0.5f;
|
||||
r = (r + x / r) * 0.5f;
|
||||
r = (r + x / r) * 0.5f;
|
||||
r = (r + x / r) * 0.5f;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* sinf / cosf — Taylor series with range reduction to [-π, π]. */
|
||||
#define _COMPAT_PI 3.14159265358979323846f
|
||||
#define _COMPAT_2PI 6.28318530717958647692f
|
||||
|
||||
float sinf(float x) {
|
||||
/* Range reduction */
|
||||
while (x > _COMPAT_PI) x -= _COMPAT_2PI;
|
||||
while (x < -_COMPAT_PI) x += _COMPAT_2PI;
|
||||
float x2 = x * x;
|
||||
/* Horner form: x(1 - x²/6(1 - x²/20(1 - x²/42))) */
|
||||
return x * (1.0f - x2 * (1.0f/6.0f * (1.0f - x2 *
|
||||
(1.0f/20.0f * (1.0f - x2 * (1.0f/42.0f))))));
|
||||
}
|
||||
|
||||
float cosf(float x) {
|
||||
return sinf(x + _COMPAT_PI * 0.5f);
|
||||
}
|
||||
|
||||
/* fmodf — IEEE 754 fmod for floats. */
|
||||
float fmodf(float x, float y) {
|
||||
if (y == 0.0f) return 0.0f;
|
||||
float q = x / y;
|
||||
/* Truncate toward zero */
|
||||
int qi = (int)q;
|
||||
return x - (float)qi * y;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* FILE* I/O stubs
|
||||
* yyjson's file-reading path (yyjson_read_fp) pulls in fread/fseek/ftell.
|
||||
* With -ffunction-sections + --gc-sections that function is dead-code
|
||||
* eliminated if it is unreachable from Dusk. These stubs are a safety
|
||||
* net in case the linker cannot eliminate the entire object in one pass.
|
||||
* Saturn has no filesystem so all file operations return error/0.
|
||||
* Use void* for the stream parameter to avoid conflicting with any
|
||||
* stdio.h FILE typedef that the sysroot may or may not provide.
|
||||
* ========================================================================= */
|
||||
|
||||
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||
(void)ptr; (void)size; (void)nmemb; (void)stream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fseek(void *stream, long offset, int whence) {
|
||||
(void)stream; (void)offset; (void)whence;
|
||||
return -1;
|
||||
}
|
||||
|
||||
long ftell(void *stream) {
|
||||
(void)stream;
|
||||
return -1L;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/displaysat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/displaysaturn.c
|
||||
)
|
||||
|
||||
add_subdirectory(render)
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/displaysat.h"
|
||||
#include "displaysaturn.h"
|
||||
|
||||
typedef displaysat_t displayplatform_t;
|
||||
typedef displaysaturn_t displayplatform_t;
|
||||
|
||||
#define displayPlatformInit displaySaturnInit
|
||||
#define displayPlatformFlush displaySaturnFlush
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/displaysaturn.h"
|
||||
#include "display/render/rendersaturn.h"
|
||||
#include "display/display.h"
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
#include <vdp1.h>
|
||||
#include <vdp2/cram.h>
|
||||
#include <vdp2/tvmd.h>
|
||||
#include <vdp2/scrn.h>
|
||||
#include <vdp2/vram.h>
|
||||
#include <vdp2.h>
|
||||
|
||||
errorret_t displaySaturnInit(void) {
|
||||
logDebug("[Saturn] displaySaturnInit: start\n");
|
||||
DISPLAY.whichBuffer = 0;
|
||||
|
||||
vdp2_tvmd_display_res_set(
|
||||
VDP2_TVMD_INTERLACE_NONE,
|
||||
VDP2_TVMD_HORZ_NORMAL_A,
|
||||
VDP2_TVMD_VERT_224
|
||||
);
|
||||
vdp2_tvmd_display_set();
|
||||
|
||||
/* Disable all NBG/RBG scroll planes; game content drawn entirely via VDP1. */
|
||||
vdp2_scrn_display_set(VDP2_SCRN_DISP_NONE);
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: calling renderSaturnInit\n");
|
||||
errorChain(renderSaturnInit());
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: done\n");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnFlush(ropbuffer_t *buf) {
|
||||
assertNotNull(buf, "Saturn flush: null ropbuffer");
|
||||
errorChain(renderSaturnFlush(buf));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnSwap(void) {
|
||||
logDebug("[Saturn] displaySaturnSwap\n");
|
||||
vdp1_sync_render();
|
||||
vdp1_sync();
|
||||
vdp2_sync();
|
||||
vdp2_sync_wait();
|
||||
DISPLAY.whichBuffer ^= 1;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void displaySaturnDispose(void) {
|
||||
logDebug("[Saturn] displaySaturnDispose\n");
|
||||
renderSaturnDispose();
|
||||
}
|
||||
@@ -10,12 +10,12 @@
|
||||
#include "display/displaystate.h"
|
||||
#include "display/render/ropbuffer.h"
|
||||
|
||||
#define SAT_SCREEN_W DUSK_DISPLAY_WIDTH
|
||||
#define SAT_SCREEN_H DUSK_DISPLAY_HEIGHT
|
||||
#define SATURN_SCREEN_W DUSK_DISPLAY_WIDTH
|
||||
#define SATURN_SCREEN_H DUSK_DISPLAY_HEIGHT
|
||||
|
||||
typedef struct {
|
||||
int_t whichBuffer;
|
||||
} displaysat_t;
|
||||
} displaysaturn_t;
|
||||
|
||||
errorret_t displaySaturnInit(void);
|
||||
errorret_t displaySaturnFlush(ropbuffer_t *buf);
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/inputsat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/rendersaturn.c
|
||||
)
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/render/rendersat.h"
|
||||
#include "display/render/rendersaturn.h"
|
||||
|
||||
#define renderPlatformTextureCreate renderSaturnTextureCreate
|
||||
#define renderPlatformTextureDispose renderSaturnTextureDispose
|
||||
+116
-115
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/render/rendersat.h"
|
||||
#include "display/render/rendersaturn.h"
|
||||
#include "display/render/rop.h"
|
||||
#include "display/color.h"
|
||||
#include "assert/assert.h"
|
||||
@@ -27,15 +27,15 @@
|
||||
*
|
||||
* VDP2 CRAM (4KB) holds palettes:
|
||||
* Each 256-color palette occupies 512 bytes (256 × 2-byte RGB1555 entries).
|
||||
* We reserve one palette slot per texture handle (up to SAT_PALETTE_MAX).
|
||||
* We reserve one palette slot per texture handle (up to SATURN_PALETTE_MAX).
|
||||
*/
|
||||
|
||||
/* ---- Limits -------------------------------------------------------------- */
|
||||
|
||||
#define SAT_RTEXTURE_MAX 128
|
||||
#define SAT_CMDT_MAX 1024
|
||||
#define SAT_TEXTURE_VRAM_BASE 0x010000u /* byte offset in VDP1 VRAM */
|
||||
#define SAT_TEXTURE_VRAM_SIZE (0x200000u - SAT_TEXTURE_VRAM_BASE)
|
||||
#define SATURN_RTEXTURE_MAX 128
|
||||
#define SATURN_CMDT_MAX 1024
|
||||
#define SATURN_TEXTURE_VRAM_BASE 0x010000u /* byte offset in VDP1 VRAM */
|
||||
#define SATURN_TEXTURE_VRAM_SIZE (0x200000u - SATURN_TEXTURE_VRAM_BASE)
|
||||
|
||||
/* ---- VDP1 command table entry (hardware layout, 32 bytes) ---------------- */
|
||||
|
||||
@@ -52,23 +52,23 @@ typedef struct __attribute__((packed)) {
|
||||
int16_t xd, yd; /* Vertex D (distorted sprite only) */
|
||||
uint16_t grda; /* Gouraud shading address (unused = 0) */
|
||||
uint16_t _pad;
|
||||
} satcmd_t;
|
||||
} saturncmd_t;
|
||||
|
||||
_Static_assert(sizeof(satcmd_t) == 32, "satcmd_t must be 32 bytes");
|
||||
_Static_assert(sizeof(saturncmd_t) == 32, "saturncmd_t must be 32 bytes");
|
||||
|
||||
/* CTRL command type bits (bits 2:0) */
|
||||
#define SATCMD_CTRL_NORMAL_SPRITE (0x0000u) /* aligned rect */
|
||||
#define SATCMD_CTRL_SCALED_SPRITE (0x0001u)
|
||||
#define SATCMD_CTRL_DISTORTED_SPRITE (0x0002u) /* arbitrary quad */
|
||||
#define SATCMD_CTRL_POLYGON (0x0004u) /* solid polygon */
|
||||
#define SATCMD_CTRL_SYSCLIP (0x0009u) /* system clipping */
|
||||
#define SATCMD_CTRL_END (0x8000u) /* end of list */
|
||||
#define SATURNCMD_CTRL_NORMAL_SPRITE (0x0000u) /* aligned rect */
|
||||
#define SATURNCMD_CTRL_SCALED_SPRITE (0x0001u)
|
||||
#define SATURNCMD_CTRL_DISTORTED_SPRITE (0x0002u) /* arbitrary quad */
|
||||
#define SATURNCMD_CTRL_POLYGON (0x0004u) /* solid polygon */
|
||||
#define SATURNCMD_CTRL_SYSCLIP (0x0009u) /* system clipping */
|
||||
#define SATURNCMD_CTRL_END (0x8000u) /* end of list */
|
||||
|
||||
/* PMOD draw mode */
|
||||
#define SATCMD_PMOD_TRANS (0x0000u) /* transparent pixel 0 */
|
||||
#define SATCMD_PMOD_8BPP_CBANK (0x0038u) /* 256-color, color bank */
|
||||
#define SATCMD_PMOD_ECD (0x0080u) /* extend color depth */
|
||||
#define SATCMD_PMOD_SPD (0x0040u) /* do not skip index 0 */
|
||||
#define SATURNCMD_PMOD_TRANS (0x0000u) /* transparent pixel 0 */
|
||||
#define SATURNCMD_PMOD_8BPP_CBANK (0x0038u) /* 256-color, color bank */
|
||||
#define SATURNCMD_PMOD_ECD (0x0080u) /* extend color depth */
|
||||
#define SATURNCMD_PMOD_SPD (0x0040u) /* do not skip index 0 */
|
||||
|
||||
/* ---- Texture table ------------------------------------------------------- */
|
||||
|
||||
@@ -78,27 +78,27 @@ typedef struct {
|
||||
uint16_t w, h;
|
||||
uint32_t vramByteOffset; /* byte offset into VDP1 VRAM pool */
|
||||
uint16_t cramWordOffset; /* word offset into VDP2 CRAM for palette */
|
||||
} sattexentry_t;
|
||||
} saturntexentry_t;
|
||||
|
||||
static sattexentry_t satTexTable[SAT_RTEXTURE_MAX];
|
||||
static uint16_t satTexNext = 1; /* 0 = white fallback */
|
||||
static uint32_t satTexVramUsed = 0;
|
||||
static uint16_t satTexCramUsed = 0; /* in 256-entry slots */
|
||||
static saturntexentry_t saturnTexTable[SATURN_RTEXTURE_MAX];
|
||||
static uint16_t saturnTexNext = 1; /* 0 = white fallback */
|
||||
static uint32_t saturnTexVramUsed = 0;
|
||||
static uint16_t saturnTexCramUsed = 0; /* in 256-entry slots */
|
||||
|
||||
/* ---- Command table buffer ------------------------------------------------ */
|
||||
|
||||
static satcmd_t satCmdBuf[SAT_CMDT_MAX];
|
||||
static uint16_t satCmdCount;
|
||||
static saturncmd_t saturnCmdBuf[SATURN_CMDT_MAX];
|
||||
static uint16_t saturnCmdCount;
|
||||
|
||||
/* ---- Projection state ---------------------------------------------------- */
|
||||
|
||||
static float satFovY = 0.0f; /* 0 = ortho */
|
||||
static float satAspect = 1.0f;
|
||||
static float satNearZ = 1.0f;
|
||||
static float satFarZ = 1000.0f;
|
||||
static float saturnFovY = 0.0f; /* 0 = ortho */
|
||||
static float saturnAspect = 1.0f;
|
||||
static float saturnNearZ = 1.0f;
|
||||
static float saturnFarZ = 1000.0f;
|
||||
|
||||
static float satViewEyeX = 0.0f, satViewEyeY = 0.0f, satViewEyeZ = 1.0f;
|
||||
static float satViewTgtX = 0.0f, satViewTgtY = 0.0f, satViewTgtZ = 0.0f;
|
||||
static float saturnViewEyeX = 0.0f, saturnViewEyeY = 0.0f, saturnViewEyeZ = 1.0f;
|
||||
static float saturnViewTgtX = 0.0f, saturnViewTgtY = 0.0f, saturnViewTgtZ = 0.0f;
|
||||
|
||||
/* ---- Helpers ------------------------------------------------------------- */
|
||||
|
||||
@@ -114,7 +114,7 @@ static uint16_t toRGB1555(color_t c) {
|
||||
/* Write palette into VDP2 CRAM at the texture's slot.
|
||||
* CRAM is mapped at 0x25F00000 (Saturn memory map).
|
||||
* Each palette slot is 512 bytes = 256 × uint16_t. */
|
||||
static void uploadPalette(sattexentry_t *e) {
|
||||
static void uploadPalette(saturntexentry_t *e) {
|
||||
volatile uint16_t *cram = (volatile uint16_t *)0x25F00000;
|
||||
uint32_t base = (uint32_t)e->cramWordOffset * 256u;
|
||||
for(uint32_t i = 0; i < 256; i++) {
|
||||
@@ -125,9 +125,9 @@ static void uploadPalette(sattexentry_t *e) {
|
||||
/* Copy indices row-by-row into VDP1 VRAM.
|
||||
* VDP1 VRAM is at 0x05C00000. Textures must be stored starting on an
|
||||
* 8-byte boundary; we keep our pool 8-byte aligned already. */
|
||||
static void uploadIndices(sattexentry_t *e) {
|
||||
static void uploadIndices(saturntexentry_t *e) {
|
||||
volatile uint8_t *vram =
|
||||
(volatile uint8_t *)(0x05C00000u + SAT_TEXTURE_VRAM_BASE + e->vramByteOffset);
|
||||
(volatile uint8_t *)(0x05C00000u + SATURN_TEXTURE_VRAM_BASE + e->vramByteOffset);
|
||||
uint32_t total = (uint32_t)e->w * e->h;
|
||||
for(uint32_t i = 0; i < total; i++) {
|
||||
vram[i] = e->cpuIndices[i];
|
||||
@@ -135,10 +135,10 @@ static void uploadIndices(sattexentry_t *e) {
|
||||
}
|
||||
|
||||
/* Return a fresh command slot or NULL if full. */
|
||||
static satcmd_t *allocCmd(void) {
|
||||
if(satCmdCount >= SAT_CMDT_MAX) return NULL;
|
||||
satcmd_t *c = &satCmdBuf[satCmdCount++];
|
||||
memoryZero(c, sizeof(satcmd_t));
|
||||
static saturncmd_t *allocCmd(void) {
|
||||
if(saturnCmdCount >= SATURN_CMDT_MAX) return NULL;
|
||||
saturncmd_t *c = &saturnCmdBuf[saturnCmdCount++];
|
||||
memoryZero(c, sizeof(saturncmd_t));
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -146,24 +146,24 @@ static satcmd_t *allocCmd(void) {
|
||||
|
||||
errorret_t renderSaturnInit(void) {
|
||||
logDebug("[Saturn] renderSaturnInit\n");
|
||||
memoryZero(satTexTable, sizeof(satTexTable));
|
||||
satTexNext = 1;
|
||||
satTexVramUsed = 0;
|
||||
satTexCramUsed = 0;
|
||||
satCmdCount = 0;
|
||||
memoryZero(saturnTexTable, sizeof(saturnTexTable));
|
||||
saturnTexNext = 1;
|
||||
saturnTexVramUsed = 0;
|
||||
saturnTexCramUsed = 0;
|
||||
saturnCmdCount = 0;
|
||||
|
||||
/* White 1×1 fallback: slot 0 */
|
||||
sattexentry_t *e = &satTexTable[0];
|
||||
saturntexentry_t *e = &saturnTexTable[0];
|
||||
e->cpuIndices = (uint8_t *)malloc(1);
|
||||
assertNotNull(e->cpuIndices, "Saturn: failed to allocate fallback index buffer");
|
||||
e->cpuIndices[0] = 0;
|
||||
memoryZero(e->palette, 256 * sizeof(color_t));
|
||||
e->palette[0] = COLOR_WHITE;
|
||||
e->w = 1; e->h = 1;
|
||||
e->vramByteOffset = satTexVramUsed;
|
||||
e->cramWordOffset = satTexCramUsed;
|
||||
satTexVramUsed += 8; /* 8-byte minimum alignment */
|
||||
satTexCramUsed++;
|
||||
e->vramByteOffset = saturnTexVramUsed;
|
||||
e->cramWordOffset = saturnTexCramUsed;
|
||||
saturnTexVramUsed += 8; /* 8-byte minimum alignment */
|
||||
saturnTexCramUsed++;
|
||||
uploadIndices(e);
|
||||
uploadPalette(e);
|
||||
|
||||
@@ -176,29 +176,29 @@ rtexture_t renderSaturnTextureCreate(
|
||||
uint16_t w, uint16_t h,
|
||||
const uint8_t *indices, const color_t *palette
|
||||
) {
|
||||
assertTrue(satTexNext < SAT_RTEXTURE_MAX, "Saturn texture table full");
|
||||
assertTrue(saturnTexNext < SATURN_RTEXTURE_MAX, "Saturn texture table full");
|
||||
|
||||
uint32_t byteCount = (uint32_t)w * h;
|
||||
/* Round up to 8-byte boundary for SRCA alignment. */
|
||||
uint32_t vramBytes = (byteCount + 7u) & ~7u;
|
||||
assertTrue(
|
||||
satTexVramUsed + vramBytes <= SAT_TEXTURE_VRAM_SIZE,
|
||||
saturnTexVramUsed + vramBytes <= SATURN_TEXTURE_VRAM_SIZE,
|
||||
"Saturn VDP1 texture VRAM exhausted"
|
||||
);
|
||||
|
||||
rtexture_t handle = (rtexture_t)satTexNext++;
|
||||
sattexentry_t *e = &satTexTable[handle];
|
||||
rtexture_t handle = (rtexture_t)saturnTexNext++;
|
||||
saturntexentry_t *e = &saturnTexTable[handle];
|
||||
|
||||
e->cpuIndices = (uint8_t *)malloc(byteCount);
|
||||
assertNotNull(e->cpuIndices, "Saturn: failed to allocate cpu index buffer");
|
||||
memoryCopy(e->cpuIndices, indices, byteCount);
|
||||
memoryCopy(e->palette, palette, 256 * sizeof(color_t));
|
||||
e->w = w; e->h = h;
|
||||
e->vramByteOffset = satTexVramUsed;
|
||||
e->cramWordOffset = satTexCramUsed;
|
||||
e->vramByteOffset = saturnTexVramUsed;
|
||||
e->cramWordOffset = saturnTexCramUsed;
|
||||
|
||||
satTexVramUsed += vramBytes;
|
||||
satTexCramUsed++;
|
||||
saturnTexVramUsed += vramBytes;
|
||||
saturnTexCramUsed++;
|
||||
|
||||
uploadIndices(e);
|
||||
uploadPalette(e);
|
||||
@@ -206,31 +206,31 @@ rtexture_t renderSaturnTextureCreate(
|
||||
}
|
||||
|
||||
void renderSaturnTextureDispose(rtexture_t tex) {
|
||||
if(tex == RTEXTURE_NONE || tex >= SAT_RTEXTURE_MAX) return;
|
||||
sattexentry_t *e = &satTexTable[tex];
|
||||
if(tex == RTEXTURE_NONE || tex >= SATURN_RTEXTURE_MAX) return;
|
||||
saturntexentry_t *e = &saturnTexTable[tex];
|
||||
if(e->cpuIndices) { free(e->cpuIndices); e->cpuIndices = NULL; }
|
||||
}
|
||||
|
||||
color_t *renderSaturnTextureGetPalette(rtexture_t tex) {
|
||||
if(tex == RTEXTURE_NONE || tex >= SAT_RTEXTURE_MAX) return NULL;
|
||||
return satTexTable[tex].palette;
|
||||
if(tex == RTEXTURE_NONE || tex >= SATURN_RTEXTURE_MAX) return NULL;
|
||||
return saturnTexTable[tex].palette;
|
||||
}
|
||||
|
||||
uint8_t *renderSaturnTextureGetIndices(rtexture_t tex) {
|
||||
if(tex == RTEXTURE_NONE || tex >= SAT_RTEXTURE_MAX) return NULL;
|
||||
return satTexTable[tex].cpuIndices;
|
||||
if(tex == RTEXTURE_NONE || tex >= SATURN_RTEXTURE_MAX) return NULL;
|
||||
return saturnTexTable[tex].cpuIndices;
|
||||
}
|
||||
|
||||
/* ---- Flush --------------------------------------------------------------- */
|
||||
|
||||
/* Fill in the SRCA/CMDSIZE/CMDCOLR fields from a texture handle. */
|
||||
static void cmdSetTexture(satcmd_t *cmd, rtexture_t tex) {
|
||||
sattexentry_t *e = (tex < SAT_RTEXTURE_MAX && satTexTable[tex].cpuIndices)
|
||||
? &satTexTable[tex]
|
||||
: &satTexTable[0];
|
||||
static void cmdSetTexture(saturncmd_t *cmd, rtexture_t tex) {
|
||||
saturntexentry_t *e = (tex < SATURN_RTEXTURE_MAX && saturnTexTable[tex].cpuIndices)
|
||||
? &saturnTexTable[tex]
|
||||
: &saturnTexTable[0];
|
||||
|
||||
/* SRCA = byte offset from VDP1 VRAM base / 8. */
|
||||
uint32_t srcByteAddr = SAT_TEXTURE_VRAM_BASE + e->vramByteOffset;
|
||||
uint32_t srcByteAddr = SATURN_TEXTURE_VRAM_BASE + e->vramByteOffset;
|
||||
cmd->srca = (uint16_t)(srcByteAddr / 8u);
|
||||
|
||||
/* SIZE = ((width/8) << 8) | height (each axis limited to 0-255 after /8). */
|
||||
@@ -241,18 +241,18 @@ static void cmdSetTexture(satcmd_t *cmd, rtexture_t tex) {
|
||||
uint32_t cramWordBase = (uint32_t)e->cramWordOffset * 256u;
|
||||
cmd->colr = (uint16_t)(cramWordBase / 16u);
|
||||
|
||||
cmd->pmod = SATCMD_PMOD_8BPP_CBANK; /* 256-color, index 0 transparent */
|
||||
cmd->pmod = SATURNCMD_PMOD_8BPP_CBANK; /* 256-color, index 0 transparent */
|
||||
}
|
||||
|
||||
static void flush2DSprite(const ropsprite_t *s) {
|
||||
satcmd_t *cmd = allocCmd();
|
||||
saturncmd_t *cmd = allocCmd();
|
||||
if(!cmd) return;
|
||||
|
||||
sattexentry_t *e = (s->texture < SAT_RTEXTURE_MAX && satTexTable[s->texture].cpuIndices)
|
||||
? &satTexTable[s->texture]
|
||||
: &satTexTable[0];
|
||||
saturntexentry_t *e = (s->texture < SATURN_RTEXTURE_MAX && saturnTexTable[s->texture].cpuIndices)
|
||||
? &saturnTexTable[s->texture]
|
||||
: &saturnTexTable[0];
|
||||
|
||||
cmd->ctrl = SATCMD_CTRL_NORMAL_SPRITE;
|
||||
cmd->ctrl = SATURNCMD_CTRL_NORMAL_SPRITE;
|
||||
cmd->link = 0;
|
||||
cmdSetTexture(cmd, s->texture);
|
||||
|
||||
@@ -265,7 +265,7 @@ static void flush2DSprite(const ropsprite_t *s) {
|
||||
/* UV sub-region: the VDP1 always draws the full texture, so to support
|
||||
* sprite atlases we would need a clipped intermediate. For now we treat
|
||||
* the full texture as the sprite frame (atlas sub-rect is a TODO). */
|
||||
(void)e; /* suppress unused warning for e->w/h if UV clipping is added */
|
||||
(void)e;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -277,20 +277,19 @@ static void project(
|
||||
float *sx, float *sy
|
||||
) {
|
||||
/* Translate relative to eye. */
|
||||
float rx = wx - satViewEyeX;
|
||||
float ry = wy - satViewEyeY;
|
||||
float rz = wz - satViewEyeZ;
|
||||
float rx = wx - saturnViewEyeX;
|
||||
float ry = wy - saturnViewEyeY;
|
||||
float rz = wz - saturnViewEyeZ;
|
||||
|
||||
/* Rotate view to look at target (approximated: no full matrix here). */
|
||||
/* TODO: replace with a proper view-matrix multiply for non-axis-aligned cameras. */
|
||||
float fwd_z = satViewTgtZ - satViewEyeZ;
|
||||
(void)fwd_z; /* simple pass-through for now */
|
||||
float fwd_z = saturnViewTgtZ - saturnViewEyeZ;
|
||||
(void)fwd_z;
|
||||
|
||||
float half_w = (float)DUSK_DISPLAY_WIDTH * 0.5f;
|
||||
float half_h = (float)DUSK_DISPLAY_HEIGHT * 0.5f;
|
||||
|
||||
if(satFovY > 0.0f && rz != 0.0f) {
|
||||
float focal = half_h / (satFovY * 0.5f);
|
||||
if(saturnFovY > 0.0f && rz != 0.0f) {
|
||||
float focal = half_h / (saturnFovY * 0.5f);
|
||||
*sx = half_w + (rx / rz) * focal;
|
||||
*sy = half_h - (ry / rz) * focal;
|
||||
} else {
|
||||
@@ -300,10 +299,10 @@ static void project(
|
||||
}
|
||||
|
||||
static void flush3DQuad(const ropquad3d_t *q) {
|
||||
satcmd_t *cmd = allocCmd();
|
||||
saturncmd_t *cmd = allocCmd();
|
||||
if(!cmd) return;
|
||||
|
||||
cmd->ctrl = SATCMD_CTRL_DISTORTED_SPRITE;
|
||||
cmd->ctrl = SATURNCMD_CTRL_DISTORTED_SPRITE;
|
||||
cmd->link = 0;
|
||||
cmdSetTexture(cmd, q->texture);
|
||||
|
||||
@@ -332,35 +331,37 @@ static void flush3DQuad(const ropquad3d_t *q) {
|
||||
/* Submit the finished command table to VDP1 VRAM and trigger rendering. */
|
||||
static void submitCmdTable(void) {
|
||||
/* Append end-of-list sentinel. */
|
||||
if(satCmdCount < SAT_CMDT_MAX) {
|
||||
satcmd_t *end = &satCmdBuf[satCmdCount];
|
||||
memoryZero(end, sizeof(satcmd_t));
|
||||
end->ctrl = SATCMD_CTRL_END;
|
||||
if(saturnCmdCount < SATURN_CMDT_MAX) {
|
||||
saturncmd_t *end = &saturnCmdBuf[saturnCmdCount];
|
||||
memoryZero(end, sizeof(saturncmd_t));
|
||||
end->ctrl = SATURNCMD_CTRL_END;
|
||||
}
|
||||
|
||||
/* DMA or CPU-copy the command table to VDP1 VRAM at offset 0x000000.
|
||||
* VDP1 VRAM starts at 0x05C00000 in the Saturn memory map. */
|
||||
volatile satcmd_t *vdp1CmdTable = (volatile satcmd_t *)0x05C00000u;
|
||||
uint32_t count = satCmdCount + 1u; /* include the END entry */
|
||||
volatile saturncmd_t *vdp1CmdTable = (volatile saturncmd_t *)0x05C00000u;
|
||||
uint32_t count = saturnCmdCount + 1u; /* include the END entry */
|
||||
for(uint32_t i = 0; i < count; i++) {
|
||||
vdp1CmdTable[i] = satCmdBuf[i];
|
||||
vdp1CmdTable[i] = saturnCmdBuf[i];
|
||||
}
|
||||
|
||||
/* Set VDP1 command table top address to 0x000000 (the default). */
|
||||
volatile uint16_t *vdp1Regs = (volatile uint16_t *)0x25D00000u;
|
||||
/* VDP1 MODR (Mode Register) — ensure draw mode is correct */
|
||||
vdp1Regs[0] = 0x0000; /* PTMR: plot trigger — VDP1 draws on frame change */
|
||||
/* EWDR, EWLR, EWRR — Erase/Write window (full screen) */
|
||||
vdp1Regs[2] = 0x0000; /* EWDR: erase write data (black) */
|
||||
vdp1Regs[3] = 0x0000; /* EWLR: top-left (0,0) */
|
||||
/* PTMR: plot trigger — VDP1 draws on frame change */
|
||||
vdp1Regs[0] = 0x0000;
|
||||
/* EWDR: erase write data (black) */
|
||||
vdp1Regs[2] = 0x0000;
|
||||
/* EWLR: top-left (0,0) */
|
||||
vdp1Regs[3] = 0x0000;
|
||||
/* EWRR: bottom-right */
|
||||
vdp1Regs[4] = (uint16_t)(((DUSK_DISPLAY_HEIGHT - 1) << 9) |
|
||||
((DUSK_DISPLAY_WIDTH / 2) - 1)); /* EWRR */
|
||||
((DUSK_DISPLAY_WIDTH / 2) - 1));
|
||||
}
|
||||
|
||||
errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
logDebug("[Saturn] renderSaturnFlush: count=%u\n", (unsigned)buf->count);
|
||||
|
||||
satCmdCount = 0;
|
||||
saturnCmdCount = 0;
|
||||
|
||||
uint32_t offset = 0;
|
||||
while(offset < buf->byteCount) {
|
||||
@@ -375,9 +376,9 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
cram[0] = toRGB1555(c->color);
|
||||
|
||||
/* Issue a VDP1 system clipping command to reset the clip window. */
|
||||
satcmd_t *clip = allocCmd();
|
||||
saturncmd_t *clip = allocCmd();
|
||||
if(clip) {
|
||||
clip->ctrl = SATCMD_CTRL_SYSCLIP;
|
||||
clip->ctrl = SATURNCMD_CTRL_SYSCLIP;
|
||||
clip->link = 0;
|
||||
clip->xa = 0;
|
||||
clip->ya = 0;
|
||||
@@ -393,21 +394,21 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
|
||||
case ROP_SET_PROJECTION: {
|
||||
const ropprojection_t *p = (const ropprojection_t *)hdr;
|
||||
satFovY = fixedToFloat(p->fovY);
|
||||
satAspect = fixedToFloat(p->aspect);
|
||||
satNearZ = fixedToFloat(p->nearZ);
|
||||
satFarZ = fixedToFloat(p->farZ);
|
||||
saturnFovY = fixedToFloat(p->fovY);
|
||||
saturnAspect = fixedToFloat(p->aspect);
|
||||
saturnNearZ = fixedToFloat(p->nearZ);
|
||||
saturnFarZ = fixedToFloat(p->farZ);
|
||||
break;
|
||||
}
|
||||
|
||||
case ROP_SET_VIEW: {
|
||||
const ropview_t *v = (const ropview_t *)hdr;
|
||||
satViewEyeX = (float)v->eyeX;
|
||||
satViewEyeY = (float)v->eyeY;
|
||||
satViewEyeZ = (float)v->eyeZ;
|
||||
satViewTgtX = (float)v->tgtX;
|
||||
satViewTgtY = (float)v->tgtY;
|
||||
satViewTgtZ = (float)v->tgtZ;
|
||||
saturnViewEyeX = (float)v->eyeX;
|
||||
saturnViewEyeY = (float)v->eyeY;
|
||||
saturnViewEyeZ = (float)v->eyeZ;
|
||||
saturnViewTgtX = (float)v->tgtX;
|
||||
saturnViewTgtY = (float)v->tgtY;
|
||||
saturnViewTgtZ = (float)v->tgtZ;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -436,11 +437,11 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
/* ---- Dispose ------------------------------------------------------------- */
|
||||
|
||||
void renderSaturnDispose(void) {
|
||||
for(uint16_t i = 0; i < satTexNext; i++) {
|
||||
sattexentry_t *e = &satTexTable[i];
|
||||
for(uint16_t i = 0; i < saturnTexNext; i++) {
|
||||
saturntexentry_t *e = &saturnTexTable[i];
|
||||
if(e->cpuIndices) { free(e->cpuIndices); e->cpuIndices = NULL; }
|
||||
}
|
||||
satTexNext = 1;
|
||||
satTexVramUsed = 0;
|
||||
satTexCramUsed = 0;
|
||||
saturnTexNext = 1;
|
||||
saturnTexVramUsed = 0;
|
||||
saturnTexCramUsed = 0;
|
||||
}
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/rendersat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/inputsaturn.c
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "inputsaturn.h"
|
||||
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
typedef inputgamepadbuttonsaturn_t inputgamepadbuttonplatform_t;
|
||||
typedef inputgamepadaxissaturn_t inputgamepadaxissplatform_t;
|
||||
#endif
|
||||
|
||||
typedef inputsaturn_t inputplatform_t;
|
||||
|
||||
#define inputInitPlatform inputInitSaturn
|
||||
#define inputUpdatePlatform inputUpdateSaturn
|
||||
#define inputButtonGetValuePlatform inputButtonGetValueSaturn
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include "assert/assert.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
/*
|
||||
* Saturn standard digital pad buttons.
|
||||
* gpButton values are bit positions within the SMPC peripheral held.button word.
|
||||
*
|
||||
* bit 11 = Right bit 10 = Left bit 9 = Down bit 8 = Up
|
||||
* bit 7 = Start bit 6 = A bit 5 = C bit 4 = B
|
||||
* bit 3 = R bit 2 = X bit 1 = Y bit 0 = Z
|
||||
*
|
||||
* The Saturn has no L shoulder in the standard pad (only R); the 3D pad
|
||||
* exposes L as bit 15 via the analog extension.
|
||||
*/
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } },
|
||||
{ .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } },
|
||||
{ .name = "c", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 5 } },
|
||||
{ .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 2 } },
|
||||
{ .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 1 } },
|
||||
{ .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 0 } },
|
||||
{ .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 7 } },
|
||||
{ .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 8 } },
|
||||
{ .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 9 } },
|
||||
{ .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 10 } },
|
||||
{ .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 11 } },
|
||||
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 15 } },
|
||||
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 3 } },
|
||||
{ .name = "accept", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } }, /* A */
|
||||
{ .name = "cancel", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } }, /* B */
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
errorret_t inputInitSaturn(void) {
|
||||
#define X(buttonName, buttonAction) \
|
||||
inputBind(inputButtonGetByName(buttonName), buttonAction);
|
||||
X("up", INPUT_ACTION_UP);
|
||||
X("down", INPUT_ACTION_DOWN);
|
||||
X("left", INPUT_ACTION_LEFT);
|
||||
X("right", INPUT_ACTION_RIGHT);
|
||||
X("accept", INPUT_ACTION_ACCEPT);
|
||||
X("cancel", INPUT_ACTION_CANCEL);
|
||||
X("start", INPUT_ACTION_RAGEQUIT);
|
||||
#undef X
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void inputUpdateSaturn(void) {
|
||||
smpc_peripheral_digital_t digital;
|
||||
smpc_peripheral_digital_port(1, &digital);
|
||||
/* Yaul's held.button bitmask matches the bit-position layout above. */
|
||||
memcpy(&INPUT.platform.padButtons, &digital.held.button, sizeof(INPUT.platform.padButtons));
|
||||
}
|
||||
|
||||
int16_t inputButtonGetValueSaturn(const inputbutton_t button) {
|
||||
switch(button.type) {
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD:
|
||||
return (INPUT.platform.padButtons & (uint16_t)(1u << button.gpButton))
|
||||
? INT16_MAX : 0;
|
||||
default:
|
||||
assertUnreachable("Unknown input button type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t inputGetDeadzoneSaturn(const inputbutton_t button) {
|
||||
(void)button;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "error/error.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
/*
|
||||
* Saturn standard digital pad buttons (smpc_peripheral_digital_t).
|
||||
* Yaul's smpc_peripheral_digital_port() fills the bitmask in held.button.
|
||||
*
|
||||
* Button bitmask:
|
||||
* bit 11 = Right bit 10 = Left bit 9 = Down bit 8 = Up
|
||||
* bit 7 = Start bit 6 = A bit 5 = C bit 4 = B
|
||||
* bit 3 = R bit 2 = X bit 1 = Y bit 0 = Z
|
||||
*/
|
||||
|
||||
/* gpButton stores the bit-position within the SMPC button word */
|
||||
typedef uint8_t inputgamepadbuttonsaturn_t;
|
||||
|
||||
/* Saturn's standard pad is digital-only; define a stub axis type */
|
||||
typedef enum {
|
||||
SATURN_GAMEPAD_AXIS_COUNT = 0
|
||||
} inputgamepadaxissaturn_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t padButtons; /* SMPC digital pad held-button bitmask, port 1 */
|
||||
} inputsaturn_t;
|
||||
|
||||
/* Forward declaration — avoids circular include with inputbutton.h */
|
||||
typedef struct inputbutton_s inputbutton_t;
|
||||
|
||||
errorret_t inputInitSaturn(void);
|
||||
void inputUpdateSaturn(void);
|
||||
int16_t inputButtonGetValueSaturn(const inputbutton_t button);
|
||||
int16_t inputGetDeadzoneSaturn(const inputbutton_t button);
|
||||
@@ -12,8 +12,9 @@
|
||||
* On Saturn, stdout goes to the debug serial port (via Yaul's dbgio module).
|
||||
* With a comm link or emulator (Mednafen, SSF) this is visible on the host.
|
||||
*
|
||||
* TODO: add dbgio_init() in systemSaturnInit() and replace vprintf with
|
||||
* dbgio_printf() for hardware-accurate serial output.
|
||||
* TODO: add dbgio_dev_default_set(DBGIO_DEV_USB_CART, NULL) in
|
||||
* systemSaturnInit() and replace vprintf with dbgio_printf() for
|
||||
* hardware-accurate serial output.
|
||||
*/
|
||||
|
||||
void logDebug(const char_t *message, ...) {
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/networksaturn.c
|
||||
)
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "networksat.h"
|
||||
#include "networksaturn.h"
|
||||
|
||||
#define networkPlatformInit networkSaturnInit
|
||||
#define networkPlatformUpdate networkSaturnUpdate
|
||||
@@ -16,4 +16,4 @@
|
||||
#define networkPlatformRequestDisconnection networkSaturnRequestDisconnection
|
||||
#define networkPlatformGetInfo networkSaturnGetInfo
|
||||
|
||||
typedef networksat_t networkplatform_t;
|
||||
typedef networksaturn_t networkplatform_t;
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
typedef struct {
|
||||
uint8_t unused;
|
||||
} networksat_t;
|
||||
} networksaturn_t;
|
||||
|
||||
errorret_t networkSaturnInit(void);
|
||||
errorret_t networkSaturnUpdate(void);
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/savesaturn.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/savestreamsaturn.c
|
||||
)
|
||||
@@ -6,11 +6,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "save/savesat.h"
|
||||
#include "save/savestreamsat.h"
|
||||
#include "save/savesaturn.h"
|
||||
#include "save/savestreamsaturn.h"
|
||||
|
||||
typedef savesat_t saveplatform_t;
|
||||
typedef savestreamsat_t saveplatformstream_t;
|
||||
typedef savesaturn_t saveplatform_t;
|
||||
typedef savestreamsaturn_t saveplatformstream_t;
|
||||
|
||||
#define saveInitPlatform saveInitSaturn
|
||||
#define saveDisposePlatform saveDisposeSaturn
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savesaturn.h"
|
||||
#include "log/log.h"
|
||||
#include <bup.h>
|
||||
|
||||
static bup_t _bupState;
|
||||
|
||||
errorret_t saveInitSaturn(void) {
|
||||
logDebug("[Saturn] saveInitSaturn\n");
|
||||
bup_devices_t devices;
|
||||
bup_init(&_bupState, &devices);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDisposeSaturn(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeleteSaturn(const uint8_t slot) {
|
||||
logDebug("[Saturn] saveDeleteSaturn: slot=%u\n", (unsigned)slot);
|
||||
/* TODO: construct filename for slot and call bup_delete:
|
||||
* char filename[8];
|
||||
* snprintf(filename, sizeof(filename), "%s%02u", SAVE_SATURN_TITLE_ID, slot);
|
||||
* bup_delete(BUP_DEV_INTERNAL, filename);
|
||||
*/
|
||||
errorOk();
|
||||
}
|
||||
@@ -15,13 +15,13 @@
|
||||
* the save file name (e.g. "DUSK00", "DUSK01", …).
|
||||
*/
|
||||
|
||||
#ifndef SAVE_SAT_TITLE_ID
|
||||
#define SAVE_SAT_TITLE_ID "DUSK"
|
||||
#ifndef SAVE_SATURN_TITLE_ID
|
||||
#define SAVE_SATURN_TITLE_ID "DUSK"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t unused;
|
||||
} savesat_t;
|
||||
} savesaturn_t;
|
||||
|
||||
errorret_t saveInitSaturn(void);
|
||||
errorret_t saveDisposeSaturn(void);
|
||||
@@ -6,64 +6,69 @@
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savestreamsat.h"
|
||||
#include "save/savestreamsaturn.h"
|
||||
#include "util/memory.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <bup.h>
|
||||
|
||||
/*
|
||||
* Saturn backup RAM (bup) does not support partial reads/seeks; data must
|
||||
* be read or written as a single contiguous block. We buffer the entire
|
||||
* save slot in heap memory and serialize to/from the bup device on open/close.
|
||||
*
|
||||
* Maximum save size = sizeof(savefile_t). Adjust SAVE_SAT_MAX if needed.
|
||||
* Maximum save size = sizeof(savefile_t). Adjust SAVE_SATURN_MAX if needed.
|
||||
*/
|
||||
|
||||
#define SAVE_SAT_MAX sizeof(savefile_t)
|
||||
#define SAVE_SATURN_MAX sizeof(savefile_t)
|
||||
|
||||
errorret_t saveStreamOpenReadSaturn(
|
||||
savestreamsat_t *p, bool_t *found, const uint8_t slot
|
||||
savestreamsaturn_t *p, bool_t *found, const uint8_t slot
|
||||
) {
|
||||
p->buf = (uint8_t *)malloc(SAVE_SAT_MAX);
|
||||
p->buf = (uint8_t *)malloc(SAVE_SATURN_MAX);
|
||||
if(!p->buf) errorThrow("Saturn: failed to allocate save read buffer");
|
||||
p->size = SAVE_SAT_MAX;
|
||||
p->size = SAVE_SATURN_MAX;
|
||||
p->pos = 0;
|
||||
p->slot = slot;
|
||||
p->writing = false;
|
||||
|
||||
/*
|
||||
* TODO: read from bup device into p->buf:
|
||||
* int32_t ret = bup_read(BUP_DEV_INTERNAL, filename, p->buf, SAVE_SAT_MAX);
|
||||
/* TODO: construct filename and read from bup device:
|
||||
* char filename[8];
|
||||
* snprintf(filename, sizeof(filename), "%s%02u", SAVE_SATURN_TITLE_ID, slot);
|
||||
* int32_t ret = bup_read(BUP_DEV_INTERNAL, filename, p->buf, SAVE_SATURN_MAX);
|
||||
* *found = (ret >= 0);
|
||||
*/
|
||||
*found = false; /* stub: always report no save */
|
||||
*found = false;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsat_t *p, const uint8_t slot) {
|
||||
p->buf = (uint8_t *)malloc(SAVE_SAT_MAX);
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsaturn_t *p, const uint8_t slot) {
|
||||
p->buf = (uint8_t *)malloc(SAVE_SATURN_MAX);
|
||||
if(!p->buf) errorThrow("Saturn: failed to allocate save write buffer");
|
||||
memoryZero(p->buf, SAVE_SAT_MAX);
|
||||
p->size = SAVE_SAT_MAX;
|
||||
memoryZero(p->buf, SAVE_SATURN_MAX);
|
||||
p->size = SAVE_SATURN_MAX;
|
||||
p->pos = 0;
|
||||
p->slot = slot;
|
||||
p->writing = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void saveStreamCloseSaturn(savestreamsat_t *p) {
|
||||
void saveStreamCloseSaturn(savestreamsaturn_t *p) {
|
||||
if(p->writing && p->buf) {
|
||||
/*
|
||||
* TODO: write p->buf to bup device:
|
||||
/* TODO: write p->buf to bup device:
|
||||
* char filename[8];
|
||||
* snprintf(filename, sizeof(filename), "%s%02u", SAVE_SATURN_TITLE_ID, p->slot);
|
||||
* bup_dir_t dir;
|
||||
* bup_write(BUP_DEV_INTERNAL, &dir, p->buf, SAVE_SAT_MAX, BUP_MODE_NEW);
|
||||
* memset(&dir, 0, sizeof(dir));
|
||||
* strncpy(dir.filename, filename, sizeof(dir.filename));
|
||||
* bup_write(BUP_DEV_INTERNAL, &dir, p->buf, SAVE_SATURN_MAX, BUP_MODE_NEW);
|
||||
*/
|
||||
}
|
||||
if(p->buf) { free(p->buf); p->buf = NULL; }
|
||||
}
|
||||
|
||||
errorret_t saveStreamReadBytesSaturn(
|
||||
savestreamsat_t *p, void *buf, const size_t len
|
||||
savestreamsaturn_t *p, void *buf, const size_t len
|
||||
) {
|
||||
if(p->pos + len > p->size) errorThrow("Saturn: read past end of save buffer");
|
||||
memoryCopy(buf, p->buf + p->pos, len);
|
||||
@@ -72,7 +77,7 @@ errorret_t saveStreamReadBytesSaturn(
|
||||
}
|
||||
|
||||
errorret_t saveStreamWriteBytesSaturn(
|
||||
savestreamsat_t *p, const void *buf, const size_t len
|
||||
savestreamsaturn_t *p, const void *buf, const size_t len
|
||||
) {
|
||||
if(p->pos + len > p->size) errorThrow("Saturn: write past end of save buffer");
|
||||
memoryCopy(p->buf + p->pos, buf, len);
|
||||
@@ -80,7 +85,7 @@ errorret_t saveStreamWriteBytesSaturn(
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamSeekSaturn(savestreamsat_t *p, const size_t pos) {
|
||||
errorret_t saveStreamSeekSaturn(savestreamsaturn_t *p, const size_t pos) {
|
||||
if(pos > p->size) errorThrow("Saturn: seek out of bounds");
|
||||
p->pos = pos;
|
||||
errorOk();
|
||||
@@ -16,17 +16,17 @@ typedef struct {
|
||||
size_t pos;
|
||||
uint8_t slot;
|
||||
bool_t writing;
|
||||
} savestreamsat_t;
|
||||
} savestreamsaturn_t;
|
||||
|
||||
errorret_t saveStreamOpenReadSaturn(
|
||||
savestreamsat_t *p, bool_t *found, const uint8_t slot
|
||||
savestreamsaturn_t *p, bool_t *found, const uint8_t slot
|
||||
);
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsat_t *p, const uint8_t slot);
|
||||
void saveStreamCloseSaturn(savestreamsat_t *p);
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsaturn_t *p, const uint8_t slot);
|
||||
void saveStreamCloseSaturn(savestreamsaturn_t *p);
|
||||
errorret_t saveStreamReadBytesSaturn(
|
||||
savestreamsat_t *p, void *buf, const size_t len
|
||||
savestreamsaturn_t *p, void *buf, const size_t len
|
||||
);
|
||||
errorret_t saveStreamWriteBytesSaturn(
|
||||
savestreamsat_t *p, const void *buf, const size_t len
|
||||
savestreamsaturn_t *p, const void *buf, const size_t len
|
||||
);
|
||||
errorret_t saveStreamSeekSaturn(savestreamsat_t *p, const size_t pos);
|
||||
errorret_t saveStreamSeekSaturn(savestreamsaturn_t *p, const size_t pos);
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "moduleplatformsat.h"
|
||||
#include "moduleplatformsaturn.h"
|
||||
|
||||
#define modulePlatformPlatform modulePlatformSaturn
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/systemsaturn.c
|
||||
)
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "system/systemsat.h"
|
||||
#include "system/systemsaturn.h"
|
||||
|
||||
#define systemInitPlatform systemInitSaturn
|
||||
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeSaturn
|
||||
@@ -5,16 +5,14 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "system/systemsat.h"
|
||||
#include "system/systemsaturn.h"
|
||||
#include "log/log.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
errorret_t systemInitSaturn(void) {
|
||||
logDebug("[Saturn] systemInitSaturn\n");
|
||||
/*
|
||||
* TODO: initialize SMPC peripheral scanning so input reads work.
|
||||
* smpc_peripheral_init();
|
||||
* smpc_peripheral_intback_issue();
|
||||
*/
|
||||
smpc_peripheral_init();
|
||||
smpc_peripheral_intback_issue();
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/networksat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/timesaturn.c
|
||||
)
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "time/timesat.h"
|
||||
#include "time/timesaturn.h"
|
||||
|
||||
#define timeTickPlatform timeTickSaturn
|
||||
#define timeGetDeltaPlatform timeGetDeltaSaturn
|
||||
#define timeGetRealPlatform timeGetRealSaturn
|
||||
#define timeTickPlatform timeTickSaturn
|
||||
#define timeGetDeltaPlatform timeGetDeltaSaturn
|
||||
#define timeGetRealPlatform timeGetRealSaturn
|
||||
#define timeGetRealTimeZonePlatform timeGetRealTimeZoneSaturn
|
||||
@@ -5,34 +5,33 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "time/timesat.h"
|
||||
#include "time/timesaturn.h"
|
||||
#include <smpc/smc.h>
|
||||
|
||||
#define SAT_FPS 60.0
|
||||
#define SATURN_FPS 60.0
|
||||
|
||||
static double_t satTimeLast = 0.0;
|
||||
static double_t satTimeDelta = 0.0;
|
||||
static double_t satTimeAcc = 0.0; /* accumulated seconds (frame counter) */
|
||||
static double_t saturnTimeLast = 0.0;
|
||||
static double_t saturnTimeDelta = 0.0;
|
||||
static double_t saturnTimeAcc = 0.0; /* accumulated seconds (frame counter) */
|
||||
|
||||
void timeTickSaturn(void) {
|
||||
double_t now = satTimeAcc + (1.0 / SAT_FPS);
|
||||
satTimeDelta = now - satTimeAcc;
|
||||
satTimeLast = satTimeAcc;
|
||||
satTimeAcc = now;
|
||||
double_t now = saturnTimeAcc + (1.0 / SATURN_FPS);
|
||||
saturnTimeDelta = now - saturnTimeAcc;
|
||||
saturnTimeLast = saturnTimeAcc;
|
||||
saturnTimeAcc = now;
|
||||
}
|
||||
|
||||
double_t timeGetDeltaSaturn(void) {
|
||||
return satTimeDelta;
|
||||
return saturnTimeDelta;
|
||||
}
|
||||
|
||||
double_t timeGetRealSaturn(void) {
|
||||
/*
|
||||
* TODO: read the SMPC RTC for actual wall-clock time:
|
||||
/* TODO: read the SMPC RTC for actual wall-clock time:
|
||||
* smpc_rtc_t rtc;
|
||||
* smpc_smc_rtc_read(&rtc);
|
||||
* return rtcToUnixSeconds(&rtc);
|
||||
*/
|
||||
return satTimeAcc;
|
||||
return saturnTimeAcc;
|
||||
}
|
||||
|
||||
double_t timeGetRealTimeZoneSaturn(void) {
|
||||
@@ -25,8 +25,8 @@ void inputUpdateSDL2(void) {
|
||||
|
||||
#ifdef DUSK_INPUT_POINTER
|
||||
case SDL_MOUSEWHEEL:
|
||||
INPUT.platform.scrollX = (float_t)e.wheel.x;
|
||||
INPUT.platform.scrollY = (float_t)e.wheel.y;
|
||||
INPUT.platform.scrollX = e.wheel.x > 0 ? INT16_MAX : (e.wheel.x < 0 ? -INT16_MAX : 0);
|
||||
INPUT.platform.scrollY = e.wheel.y > 0 ? INT16_MAX : (e.wheel.y < 0 ? -INT16_MAX : 0);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -61,16 +61,16 @@ void inputUpdateSDL2(void) {
|
||||
int_t windowWidth, windowHeight;
|
||||
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
|
||||
|
||||
INPUT.platform.mouseX = (float_t)pointerX / (float_t)windowWidth;
|
||||
INPUT.platform.mouseY = (float_t)pointerY / (float_t)windowHeight;
|
||||
INPUT.platform.mouseX = (int16_t)((float_t)pointerX / (float_t)windowWidth * INT16_MAX);
|
||||
INPUT.platform.mouseY = (int16_t)((float_t)pointerY / (float_t)windowHeight * INT16_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
int16_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
switch(button.type) {
|
||||
#ifdef DUSK_INPUT_KEYBOARD
|
||||
case INPUT_BUTTON_TYPE_KEYBOARD: {
|
||||
return INPUT.platform.keyboardState[button.scancode] ? 1.0f : 0.0f;
|
||||
return INPUT.platform.keyboardState[button.scancode] ? INT16_MAX : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -84,7 +84,7 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
return INPUT.platform.mouseY;
|
||||
|
||||
case INPUT_POINTER_AXIS_Z:
|
||||
return 0.0f; // Not supported in SDL2
|
||||
return 0;
|
||||
|
||||
case INPUT_POINTER_AXIS_WHEEL_X:
|
||||
return INPUT.platform.scrollX;
|
||||
@@ -94,7 +94,7 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
|
||||
default:
|
||||
assertUnreachable("Unknown pointer axis");
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -106,30 +106,24 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
"Gamepad button out of range"
|
||||
);
|
||||
|
||||
if(SDL_GameControllerGetButton(
|
||||
return SDL_GameControllerGetButton(
|
||||
INPUT.platform.controller, button.gpButton
|
||||
)) {
|
||||
return 1.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
) ? INT16_MAX : 0;
|
||||
}
|
||||
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD_AXIS: {
|
||||
float_t value = 0.0f;
|
||||
|
||||
Sint16 axis = SDL_GameControllerGetAxis(
|
||||
Sint16 raw = SDL_GameControllerGetAxis(
|
||||
INPUT.platform.controller, button.gpAxis.axis
|
||||
);
|
||||
value = (float_t)axis / 32767.0f;
|
||||
if(!button.gpAxis.positive) value = -value;
|
||||
value = inputDeadzone(value, inputGetDeadzoneSDL2(button));
|
||||
return value;
|
||||
int16_t value = button.gpAxis.positive ? raw : (int16_t)-raw;
|
||||
if(value < 0) return 0;
|
||||
return inputDeadzone(value, inputGetDeadzoneSDL2(button));
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
assertUnreachable("Unknown input button type");
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ typedef struct {
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_INPUT_POINTER
|
||||
float_t mouseX, mouseY;
|
||||
float_t scrollX, scrollY;
|
||||
int16_t mouseX, mouseY;
|
||||
int16_t scrollX, scrollY;
|
||||
#endif
|
||||
} inputsdl2_t;
|
||||
|
||||
@@ -69,18 +69,17 @@ typedef inputsdl2_t inputplatform_t;
|
||||
void inputUpdateSDL2(void);
|
||||
|
||||
/**
|
||||
* Returns the deadzone for the given gamepad axis. Requires implementation by
|
||||
* the host platform.
|
||||
*
|
||||
* Returns the deadzone for the given gamepad axis (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the deadzone of.
|
||||
* @return The deadzone for the given gamepad axis.
|
||||
* @return The deadzone threshold (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button);
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button);
|
||||
|
||||
/**
|
||||
* Returns the input value (between 0 and 1) of the given button.
|
||||
*
|
||||
* Returns the input value of the given button (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the value of.
|
||||
* @return The value of the button, between 0 and 1.
|
||||
* @return The value of the button (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputButtonGetValueSDL2(const inputbutton_t button);
|
||||
int16_t inputButtonGetValueSDL2(const inputbutton_t button);
|
||||
@@ -82,6 +82,6 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.17f;
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return (int16_t)(0.17f * INT16_MAX);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user