14 #include <sys/types.h>
16 #include <sys/ioctl.h>
19 #include "platforms/platforms.h"
20 #include "input/input.h"
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"
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];
42 int kb_hook_enabled = 0;
43 int mouse_hook_enabled = 0;
44 int cpu_emulation_running = 1;
46 uint8_t mouse_dx = 0, mouse_dy = 0;
47 uint8_t mouse_buttons = 0;
48 uint8_t mouse_extra = 0;
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;
60 char disasm_buf[4096];
62 #define KICKBASE 0xF80000
63 #define KICKSIZE 0x7FFFF
65 int mem_fd, mouse_fd = -1, keyboard_fd = -1;
74 extern m68ki_cpu_core m68ki_cpu;
75 extern int m68ki_initial_cycles;
76 extern int m68ki_remaining_cycles;
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(); \
85 #define M68K_SET_IRQ m68k_set_irq
86 #define M68K_END_TIMESLICE m68k_end_timeslice()
89 #define NOP asm("nop"); asm("nop"); asm("nop"); asm("nop");
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";
97 uint64_t trig_irq = 0, serv_irq = 0;
99 void *iplThread(void *args) {
100 printf("IPL thread running\n");
103 if (!gpio_get_irq()) {
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;
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
137 void stop_cpu_emulation(uint8_t 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;
149 cpu_emulation_running = 0;
154 static volatile unsigned char maprom;
156 void sigint_handler(int sig_num) {
158 //cpu_emulation_running = 0;
161 printf("Received sigint %d, exiting.\n", sig_num);
167 if (cfg->platform->shutdown) {
168 cfg->platform->shutdown(cfg);
171 printf("IRQs triggered: %lld\n", trig_irq);
172 printf("IRQs serviced: %lld\n", serv_irq);
177 int main(int argc, char *argv[]) {
179 //const struct sched_param priority = {99};
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) {
185 printf("%s switch found, but no CPU type specified.\n", argv[g]);
188 cpu_type = get_m68k_cpu_type(argv[g]);
191 else if (strcmp(argv[g], "--config-file") == 0 || strcmp(argv[g], "--config") == 0) {
193 printf("%s switch found, but no config filename specified.\n", argv[g]);
196 cfg = load_config_file(argv[g]);
199 else if (strcmp(argv[g], "--keyboard-file") == 0 || strcmp(argv[g], "--kbfile") == 0) {
201 printf("%s switch found, but no keyboard device path specified.\n", argv[g]);
204 strcpy(keyboard_file, argv[g]);
210 printf("No config file specified. Trying to load default.cfg...\n");
211 cfg = load_config_file("default.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));
216 printf("Failed to allocate memory for emulator config!\n");
219 memset(cfg, 0x00, sizeof(struct emulator_config));
224 if (cfg->cpu_type) cpu_type = cfg->cpu_type;
225 if (cfg->loop_cycles) loop_cycles = cfg->loop_cycles;
228 cfg->platform = make_platform_config("none", "generic");
229 cfg->platform->platform_initial_setup(cfg);
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;
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.
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");
252 printf("[MOUSE] Mouse didn't respond to normal PS/2 init; have you plugged a brick in by mistake?\n");
256 keyboard_fd = open(keyboard_file, O_RDONLY | O_NONBLOCK);
257 if (keyboard_fd == -1) {
258 printf("Failed to open keyboard event source.\n");
263 signal(SIGINT, sigint_handler);
266 //goto skip_everything;
268 // Enable 200MHz CLK output on GPIO4, adjust divider and pll source depending
270 printf("Enable 200MHz GPCLK0 on GPIO4\n");
271 gpio_enable_200mhz();
273 // reset cpld statemachine first
281 // reset amiga and statemachine
287 printf("Setting CPU type to %d.\n", cpu_type);
288 m68k_set_cpu_type(cpu_type);
292 m68k_set_reg(M68K_REG_PC, 0xF80002);
294 m68k_set_reg(M68K_REG_PC, 0x0);
297 ps_reset_state_machine();
302 printf("Setting CPU type to %d.\n", cpu_type);
303 m68k_set_cpu_type(cpu_type);
306 char c = 0, c_code = 0, c_type = 0;
307 uint32_t last_irq = 0;
311 err = pthread_create(&id, NULL, &iplThread, NULL);
313 printf("can't create IPL thread :[%s]", strerror(err));
315 printf("IPL Thread created successfully\n");
319 if (mouse_hook_enabled) {
320 get_mouse_status(&mouse_dx, &mouse_dy, &mouse_buttons, &mouse_extra);
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);
335 if (cpu_emulation_running)
336 m68k_execute(loop_cycles);
340 unsigned int status = read_reg();
341 if (((status & 0xe000) >> 13) != last_irq) {
342 last_irq = ((status & 0xe000) >> 13);
344 M68K_SET_IRQ(last_irq);
347 else if (gayleirq && int2_enabled) {
348 write16(0xdff09c, 0x8000 | (1 << 3));
359 while (get_key_char(&c, &c_code, &c_type)) {
360 if (c && c == cfg->keyboard_toggle_key && !kb_hook_enabled) {
362 printf("Keyboard hook enabled.\n");
364 else if (kb_hook_enabled) {
365 if (c == 0x1B && c_type) {
367 printf("Keyboard hook disabled.\n");
370 /*printf("Key code: %.2X - ", c_code);
373 printf("released.\n");
376 printf("pressed.\n");
382 printf("unknown.\n");
385 if (queue_keypress(c_code, c_type, cfg->platform->id) && int2_enabled) {
392 // pause pressed; trigger nmi (int level 7)
393 if (c == 0x01 && c_type) {
394 printf("[INT] Sending NMI\n");
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;
406 cpu_emulation_running ^= 1;
407 printf("CPU emulation is now %s\n", cpu_emulation_running ? "running" : "stopped");
410 realtime_graphics_debug ^= 1;
411 printf("Real time graphics debug is now %s\n", realtime_graphics_debug ? "on" : "off");
415 //m68k_pulse_reset();
416 printf("CPU emulation reset.\n");
419 printf("Quitting and exiting emulator.\n");
420 goto stop_cpu_emulation;
423 realtime_disassembly ^= 1;
425 printf("Real time disassembly is now %s\n", realtime_disassembly ? "on" : "off");
428 int r = get_mapped_item_by_address(cfg, 0x08000000);
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);
436 if (c == 's' && realtime_disassembly) {
439 if (c == 'S' && realtime_disassembly) {
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) {
450 queue_keypress(0xfe, KEYPRESS_PRESS, PLATFORM_AMIGA);
454 queue_keypress(0xff, KEYPRESS_PRESS, PLATFORM_AMIGA);
458 // dampen the scroll wheel until next while loop iteration
473 void cpu_pulse_reset(void) {
476 // printf("Status Reg%x\n",read_reg());
479 // printf("Status Reg%x\n",read_reg());
480 if (cfg->platform->handle_reset)
481 cfg->platform->handle_reset(cfg);
484 m68k_write_memory_8(0xbfe201, 0x0001); // AMIGA OVL
485 m68k_write_memory_8(0xbfe001, 0x0001); // AMIGA OVL high (ROM@0x0)
490 int cpu_irq_ack(int level) {
491 printf("cpu irq ack\n");
495 static unsigned int target = 0;
496 static uint8_t send_keypress = 0;
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);
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); \
511 if (address >= PINET_OFFSET && address < PINET_UPPER) { \
512 return handle_pinet_read(address, a); \
514 if (address >= PIGFX_RTG_BASE && address < PIGFX_UPPER) { \
515 return rtg_read((address & 0x0FFFFFFF), a); \
517 if (custom_read_amiga(cfg, address, &target, a) != -1) { \
526 if (ovl || (address >= cfg->mapped_low && address < cfg->mapped_high)) { \
527 if (handle_mapped_read(cfg, address, &target, a) != -1) \
531 unsigned int m68k_read_memory_8(unsigned int address) {
532 PLATFORM_CHECK_READ(OP_TYPE_BYTE);
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);
539 cpu_emulation_running = 0;
543 /*if (m68k_get_reg(NULL, M68K_REG_PC) >= 0x080032F0 && m68k_get_reg(NULL, M68K_REG_PC) <= 0x080032F0 + 0x4000) {
544 stop_cpu_emulation(1);
548 if (address & 0xFF000000)
551 unsigned char result = (unsigned int)read8((uint32_t)address);
553 if (mouse_hook_enabled) {
554 if (address == CIAAPRA) {
555 if (mouse_buttons & 0x01) {
556 //mouse_buttons -= 1;
557 return (unsigned int)(result ^ 0x40);
560 return (unsigned int)result;
564 if (kb_hook_enabled) {
565 if (address == CIAAICR) {
566 if (get_num_kb_queued() && (!send_keypress || send_keypress == 1)) {
571 if (send_keypress == 2) {
577 if (address == CIAADAT) {
579 uint8_t c = 0, t = 0;
580 pop_queued_key(&c, &t);
582 result = ((c << 1) | t) ^ 0xFF;
592 unsigned int m68k_read_memory_16(unsigned int address) {
593 PLATFORM_CHECK_READ(OP_TYPE_WORD);
595 /*if (m68k_get_reg(NULL, M68K_REG_PC) >= 0x080032F0 && m68k_get_reg(NULL, M68K_REG_PC) <= 0x080032F0 + 0x4000) {
596 stop_cpu_emulation(1);
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);
604 cpu_emulation_running = 0;
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;
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);
620 return (unsigned int)result;
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
629 return (unsigned int)(result & 0xfffd);
633 if (address & 0xFF000000)
636 if (address & 0x01) {
637 return ((read8(address) << 8) | read8(address + 1));
639 return (unsigned int)read16((uint32_t)address);
642 unsigned int m68k_read_memory_32(unsigned int address) {
643 PLATFORM_CHECK_READ(OP_TYPE_LONGWORD);
645 /*if (m68k_get_reg(NULL, M68K_REG_PC) >= 0x080032F0 && m68k_get_reg(NULL, M68K_REG_PC) <= 0x080032F0 + 0x4000) {
646 stop_cpu_emulation(1);
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);
654 cpu_emulation_running = 0;
658 if (address & 0xFF000000)
661 if (address & 0x01) {
662 uint32_t c = read8(address);
663 c |= (be16toh(read16(address+1)) << 8);
664 c |= (read8(address + 3) << 24);
667 uint16_t a = read16(address);
668 uint16_t b = read16(address + 2);
669 return (a << 16) | b;
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); \
679 if (address >= PINET_OFFSET && address < PINET_UPPER) { \
680 handle_pinet_write(address, value, a); \
682 if (address >= PIGFX_RTG_BASE && address < PIGFX_UPPER) { \
683 rtg_write((address & 0x0FFFFFFF), value, a); \
686 if (custom_write_amiga(cfg, address, value, a) != -1) { \
695 if (address >= cfg->mapped_low && address < cfg->mapped_high) { \
696 if (handle_mapped_write(cfg, address, value, a) != -1) \
700 void m68k_write_memory_8(unsigned int address, unsigned int value) {
701 PLATFORM_CHECK_WRITE(OP_TYPE_BYTE);
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);
707 cpu_emulation_running = 0;
711 if (address == 0xbfe001) {
712 if (ovl != (value & (1 << 0))) {
713 ovl = (value & (1 << 0));
714 printf("OVL:%x\n", ovl);
718 if (address & 0xFF000000)
721 write8((uint32_t)address, value);
725 void m68k_write_memory_16(unsigned int address, unsigned int value) {
726 PLATFORM_CHECK_WRITE(OP_TYPE_WORD);
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);
732 cpu_emulation_running = 0;
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]);
742 if (address == 0xDFF09A) {
743 if (!(value & 0x8000)) {
748 else if (value & 0x04) {
753 if (address & 0xFF000000)
757 printf("Unaligned WORD write!\n");
759 write16((uint32_t)address, value);
763 void m68k_write_memory_32(unsigned int address, unsigned int value) {
764 PLATFORM_CHECK_WRITE(OP_TYPE_LONGWORD);
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);
770 cpu_emulation_running = 0;
774 if (address & 0xFF000000)
778 printf("Unaligned LONGWORD write!\n");
780 write16(address, value >> 16);
781 write16(address + 2, value);