Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/DebugInterface/CDebugInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,14 @@ void CDebugInterface::StepOverSubroutine()

bool CDebugInterface::RunEmulationForOneFrame()
{
return RunEmulationForFrames(1);
}

bool CDebugInterface::RunEmulationForFrames(uint32 numFrames)
{
if (numFrames < 1)
return false;

if (!isRunning)
return false;

Expand All @@ -642,15 +650,17 @@ bool CDebugInterface::RunEmulationForOneFrame()

frameStepRequestGeneration++;
requestGeneration = frameStepRequestGeneration;
frameStepTargetFrameNumber = GetEmulationFrameNumber() + 1;
frameStepTargetFrameNumber = GetEmulationFrameNumber() + numFrames;
frameStepPending = true;
frameStepTargetVSyncReached = false;
}

SetDebugMode(DEBUGGER_MODE_RUNNING);

bool frameCompleted = false;
for (int tries = 0; tries < 100; tries++)
// wait budget scales with the requested frame count (50 ms per try; a realtime PAL frame is 20 ms)
const int maxTries = 100 + (int)numFrames * 2;
for (int tries = 0; tries < maxTries; tries++)
{
std::unique_lock<std::mutex> lock(frameStepMutex);
frameCompleted = frameStepCV.wait_for(lock, std::chrono::milliseconds(50), [this, requestGeneration]() {
Expand Down
1 change: 1 addition & 0 deletions src/DebugInterface/CDebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class CDebugInterface
virtual void StepOneCycle();
virtual void StepOverSubroutine();
virtual bool RunEmulationForOneFrame();
virtual bool RunEmulationForFrames(uint32 numFrames);

virtual void RunContinueEmulation();

Expand Down
26 changes: 22 additions & 4 deletions src/Emulators/vice/ViceInterface/CDebugInterfaceVice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1317,11 +1317,20 @@ bool CDebugInterfaceVice::KeyboardDown(uint32 mtKeyCode)
return false;
}

C64KeyMap *keyMap = C64KeyMapGetDefault();
if (keyMap == NULL)
return false;

C64KeyCode *key = keyMap->FindKeyCode(mtKeyCode);
if (key == NULL)
return false;

CDebugInterfaceViceTaskKeyboardEvent *task =
new CDebugInterfaceViceTaskKeyboardEvent(this, DEBUGGER_EVENT_BUTTON_DOWN, mtKeyCode);
new CDebugInterfaceViceTaskKeyboardEvent(this, DEBUGGER_EVENT_BUTTON_DOWN, mtKeyCode,
key->matrixRow, key->matrixCol, key->shift);
AddCpuDebugInterruptTask(task);
c64d_vice_input_tasks_flag = 1;
return false;
return true;
}

bool CDebugInterfaceVice::KeyboardUp(uint32 mtKeyCode)
Expand All @@ -1335,11 +1344,20 @@ bool CDebugInterfaceVice::KeyboardUp(uint32 mtKeyCode)
return false;
}

C64KeyMap *keyMap = C64KeyMapGetDefault();
if (keyMap == NULL)
return false;

C64KeyCode *key = keyMap->FindKeyCode(mtKeyCode);
if (key == NULL)
return false;

CDebugInterfaceViceTaskKeyboardEvent *task =
new CDebugInterfaceViceTaskKeyboardEvent(this, DEBUGGER_EVENT_BUTTON_UP, mtKeyCode);
new CDebugInterfaceViceTaskKeyboardEvent(this, DEBUGGER_EVENT_BUTTON_UP, mtKeyCode,
key->matrixRow, key->matrixCol, key->shift);
AddCpuDebugInterruptTask(task);
c64d_vice_input_tasks_flag = 1;
return false;
return true;
}

void CDebugInterfaceVice::JoystickDown(int port, uint32 axis)
Expand Down
37 changes: 34 additions & 3 deletions src/Emulators/vice/ViceInterface/CDebugInterfaceViceTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ extern "C" {
void c64d_joystick_key_up(int key, unsigned int joyport);
int keyboard_key_pressed(signed long key);
int keyboard_key_released(signed long key);
int keyboard_key_pressed_matrix(int row, int column, int shift);
int keyboard_key_released_matrix(int row, int column, int shift);
int keyboard_set_latch_keyarr(int row, int col, int value);
void keyboard_set_keyarr_any(int row, int col, int value);
void c64d_keyboard_key_down_latch();
void c64d_keyboard_key_up_latch();
}

CDebugInterfaceViceTaskJoystickEvent::CDebugInterfaceViceTaskJoystickEvent(
Expand Down Expand Up @@ -83,11 +89,15 @@ void CDebugInterfaceViceTaskJoystickEvent::ExecuteTask()
}

CDebugInterfaceViceTaskKeyboardEvent::CDebugInterfaceViceTaskKeyboardEvent(
CDebugInterfaceVice *debugInterface, u8 buttonState, u32 mtKeyCode)
CDebugInterfaceVice *debugInterface, u8 buttonState, u32 mtKeyCode,
int matrixRow, int matrixCol, int shift)
{
this->debugInterface = debugInterface;
this->buttonState = buttonState;
this->mtKeyCode = mtKeyCode;
this->matrixRow = matrixRow;
this->matrixCol = matrixCol;
this->shift = shift;
}

void CDebugInterfaceViceTaskKeyboardEvent::ExecuteTask()
Expand All @@ -100,13 +110,34 @@ void CDebugInterfaceViceTaskKeyboardEvent::ExecuteTask()
inputEventsBuffer->PutU8(buttonState);
}

// press/release via the resolved matrix position — same sequence the virtual keyboard
// uses (CViewC64KeyMap::SelectKey); the c64d_*_latch calls latch immediately, without
// the randomized KEYBOARD_RAND alarm, so measurements stay cycle-deterministic
if (buttonState == DEBUGGER_EVENT_BUTTON_DOWN)
{
keyboard_key_pressed((signed long)mtKeyCode);
if (matrixRow < 0)
{
keyboard_set_keyarr_any(matrixRow, matrixCol, 1);
}
else
{
keyboard_key_pressed_matrix(matrixRow, matrixCol, shift);
keyboard_set_latch_keyarr(matrixRow, matrixCol, 1);
c64d_keyboard_key_down_latch();
}
}
else
{
keyboard_key_released((signed long)mtKeyCode);
if (matrixRow < 0)
{
keyboard_set_keyarr_any(matrixRow, matrixCol, 0);
}
else
{
keyboard_key_released_matrix(matrixRow, matrixCol, shift);
keyboard_set_latch_keyarr(matrixRow, matrixCol, 0);
c64d_keyboard_key_up_latch();
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/Emulators/vice/ViceInterface/CDebugInterfaceViceTasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ class CDebugInterfaceViceTaskJoystickEvent : public CDebugInterfaceTask
class CDebugInterfaceViceTaskKeyboardEvent : public CDebugInterfaceTask
{
public:
CDebugInterfaceViceTaskKeyboardEvent(CDebugInterfaceVice *debugInterface, u8 buttonState, u32 mtKeyCode);
CDebugInterfaceViceTaskKeyboardEvent(CDebugInterfaceVice *debugInterface, u8 buttonState, u32 mtKeyCode,
int matrixRow, int matrixCol, int shift);
virtual void ExecuteTask();

CDebugInterfaceVice *debugInterface;
u8 buttonState;
u32 mtKeyCode;
// C64 keyboard matrix position resolved from C64KeyMap (matrixRow < 0 = special key, e.g. RESTORE)
int matrixRow;
int matrixCol;
int shift;
};

class CDebugInterfaceViceTaskReset : public CDebugInterfaceTask
Expand Down
73 changes: 67 additions & 6 deletions src/Remote/CDebuggerServerApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,37 @@ void CDebuggerServerApi::RegisterEndpoints(CDebuggerServer *server)
return server->PrepareResult(HTTP_OK, token, json(), NULL, 0);
});

// Deterministic replay: run exactly N frames then pause. Blocks until finished,
// so the reply arriving means the frames have already run (frame = final counter).
sprintf(buf, "%s/run/frames", plat);
RegisterEndpoint(server, buf, plat, "control", "Run exactly N frames then pause (blocks until finished; count 1..100000)",
[this, server](const string token, json params, unsigned char *binaryData, int binaryDataSize) -> vector<char>*
{
int count = 1;
if (params.is_object() && params.contains("count"))
{
count = params.at("count").get<int>();
}
if (count < 1 || count > 100000)
{
return server->PrepareResult(HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
}
if (debugInterface->GetDebugMode() != DEBUGGER_MODE_PAUSED)
{
json errorJson;
errorJson["error"] = "machine must be paused; current+N would race the emulation thread";
return server->PrepareResult(HTTP_NOT_ACCEPTABLE, token, errorJson, NULL, 0);
}
unsigned int frameStart = debuggerApi->GetEmulationFrameNumber();
bool completed = debugInterface->RunEmulationForFrames((uint32)count);
json result;
result["completed"] = completed;
result["count"] = count;
result["frameStart"] = frameStart;
result["frame"] = debuggerApi->GetEmulationFrameNumber();
return server->PrepareResult(completed ? HTTP_OK : HTTP_NOT_ACCEPTABLE, token, result, NULL, 0);
});

sprintf(buf, "%s/step/cycle", plat);
RegisterEndpoint(server, buf, plat, "cpu", "Step one CPU cycle",
[this, server](const string token, json params, unsigned char *binaryData, int binaryDataSize) -> vector<char>*
Expand Down Expand Up @@ -319,18 +350,34 @@ void CDebuggerServerApi::RegisterEndpoints(CDebuggerServer *server)
RegisterEndpoint(server, buf, plat, "input", "Send key-down event",
[this, server](const string token, json params, unsigned char* binaryData, int binaryDataSize) -> vector<char>*
{
if (!params.is_object() || !params.contains("keyCode") || !params.at("keyCode").is_number_integer())
{
return server->PrepareResult(HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
}
int mtKeyCode = params.at("keyCode").get<int>();
debugInterface->LockIoMutex();
bool res = debuggerApi->KeyboardDown(mtKeyCode);
return server->PrepareResult(res ? HTTP_OK : HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
debugInterface->UnlockIoMutex();
json result;
result["queued"] = res;
return server->PrepareResult(res ? HTTP_ACCEPTED : HTTP_NOT_ACCEPTABLE, token, result, NULL, 0);
});

sprintf(buf, "%s/input/key/up", plat);
RegisterEndpoint(server, buf, plat, "input", "Send key-up event",
[this, server](const string token, json params, unsigned char* binaryData, int binaryDataSize) -> vector<char>*
{
if (!params.is_object() || !params.contains("keyCode") || !params.at("keyCode").is_number_integer())
{
return server->PrepareResult(HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
}
int mtKeyCode = params.at("keyCode").get<int>();
debugInterface->LockIoMutex();
bool res = debuggerApi->KeyboardUp(mtKeyCode);
return server->PrepareResult(res ? HTTP_OK : HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
debugInterface->UnlockIoMutex();
json result;
result["queued"] = res;
return server->PrepareResult(res ? HTTP_ACCEPTED : HTTP_NOT_ACCEPTABLE, token, result, NULL, 0);
});

sprintf(buf, "%s/input/joystick/down", plat);
Expand Down Expand Up @@ -941,23 +988,37 @@ void CDebuggerServerApi::RegisterEndpoints(CDebuggerServer *server)
RegisterEndpoint(server, buf, plat, "input", "Press a keyboard key (keyCode: MTKEY/SDL keycode; ASCII for printable chars)",
[this, server](const string token, json params, unsigned char *binaryData, int binaryDataSize) -> vector<char>*
{
if (!params.is_object() || !params.contains("keyCode") || !params.at("keyCode").is_number_integer())
{
return server->PrepareResult(HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
}
uint32_t keyCode = params.at("keyCode").get<uint32_t>();
debugInterface->KeyboardDown(keyCode);
debugInterface->LockIoMutex();
bool queued = debugInterface->KeyboardDown(keyCode);
debugInterface->UnlockIoMutex();
json result;
result["keyCode"] = keyCode;
return server->PrepareResult(HTTP_OK, token, result, NULL, 0);
result["queued"] = queued;
return server->PrepareResult(queued ? HTTP_ACCEPTED : HTTP_NOT_ACCEPTABLE, token, result, NULL, 0);
});

// Keyboard key release
sprintf(buf, "%s/input/keyUp", plat);
RegisterEndpoint(server, buf, plat, "input", "Release a keyboard key (keyCode: MTKEY/SDL keycode; ASCII for printable chars)",
[this, server](const string token, json params, unsigned char *binaryData, int binaryDataSize) -> vector<char>*
{
if (!params.is_object() || !params.contains("keyCode") || !params.at("keyCode").is_number_integer())
{
return server->PrepareResult(HTTP_NOT_ACCEPTABLE, token, json(), NULL, 0);
}
uint32_t keyCode = params.at("keyCode").get<uint32_t>();
debugInterface->KeyboardUp(keyCode);
debugInterface->LockIoMutex();
bool queued = debugInterface->KeyboardUp(keyCode);
debugInterface->UnlockIoMutex();
json result;
result["keyCode"] = keyCode;
return server->PrepareResult(HTTP_OK, token, result, NULL, 0);
result["queued"] = queued;
return server->PrepareResult(queued ? HTTP_ACCEPTED : HTTP_NOT_ACCEPTABLE, token, result, NULL, 0);
});

// Emulator start — starts the emulation thread for this platform
Expand Down