In /emu_65c02 implement SDL2 joystick and Apple game port emulation
authorNick Downing <nick@ndcode.org>
Sun, 26 Jun 2022 09:19:33 +0000 (19:19 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 28 Jun 2022 03:21:38 +0000 (13:21 +1000)
emu_65c02/emu_65c02.c

index 0f8a1ff..707a1e9 100644 (file)
 #define HW_CLRAN2 0xc05d // RW annunciator 2 on
 #define HW_SETAN3 0xc05e // RW annunciator 2 off
 #define HW_CLRAN3 0xc05f // RW annunciator 3 on
+#define HW_TAPEIN 0xc060 // R
 #define HW_PB0 0xc061 // R switch input 0 / open-apple (orig: BUTN0)
 #define HW_PB1 0xc062 // R switch input 1 / closed-apple (orig: BUTN1)
-#define HW_PADDL0 0xc064
-#define HW_PADDL1 0xc065
+#define HW_PB2 0xc063 // R
+#define HW_PADDL0 0xc064 // R
+#define HW_PADDL1 0xc065 // R
+#define HW_PADDL2 0xc066 // R
+#define HW_PADDL3 0xc067 // R
 #define HW_PTRIG 0xc070
 #define HW_LC_BANK2_RAM_WP 0xc080 // RW
 #define HW_LC_BANK2_ROM_WE 0xc081 // RWx2
@@ -234,6 +238,26 @@ uint8_t c0x0_soft_switches_buf[C0X0_SOFT_SWITCHES_BUF_SIZE];
 #define C05X_SOFT_SWITCH_NOTAN3 0x80
 uint8_t c05x_soft_switches = C05X_SOFT_SWITCH_TEXT;
 
+#define C06X_INPUT_TAPEIN 1
+#define C06X_INPUT_PB0 2
+#define C06X_INPUT_PB1 4
+#define C06X_INPUT_PB2 8
+#define C06X_INPUT_PADDL0 0x10
+#define C06X_INPUT_PADDL1 0x20
+#define C06X_INPUT_PADDL2 0x40
+#define C06X_INPUT_PADDL3 0x80
+#define C06X_INPUT_PADDLX 0xf0
+uint8_t c06x_inputs;
+
+#define JOYSTICK_COUNT 0x900 // 9 cycles per loop of ROM_PREAD
+int joystick_count = JOYSTICK_COUNT;
+int joystick_axes[4] = {
+  JOYSTICK_COUNT,
+  JOYSTICK_COUNT,
+  JOYSTICK_COUNT,
+  JOYSTICK_COUNT
+};
+
 #define LC_BANK 4
 #define LC_BANK2 0
 #define LC_BANK1 4
@@ -516,6 +540,19 @@ void dos(char *line) {
 }
 
 uint8_t mem_read(uint16_t addr0, bool isDbg) {
+  // for joystick, for now count memory accesses as proxy for cycles
+  if (joystick_count < JOYSTICK_COUNT) {
+    ++joystick_count;
+    if (joystick_axes[0] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL0;
+    if (joystick_axes[1] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL1;
+    if (joystick_axes[2] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL2;
+    if (joystick_axes[3] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL3;
+  }
+
   int addr = addr0;
   if ((addr & 0xff00) != IO_PAGE) {
 #if APPLE_IIE
@@ -829,9 +866,19 @@ uint8_t mem_read(uint16_t addr0, bool isDbg) {
   case HW_CLRAN3 : // 0xc05f
     c05x_soft_switches |= 1 << ((addr >> 1) & 7);
     break;
-  case HW_PB0:
-  case HW_PB1:
-    return 0; // needed for proper boot with IIe ROM
+  case HW_TAPEIN: // 0xc060
+  case HW_PB0: // 0xc061
+  case HW_PB1: // 0xc062
+  case HW_PB2: // 0xc063
+  case HW_PADDL0: // 0xc064
+  case HW_PADDL1: // 0xc065
+  case HW_PADDL2: // 0xc066
+  case HW_PADDL3: // 0xc067
+    return ((c06x_inputs & (1 << (addr & 7))) != 0) << 7;
+  case HW_PTRIG:
+    joystick_count = 0;
+    c06x_inputs |= C06X_INPUT_PADDLX;
+    break;
   case HW_LC_BANK2_RAM_WP: // 0xc080
   case HW_LC_BANK2_ROM_WE: // 0xc081
   case HW_LC_BANK2_ROM_WP: // 0xc082
@@ -892,6 +939,19 @@ uint8_t mem_read(uint16_t addr0, bool isDbg) {
 }
 
 void mem_write(uint16_t addr0, uint8_t val) {
+  // for joystick, for now count memory accesses as proxy for cycles
+  if (joystick_count < JOYSTICK_COUNT) {
+    ++joystick_count;
+    if (joystick_axes[0] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL0;
+    if (joystick_axes[1] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL1;
+    if (joystick_axes[2] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL2;
+    if (joystick_axes[3] < joystick_count)
+      c06x_inputs &= ~C06X_INPUT_PADDL3;
+  }
+
   int addr = addr0;
 #if 0 // vectors
   if (addr >= 4 && addr < 0x80) {
@@ -1408,7 +1468,7 @@ int main(int argc, char **argv) {
     }
   }
 
-  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
+  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
     fprintf(stderr, "SDL_Init(): %s\n\n", SDL_GetError());
     exit(EXIT_FAILURE);
   }
@@ -1479,6 +1539,25 @@ int main(int argc, char **argv) {
  //);
   SDL_PauseAudioDevice(audio_device_id, 0);
 
+  SDL_Joystick *joystick = NULL;
+  if (SDL_NumJoysticks()) {
+    joystick = SDL_JoystickOpen(0);
+    if (joystick == NULL) {
+      fprintf(stderr, "SDL_JoystickOpen(): %s\n", SDL_GetError());
+      exit(EXIT_FAILURE);
+    }
+
+    if (SDL_JoystickEventState(SDL_ENABLE) != 1) {
+      fprintf(stderr, "SDL_JoystickEventState(): %s\n", SDL_GetError());
+      exit(EXIT_FAILURE);
+    }
+
+    // we should get the current position to use until first event,
+    // for now just centre it (no joystick is fully to right and down)
+    for (int i = 0; i < 4; ++i)
+      joystick_axes[i] = JOYSTICK_COUNT >> 1;
+  }
+
   cpu = vrEmu6502New(CPU_65C02, mem_read, mem_write);
   if (cpu == NULL) {
     perror("malloc()");
@@ -1585,6 +1664,40 @@ int main(int argc, char **argv) {
           }
         }
         break;
+      case SDL_JOYBUTTONUP:
+        {
+          SDL_JoyButtonEvent *e = (SDL_JoyButtonEvent *)&event;
+          if (
+            joystick &&
+              e->which == SDL_JoystickInstanceID(joystick) &&
+              e->button < 3
+          )
+            c06x_inputs &= ~(C06X_INPUT_PB0 << e->button);
+        }
+        break;
+      case SDL_JOYBUTTONDOWN:
+        {
+          SDL_JoyButtonEvent *e = (SDL_JoyButtonEvent *)&event;
+          if (
+            joystick &&
+              e->which == SDL_JoystickInstanceID(joystick) &&
+              e->button < 3
+          )
+            c06x_inputs |= C06X_INPUT_PB0 << e->button;
+        }
+        break;
+      case SDL_JOYAXISMOTION:
+        {
+          SDL_JoyAxisEvent *e = (SDL_JoyAxisEvent *)&event;
+          if (
+            joystick &&
+              e->which == SDL_JoystickInstanceID(joystick) &&
+              e->axis < 4
+          )
+            joystick_axes[e->axis] =
+              ((e->value + 0x8000) * JOYSTICK_COUNT) >> 16;
+        }
+        break;
       }
 
     // video update
@@ -1947,6 +2060,8 @@ jam:
 quit:
   vrEmu6502Destroy(cpu);
 
+  if (joystick)
+    SDL_JoystickClose(joystick);
   SDL_CloseAudioDevice(audio_device_id);
   SDL_DestroyRenderer(renderer);
   SDL_DestroyWindow(window);