This commit is contained in:
Jan Racek
2026-03-27 18:31:54 +01:00
parent 480323f5b9
commit 4ff7ecdd57
6 changed files with 155 additions and 5 deletions

View File

@@ -1,7 +1,92 @@
#include "controls.hh"
#include <psyqo/hardware/cpu.hh>
#include <psyqo/hardware/sio.hh>
#include <psyqo/vector.hh>
namespace {
using namespace psyqo::Hardware;
void busyLoop(unsigned delay) {
unsigned cycles = 0;
while (++cycles < delay) asm("");
}
void flushRxBuffer() {
while (SIO::Stat & SIO::Status::STAT_RXRDY) {
SIO::Data.throwAway();
}
}
uint8_t transceive(uint8_t dataOut) {
SIO::Ctrl |= SIO::Control::CTRL_ERRRES;
CPU::IReg.clear(CPU::IRQ::Controller);
SIO::Data = dataOut;
while (!(SIO::Stat & SIO::Status::STAT_RXRDY));
return SIO::Data;
}
bool waitForAck() {
int cyclesWaited = 0;
static constexpr int ackTimeout = 0x137;
while (!(CPU::IReg.isSet(CPU::IRQ::Controller)) && ++cyclesWaited < ackTimeout);
if (cyclesWaited >= ackTimeout) return false;
while (SIO::Stat & SIO::Status::STAT_ACK); // Wait for ACK to go high
return true;
}
void configurePort(uint8_t port) {
SIO::Ctrl = (port * SIO::Control::CTRL_PORTSEL) | SIO::Control::CTRL_DTR;
SIO::Baud = 0x88;
flushRxBuffer();
SIO::Ctrl |= (SIO::Control::CTRL_TXEN | SIO::Control::CTRL_ACKIRQEN);
busyLoop(100);
}
// Send a command sequence to the pad and wait for ACK between each byte.
// Returns false if ACK was lost at any point.
bool sendCommand(const uint8_t *cmd, unsigned len) {
for (unsigned i = 0; i < len; i++) {
transceive(cmd[i]);
if (i < len - 1) {
if (!waitForAck()) return false;
}
}
return true;
}
} // namespace
void psxsplash::Controls::forceAnalogMode() {
// Initialize SIO for pad communication
using namespace psyqo::Hardware;
SIO::Ctrl = SIO::Control::CTRL_IR;
SIO::Baud = 0x88;
SIO::Mode = 0xd;
SIO::Ctrl = 0;
// Sequence for port 0 (Pad 1):
// 1) Enter config mode
static const uint8_t enterConfig[] = {0x01, 0x43, 0x00, 0x01, 0x00};
// 2) Set analog mode (0x01) + lock (0x03)
static const uint8_t setAnalog[] = {0x01, 0x44, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00};
// 3) Exit config mode
static const uint8_t exitConfig[] = {0x01, 0x43, 0x00, 0x00, 0x00};
configurePort(0);
sendCommand(enterConfig, sizeof(enterConfig));
SIO::Ctrl = 0;
configurePort(0);
sendCommand(setAnalog, sizeof(setAnalog));
SIO::Ctrl = 0;
configurePort(0);
sendCommand(exitConfig, sizeof(exitConfig));
SIO::Ctrl = 0;
}
void psxsplash::Controls::Init() { m_input.initialize(); }
bool psxsplash::Controls::isDigitalPad() const {

View File

@@ -12,6 +12,10 @@ using namespace psyqo::trig_literals;
class Controls {
public:
/// Force DualShock into analog mode (LED=Red) via raw SIO commands.
/// Must be called BEFORE Init() since Init() hands SIO control to AdvancedPad.
void forceAnalogMode();
void Init();
void HandleControls(psyqo::Vec3 &playerPosition, psyqo::Angle &playerRotationX, psyqo::Angle &playerRotationY,
psyqo::Angle &playerRotationZ, bool freecam, int deltaFrames);

View File

@@ -460,6 +460,47 @@ void psxsplash::Lua::OnUpdate(GameObject* go, int deltaFrames) {
onUpdateMethodWrapper.callMethod(*this, go, deltaFrames);
}
void psxsplash::Lua::RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta) {
auto L = m_state;
for (size_t i = 0; i < count; i++) {
// objects[i] is already the NEW pointer (relocated by shrinkBuffer).
// Compute the OLD pointer by subtracting delta.
uint8_t* newPtr = reinterpret_cast<uint8_t*>(objects[i]);
uint8_t* oldPtr = newPtr - delta;
// Re-key the main game object table: registry[oldPtr] -> registry[newPtr]
L.push(oldPtr);
L.rawGet(LUA_REGISTRYINDEX);
if (L.isTable(-1)) {
// Update __cpp_ptr inside the table
L.push(newPtr);
L.setField(-2, "__cpp_ptr");
// Store at new key
L.push(newPtr);
L.copy(-2);
L.rawSet(LUA_REGISTRYINDEX);
// Remove old key
L.push(oldPtr);
L.push(); // nil
L.rawSet(LUA_REGISTRYINDEX);
}
L.pop();
// Re-key the methods table: registry[oldPtr+1] -> registry[newPtr+1]
L.push(oldPtr + 1);
L.rawGet(LUA_REGISTRYINDEX);
if (L.isTable(-1)) {
L.push(newPtr + 1);
L.copy(-2);
L.rawSet(LUA_REGISTRYINDEX);
L.push(oldPtr + 1);
L.push();
L.rawSet(LUA_REGISTRYINDEX);
}
L.pop();
}
}
void psxsplash::Lua::PushGameObject(GameObject* go) {
auto L = m_state;
L.push(go);

View File

@@ -49,6 +49,7 @@ class Lua {
void LoadLuaFile(const char* code, size_t len, int index);
void RegisterSceneScripts(int index);
void RegisterGameObject(GameObject* go);
void RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta);
// Get the underlying psyqo::Lua state for API registration
psyqo::Lua& getState() { return m_state; }

View File

@@ -1179,31 +1179,38 @@ int LuaAPI::Camera_LookAt(lua_State* L) {
// ============================================================================
int LuaAPI::Audio_Play(lua_State* L) {
printf("[Audio_Play] ENTER top=%d\n", lua_gettop(L));
psyqo::Lua lua(L);
int soundId = -1;
// Accept number (index) or string (name lookup) like Entity.Find
// Check isNumber FIRST — in Lua, numbers pass isString too.
if (lua.isNumber(1)) {
soundId = static_cast<int>(lua.toNumber(1));
printf("[Audio_Play] by index: %d\n", soundId);
} else if (lua.isString(1)) {
const char* name = lua.toString(1);
printf("[Audio_Play] by name: '%s'\n", name ? name : "(null)");
soundId = s_sceneManager->findAudioClipByName(name);
if (soundId < 0) {
printf("[Lua] Audio.Play: clip '%s' not found\n", name);
printf("[Audio_Play] clip not found\n");
lua.pushNumber(-1);
return 1;
}
printf("[Audio_Play] found at index %d\n", soundId);
} else {
printf("[Audio_Play] invalid arg type\n");
lua.pushNumber(-1);
return 1;
}
int volume = static_cast<int>(lua.optNumber(2, 100));
int pan = static_cast<int>(lua.optNumber(3, 64));
printf("[Audio_Play] play(%d, vol=%d, pan=%d)\n", soundId, volume, pan);
int voice = s_sceneManager->getAudio().play(soundId, volume, pan);
printf("[Audio_Play] voice=%d OK\n", voice);
lua.pushNumber(voice);
return 1;
}

View File

@@ -231,6 +231,7 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
L.RegisterGameObject(object);
}
m_controls.forceAnalogMode();
m_controls.Init();
Renderer::GetInstance().SetCamera(m_currentCamera);
@@ -815,6 +816,17 @@ void psxsplash::SceneManager::shrinkBuffer() {
m_uiSystem.relocate(delta);
// Re-key Lua registry entries for game objects. RegisterGameObject stored
// lightuserdata keys using the OLD buffer addresses. After relocation, the
// game object pointers changed but the registry keys are stale. Without
// this, Entity.Find/FindByIndex return nil (wrong key), and in release
// builds (-Os) the optimizer can turn this into a hard crash.
if (!m_gameObjects.empty()) {
L.RelocateGameObjects(
reinterpret_cast<GameObject**>(m_gameObjects.data()),
m_gameObjects.size(), delta);
}
FileLoader::Get().FreeFile(oldBase);
m_currentSceneData = newBase;
}