bugfixes
This commit is contained in:
@@ -1,7 +1,92 @@
|
|||||||
#include "controls.hh"
|
#include "controls.hh"
|
||||||
|
|
||||||
|
#include <psyqo/hardware/cpu.hh>
|
||||||
|
#include <psyqo/hardware/sio.hh>
|
||||||
#include <psyqo/vector.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(); }
|
void psxsplash::Controls::Init() { m_input.initialize(); }
|
||||||
|
|
||||||
bool psxsplash::Controls::isDigitalPad() const {
|
bool psxsplash::Controls::isDigitalPad() const {
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ using namespace psyqo::trig_literals;
|
|||||||
|
|
||||||
class Controls {
|
class Controls {
|
||||||
public:
|
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 Init();
|
||||||
void HandleControls(psyqo::Vec3 &playerPosition, psyqo::Angle &playerRotationX, psyqo::Angle &playerRotationY,
|
void HandleControls(psyqo::Vec3 &playerPosition, psyqo::Angle &playerRotationX, psyqo::Angle &playerRotationY,
|
||||||
psyqo::Angle &playerRotationZ, bool freecam, int deltaFrames);
|
psyqo::Angle &playerRotationZ, bool freecam, int deltaFrames);
|
||||||
|
|||||||
41
src/lua.cpp
41
src/lua.cpp
@@ -460,6 +460,47 @@ void psxsplash::Lua::OnUpdate(GameObject* go, int deltaFrames) {
|
|||||||
onUpdateMethodWrapper.callMethod(*this, go, 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) {
|
void psxsplash::Lua::PushGameObject(GameObject* go) {
|
||||||
auto L = m_state;
|
auto L = m_state;
|
||||||
L.push(go);
|
L.push(go);
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class Lua {
|
|||||||
void LoadLuaFile(const char* code, size_t len, int index);
|
void LoadLuaFile(const char* code, size_t len, int index);
|
||||||
void RegisterSceneScripts(int index);
|
void RegisterSceneScripts(int index);
|
||||||
void RegisterGameObject(GameObject* go);
|
void RegisterGameObject(GameObject* go);
|
||||||
|
void RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta);
|
||||||
|
|
||||||
// Get the underlying psyqo::Lua state for API registration
|
// Get the underlying psyqo::Lua state for API registration
|
||||||
psyqo::Lua& getState() { return m_state; }
|
psyqo::Lua& getState() { return m_state; }
|
||||||
|
|||||||
@@ -1179,6 +1179,7 @@ int LuaAPI::Camera_LookAt(lua_State* L) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
int LuaAPI::Audio_Play(lua_State* L) {
|
int LuaAPI::Audio_Play(lua_State* L) {
|
||||||
|
printf("[Audio_Play] ENTER top=%d\n", lua_gettop(L));
|
||||||
psyqo::Lua lua(L);
|
psyqo::Lua lua(L);
|
||||||
|
|
||||||
int soundId = -1;
|
int soundId = -1;
|
||||||
@@ -1187,15 +1188,19 @@ int LuaAPI::Audio_Play(lua_State* L) {
|
|||||||
// Check isNumber FIRST — in Lua, numbers pass isString too.
|
// Check isNumber FIRST — in Lua, numbers pass isString too.
|
||||||
if (lua.isNumber(1)) {
|
if (lua.isNumber(1)) {
|
||||||
soundId = static_cast<int>(lua.toNumber(1));
|
soundId = static_cast<int>(lua.toNumber(1));
|
||||||
|
printf("[Audio_Play] by index: %d\n", soundId);
|
||||||
} else if (lua.isString(1)) {
|
} else if (lua.isString(1)) {
|
||||||
const char* name = lua.toString(1);
|
const char* name = lua.toString(1);
|
||||||
|
printf("[Audio_Play] by name: '%s'\n", name ? name : "(null)");
|
||||||
soundId = s_sceneManager->findAudioClipByName(name);
|
soundId = s_sceneManager->findAudioClipByName(name);
|
||||||
if (soundId < 0) {
|
if (soundId < 0) {
|
||||||
printf("[Lua] Audio.Play: clip '%s' not found\n", name);
|
printf("[Audio_Play] clip not found\n");
|
||||||
lua.pushNumber(-1);
|
lua.pushNumber(-1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
printf("[Audio_Play] found at index %d\n", soundId);
|
||||||
} else {
|
} else {
|
||||||
|
printf("[Audio_Play] invalid arg type\n");
|
||||||
lua.pushNumber(-1);
|
lua.pushNumber(-1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1203,7 +1208,9 @@ int LuaAPI::Audio_Play(lua_State* L) {
|
|||||||
int volume = static_cast<int>(lua.optNumber(2, 100));
|
int volume = static_cast<int>(lua.optNumber(2, 100));
|
||||||
int pan = static_cast<int>(lua.optNumber(3, 64));
|
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);
|
int voice = s_sceneManager->getAudio().play(soundId, volume, pan);
|
||||||
|
printf("[Audio_Play] voice=%d OK\n", voice);
|
||||||
lua.pushNumber(voice);
|
lua.pushNumber(voice);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
|
|||||||
L.RegisterGameObject(object);
|
L.RegisterGameObject(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_controls.forceAnalogMode();
|
||||||
m_controls.Init();
|
m_controls.Init();
|
||||||
Renderer::GetInstance().SetCamera(m_currentCamera);
|
Renderer::GetInstance().SetCamera(m_currentCamera);
|
||||||
|
|
||||||
@@ -815,6 +816,17 @@ void psxsplash::SceneManager::shrinkBuffer() {
|
|||||||
|
|
||||||
m_uiSystem.relocate(delta);
|
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);
|
FileLoader::Get().FreeFile(oldBase);
|
||||||
m_currentSceneData = newBase;
|
m_currentSceneData = newBase;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user