]> git.sesse.net Git - pistorm/blob - emulator.c
mouse wheel support
[pistorm] / emulator.c
1 #include <assert.h>
2 #include <dirent.h>
3 #include <endian.h>
4 #include <fcntl.h>
5 #include <pthread.h>
6 #include <sched.h>
7 #include <signal.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <sys/ioctl.h>
17 #include "m68k.h"
18 #include "main.h"
19 #include "platforms/platforms.h"
20 #include "input/input.h"
21
22 #include "platforms/amiga/Gayle.h"
23 #include "platforms/amiga/gayle-ide/ide.h"
24 #include "platforms/amiga/amiga-registers.h"
25 #include "platforms/amiga/rtg/rtg.h"
26 #include "platforms/amiga/hunk-reloc.h"
27 #include "platforms/amiga/piscsi/piscsi.h"
28 #include "platforms/amiga/piscsi/piscsi-enums.h"
29 #include "platforms/amiga/net/pi-net.h"
30 #include "platforms/amiga/net/pi-net-enums.h"
31 #include "gpio/ps_protocol.h"
32
33 unsigned char read_ranges;
34 unsigned int read_addr[8];
35 unsigned int read_upper[8];
36 unsigned char *read_data[8];
37 unsigned char write_ranges;
38 unsigned int write_addr[8];
39 unsigned int write_upper[8];
40 unsigned char *write_data[8];
41
42 int kb_hook_enabled = 0;
43 int mouse_hook_enabled = 0;
44 int cpu_emulation_running = 1;
45
46 uint8_t mouse_dx = 0, mouse_dy = 0;
47 uint8_t mouse_buttons = 0;
48 uint8_t mouse_extra = 0;
49
50 extern uint8_t gayle_int;
51 extern uint8_t gayle_ide_enabled;
52 extern uint8_t gayle_emulation_enabled;
53 extern uint8_t gayle_a4k_int;
54 extern volatile unsigned int *gpio;
55 extern volatile uint16_t srdata;
56 extern uint8_t realtime_graphics_debug;
57 uint8_t realtime_disassembly, int2_enabled = 0;
58 uint32_t do_disasm = 0, old_level;
59
60 char disasm_buf[4096];
61
62 #define KICKBASE 0xF80000
63 #define KICKSIZE 0x7FFFF
64
65 int mem_fd, mouse_fd = -1, keyboard_fd = -1;
66 int mem_fd_gpclk;
67 int irq;
68 int gayleirq;
69
70 //#define MUSASHI_HAX
71
72 #ifdef MUSASHI_HAX
73 #include "m68kcpu.h"
74 extern m68ki_cpu_core m68ki_cpu;
75 extern int m68ki_initial_cycles;
76 extern int m68ki_remaining_cycles;
77
78 #define M68K_SET_IRQ(i) old_level = CPU_INT_LEVEL; \
79         CPU_INT_LEVEL = (i << 8); \
80         if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700) \
81                 m68ki_cpu.nmi_pending = TRUE;
82 #define M68K_END_TIMESLICE      m68ki_initial_cycles = GET_CYCLES(); \
83         SET_CYCLES(0);
84 #else
85 #define M68K_SET_IRQ m68k_set_irq
86 #define M68K_END_TIMESLICE m68k_end_timeslice()
87 #endif
88
89 #define NOP asm("nop"); asm("nop"); asm("nop"); asm("nop");
90
91 // Configurable emulator options
92 unsigned int cpu_type = M68K_CPU_TYPE_68000;
93 unsigned int loop_cycles = 300, irq_status = 0;
94 struct emulator_config *cfg = NULL;
95 char keyboard_file[256] = "/dev/input/event1";
96
97 uint64_t trig_irq = 0, serv_irq = 0;
98
99 void *iplThread(void *args) {
100   printf("IPL thread running\n");
101
102   while (1) {
103     if (!gpio_get_irq()) {
104       irq = 1;
105       M68K_END_TIMESLICE;
106     }
107     else {
108       irq = 0;
109     }
110
111     if (gayle_ide_enabled) {
112       if (((gayle_int & 0x80) || gayle_a4k_int) && (get_ide(0)->drive[0].intrq || get_ide(0)->drive[1].intrq)) {
113         //get_ide(0)->drive[0].intrq = 0;
114         gayleirq = 1;
115         M68K_END_TIMESLICE;
116       }
117       else
118         gayleirq = 0;
119     }
120     //usleep(0);
121     NOP NOP NOP NOP NOP NOP
122     NOP NOP NOP NOP NOP NOP
123     NOP NOP NOP NOP NOP NOP
124     NOP NOP NOP NOP NOP NOP
125     NOP NOP NOP NOP NOP NOP
126     NOP NOP NOP NOP NOP NOP
127     NOP NOP NOP NOP NOP NOP
128     NOP NOP NOP NOP NOP NOP
129     NOP NOP NOP NOP NOP NOP
130     NOP NOP NOP NOP NOP NOP
131     NOP NOP NOP NOP NOP NOP
132     NOP NOP NOP NOP NOP NOP
133   }
134   return args;
135 }
136
137 void stop_cpu_emulation(uint8_t disasm_cur) {
138   M68K_END_TIMESLICE;
139   if (disasm_cur) {
140     m68k_disassemble(disasm_buf, m68k_get_reg(NULL, M68K_REG_PC), cpu_type);
141     printf("REGA: 0:$%.8X 1:$%.8X 2:$%.8X 3:$%.8X 4:$%.8X 5:$%.8X 6:$%.8X 7:$%.8X\n", m68k_get_reg(NULL, M68K_REG_A0), m68k_get_reg(NULL, M68K_REG_A1), m68k_get_reg(NULL, M68K_REG_A2), m68k_get_reg(NULL, M68K_REG_A3), \
142             m68k_get_reg(NULL, M68K_REG_A4), m68k_get_reg(NULL, M68K_REG_A5), m68k_get_reg(NULL, M68K_REG_A6), m68k_get_reg(NULL, M68K_REG_A7));
143     printf("REGD: 0:$%.8X 1:$%.8X 2:$%.8X 3:$%.8X 4:$%.8X 5:$%.8X 6:$%.8X 7:$%.8X\n", m68k_get_reg(NULL, M68K_REG_D0), m68k_get_reg(NULL, M68K_REG_D1), m68k_get_reg(NULL, M68K_REG_D2), m68k_get_reg(NULL, M68K_REG_D3), \
144             m68k_get_reg(NULL, M68K_REG_D4), m68k_get_reg(NULL, M68K_REG_D5), m68k_get_reg(NULL, M68K_REG_D6), m68k_get_reg(NULL, M68K_REG_D7));
145     printf("%.8X (%.8X)]] %s\n", m68k_get_reg(NULL, M68K_REG_PC), (m68k_get_reg(NULL, M68K_REG_PC) & 0xFFFFFF), disasm_buf);
146     realtime_disassembly = 1;
147   }
148
149   cpu_emulation_running = 0;
150   do_disasm = 0;
151 }
152
153 unsigned int ovl;
154 static volatile unsigned char maprom;
155
156 void sigint_handler(int sig_num) {
157   //if (sig_num) { }
158   //cpu_emulation_running = 0;
159
160   //return;
161   printf("Received sigint %d, exiting.\n", sig_num);
162   if (mouse_fd != -1)
163     close(mouse_fd);
164   if (mem_fd)
165     close(mem_fd);
166
167   if (cfg->platform->shutdown) {
168     cfg->platform->shutdown(cfg);
169   }
170
171   printf("IRQs triggered: %lld\n", trig_irq);
172   printf("IRQs serviced: %lld\n", serv_irq);
173
174   exit(0);
175 }
176
177 int main(int argc, char *argv[]) {
178   int g;
179   //const struct sched_param priority = {99};
180
181   // Some command line switch stuffles
182   for (g = 1; g < argc; g++) {
183     if (strcmp(argv[g], "--cpu_type") == 0 || strcmp(argv[g], "--cpu") == 0) {
184       if (g + 1 >= argc) {
185         printf("%s switch found, but no CPU type specified.\n", argv[g]);
186       } else {
187         g++;
188         cpu_type = get_m68k_cpu_type(argv[g]);
189       }
190     }
191     else if (strcmp(argv[g], "--config-file") == 0 || strcmp(argv[g], "--config") == 0) {
192       if (g + 1 >= argc) {
193         printf("%s switch found, but no config filename specified.\n", argv[g]);
194       } else {
195         g++;
196         cfg = load_config_file(argv[g]);
197       }
198     }
199     else if (strcmp(argv[g], "--keyboard-file") == 0 || strcmp(argv[g], "--kbfile") == 0) {
200       if (g + 1 >= argc) {
201         printf("%s switch found, but no keyboard device path specified.\n", argv[g]);
202       } else {
203         g++;
204         strcpy(keyboard_file, argv[g]);
205       }
206     }
207   }
208
209   if (!cfg) {
210     printf("No config file specified. Trying to load default.cfg...\n");
211     cfg = load_config_file("default.cfg");
212     if (!cfg) {
213       printf("Couldn't load default.cfg, empty emulator config will be used.\n");
214       cfg = (struct emulator_config *)calloc(1, sizeof(struct emulator_config));
215       if (!cfg) {
216         printf("Failed to allocate memory for emulator config!\n");
217         return 1;
218       }
219       memset(cfg, 0x00, sizeof(struct emulator_config));
220     }
221   }
222
223   if (cfg) {
224     if (cfg->cpu_type) cpu_type = cfg->cpu_type;
225     if (cfg->loop_cycles) loop_cycles = cfg->loop_cycles;
226
227     if (!cfg->platform)
228       cfg->platform = make_platform_config("none", "generic");
229     cfg->platform->platform_initial_setup(cfg);
230   }
231
232   if (cfg->mouse_enabled) {
233     mouse_fd = open(cfg->mouse_file, O_RDWR | O_NONBLOCK);
234     if (mouse_fd == -1) {
235       printf("Failed to open %s, can't enable mouse hook.\n", cfg->mouse_file);
236       cfg->mouse_enabled = 0;
237     } else {
238       /**
239        * *-*-*-* magic numbers! *-*-*-*
240        * great, so waaaay back in the history of the pc, the ps/2 protocol set the standard for mice
241        * and in the process, the mouse sample rate was defined as a way of putting mice into vendor-specific modes.
242        * as the ancient gpm command explains, almost everything except incredibly old mice talk the IntelliMouse
243        * protocol, which reports four bytes. by default, every mouse starts in 3-byte mode (don't report wheel or
244        * additional buttons) until imps2 magic is sent. so, command $f3 is "set sample rate", followed by a byte.
245        */
246       uint8_t mouse_init[] = { 0xf4, 0xf3, 0x64 }; // enable, then set sample rate 100
247       uint8_t imps2_init[] = { 0xf3, 0xc8, 0xf3, 0x64, 0xf3, 0x50 }; // magic sequence; set sample 200, 100, 80
248       if (write(mouse_fd, mouse_init, sizeof(mouse_init)) != -1) {
249         if (write(mouse_fd, imps2_init, sizeof(imps2_init)) == -1)
250           printf("[MOUSE] Couldn't enable scroll wheel events; is this mouse from the 1980s?\n");
251       } else
252         printf("[MOUSE] Mouse didn't respond to normal PS/2 init; have you plugged a brick in by mistake?\n");
253     }
254   }
255
256   keyboard_fd = open(keyboard_file, O_RDONLY | O_NONBLOCK);
257   if (keyboard_fd == -1) {
258     printf("Failed to open keyboard event source.\n");
259   }
260
261   InitGayle();
262
263   signal(SIGINT, sigint_handler);
264   /*setup_io();
265
266   //goto skip_everything;
267
268   // Enable 200MHz CLK output on GPIO4, adjust divider and pll source depending
269   // on pi model
270   printf("Enable 200MHz GPCLK0 on GPIO4\n");
271   gpio_enable_200mhz();
272
273   // reset cpld statemachine first
274
275   write_reg(0x01);
276   usleep(100);
277   usleep(1500);
278   write_reg(0x00);
279   usleep(100);
280
281   // reset amiga and statemachine
282   skip_everything:;
283
284   usleep(1500);
285
286   m68k_init();
287   printf("Setting CPU type to %d.\n", cpu_type);
288   m68k_set_cpu_type(cpu_type);
289   cpu_pulse_reset();
290
291   if (maprom == 1) {
292     m68k_set_reg(M68K_REG_PC, 0xF80002);
293   } else {
294     m68k_set_reg(M68K_REG_PC, 0x0);
295   }*/
296   ps_setup_protocol();
297   ps_reset_state_machine();
298   ps_pulse_reset();
299
300   usleep(1500);
301   m68k_init();
302   printf("Setting CPU type to %d.\n", cpu_type);
303   m68k_set_cpu_type(cpu_type);
304   cpu_pulse_reset();
305
306   char c = 0, c_code = 0, c_type = 0;
307   uint32_t last_irq = 0;
308
309   pthread_t id;
310   int err;
311   err = pthread_create(&id, NULL, &iplThread, NULL);
312   if (err != 0)
313     printf("can't create IPL thread :[%s]", strerror(err));
314   else
315     printf("IPL Thread created successfully\n");
316
317   m68k_pulse_reset();
318   while (42) {
319     if (mouse_hook_enabled) {
320       get_mouse_status(&mouse_dx, &mouse_dy, &mouse_buttons, &mouse_extra);
321     }
322
323     if (realtime_disassembly && (do_disasm || cpu_emulation_running)) {
324       m68k_disassemble(disasm_buf, m68k_get_reg(NULL, M68K_REG_PC), cpu_type);
325       printf("REGA: 0:$%.8X 1:$%.8X 2:$%.8X 3:$%.8X 4:$%.8X 5:$%.8X 6:$%.8X 7:$%.8X\n", m68k_get_reg(NULL, M68K_REG_A0), m68k_get_reg(NULL, M68K_REG_A1), m68k_get_reg(NULL, M68K_REG_A2), m68k_get_reg(NULL, M68K_REG_A3), \
326               m68k_get_reg(NULL, M68K_REG_A4), m68k_get_reg(NULL, M68K_REG_A5), m68k_get_reg(NULL, M68K_REG_A6), m68k_get_reg(NULL, M68K_REG_A7));
327       printf("REGD: 0:$%.8X 1:$%.8X 2:$%.8X 3:$%.8X 4:$%.8X 5:$%.8X 6:$%.8X 7:$%.8X\n", m68k_get_reg(NULL, M68K_REG_D0), m68k_get_reg(NULL, M68K_REG_D1), m68k_get_reg(NULL, M68K_REG_D2), m68k_get_reg(NULL, M68K_REG_D3), \
328               m68k_get_reg(NULL, M68K_REG_D4), m68k_get_reg(NULL, M68K_REG_D5), m68k_get_reg(NULL, M68K_REG_D6), m68k_get_reg(NULL, M68K_REG_D7));
329       printf("%.8X (%.8X)]] %s\n", m68k_get_reg(NULL, M68K_REG_PC), (m68k_get_reg(NULL, M68K_REG_PC) & 0xFFFFFF), disasm_buf);
330       if (do_disasm)
331         do_disasm--;
332       m68k_execute(1);
333     }
334     else {
335       if (cpu_emulation_running)
336         m68k_execute(loop_cycles);
337     }
338
339     if (irq) {
340       unsigned int status = read_reg();
341       if (((status & 0xe000) >> 13) != last_irq) {
342         last_irq = ((status & 0xe000) >> 13);
343         serv_irq++;
344         M68K_SET_IRQ(last_irq);
345       }
346     }
347     else if (gayleirq && int2_enabled) {
348       write16(0xdff09c, 0x8000 | (1 << 3));
349       last_irq = 2;
350       M68K_SET_IRQ(2);
351     }
352     else {
353       if (last_irq != 0) {
354         last_irq = 0;
355         M68K_SET_IRQ(0);
356       }
357     }
358
359     while (get_key_char(&c, &c_code, &c_type)) {
360       if (c && c == cfg->keyboard_toggle_key && !kb_hook_enabled) {
361         kb_hook_enabled = 1;
362         printf("Keyboard hook enabled.\n");
363       }
364       else if (kb_hook_enabled) {
365         if (c == 0x1B && c_type) {
366           kb_hook_enabled = 0;
367           printf("Keyboard hook disabled.\n");
368         }
369         else {
370           /*printf("Key code: %.2X - ", c_code);
371           switch (c_type) {
372             case 0:
373               printf("released.\n");
374               break;
375             case 1:
376               printf("pressed.\n");
377               break;
378             case 2:
379               printf("repeat.\n");
380               break;
381             default:
382               printf("unknown.\n");
383               break;
384           }*/
385           if (queue_keypress(c_code, c_type, cfg->platform->id) && int2_enabled) {
386             last_irq = 2;
387             M68K_SET_IRQ(2);
388           }
389         }
390       }
391
392       // pause pressed; trigger nmi (int level 7)
393       if (c == 0x01 && c_type) {
394         printf("[INT] Sending NMI\n");
395         last_irq = 7;
396         M68K_SET_IRQ(7);
397       }
398
399       if (!kb_hook_enabled && c_type) {
400         if (c && c == cfg->mouse_toggle_key) {
401           mouse_hook_enabled ^= 1;
402           printf("Mouse hook %s.\n", mouse_hook_enabled ? "enabled" : "disabled");
403           mouse_dx = mouse_dy = mouse_buttons = mouse_extra = 0;
404         }
405         if (c == 'r') {
406           cpu_emulation_running ^= 1;
407           printf("CPU emulation is now %s\n", cpu_emulation_running ? "running" : "stopped");
408         }
409         if (c == 'g') {
410           realtime_graphics_debug ^= 1;
411           printf("Real time graphics debug is now %s\n", realtime_graphics_debug ? "on" : "off");
412         }
413         if (c == 'R') {
414           cpu_pulse_reset();
415           //m68k_pulse_reset();
416           printf("CPU emulation reset.\n");
417         }
418         if (c == 'q') {
419           printf("Quitting and exiting emulator.\n");
420           goto stop_cpu_emulation;
421         }
422         if (c == 'd') {
423           realtime_disassembly ^= 1;
424           do_disasm = 1;
425           printf("Real time disassembly is now %s\n", realtime_disassembly ? "on" : "off");
426         }
427         if (c == 'D') {
428           int r = get_mapped_item_by_address(cfg, 0x08000000);
429           if (r != -1) {
430             printf("Dumping first 16MB of mapped range %d.\n", r);
431             FILE *dmp = fopen("./memdmp.bin", "wb+");
432             fwrite(cfg->map_data[r], 16 * SIZE_MEGA, 1, dmp);
433             fclose(dmp);
434           }
435         }
436         if (c == 's' && realtime_disassembly) {
437           do_disasm = 1;
438         }
439         if (c == 'S' && realtime_disassembly) {
440           do_disasm = 128;
441         }
442       }
443     }
444
445     if (mouse_hook_enabled && (mouse_extra != 0x00)) {
446       // mouse wheel events have occurred; unlike l/m/r buttons, these are queued as keypresses, so add to end of buffer
447       switch (mouse_extra) {
448         case 0xff:
449           // wheel up
450           queue_keypress(0xfe, KEYPRESS_PRESS, PLATFORM_AMIGA);
451           break;
452         case 0x01:
453           // wheel down
454           queue_keypress(0xff, KEYPRESS_PRESS, PLATFORM_AMIGA);
455           break;
456       }
457
458       // dampen the scroll wheel until next while loop iteration
459       mouse_extra = 0x00;
460     }
461   }
462
463   stop_cpu_emulation:;
464
465   if (mouse_fd != -1)
466     close(mouse_fd);
467   if (mem_fd)
468     close(mem_fd);
469
470   return 0;
471 }
472
473 void cpu_pulse_reset(void) {
474   ps_pulse_reset();
475   //write_reg(0x00);
476   // printf("Status Reg%x\n",read_reg());
477   //usleep(100000);
478   //write_reg(0x02);
479   // printf("Status Reg%x\n",read_reg());
480   if (cfg->platform->handle_reset)
481     cfg->platform->handle_reset(cfg);
482
483   ovl = 1;
484   m68k_write_memory_8(0xbfe201, 0x0001);  // AMIGA OVL
485   m68k_write_memory_8(0xbfe001, 0x0001);  // AMIGA OVL high (ROM@0x0)
486
487   m68k_pulse_reset();
488 }
489
490 int cpu_irq_ack(int level) {
491   printf("cpu irq ack\n");
492   return level;
493 }
494
495 static unsigned int target = 0;
496 static uint8_t send_keypress = 0;
497
498 uint8_t cdtv_dmac_reg_idx_read();
499 void cdtv_dmac_reg_idx_write(uint8_t value);
500 uint32_t cdtv_dmac_read(uint32_t address, uint8_t type);
501 void cdtv_dmac_write(uint32_t address, uint32_t value, uint8_t type);
502
503 #define PLATFORM_CHECK_READ(a) \
504   if (address >= cfg->custom_low && address < cfg->custom_high) { \
505     unsigned int target = 0; \
506     switch(cfg->platform->id) { \
507       case PLATFORM_AMIGA: { \
508         if (address >= PISCSI_OFFSET && address < PISCSI_UPPER) { \
509           return handle_piscsi_read(address, a); \
510         } \
511         if (address >= PINET_OFFSET && address < PINET_UPPER) { \
512           return handle_pinet_read(address, a); \
513         } \
514         if (address >= PIGFX_RTG_BASE && address < PIGFX_UPPER) { \
515           return rtg_read((address & 0x0FFFFFFF), a); \
516         } \
517         if (custom_read_amiga(cfg, address, &target, a) != -1) { \
518           return target; \
519         } \
520         break; \
521       } \
522       default: \
523         break; \
524     } \
525   } \
526   if (ovl || (address >= cfg->mapped_low && address < cfg->mapped_high)) { \
527     if (handle_mapped_read(cfg, address, &target, a) != -1) \
528       return target; \
529   }
530
531 unsigned int m68k_read_memory_8(unsigned int address) {
532   PLATFORM_CHECK_READ(OP_TYPE_BYTE);
533
534   /*if (address >= 0xE90000 && address < 0xF00000) {
535     printf("BYTE read from DMAC @%.8X:", address);
536     uint32_t v = cdtv_dmac_read(address & 0xFFFF, OP_TYPE_BYTE);
537     printf("%.2X\n", v);
538     M68K_END_TIMESLICE;
539     cpu_emulation_running = 0;
540     return v;
541   }*/
542
543   /*if (m68k_get_reg(NULL, M68K_REG_PC) >= 0x080032F0 && m68k_get_reg(NULL, M68K_REG_PC) <= 0x080032F0 + 0x4000) {
544     stop_cpu_emulation(1);
545   }*/
546
547
548   if (address & 0xFF000000)
549     return 0;
550
551   unsigned char result = (unsigned int)read8((uint32_t)address);
552
553   if (mouse_hook_enabled) {
554     if (address == CIAAPRA) {
555       if (mouse_buttons & 0x01) {
556         //mouse_buttons -= 1;
557         return (unsigned int)(result ^ 0x40);
558       }
559
560       return (unsigned int)result;
561     }
562   }
563
564   if (kb_hook_enabled) {
565     if (address == CIAAICR) {
566       if (get_num_kb_queued() && (!send_keypress || send_keypress == 1)) {
567         result |= 0x08;
568         if (!send_keypress)
569           send_keypress = 1;
570       }
571       if (send_keypress == 2) {
572         result |= 0x02;
573         send_keypress = 0;
574       }
575       return result;
576     }
577     if (address == CIAADAT) {
578       if (send_keypress) {
579         uint8_t c = 0, t = 0;
580         pop_queued_key(&c, &t);
581         t ^= 0x01;
582         result = ((c << 1) | t) ^ 0xFF;
583         send_keypress = 2;
584       }
585       return result;
586     }
587   }
588
589   return result;
590 }
591
592 unsigned int m68k_read_memory_16(unsigned int address) {
593   PLATFORM_CHECK_READ(OP_TYPE_WORD);
594
595   /*if (m68k_get_reg(NULL, M68K_REG_PC) >= 0x080032F0 && m68k_get_reg(NULL, M68K_REG_PC) <= 0x080032F0 + 0x4000) {
596     stop_cpu_emulation(1);
597   }*/
598
599   /*if (address >= 0xE90000 && address < 0xF00000) {
600     printf("WORD read from DMAC @%.8X:", address);
601     uint32_t v = cdtv_dmac_read(address & 0xFFFF, OP_TYPE_WORD);
602     printf("%.2X\n", v);
603     M68K_END_TIMESLICE;
604     cpu_emulation_running = 0;
605     return v;
606   }*/
607
608   if (mouse_hook_enabled) {
609     if (address == JOY0DAT) {
610       // Forward mouse valueses to Amyga.
611       unsigned short result = (mouse_dy << 8) | (mouse_dx);
612       return (unsigned int)result;
613     }
614     /*if (address == CIAAPRA) {
615       unsigned short result = (unsigned int)read16((uint32_t)address);
616       if (mouse_buttons & 0x01) {
617         return (unsigned int)(result | 0x40);
618       }
619       else
620           return (unsigned int)result;
621     }*/
622     if (address == POTGOR) {
623       unsigned short result = (unsigned int)read16((uint32_t)address);
624       // bit 1 rmb, bit 2 mmb
625       if (mouse_buttons & 0x06) {
626         return (unsigned int)((result ^ ((mouse_buttons & 0x02) << 9))   // move rmb to bit 10
627                             & (result ^ ((mouse_buttons & 0x04) << 6))); // move mmb to bit 8
628       }
629       return (unsigned int)(result & 0xfffd);
630     }
631   }
632
633   if (address & 0xFF000000)
634     return 0;
635
636   if (address & 0x01) {
637     return ((read8(address) << 8) | read8(address + 1));
638   }
639   return (unsigned int)read16((uint32_t)address);
640 }
641
642 unsigned int m68k_read_memory_32(unsigned int address) {
643   PLATFORM_CHECK_READ(OP_TYPE_LONGWORD);
644
645   /*if (m68k_get_reg(NULL, M68K_REG_PC) >= 0x080032F0 && m68k_get_reg(NULL, M68K_REG_PC) <= 0x080032F0 + 0x4000) {
646     stop_cpu_emulation(1);
647   }*/
648
649   /*if (address >= 0xE90000 && address < 0xF00000) {
650     printf("LONGWORD read from DMAC @%.8X:", address);
651     uint32_t v = cdtv_dmac_read(address & 0xFFFF, OP_TYPE_LONGWORD);
652     printf("%.2X\n", v);
653     M68K_END_TIMESLICE;
654     cpu_emulation_running = 0;
655     return v;
656   }*/
657
658   if (address & 0xFF000000)
659     return 0;
660
661   if (address & 0x01) {
662     uint32_t c = read8(address);
663     c |= (be16toh(read16(address+1)) << 8);
664     c |= (read8(address + 3) << 24);
665     return htobe32(c);
666   }
667   uint16_t a = read16(address);
668   uint16_t b = read16(address + 2);
669   return (a << 16) | b;
670 }
671
672 #define PLATFORM_CHECK_WRITE(a) \
673   if (address >= cfg->custom_low && address < cfg->custom_high) { \
674     switch(cfg->platform->id) { \
675       case PLATFORM_AMIGA: { \
676         if (address >= PISCSI_OFFSET && address < PISCSI_UPPER) { \
677           handle_piscsi_write(address, value, a); \
678         } \
679         if (address >= PINET_OFFSET && address < PINET_UPPER) { \
680           handle_pinet_write(address, value, a); \
681         } \
682         if (address >= PIGFX_RTG_BASE && address < PIGFX_UPPER) { \
683           rtg_write((address & 0x0FFFFFFF), value, a); \
684           return; \
685         } \
686         if (custom_write_amiga(cfg, address, value, a) != -1) { \
687           return; \
688         } \
689         break; \
690       } \
691       default: \
692         break; \
693     } \
694   } \
695   if (address >= cfg->mapped_low && address < cfg->mapped_high) { \
696     if (handle_mapped_write(cfg, address, value, a) != -1) \
697       return; \
698   }
699
700 void m68k_write_memory_8(unsigned int address, unsigned int value) {
701   PLATFORM_CHECK_WRITE(OP_TYPE_BYTE);
702
703   /*if (address >= 0xE90000 && address < 0xF00000) {
704     printf("BYTE write to DMAC @%.8X: %.2X\n", address, value);
705     cdtv_dmac_write(address & 0xFFFF, value, OP_TYPE_BYTE);
706     M68K_END_TIMESLICE;
707     cpu_emulation_running = 0;
708     return;
709   }*/
710
711   if (address == 0xbfe001) {
712     if (ovl != (value & (1 << 0))) {
713       ovl = (value & (1 << 0));
714       printf("OVL:%x\n", ovl);
715     }
716   }
717
718   if (address & 0xFF000000)
719     return;
720
721   write8((uint32_t)address, value);
722   return;
723 }
724
725 void m68k_write_memory_16(unsigned int address, unsigned int value) {
726   PLATFORM_CHECK_WRITE(OP_TYPE_WORD);
727
728   /*if (address >= 0xE90000 && address < 0xF00000) {
729     printf("WORD write to DMAC @%.8X: %.4X\n", address, value);
730     cdtv_dmac_write(address & 0xFFFF, value, OP_TYPE_WORD);
731     M68K_END_TIMESLICE;
732     cpu_emulation_running = 0;
733     return;
734   }*/
735
736   if (address == 0xDFF030) {
737     char *serdat = (char *)&value;
738     // SERDAT word. see amiga dev docs appendix a; upper byte is control codes, and bit 0 is always 1.
739     // ignore this upper byte as it's not viewable data, only display lower byte.
740     printf("%c", serdat[0]);
741   }
742   if (address == 0xDFF09A) {
743     if (!(value & 0x8000)) {
744       if (value & 0x04) {
745         int2_enabled = 0;
746       }
747     }
748     else if (value & 0x04) {
749       int2_enabled = 1;
750     }
751   }
752
753   if (address & 0xFF000000)
754     return;
755
756   if (address & 0x01)
757     printf("Unaligned WORD write!\n");
758
759   write16((uint32_t)address, value);
760   return;
761 }
762
763 void m68k_write_memory_32(unsigned int address, unsigned int value) {
764   PLATFORM_CHECK_WRITE(OP_TYPE_LONGWORD);
765
766   /*if (address >= 0xE90000 && address < 0xF00000) {
767     printf("LONGWORD write to DMAC @%.8X: %.8X\n", address, value);
768     cdtv_dmac_write(address & 0xFFFF, value, OP_TYPE_LONGWORD);
769     M68K_END_TIMESLICE;
770     cpu_emulation_running = 0;
771     return;
772   }*/
773
774   if (address & 0xFF000000)
775     return;
776
777   if (address & 0x01)
778     printf("Unaligned LONGWORD write!\n");
779
780   write16(address, value >> 16);
781   write16(address + 2, value);
782   return;
783 }