diff --git a/README.md b/README.md index 7fc16b7..3059e4c 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ I wanted something: * Adds a small 'attack' filter when playing back to soften the often annoying 'click' of typical audio effects from the apple II. * Mouse Card -- mouse isn't captured like in some other emulators. * No Slot Clock + * Joystick (in a limited way...) * Smartport DMA 'hard drive' card * "Titan Accelerator //e" simulation, to turn on/off fast mode. * Terence's J Boldt [1MB ROM card](https://github.com/tjboldt/ProDOS-ROM-Drive), also because I own a couple! @@ -127,6 +128,7 @@ There are just a few keys that are mapped for anything useful. * A2Desktop PT3 player doesn't see keypresses. * Sometimes the emulator goes in 'slow mode', ie 0.2MHz. Likely the frame scheduler playing up. * Thats' about it really, all the other things I tried work + * Joystick support is a bit limited, no 'mapping' I used a (USB) 8bitdo NES30 Pro, and it works, but it's not perfect. But, I can play choplifter with it, so it's good enough for now... ## What it could do with * Not sure about keeping Nuklear, it does a lot bit it's hard work customizing anything diff --git a/nuklear/mii_mish.c b/nuklear/mii_mish.c index a7226d5..0a9063f 100644 --- a/nuklear/mii_mish.c +++ b/nuklear/mii_mish.c @@ -86,6 +86,12 @@ show_state: } return; } + if (!strcmp(argv[1], "analog")) { + printf("analog: %3d %3d %3d %3d\n", mii->analog.v[0].value, + mii->analog.v[1].value, mii->analog.v[2].value, + mii->analog.v[3].value); + return; + } if (!strcmp(argv[1], "trace")) { mii->trace_cpu = !mii->trace_cpu; printf("trace_cpu %d\n", mii->trace_cpu); diff --git a/nuklear/mii_thread.c b/nuklear/mii_thread.c index 31b14cc..5a57bfd 100644 --- a/nuklear/mii_thread.c +++ b/nuklear/mii_thread.c @@ -12,6 +12,15 @@ #include #include #include +#include +#include +#include + +// 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" @@ -31,19 +40,17 @@ mii_get_time() return time; } - -static pthread_t mii_thread; static bool mii_thread_running = false; static float default_fps = 60; mii_th_fifo_t signal_fifo; static void * -mii_thread_func( +mii_thread_cpu_regulator( void *arg) { mii_t *mii = (mii_t *) arg; mii_thread_running = true; - __uint128_t last_cycles = mii->cycles; + 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; @@ -117,8 +124,8 @@ mii_thread_func( last_frame_stamp += target_fps_us; // calculate the MHz - __uint128_t cycles = mii->cycles; - __uint128_t delta_cycles = cycles - last_cycles; + 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; } @@ -129,13 +136,87 @@ mii_thread_func( return NULL; } +#if HAVE_JOYSTICK +#include + +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_create(&mii_thread, NULL, mii_thread_func, mii); + + 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* diff --git a/src/mii.c b/src/mii.c index aa83ddf..8ea25c1 100644 --- a/src/mii.c +++ b/src/mii.c @@ -313,6 +313,11 @@ mii_access_soft_switches( res = true; mii_speaker_click(&mii->speaker); break; + case 0xc064 ... 0xc067: // Joystick, buttons + case 0xc070: // Analog reset + res = true; + mii_analog_access(mii, &mii->analog, addr, byte, write); + break; case 0xc068: res = true; // IIgs register, read by prodos tho diff --git a/src/mii_analog.c b/src/mii_analog.c new file mode 100644 index 0000000..e56e4dc --- /dev/null +++ b/src/mii_analog.c @@ -0,0 +1,59 @@ +/* + * mii_analog.c + * + * Copyright (C) 2023 Michel Pollet + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +#include "mii.h" +#include "mii_analog.h" + +void +mii_analog_init( + struct mii_t *mii, + mii_analog_t * a ) +{ + memset(a, 0, sizeof(*a)); +} + +/* + * https://retrocomputing.stackexchange.com/questions/15093/how-do-i-read-the-position-of-an-apple-ii-joystick + */ +void +mii_analog_access( + mii_t *mii, + mii_analog_t * a, + uint16_t addr, + uint8_t * byte, + bool write) +{ + if (write) + return; + switch (addr) { + case 0xc070: { + // multiplying by mii->speed allows reading joystick in 'fast' mode, + // this basically simulate slowing down just for the joystick reading + + /* TODO: According to various artivles, the multiplier ought + * to be 11, but we're not making the count here, which means it's + * likely the emulated core is missing a cycle for one instruction + * somewhere... */ + for (int i = 0; i < 4; i++) { + a->v[i].decay = mii->cycles + + ((a->v[i].value * 10.10) * mii->speed); + // printf("joystick %d: %d\n", i, a->v[i].value); + } + } break; + case 0xc064 ... 0xc067: { + addr -= 0xc064; + *byte = mii->cycles <= a->v[addr].decay ? 0x80 : 0x00; + } break; + } +} + diff --git a/src/mii_analog.h b/src/mii_analog.h new file mode 100644 index 0000000..4f4aab8 --- /dev/null +++ b/src/mii_analog.h @@ -0,0 +1,33 @@ +/* + * mii_analog.h + * + * Copyright (C) 2023 Michel Pollet + * + * SPDX-License-Identifier: MIT + */ +#pragma once + +#include "mii_types.h" + + +typedef struct mii_analog_t { + struct { + uint8_t value; + mii_cycles_t decay; + } v[4]; +} mii_analog_t; + +struct mii_t; + +void +mii_analog_init( + struct mii_t *mii, + mii_analog_t * analog ); + +void +mii_analog_access( + mii_t *mii, + mii_analog_t * analog, + uint16_t addr, + uint8_t * byte, + bool write);