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