mii_emu/nuklear/mii_thread.c
Michel Pollet d53b0bceff Added joystick support
It is limited for now, hard coded to mine. Still need some sort of way
to specify which button to map to the emulated joytick. Same with the
axis, currently default to 0,1

Signed-off-by: Michel Pollet <buserror@gmail.com>
2023-10-28 16:59:08 +01:00

228 lines
5.0 KiB
C

/*
* mii_thread.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
// probably should wrap these into a HAVE_JOYSTICK define for non-linux
#ifndef HAVE_JOYSTICK
#define HAVE_JOYSTICK 1
#endif
#include "mii.h"
#include "mii_thread.h"
typedef uint64_t mii_time_t;
enum {
MII_TIME_RES = 1,
MII_TIME_SECOND = 1000000,
MII_TIME_MS = (MII_TIME_SECOND/1000),
};
mii_time_t
mii_get_time()
{
struct timespec tim;
clock_gettime(CLOCK_MONOTONIC_RAW, &tim);
uint64_t time = ((uint64_t)tim.tv_sec) * (1000000 / MII_TIME_RES) +
tim.tv_nsec / (1000 * MII_TIME_RES);
return time;
}
static bool mii_thread_running = false;
static float default_fps = 60;
mii_th_fifo_t signal_fifo;
static void *
mii_thread_cpu_regulator(
void *arg)
{
mii_t *mii = (mii_t *) arg;
mii_thread_running = true;
mii_cycles_t last_cycles = mii->cycles;
uint32_t running = 1;
unsigned long target_fps_us = 1000000 / default_fps;
long sleep_time = target_fps_us;
//mii_time_t base = mii_get_time(NULL);
uint32_t last_frame = mii->video.frame_count;
mii_time_t last_frame_stamp = mii_get_time();
while (mii_thread_running) {
mii_th_signal_t sig;
while (!mii_th_fifo_isempty(&signal_fifo)) {
sig = mii_th_fifo_read(&signal_fifo);
switch (sig.cmd) {
case SIGNAL_RESET:
mii_reset(mii, sig.data);
break;
case SIGNAL_STOP:
mii_dump_run_trace(mii);
mii_dump_trace_state(mii);
mii->state = MII_STOPPED;
break;
case SIGNAL_STEP:
mii->state = MII_STEP;
running = 1;
break;
case SIGNAL_RUN:
mii->state = MII_RUNNING;
last_frame_stamp = mii_get_time();
running = 1;
break;
}
}
if (mii->state != MII_STOPPED)
mii_run(mii);
switch (mii->state) {
case MII_STOPPED:
usleep(1000);
break;
case MII_STEP:
if (running) {
running--;
mii_dump_trace_state(mii);
usleep(1000);
running = 1;
if (mii->trace.step_inst)
mii->trace.step_inst--;
if (mii->trace.step_inst == 0)
mii->state = MII_STOPPED;
}
break;
case MII_RUNNING:
break;
case MII_TERMINATE:
mii_thread_running = false;
break;
}
if (mii->video.frame_count != last_frame) {
sleep_time = target_fps_us;
mii_time_t now = mii_get_time();
if (mii->state == MII_RUNNING) {
mii_time_t delta = now - last_frame_stamp;
// printf("frame time %d/%d sleep time %d\n",
// (int)delta, (int)target_fps_us,
// (int)target_fps_us - delta);
sleep_time = target_fps_us - delta;
if (sleep_time < 0)
sleep_time = 0;
last_frame = mii->video.frame_count;
while (last_frame_stamp <= now)
last_frame_stamp += target_fps_us;
// calculate the MHz
mii_cycles_t cycles = mii->cycles;
mii_cycles_t delta_cycles = cycles - last_cycles;
last_cycles = cycles;
mii->speed_current = delta_cycles / (float)target_fps_us;
}
usleep(sleep_time);
}
}
mii_dispose(mii);
return NULL;
}
#if HAVE_JOYSTICK
#include <linux/joystick.h>
static void *
mii_thread_joystick(
void *arg)
{
int fd = open("/dev/input/js0", O_RDONLY);
if (fd < 0) {
printf("No joystick found\n");
return NULL;
}
uint8_t axes, buttons;
if (ioctl(fd, JSIOCGAXES, &axes) == -1 ||
ioctl(fd, JSIOCGBUTTONS, &buttons) == -1) {
perror(__func__);
return NULL;
}
struct js_event event;
mii_t *mii = (mii_t *)arg;
mii->analog.v[0].value = 127;
mii->analog.v[1].value = 127;
do {
ssize_t rd = read(fd, &event, sizeof(event));
if (rd != sizeof(event)) {
perror(__func__);
break;
}
switch (event.type) {
case JS_EVENT_BUTTON:
// printf("button %u %s\n", event.number, event.value ? "pressed" : "released");
switch (event.number) {
case 2 ... 3:
mii_bank_poke(&mii->bank[MII_BANK_MAIN],
0xc061 + (event.number - 2),
event.value ? 0x80 : 0);
break;
case 4 ... 5:
mii_bank_poke(&mii->bank[MII_BANK_MAIN],
0xc061 + (event.number - 4),
event.value ? 0x80 : 0);
break;
}
break;
case JS_EVENT_AXIS:
switch (event.number) {
case 0 ... 1: {// X
uint32_t v = (event.value + 0x8000) / 256;
if (v > 255)
v = 255;
mii->analog.v[event.number ? 1 : 0].value = v;
// printf("axis %u %6d %3dx%3d\n"
// event.number, event.value,
// mii->analog.v[0].value, mii->analog.v[1].value);
} break;
}
break;
default:
/* Ignore init events. */
break;
}
} while (1);
close(fd);
printf("Joystick thread terminated\n");
return NULL;
}
#endif
void
mii_thread_start(
mii_t *mii)
{
const mii_th_fifo_t zero = {};
signal_fifo = zero;
pthread_t thread;
pthread_create(&thread, NULL, mii_thread_cpu_regulator, mii);
#if HAVE_JOYSTICK
pthread_create(&thread, NULL, mii_thread_joystick, mii);
#endif
}
struct mii_th_fifo_t*
mii_thread_get_fifo(
struct mii_t *mii)
{
return &signal_fifo;
}