aturn
This commit is contained in:
@@ -157,6 +157,8 @@ add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_SAT_CD_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_SAT_AUDIO_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${DUSK_SAT_BIN}" "${DUSK_SAT_CD_DIR}/A.BIN"
|
||||
# Asset archive — read at runtime via cdfs + zip_source_buffer
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${DUSK_ASSETS_ZIP}" "${DUSK_SAT_CD_DIR}/DUSK.DSK"
|
||||
# ISO 9660 requires these auxiliary text files
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${DUSK_SAT_CD_DIR}/ABS.TXT"
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${DUSK_SAT_CD_DIR}/BIB.TXT"
|
||||
|
||||
@@ -40,7 +40,6 @@ RUN apt-get update && apt-get install -y \
|
||||
libgmp-dev \
|
||||
libmpfr-dev \
|
||||
libmpc-dev \
|
||||
xorriso \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p "${YAUL_INSTALL_ROOT}"
|
||||
@@ -358,5 +357,12 @@ RUN wget -q https://github.com/nih-at/libzip/releases/download/v1.10.1/libzip-1.
|
||||
&& rm -rf /tmp/libzip-1.10.1 /tmp/libzip-build /tmp/sat-xc.cmake \
|
||||
; rm -f /tmp/libzip.tar.gz /tmp/zlib.tar.gz 2>/dev/null ; true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 15. Disc-image tools (separate layer so it does not invalidate the
|
||||
# expensive cross-compiler cache layers above on changes).
|
||||
# xorriso provides xorrisofs, needed by Yaul's make-iso script.
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN apt-get update && apt-get install -y xorriso && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /workdir
|
||||
VOLUME ["/workdir"]
|
||||
|
||||
@@ -35,8 +35,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
errorChain(sceneInit());
|
||||
|
||||
consolePrint("Engine initialized");
|
||||
// sceneSet(SCENE_TYPE_SPINNINGBOX);
|
||||
sceneSet(SCENE_TYPE_SPINNINGBOX);
|
||||
sceneSet(SCENE_TYPE_RAINBOWNOTHING);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
+2
-33
@@ -161,39 +161,8 @@ 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 (int16_t)(inputGetCurrentValue(pos) - inputGetCurrentValue(neg));
|
||||
}
|
||||
|
||||
void inputAxis2D(
|
||||
const inputaction_t negX, const inputaction_t posX,
|
||||
const inputaction_t negY, const inputaction_t posY,
|
||||
vec2 result
|
||||
) {
|
||||
assertNotNull(result, "Result vector cannot be null");
|
||||
result[0] = (float_t)inputAxis(negX, posX) / (float_t)INT16_MAX;
|
||||
result[1] = (float_t)inputAxis(negY, posY) / (float_t)INT16_MAX;
|
||||
}
|
||||
|
||||
void inputAngle2D(
|
||||
const inputaction_t negX, const inputaction_t posX,
|
||||
const inputaction_t negY, const inputaction_t posY,
|
||||
vec2 result
|
||||
) {
|
||||
assertNotNull(result, "Result vector cannot be null");
|
||||
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;
|
||||
result[1] = 0.0f;
|
||||
return;
|
||||
}
|
||||
if(mag > 1.0f) {
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
}
|
||||
result[0] = x;
|
||||
result[1] = y;
|
||||
int32_t raw = (int32_t)inputGetCurrentValue(pos) - (int32_t)inputGetCurrentValue(neg);
|
||||
return (int16_t)mathClamp(raw, -INT16_MAX, INT16_MAX);
|
||||
}
|
||||
|
||||
void inputBind(const inputbutton_t button, const inputaction_t act) {
|
||||
|
||||
@@ -110,37 +110,6 @@ bool_t inputReleased(const inputaction_t action);
|
||||
*/
|
||||
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
|
||||
* and positive for each axis).
|
||||
*
|
||||
* @param negX The action representing the negative direction of the X axis.
|
||||
* @param posX The action representing the positive direction of the X axis.
|
||||
* @param negY The action representing the negative direction of the Y axis.
|
||||
* @param posY The action representing the positive direction of the Y axis.
|
||||
* @param result A vec2 to store the resulting axis values (-1.0f to 1.0f).
|
||||
*/
|
||||
void inputAxis2D(
|
||||
const inputaction_t negX, const inputaction_t posX,
|
||||
const inputaction_t negY, const inputaction_t posY,
|
||||
vec2 result
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an angled 2D vector based on the input of 4 actions. Functionally
|
||||
* using atan2 to get the angle of the input then multiplying by a unit vector.
|
||||
*
|
||||
* @param negX The action representing the negative direction of the X axis.
|
||||
* @param posX The action representing the positive direction of the X axis.
|
||||
* @param negY The action representing the negative direction of the Y axis.
|
||||
* @param posY The action representing the positive direction of the Y axis.
|
||||
* @param result A vec2 to store the resulting axis values (-1.0f to 1.0f).
|
||||
*/
|
||||
void inputAngle2D(
|
||||
const inputaction_t negX, const inputaction_t posX,
|
||||
const inputaction_t negY, const inputaction_t posY,
|
||||
vec2 result
|
||||
);
|
||||
|
||||
/**
|
||||
* Binds an input button to an action.
|
||||
|
||||
@@ -10,8 +10,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(full)
|
||||
add_subdirectory(overworld)
|
||||
add_subdirectory(rainbownothing)
|
||||
add_subdirectory(spinningbox)
|
||||
add_subdirectory(white32)
|
||||
add_subdirectory(testscenes)
|
||||
@@ -10,11 +10,11 @@
|
||||
scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
||||
[SCENE_TYPE_NULL] = { 0 },
|
||||
|
||||
[SCENE_TYPE_FULL] = {
|
||||
.init = sceneFullInit,
|
||||
.update = sceneFullUpdate,
|
||||
.render = sceneFullRender,
|
||||
.dispose = sceneFullDispose
|
||||
[SCENE_TYPE_INPUT] = {
|
||||
.init = sceneInputInit,
|
||||
.update = sceneInputUpdate,
|
||||
.render = sceneInputRender,
|
||||
.dispose = sceneInputDispose
|
||||
},
|
||||
|
||||
[SCENE_TYPE_OVERWORLD] = {
|
||||
@@ -24,6 +24,13 @@ scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
||||
.dispose = sceneOverworldDispose
|
||||
},
|
||||
|
||||
[SCENE_TYPE_FULL] = {
|
||||
.init = sceneFullInit,
|
||||
.update = sceneFullUpdate,
|
||||
.render = sceneFullRender,
|
||||
.dispose = sceneFullDispose
|
||||
},
|
||||
|
||||
[SCENE_TYPE_RAINBOWNOTHING] = {
|
||||
.init = sceneRainbowNothingInit,
|
||||
.update = sceneRainbowNothingUpdate,
|
||||
@@ -38,6 +45,13 @@ scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
||||
.dispose = sceneSpinningBoxDispose
|
||||
},
|
||||
|
||||
[SCENE_TYPE_TEST] = {
|
||||
.init = sceneTestInit,
|
||||
.update = sceneTestUpdate,
|
||||
.render = sceneTestRender,
|
||||
.dispose = sceneTestDispose
|
||||
},
|
||||
|
||||
[SCENE_TYPE_WHITE32] = {
|
||||
.init = sceneWhite32Init,
|
||||
.update = sceneWhite32Update,
|
||||
|
||||
@@ -7,17 +7,21 @@
|
||||
|
||||
#pragma once
|
||||
#include "scene/scenebase.h"
|
||||
#include "scene/full/scenefull.h"
|
||||
#include "scene/testscenes/inputscene/sceneinput.h"
|
||||
#include "scene/overworld/sceneoverworld.h"
|
||||
#include "scene/rainbownothing/scenerainbownothing.h"
|
||||
#include "scene/spinningbox/scenespinningbox.h"
|
||||
#include "scene/white32/scenewhite32.h"
|
||||
#include "scene/testscenes/full/scenefull.h"
|
||||
#include "scene/testscenes/rainbownothing/scenerainbownothing.h"
|
||||
#include "scene/testscenes/spinningbox/scenespinningbox.h"
|
||||
#include "scene/testscenes/test/scenetest.h"
|
||||
#include "scene/testscenes/white32/scenewhite32.h"
|
||||
|
||||
typedef union scenedata_u {
|
||||
scenefull_t full;
|
||||
sceneinput_t input;
|
||||
sceneoverworld_t overworld;
|
||||
scenefull_t full;
|
||||
scenerainbownothing_t rainbownothing;
|
||||
scenespinningbox_t spinningbox;
|
||||
scenetest_t test;
|
||||
scenewhite32_t white32;
|
||||
} scenedata_t;
|
||||
|
||||
@@ -32,12 +36,14 @@ typedef struct {
|
||||
|
||||
typedef enum {
|
||||
SCENE_TYPE_NULL,
|
||||
SCENE_TYPE_FULL,
|
||||
SCENE_TYPE_INPUT,
|
||||
SCENE_TYPE_OVERWORLD,
|
||||
SCENE_TYPE_FULL,
|
||||
SCENE_TYPE_RAINBOWNOTHING,
|
||||
SCENE_TYPE_SPINNINGBOX,
|
||||
SCENE_TYPE_TEST,
|
||||
SCENE_TYPE_WHITE32,
|
||||
SCENE_TYPE_COUNT
|
||||
} scenetype_t;
|
||||
|
||||
extern scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT];
|
||||
extern scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
add_subdirectory(inputscene)
|
||||
add_subdirectory(full)
|
||||
add_subdirectory(rainbownothing)
|
||||
add_subdirectory(spinningbox)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(white32)
|
||||
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scene/full/scenefull.h"
|
||||
#include "scene/testscenes/full/scenefull.h"
|
||||
#include "assert/assert.h"
|
||||
#include "display/render/render.h"
|
||||
#include "display/screen.h"
|
||||
@@ -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_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
sceneinput.c
|
||||
)
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scene/scenetype.h"
|
||||
#include "assert/assert.h"
|
||||
#include "display/render/render.h"
|
||||
#include "display/screen.h"
|
||||
#include "display/color.h"
|
||||
#include "input/input.h"
|
||||
#include "time/time.h"
|
||||
#include "util/short.h"
|
||||
|
||||
// Rotations per second at full stick deflection.
|
||||
#define INPUT_SCENE_ROTATE_SPEED 0.5f
|
||||
|
||||
static const uint8_t BOX_INDICES[3 * 3] = {
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
6, 7, 8,
|
||||
};
|
||||
|
||||
static const color_t BOX_PALETTE[256] = {
|
||||
[0] = { 255, 0, 0, 255 },
|
||||
[1] = { 0, 255, 0, 255 },
|
||||
[2] = { 0, 0, 255, 255 },
|
||||
[3] = { 255, 255, 0, 255 },
|
||||
[4] = { 255, 0, 255, 255 },
|
||||
[5] = { 0, 255, 255, 255 },
|
||||
[6] = { 255, 165, 0, 255 },
|
||||
[7] = { 128, 0, 255, 255 },
|
||||
[8] = { 255, 255, 255, 255 },
|
||||
};
|
||||
|
||||
static rtexture_t inputTex;
|
||||
|
||||
#define ASPECT FIXED((float_t)SCREEN.width / (float_t)SCREEN.height)
|
||||
#define FOV_Y FIXED(1.0472f)
|
||||
|
||||
errorret_t sceneInputInit(scenedata_t *data) {
|
||||
data->input.angleX = SHORT_ZERO;
|
||||
data->input.angleY = SHORT_ZERO;
|
||||
inputTex = renderTextureCreate(3, 3, BOX_INDICES, BOX_PALETTE);
|
||||
assertTrue(inputTex != RTEXTURE_NONE, "Failed to create input scene texture");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t sceneInputUpdate(scenedata_t *data) {
|
||||
// 65536 binary angle units = 1 full rotation; 65536 / SHORT_MAX ≈ 2
|
||||
int16_t axisY = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT);
|
||||
int32_t dY = (int32_t)((float_t)axisY * 2.0f * INPUT_SCENE_ROTATE_SPEED * TIME.delta);
|
||||
data->input.angleY = shortAdd(data->input.angleY, (short_t)dY);
|
||||
|
||||
int16_t axisX = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN);
|
||||
int32_t dX = (int32_t)((float_t)axisX * 2.0f * INPUT_SCENE_ROTATE_SPEED * TIME.delta);
|
||||
data->input.angleX = shortAdd(data->input.angleX, (short_t)dX);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t sceneInputRender(scenedata_t *data) {
|
||||
renderClear(color(16, 16, 24, 255));
|
||||
|
||||
renderSetProjection(FOV_Y, ASPECT, FIXED(10), FIXED(10000));
|
||||
renderSetView(0, 0, 270, 0, 0, 0);
|
||||
|
||||
short_t aX = data->input.angleX;
|
||||
short_t aY = data->input.angleY;
|
||||
renderQuad3D(
|
||||
0, 0, 0,
|
||||
(int16_t)((int32_t)shortCos(aY) * 72 / SHORT_MAX), 0,
|
||||
(int16_t)((int32_t)shortSin(aY) * 72 / SHORT_MAX),
|
||||
0,
|
||||
(int16_t)((int32_t)shortCos(aX) * 72 / SHORT_MAX),
|
||||
(int16_t)((int32_t)shortSin(aX) * 72 / SHORT_MAX),
|
||||
0, inputTex, COLOR_WHITE
|
||||
);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t sceneInputDispose(scenedata_t *data) {
|
||||
renderTextureDispose(inputTex);
|
||||
errorOk();
|
||||
}
|
||||
@@ -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 "scene/scenebase.h"
|
||||
#include "util/short.h"
|
||||
|
||||
typedef struct {
|
||||
short_t angleX;
|
||||
short_t angleY;
|
||||
} sceneinput_t;
|
||||
|
||||
errorret_t sceneInputInit(scenedata_t *data);
|
||||
errorret_t sceneInputUpdate(scenedata_t *data);
|
||||
errorret_t sceneInputRender(scenedata_t *data);
|
||||
errorret_t sceneInputDispose(scenedata_t *data);
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scene/rainbownothing/scenerainbownothing.h"
|
||||
#include "scene/testscenes/rainbownothing/scenerainbownothing.h"
|
||||
#include "display/render/render.h"
|
||||
#include "display/color.h"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scene/spinningbox/scenespinningbox.h"
|
||||
#include "scene/testscenes/spinningbox/scenespinningbox.h"
|
||||
#include "assert/assert.h"
|
||||
#include "display/render/render.h"
|
||||
#include "display/screen.h"
|
||||
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scene/test/scenetest.h"
|
||||
#include "scene/testscenes/test/scenetest.h"
|
||||
#include "display/render/render.h"
|
||||
#include "display/color.h"
|
||||
#include "engine/engine.h"
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scene/white32/scenewhite32.h"
|
||||
#include "scene/testscenes/white32/scenewhite32.h"
|
||||
#include "display/render/render.h"
|
||||
#include "display/color.h"
|
||||
#include "engine/engine.h"
|
||||
@@ -41,6 +41,11 @@ void threadStartRequest(thread_t *thread) {
|
||||
thread
|
||||
);
|
||||
pthread_detach(thread->threadId);
|
||||
#else
|
||||
// No threading: skip straight to STOPPED so threadStart's wait loop exits.
|
||||
threadMutexLock(&thread->stateMutex);
|
||||
thread->state = THREAD_STATE_STOPPED;
|
||||
threadMutexUnlock(&thread->stateMutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -15,4 +15,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
math.c
|
||||
sort.c
|
||||
ref.c
|
||||
short.c
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "short.h"
|
||||
|
||||
/* LUT: 256 entries covering one full rotation (0..2π).
|
||||
Generated: sin(i * 2π / 256) * SHORT_MAX, i = 0..255. */
|
||||
static const short_t SHORT_SIN_LUT[256] = {
|
||||
0, 804, 1608, 2410, 3212, 4011, 4808, 5602,
|
||||
6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793,
|
||||
12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530,
|
||||
18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594,
|
||||
23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790,
|
||||
27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956,
|
||||
30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971,
|
||||
32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757,
|
||||
32767, 32757, 32728, 32678, 32609, 32521, 32412, 32285,
|
||||
32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571,
|
||||
30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683,
|
||||
27245, 26790, 26319, 25832, 25329, 24811, 24279, 23731,
|
||||
23170, 22594, 22005, 21403, 20787, 20159, 19519, 18868,
|
||||
18204, 17530, 16846, 16151, 15446, 14732, 14010, 13279,
|
||||
12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179,
|
||||
6393, 5602, 4808, 4011, 3212, 2410, 1608, 804,
|
||||
0, -804, -1608, -2410, -3212, -4011, -4808, -5602,
|
||||
-6393, -7179, -7962, -8739, -9512, -10278, -11039, -11793,
|
||||
-12539, -13279, -14010, -14732, -15446, -16151, -16846, -17530,
|
||||
-18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594,
|
||||
-23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790,
|
||||
-27245, -27683, -28105, -28510, -28898, -29268, -29621, -29956,
|
||||
-30273, -30571, -30852, -31113, -31356, -31580, -31785, -31971,
|
||||
-32137, -32285, -32412, -32521, -32609, -32678, -32728, -32757,
|
||||
-32767, -32757, -32728, -32678, -32609, -32521, -32412, -32285,
|
||||
-32137, -31971, -31785, -31580, -31356, -31113, -30852, -30571,
|
||||
-30273, -29956, -29621, -29268, -28898, -28510, -28105, -27683,
|
||||
-27245, -26790, -26319, -25832, -25329, -24811, -24279, -23731,
|
||||
-23170, -22594, -22005, -21403, -20787, -20159, -19519, -18868,
|
||||
-18204, -17530, -16846, -16151, -15446, -14732, -14010, -13279,
|
||||
-12539, -11793, -11039, -10278, -9512, -8739, -7962, -7179,
|
||||
-6393, -5602, -4808, -4011, -3212, -2410, -1608, -804,
|
||||
};
|
||||
|
||||
short_t shortSin(short_t angle) {
|
||||
return SHORT_SIN_LUT[(uint16_t)angle >> 8];
|
||||
}
|
||||
|
||||
short_t shortCos(short_t angle) {
|
||||
return SHORT_SIN_LUT[(uint8_t)(((uint16_t)angle >> 8) + 64u)];
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "util/math.h"
|
||||
|
||||
/**
|
||||
* Signed 16-bit integer used to represent normalised directional values.
|
||||
* Range: -SHORT_MAX to SHORT_MAX (-32767 to 32767).
|
||||
* INT16_MIN (-32768) is intentionally excluded to keep the range symmetric.
|
||||
*/
|
||||
typedef int16_t short_t;
|
||||
|
||||
#define SHORT_MAX ((short_t)INT16_MAX)
|
||||
#define SHORT_MIN ((short_t)-INT16_MAX)
|
||||
#define SHORT_ZERO ((short_t)0)
|
||||
|
||||
/**
|
||||
* Converts a float in [-1, 1] to a short_t.
|
||||
*
|
||||
* @param f Float value in [-1, 1].
|
||||
* @returns Scaled short_t in [-SHORT_MAX, SHORT_MAX].
|
||||
*/
|
||||
#define shortFromFloat(f) \
|
||||
((short_t)mathClamp((int32_t)((f) * (float_t)SHORT_MAX), SHORT_MIN, SHORT_MAX))
|
||||
|
||||
/**
|
||||
* Converts a short_t to a float in [-1, 1].
|
||||
*
|
||||
* @param s short_t value.
|
||||
* @returns Float in [-1, 1].
|
||||
*/
|
||||
#define shortToFloat(s) ((float_t)(s) / (float_t)SHORT_MAX)
|
||||
|
||||
/**
|
||||
* Adds two short_t values, clamping to [-SHORT_MAX, SHORT_MAX].
|
||||
*
|
||||
* @param a First operand.
|
||||
* @param b Second operand.
|
||||
* @returns Clamped a + b.
|
||||
*/
|
||||
#define shortAdd(a, b) \
|
||||
((short_t)mathClamp((int32_t)(a) + (int32_t)(b), SHORT_MIN, SHORT_MAX))
|
||||
|
||||
/**
|
||||
* Subtracts two short_t values, clamping to [-SHORT_MAX, SHORT_MAX].
|
||||
*
|
||||
* @param a First operand.
|
||||
* @param b Second operand.
|
||||
* @returns Clamped a - b.
|
||||
*/
|
||||
#define shortSub(a, b) \
|
||||
((short_t)mathClamp((int32_t)(a) - (int32_t)(b), SHORT_MIN, SHORT_MAX))
|
||||
|
||||
/**
|
||||
* Negates a short_t, clamping to [-SHORT_MAX, SHORT_MAX].
|
||||
*
|
||||
* @param s Value to negate.
|
||||
* @returns Clamped -s.
|
||||
*/
|
||||
#define shortNeg(s) ((short_t)mathClamp(-(int32_t)(s), SHORT_MIN, SHORT_MAX))
|
||||
|
||||
/**
|
||||
* Returns the absolute value of a short_t.
|
||||
*
|
||||
* @param s Value.
|
||||
* @returns |s|, in [0, SHORT_MAX].
|
||||
*/
|
||||
#define shortAbs(s) ((short_t)((s) < 0 ? -(s) : (s)))
|
||||
|
||||
/**
|
||||
* Clamps a short_t between lo and hi.
|
||||
*
|
||||
* @param s Value to clamp.
|
||||
* @param lo Lower bound.
|
||||
* @param hi Upper bound.
|
||||
* @returns Clamped value.
|
||||
*/
|
||||
#define shortClamp(s, lo, hi) ((short_t)mathClamp((int32_t)(s), (int32_t)(lo), (int32_t)(hi)))
|
||||
|
||||
/**
|
||||
* Returns the sine of an angle as a short_t.
|
||||
* Angle is a binary angle: the full uint16_t range (0..65535) = one full
|
||||
* rotation. Key values: 0=0°, 16384=90°, 32767≈180°, -16384=270°.
|
||||
* Result is in [-SHORT_MAX, SHORT_MAX].
|
||||
*
|
||||
* @param angle Binary angle.
|
||||
* @returns Sine scaled to [-SHORT_MAX, SHORT_MAX].
|
||||
*/
|
||||
short_t shortSin(short_t angle);
|
||||
|
||||
/**
|
||||
* Returns the cosine of an angle as a short_t.
|
||||
* Same angle convention as shortSin.
|
||||
*
|
||||
* @param angle Binary angle.
|
||||
* @returns Cosine scaled to [-SHORT_MAX, SHORT_MAX].
|
||||
*/
|
||||
short_t shortCos(short_t angle);
|
||||
|
||||
/**
|
||||
* Linearly interpolates between two short_t values.
|
||||
* t is a short_t in [0, SHORT_MAX] representing 0..1.
|
||||
*
|
||||
* @param a Start value.
|
||||
* @param b End value.
|
||||
* @param t Interpolation factor in [0, SHORT_MAX].
|
||||
* @returns Interpolated short_t.
|
||||
*/
|
||||
#define shortLerp(a, b, t) \
|
||||
((short_t)((int32_t)(a) + (((int32_t)((b) - (a)) * (int32_t)(t)) / SHORT_MAX)))
|
||||
@@ -6,11 +6,84 @@
|
||||
*/
|
||||
|
||||
#include "assetsaturn.h"
|
||||
#include "asset/asset.h"
|
||||
#include "log/log.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include <fs/cd/cdfs.h>
|
||||
#include <zip.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
errorret_t assetInitSaturn(void) {
|
||||
logDebug("[Saturn] assetInitSaturn: initializing CD-Block\n");
|
||||
cd_block_init();
|
||||
|
||||
cdfs_init();
|
||||
cdfs_config_default_set();
|
||||
|
||||
cdfs_filelist_entry_t *entries = cdfs_entries_alloc(CDFS_FILELIST_ENTRIES_COUNT);
|
||||
if(!entries) errorThrow("Failed to alloc CDFS entries");
|
||||
|
||||
cdfs_filelist_t filelist;
|
||||
cdfs_filelist_init(&filelist, entries, CDFS_FILELIST_ENTRIES_COUNT);
|
||||
cdfs_filelist_root_read(&filelist);
|
||||
|
||||
// Find dusk.dsk — ISO 9660 Level-1 names are uppercase and may carry a ";N"
|
||||
// version suffix ("DUSK.DSK;1"). Compare only the base 8.3 name.
|
||||
const cdfs_filelist_entry_t *dskEntry = NULL;
|
||||
for(uint32_t i = 0; i < filelist.entries_count; i++) {
|
||||
if(filelist.entries[i].type != CDFS_ENTRY_TYPE_FILE) continue;
|
||||
|
||||
char baseName[ISO_FILENAME_MAX_LENGTH + 1];
|
||||
uint8_t j = 0;
|
||||
while(j < ISO_FILENAME_MAX_LENGTH) {
|
||||
char c = filelist.entries[i].name[j];
|
||||
if(c == '\0' || c == ';') break;
|
||||
baseName[j++] = c;
|
||||
}
|
||||
baseName[j] = '\0';
|
||||
|
||||
if(stringCompareInsensitive(baseName, ASSET_FILE_NAME) == 0) {
|
||||
dskEntry = &filelist.entries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dskEntry) {
|
||||
cdfs_entries_free(entries);
|
||||
errorThrow("dusk.dsk not found on disc");
|
||||
}
|
||||
|
||||
const size_t fileSize = dskEntry->size;
|
||||
const fad_t startFad = dskEntry->starting_fad;
|
||||
cdfs_entries_free(entries);
|
||||
|
||||
// cd_block_sectors_read requires byte-length to be sector-aligned.
|
||||
uint32_t readLen = ((uint32_t)fileSize + CDFS_SECTOR_SIZE - 1u) &
|
||||
~(CDFS_SECTOR_SIZE - 1u);
|
||||
void *data = malloc(readLen);
|
||||
if(!data) errorThrow("Failed to alloc buffer for dusk.dsk");
|
||||
|
||||
if(cd_block_sectors_read(startFad, data, readLen) != 0) {
|
||||
free(data);
|
||||
errorThrow("CD read error reading dusk.dsk");
|
||||
}
|
||||
|
||||
// Open zip from the in-memory buffer (flag=1 → libzip owns and frees data).
|
||||
zip_error_t zerr;
|
||||
zip_source_t *src = zip_source_buffer_create(data, (zip_uint64_t)fileSize, 1, &zerr);
|
||||
if(!src) {
|
||||
free(data);
|
||||
errorThrow("Failed to create zip source from disc buffer");
|
||||
}
|
||||
|
||||
ASSET.zip = zip_open_from_source(src, ZIP_RDONLY, &zerr);
|
||||
if(!ASSET.zip) {
|
||||
zip_source_free(src);
|
||||
errorThrow("Failed to open dusk.dsk from disc");
|
||||
}
|
||||
|
||||
logDebug("[Saturn] dusk.dsk opened (%u bytes)\n", (unsigned)fileSize);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,31 @@ float fmodf(float x, float y) {
|
||||
* stdio.h FILE typedef that the sysroot may or may not provide.
|
||||
* ========================================================================= */
|
||||
|
||||
/* fdopen / fileno / ftello / fseeko — POSIX file-descriptor/stdio bridge.
|
||||
* libzip's stdio source objects reference these even when only the buffer
|
||||
* source path is used (no -ffunction-sections in the pre-built sysroot lib).
|
||||
* Saturn has no file descriptors; return errors unconditionally. */
|
||||
|
||||
void *fdopen(int fd, const void *mode) {
|
||||
(void)fd; (void)mode;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fileno(void *stream) {
|
||||
(void)stream;
|
||||
return -1;
|
||||
}
|
||||
|
||||
long long ftello(void *stream) {
|
||||
(void)stream;
|
||||
return -1LL;
|
||||
}
|
||||
|
||||
int fseeko(void *stream, long long offset, int whence) {
|
||||
(void)stream; (void)offset; (void)whence;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||
(void)ptr; (void)size; (void)nmemb; (void)stream;
|
||||
return 0;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <vdp2.h>
|
||||
|
||||
errorret_t displaySaturnInit(void) {
|
||||
logDebug("[Saturn] displaySaturnInit: start\n");
|
||||
DISPLAY.whichBuffer = 0;
|
||||
|
||||
vdp2_tvmd_display_res_set(
|
||||
@@ -31,10 +30,7 @@ errorret_t displaySaturnInit(void) {
|
||||
/* 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();
|
||||
}
|
||||
|
||||
@@ -45,7 +41,6 @@ errorret_t displaySaturnFlush(ropbuffer_t *buf) {
|
||||
}
|
||||
|
||||
errorret_t displaySaturnSwap(void) {
|
||||
logDebug("[Saturn] displaySaturnSwap\n");
|
||||
vdp1_sync_render();
|
||||
vdp1_sync();
|
||||
vdp2_sync();
|
||||
@@ -55,6 +50,5 @@ errorret_t displaySaturnSwap(void) {
|
||||
}
|
||||
|
||||
void displaySaturnDispose(void) {
|
||||
logDebug("[Saturn] displaySaturnDispose\n");
|
||||
renderSaturnDispose();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "util/memory.h"
|
||||
#include "log/log.h"
|
||||
#include <vdp1/cmdt.h>
|
||||
#include <vdp1/env.h>
|
||||
#include <vdp1/vram.h>
|
||||
#include <vdp2/cram.h>
|
||||
|
||||
@@ -19,15 +20,19 @@
|
||||
* VDP1 renders sprites and polygons from a command table stored in VRAM.
|
||||
* VDP2 handles the scroll plane background (used for tilemap chunks).
|
||||
*
|
||||
* VRAM layout (VDP1, 4MB total):
|
||||
* 0x000000 – 0x00FFFF : VDP1 command table (64KB = 2048 entries max)
|
||||
* 0x010000 – 0x1FFFFF : texture pool (1984KB)
|
||||
* Framebuffers are managed automatically by the VDP1 hardware in the
|
||||
* upper half of VRAM when double-buffering is enabled.
|
||||
* VRAM layout (VDP1, 512KB total):
|
||||
* 0x000000 – 0x007FFF : VDP1 command table (32KB = 1024 entries max)
|
||||
* 0x010000 – 0x07FFFF : texture pool
|
||||
* Framebuffers are managed automatically by the VDP1 hardware.
|
||||
*
|
||||
* 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 SATURN_PALETTE_MAX).
|
||||
*
|
||||
* Every frame, renderSaturnFlush() builds a command table in saturnCmdBuf,
|
||||
* then submits it to Yaul via vdp1_sync_cmdt_put(). Yaul DMA-transfers the
|
||||
* table to VDP1 VRAM and, after vdp1_sync_render() (called from
|
||||
* displaySaturnSwap), triggers VDP1 to draw.
|
||||
*/
|
||||
|
||||
/* ---- Limits -------------------------------------------------------------- */
|
||||
@@ -35,7 +40,14 @@
|
||||
#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)
|
||||
#define SATURN_TEXTURE_VRAM_SIZE (0x080000u - SATURN_TEXTURE_VRAM_BASE)
|
||||
|
||||
/* Preamble slots at the front of every command table:
|
||||
* [0] SYSCLIP – system clip coordinate (full screen)
|
||||
* [1] USERCLIP – user clip coordinate (full screen, disabled by default)
|
||||
* [2] LOCALCRD – local coordinate (origin at top-left)
|
||||
* All drawing commands follow from index 3 onward. */
|
||||
#define SATURN_CMDT_PREAMBLE 3
|
||||
|
||||
/* ---- VDP1 command table entry (hardware layout, 32 bytes) ---------------- */
|
||||
|
||||
@@ -55,20 +67,24 @@ typedef struct __attribute__((packed)) {
|
||||
} saturncmd_t;
|
||||
|
||||
_Static_assert(sizeof(saturncmd_t) == 32, "saturncmd_t must be 32 bytes");
|
||||
_Static_assert(sizeof(saturncmd_t) == sizeof(vdp1_cmdt_t),
|
||||
"saturncmd_t/vdp1_cmdt_t size mismatch");
|
||||
|
||||
/* CTRL command type bits (bits 2:0) */
|
||||
#define SATURNCMD_CTRL_NORMAL_SPRITE (0x0000u) /* aligned rect */
|
||||
/* CTRL command type bits */
|
||||
#define SATURNCMD_CTRL_NORMAL_SPRITE (0x0000u)
|
||||
#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 */
|
||||
#define SATURNCMD_CTRL_DISTORTED_SPRITE (0x0002u)
|
||||
#define SATURNCMD_CTRL_POLYGON (0x0004u)
|
||||
#define SATURNCMD_CTRL_USERCLIP (0x0008u)
|
||||
#define SATURNCMD_CTRL_SYSCLIP (0x0009u)
|
||||
#define SATURNCMD_CTRL_LOCALCRD (0x000Au)
|
||||
#define SATURNCMD_CTRL_END (0x8000u)
|
||||
|
||||
/* PMOD draw mode */
|
||||
#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_8BPP_CBANK (4u << 3) /* 256-color, color bank */
|
||||
#define SATURNCMD_PMOD_RGB_DIRECT (5u << 3) /* RGB1555 in COLR, no palette */
|
||||
#define SATURNCMD_PMOD_SPD (0x0040u) /* do not skip index 0 */
|
||||
#define SATURNCMD_PMOD_ECD (0x0080u) /* end-code disable */
|
||||
|
||||
/* ---- Texture table ------------------------------------------------------- */
|
||||
|
||||
@@ -102,7 +118,7 @@ static float saturnViewTgtX = 0.0f, saturnViewTgtY = 0.0f, saturnViewTgtZ = 0.0f
|
||||
|
||||
/* ---- Helpers ------------------------------------------------------------- */
|
||||
|
||||
/* Convert color_t RGBA → VDP2 CRAM RGB1555 (1 MSB unused, RGB555). */
|
||||
/* Convert color_t RGBA → VDP1/VDP2 RGB1555. */
|
||||
static uint16_t toRGB1555(color_t c) {
|
||||
return (uint16_t)(
|
||||
((uint16_t)(c.b >> 3) << 10) |
|
||||
@@ -112,8 +128,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. */
|
||||
* CRAM is mapped at 0x25F00000. Each 256-entry slot = 512 bytes. */
|
||||
static void uploadPalette(saturntexentry_t *e) {
|
||||
volatile uint16_t *cram = (volatile uint16_t *)0x25F00000;
|
||||
uint32_t base = (uint32_t)e->cramWordOffset * 256u;
|
||||
@@ -122,9 +137,8 @@ static void uploadPalette(saturntexentry_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. */
|
||||
/* Copy indices into VDP1 VRAM texture pool.
|
||||
* VDP1 VRAM is at 0x05C00000; textures start at SATURN_TEXTURE_VRAM_BASE. */
|
||||
static void uploadIndices(saturntexentry_t *e) {
|
||||
volatile uint8_t *vram =
|
||||
(volatile uint8_t *)(0x05C00000u + SATURN_TEXTURE_VRAM_BASE + e->vramByteOffset);
|
||||
@@ -134,7 +148,7 @@ static void uploadIndices(saturntexentry_t *e) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a fresh command slot or NULL if full. */
|
||||
/* Reserve a command slot (saturnCmdCount starts at SATURN_CMDT_PREAMBLE). */
|
||||
static saturncmd_t *allocCmd(void) {
|
||||
if(saturnCmdCount >= SATURN_CMDT_MAX) return NULL;
|
||||
saturncmd_t *c = &saturnCmdBuf[saturnCmdCount++];
|
||||
@@ -145,13 +159,18 @@ static saturncmd_t *allocCmd(void) {
|
||||
/* ---- Init ---------------------------------------------------------------- */
|
||||
|
||||
errorret_t renderSaturnInit(void) {
|
||||
logDebug("[Saturn] renderSaturnInit\n");
|
||||
memoryZero(saturnTexTable, sizeof(saturnTexTable));
|
||||
saturnTexNext = 1;
|
||||
saturnTexVramUsed = 0;
|
||||
saturnTexCramUsed = 0;
|
||||
saturnCmdCount = 0;
|
||||
|
||||
/* Initialise VDP1 hardware: sets TVMR (bpp/rotation), EWDR (erase colour),
|
||||
* EWLR/EWRR (erase area = full screen), and updates VDP2 SPCTL sprite type.
|
||||
* Must be called after vdp2_tvmd_display_res_set() so the erase area is
|
||||
* computed from the correct TV resolution. */
|
||||
vdp1_env_default_set();
|
||||
|
||||
/* White 1×1 fallback: slot 0 */
|
||||
saturntexentry_t *e = &saturnTexTable[0];
|
||||
e->cpuIndices = (uint8_t *)malloc(1);
|
||||
@@ -223,25 +242,24 @@ uint8_t *renderSaturnTextureGetIndices(rtexture_t tex) {
|
||||
|
||||
/* ---- Flush --------------------------------------------------------------- */
|
||||
|
||||
/* Fill in the SRCA/CMDSIZE/CMDCOLR fields from a texture handle. */
|
||||
/* Fill in the texture fields of a sprite/distorted-sprite command. */
|
||||
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. */
|
||||
/* SRCA = byte offset from VDP1 VRAM start / 8. */
|
||||
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). */
|
||||
/* SIZE = ((width/8) << 8) | height. */
|
||||
cmd->size = (uint16_t)(((e->w / 8u) << 8) | e->h);
|
||||
|
||||
/* COLR = CRAM word address of palette / 16 (256-color bank mode).
|
||||
* Each 256-entry slot is 512 bytes = 256 words. Word offset / 16. */
|
||||
/* COLR for 256-color bank mode = CRAM word base / 16. */
|
||||
uint32_t cramWordBase = (uint32_t)e->cramWordOffset * 256u;
|
||||
cmd->colr = (uint16_t)(cramWordBase / 16u);
|
||||
|
||||
cmd->pmod = SATURNCMD_PMOD_8BPP_CBANK; /* 256-color, index 0 transparent */
|
||||
cmd->pmod = SATURNCMD_PMOD_8BPP_CBANK;
|
||||
}
|
||||
|
||||
static void flush2DSprite(const ropsprite_t *s) {
|
||||
@@ -256,32 +274,26 @@ static void flush2DSprite(const ropsprite_t *s) {
|
||||
cmd->link = 0;
|
||||
cmdSetTexture(cmd, s->texture);
|
||||
|
||||
/* VDP1 normal sprite: XA/YA = top-left, XB/YB = size (w-1, h-1). */
|
||||
/* Normal sprite: XA/YA = top-left, XB/YB = (w-1, h-1). */
|
||||
cmd->xa = (int16_t)s->x;
|
||||
cmd->ya = (int16_t)s->y;
|
||||
cmd->xb = (int16_t)(s->w > 0 ? s->w - 1 : 0);
|
||||
cmd->yb = (int16_t)(s->h > 0 ? s->h - 1 : 0);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Project a 3D world-space point onto the VDP1 2D screen.
|
||||
* Uses a simple perspective divide; view/projection state is kept CPU-side.
|
||||
*/
|
||||
static void project(
|
||||
float wx, float wy, float wz,
|
||||
float *sx, float *sy
|
||||
) {
|
||||
/* Translate relative to eye. */
|
||||
float rx = wx - saturnViewEyeX;
|
||||
float ry = wy - saturnViewEyeY;
|
||||
float rz = wz - saturnViewEyeZ;
|
||||
|
||||
/* TODO: replace with a proper view-matrix multiply for non-axis-aligned cameras. */
|
||||
float fwd_z = saturnViewTgtZ - saturnViewEyeZ;
|
||||
(void)fwd_z;
|
||||
|
||||
@@ -310,7 +322,6 @@ static void flush3DQuad(const ropquad3d_t *q) {
|
||||
float rx = (float)q->rx, ry = (float)q->ry, rz = (float)q->rz;
|
||||
float ux = (float)q->ux, uy = (float)q->uy, uz = (float)q->uz;
|
||||
|
||||
/* Corners: TL = center - right + up, etc. */
|
||||
float tlx = cx-rx+ux, tly = cy-ry+uy, tlz = cz-rz+uz;
|
||||
float trx = cx+rx+ux, try_ = cy+ry+uy, trz = cz+rz+uz;
|
||||
float blx = cx-rx-ux, bly = cy-ry-uy, blz = cz-rz-uz;
|
||||
@@ -328,7 +339,9 @@ static void flush3DQuad(const ropquad3d_t *q) {
|
||||
cmd->xd = (int16_t)sxd; cmd->yd = (int16_t)syd;
|
||||
}
|
||||
|
||||
/* Submit the finished command table to VDP1 VRAM and trigger rendering. */
|
||||
/* Submit command table to Yaul's VDP1 sync queue.
|
||||
* vdp1_sync_cmdt_put() sets VDP1_FLAG_REQUEST_XFER_LIST which vdp1_sync_render()
|
||||
* (called from displaySaturnSwap) requires before it will trigger VDP1 drawing. */
|
||||
static void submitCmdTable(void) {
|
||||
/* Append end-of-list sentinel. */
|
||||
if(saturnCmdCount < SATURN_CMDT_MAX) {
|
||||
@@ -337,31 +350,41 @@ static void submitCmdTable(void) {
|
||||
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 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] = saturnCmdBuf[i];
|
||||
}
|
||||
|
||||
/* Set VDP1 command table top address to 0x000000 (the default). */
|
||||
volatile uint16_t *vdp1Regs = (volatile uint16_t *)0x25D00000u;
|
||||
/* 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));
|
||||
/* Hand the completed list to Yaul. Yaul SCU-DMA's it to VDP1 VRAM and
|
||||
* sets the transfer-complete flag that vdp1_sync_render() waits for. */
|
||||
vdp1_sync_cmdt_put(
|
||||
(const vdp1_cmdt_t *)saturnCmdBuf,
|
||||
(uint16_t)(saturnCmdCount + 1u),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
logDebug("[Saturn] renderSaturnFlush: count=%u\n", (unsigned)buf->count);
|
||||
/* Write fixed preamble into the first SATURN_CMDT_PREAMBLE slots. */
|
||||
{
|
||||
saturncmd_t *sc;
|
||||
|
||||
saturnCmdCount = 0;
|
||||
/* [0] System clip – defines the hardware clip boundary (full screen). */
|
||||
sc = &saturnCmdBuf[0];
|
||||
memoryZero(sc, sizeof(saturncmd_t));
|
||||
sc->ctrl = SATURNCMD_CTRL_SYSCLIP;
|
||||
sc->xb = (int16_t)(DUSK_DISPLAY_WIDTH - 1);
|
||||
sc->yb = (int16_t)(DUSK_DISPLAY_HEIGHT - 1);
|
||||
|
||||
/* [1] User clip – mirrors full screen; disabled per-command by default. */
|
||||
sc = &saturnCmdBuf[1];
|
||||
memoryZero(sc, sizeof(saturncmd_t));
|
||||
sc->ctrl = SATURNCMD_CTRL_USERCLIP;
|
||||
sc->xc = (int16_t)(DUSK_DISPLAY_WIDTH - 1);
|
||||
sc->yc = (int16_t)(DUSK_DISPLAY_HEIGHT - 1);
|
||||
|
||||
/* [2] Local coordinate – place origin at screen top-left. */
|
||||
sc = &saturnCmdBuf[2];
|
||||
memoryZero(sc, sizeof(saturncmd_t));
|
||||
sc->ctrl = SATURNCMD_CTRL_LOCALCRD;
|
||||
/* xa = ya = 0 (origin at TL), already zero'd */
|
||||
}
|
||||
saturnCmdCount = SATURN_CMDT_PREAMBLE;
|
||||
|
||||
uint32_t offset = 0;
|
||||
while(offset < buf->byteCount) {
|
||||
@@ -371,19 +394,22 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
switch(op) {
|
||||
case ROP_CLEAR: {
|
||||
const ropclear_t *c = (const ropclear_t *)hdr;
|
||||
/* Set VDP2 back-screen color. */
|
||||
volatile uint16_t *cram = (volatile uint16_t *)0x25F00000u;
|
||||
cram[0] = toRGB1555(c->color);
|
||||
|
||||
/* Issue a VDP1 system clipping command to reset the clip window. */
|
||||
saturncmd_t *clip = allocCmd();
|
||||
if(clip) {
|
||||
clip->ctrl = SATURNCMD_CTRL_SYSCLIP;
|
||||
clip->link = 0;
|
||||
clip->xa = 0;
|
||||
clip->ya = 0;
|
||||
clip->xb = (int16_t)(DUSK_DISPLAY_WIDTH - 1);
|
||||
clip->yb = (int16_t)(DUSK_DISPLAY_HEIGHT - 1);
|
||||
/* Fill the entire screen with a solid RGB polygon.
|
||||
* RGB direct mode (PMOD = SATURNCMD_PMOD_RGB_DIRECT) places the
|
||||
* RGB1555 color in COLR directly — no palette lookup required. */
|
||||
saturncmd_t *poly = allocCmd();
|
||||
if(poly) {
|
||||
poly->ctrl = SATURNCMD_CTRL_POLYGON;
|
||||
poly->pmod = SATURNCMD_PMOD_RGB_DIRECT;
|
||||
poly->colr = toRGB1555(c->color);
|
||||
poly->xa = 0;
|
||||
poly->ya = 0;
|
||||
poly->xb = (int16_t)(DUSK_DISPLAY_WIDTH - 1);
|
||||
poly->yb = 0;
|
||||
poly->xc = (int16_t)(DUSK_DISPLAY_WIDTH - 1);
|
||||
poly->yc = (int16_t)(DUSK_DISPLAY_HEIGHT - 1);
|
||||
poly->xd = 0;
|
||||
poly->yd = (int16_t)(DUSK_DISPLAY_HEIGHT - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -417,13 +443,10 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
break;
|
||||
|
||||
case ROP_DRAW_TILEMAP_CHUNK:
|
||||
/* TODO: Saturn tilemap chunks drive VDP2 scroll plane registers.
|
||||
* For now we fall through and emit nothing; a proper implementation
|
||||
* writes tile indices to VDP2 VRAM and sets scroll offsets. */
|
||||
/* TODO: drive VDP2 scroll plane registers for tilemap support. */
|
||||
break;
|
||||
|
||||
default:
|
||||
logDebug("[Saturn] unknown ROP op=%u\n", (unsigned)op);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,27 +6,45 @@
|
||||
*/
|
||||
|
||||
#include "log/log.h"
|
||||
#include <dbgio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* 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_dev_default_set(DBGIO_DEV_USB_CART, NULL) in
|
||||
* systemSaturnInit() and replace vprintf with dbgio_printf() for
|
||||
* hardware-accurate serial output.
|
||||
* Yaul's bare-metal stdio (stdout/stderr) has uninitialised function pointers
|
||||
* and will crash if called via vprintf/vfprintf. Use Yaul's dbgio subsystem
|
||||
* instead: DBGIO_DEV_MEDNAFEN_DEBUG outputs to Mednafen's debug console;
|
||||
* the same code works with USB cart or serial on hardware by changing the
|
||||
* device constant.
|
||||
*/
|
||||
|
||||
static int _dbgioReady = 0;
|
||||
|
||||
static void _ensureDbgio(void) {
|
||||
if (!_dbgioReady) {
|
||||
dbgio_dev_default_init(DBGIO_DEV_MEDNAFEN_DEBUG);
|
||||
_dbgioReady = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void _logWrite(const char_t *message, va_list args) {
|
||||
char buf[256];
|
||||
vsnprintf(buf, sizeof(buf), message, args);
|
||||
dbgio_puts(buf);
|
||||
}
|
||||
|
||||
void logDebug(const char_t *message, ...) {
|
||||
_ensureDbgio();
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vprintf(message, args);
|
||||
_logWrite(message, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logError(const char_t *message, ...) {
|
||||
_ensureDbgio();
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vfprintf(stderr, message, args);
|
||||
_logWrite(message, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user