199 lines
7 KiB
C
199 lines
7 KiB
C
/*
|
|
* test_hatari_0001.c
|
|
*
|
|
* Unit test for hatari-0001: IKBD_RunKeyboardCommand O(C) linear scan
|
|
* replaced with O(1) dispatch table.
|
|
*
|
|
* Tests that the dispatch table correctly maps all known command bytes
|
|
* and returns -1 for unknown bytes, verifying O(1) correctness.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
/* Minimal reproduction of the KeyboardCommands table and dispatch logic. */
|
|
|
|
typedef struct {
|
|
uint8_t Command;
|
|
uint8_t NumParameters;
|
|
void (*pCallFunction)(void);
|
|
} KeyboardCommand;
|
|
|
|
/* Representative subset of IKBD commands from Hatari's ikbd.c */
|
|
static const KeyboardCommand KeyboardCommands[] = {
|
|
{ 0x80, 2, NULL }, /* Reset */
|
|
{ 0x07, 2, NULL }, /* MouseAction */
|
|
{ 0x08, 1, NULL }, /* RelMouseMode */
|
|
{ 0x09, 5, NULL }, /* AbsMouseMode */
|
|
{ 0x0A, 3, NULL }, /* MouseCursorKeycodes */
|
|
{ 0x0B, 3, NULL }, /* SetMouseThreshold */
|
|
{ 0x0C, 3, NULL }, /* SetMouseScale */
|
|
{ 0x0D, 1, NULL }, /* ReadAbsMousePos */
|
|
{ 0x0E, 6, NULL }, /* SetInternalMousePos */
|
|
{ 0x0F, 1, NULL }, /* SetYAxisDown */
|
|
{ 0x10, 1, NULL }, /* SetYAxisUp */
|
|
{ 0x11, 1, NULL }, /* StartKeyboardTransfer */
|
|
{ 0x12, 1, NULL }, /* TurnMouseOff */
|
|
{ 0x13, 1, NULL }, /* StopKeyboardTransfer */
|
|
{ 0x14, 1, NULL }, /* ReturnJoystickAuto */
|
|
{ 0x15, 1, NULL }, /* StopJoystick */
|
|
{ 0x16, 1, NULL }, /* ReturnJoystick */
|
|
{ 0x17, 2, NULL }, /* SetJoystickMonitoring */
|
|
{ 0x18, 1, NULL }, /* SetJoystickFireDuration */
|
|
{ 0x19, 7, NULL }, /* SetCursorForJoystick */
|
|
{ 0x1A, 1, NULL }, /* DisableJoysticks */
|
|
{ 0x1B, 7, NULL }, /* SetClock */
|
|
{ 0x1C, 1, NULL }, /* ReadClock */
|
|
{ 0x20, 4, NULL }, /* LoadMemory */
|
|
{ 0x21, 3, NULL }, /* ReadMemory */
|
|
{ 0x22, 3, NULL }, /* Execute */
|
|
/* Report messages */
|
|
{ 0x87, 1, NULL }, /* ReportMouseAction */
|
|
{ 0x88, 1, NULL }, /* ReportMouseMode */
|
|
{ 0x89, 1, NULL }, /* ReportMouseMode */
|
|
{ 0x8A, 1, NULL }, /* ReportMouseMode */
|
|
{ 0x8B, 1, NULL }, /* ReportMouseThreshold */
|
|
{ 0x8C, 1, NULL }, /* ReportMouseScale */
|
|
{ 0x8F, 1, NULL }, /* ReportMouseVertical */
|
|
{ 0x90, 1, NULL }, /* ReportMouseVertical */
|
|
{ 0x92, 1, NULL }, /* ReportMouseAvailability */
|
|
{ 0x94, 1, NULL }, /* ReportJoystickMode */
|
|
{ 0x95, 1, NULL }, /* ReportJoystickMode */
|
|
{ 0x99, 1, NULL }, /* ReportJoystickMode */
|
|
{ 0x9A, 1, NULL }, /* ReportJoystickAvailability */
|
|
{ 0xFF, 0, NULL } /* Terminator */
|
|
};
|
|
|
|
/* O(C) original: linear scan */
|
|
static int ikbd_find_command_linear(uint8_t cmd)
|
|
{
|
|
int i = 0;
|
|
while (KeyboardCommands[i].Command != 0xFF) {
|
|
if (KeyboardCommands[i].Command == cmd)
|
|
return i;
|
|
i++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* O(1) patched: dispatch table */
|
|
static int IKBDCmdIndex[256];
|
|
|
|
static void ikbd_init_dispatch(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 256; i++)
|
|
IKBDCmdIndex[i] = -1;
|
|
for (i = 0; KeyboardCommands[i].Command != 0xFF; i++)
|
|
IKBDCmdIndex[KeyboardCommands[i].Command] = i;
|
|
}
|
|
|
|
static int ikbd_find_command_dispatch(uint8_t cmd)
|
|
{
|
|
return IKBDCmdIndex[cmd];
|
|
}
|
|
|
|
/* Count iterations in linear scan for given command byte */
|
|
static int ikbd_linear_iterations(uint8_t cmd)
|
|
{
|
|
int i = 0, count = 0;
|
|
while (KeyboardCommands[i].Command != 0xFF) {
|
|
count++;
|
|
if (KeyboardCommands[i].Command == cmd)
|
|
return count;
|
|
i++;
|
|
}
|
|
return count; /* not found, scanned all */
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
int i, linear_idx, dispatch_idx;
|
|
int total_tests = 0, pass = 0;
|
|
int max_linear_iter = 0, total_linear_iter = 0;
|
|
|
|
ikbd_init_dispatch();
|
|
|
|
/* Test 1: dispatch table matches linear scan for all 256 byte values */
|
|
printf("Test 1: dispatch table correctness for all 256 command bytes\n");
|
|
for (i = 0; i < 256; i++) {
|
|
linear_idx = ikbd_find_command_linear((uint8_t)i);
|
|
dispatch_idx = ikbd_find_command_dispatch((uint8_t)i);
|
|
total_tests++;
|
|
if (linear_idx == dispatch_idx) {
|
|
pass++;
|
|
} else {
|
|
printf(" FAIL byte=0x%02x linear=%d dispatch=%d\n", i, linear_idx, dispatch_idx);
|
|
}
|
|
}
|
|
printf(" %d/%d passed\n", pass, total_tests);
|
|
assert(pass == total_tests);
|
|
|
|
/* Test 2: O(1) vs O(C) -- dispatch always 1 step, linear varies */
|
|
printf("Test 2: iteration count reduction\n");
|
|
{
|
|
/* Known commands: reset (first entry scanned), 0x9A (last entry scanned) */
|
|
int iter_first = ikbd_linear_iterations(0x80); /* 0x80 is first */
|
|
int iter_last = ikbd_linear_iterations(0x9A); /* 0x9A is near last */
|
|
int iter_unknown = ikbd_linear_iterations(0x01); /* unknown -- scans all */
|
|
|
|
/* Count total commands (excluding terminator) */
|
|
int num_cmds = 0;
|
|
while (KeyboardCommands[num_cmds].Command != 0xFF) num_cmds++;
|
|
|
|
printf(" first-cmd iterations: %d (linear) vs 1 (dispatch)\n", iter_first);
|
|
printf(" last-cmd iterations: %d (linear) vs 1 (dispatch)\n", iter_last);
|
|
printf(" unknown-cmd iterations: %d (linear) vs 1 (dispatch)\n", iter_unknown);
|
|
printf(" total commands in table: %d\n", num_cmds);
|
|
|
|
assert(iter_first >= 1);
|
|
assert(iter_last > iter_first); /* later entries cost more in linear */
|
|
assert(iter_unknown >= num_cmds); /* unknown scans everything */
|
|
|
|
/* dispatch is O(1): always 1 table lookup regardless */
|
|
total_linear_iter += iter_first + iter_last + iter_unknown;
|
|
max_linear_iter = iter_unknown;
|
|
|
|
printf(" PASS: dispatch eliminates up to %dx overhead vs worst-case linear\n",
|
|
max_linear_iter);
|
|
}
|
|
|
|
/* Test 3: NumParameters preserved correctly */
|
|
printf("Test 3: NumParameters preserved in dispatch table\n");
|
|
{
|
|
int idx;
|
|
/* 0x09 AbsMouseMode expects 5 parameters */
|
|
idx = ikbd_find_command_dispatch(0x09);
|
|
assert(idx >= 0);
|
|
assert(KeyboardCommands[idx].NumParameters == 5);
|
|
|
|
/* 0x19 SetCursorForJoystick expects 7 parameters */
|
|
idx = ikbd_find_command_dispatch(0x19);
|
|
assert(idx >= 0);
|
|
assert(KeyboardCommands[idx].NumParameters == 7);
|
|
|
|
/* 0x08 RelMouseMode expects 1 parameter */
|
|
idx = ikbd_find_command_dispatch(0x08);
|
|
assert(idx >= 0);
|
|
assert(KeyboardCommands[idx].NumParameters == 1);
|
|
|
|
printf(" PASS: NumParameters correct for 0x09 (5), 0x19 (7), 0x08 (1)\n");
|
|
}
|
|
|
|
/* Test 4: unknown commands return -1 */
|
|
printf("Test 4: unknown command bytes return -1\n");
|
|
{
|
|
/* 0x01, 0x02, 0x03 are not in the IKBD command set */
|
|
assert(ikbd_find_command_dispatch(0x01) == -1);
|
|
assert(ikbd_find_command_dispatch(0x02) == -1);
|
|
assert(ikbd_find_command_dispatch(0x03) == -1);
|
|
assert(ikbd_find_command_dispatch(0x00) == -1);
|
|
printf(" PASS: 0x00, 0x01, 0x02, 0x03 all return -1\n");
|
|
}
|
|
|
|
printf("\nAll tests passed. O(1) dispatch table eliminates O(%d) linear scan.\n",
|
|
max_linear_iter);
|
|
return 0;
|
|
}
|