2 // m68kinterface.c: Code interface to the UAE 68000 core and support code
5 // (C) 2011 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -------------------------------------------------------------
11 // JLH 10/28/2011 Created this file ;-)
14 #include "m68kinterface.h"
15 //#include <pthread.h>
21 // Exception Vectors handled by emulation
22 #define EXCEPTION_BUS_ERROR 2 /* This one is not emulated! */
23 #define EXCEPTION_ADDRESS_ERROR 3 /* This one is partially emulated (doesn't stack a proper frame yet) */
24 #define EXCEPTION_ILLEGAL_INSTRUCTION 4
25 #define EXCEPTION_ZERO_DIVIDE 5
26 #define EXCEPTION_CHK 6
27 #define EXCEPTION_TRAPV 7
28 #define EXCEPTION_PRIVILEGE_VIOLATION 8
29 #define EXCEPTION_TRACE 9
30 #define EXCEPTION_1010 10
31 #define EXCEPTION_1111 11
32 #define EXCEPTION_FORMAT_ERROR 14
33 #define EXCEPTION_UNINITIALIZED_INTERRUPT 15
34 #define EXCEPTION_SPURIOUS_INTERRUPT 24
35 #define EXCEPTION_INTERRUPT_AUTOVECTOR 24
36 #define EXCEPTION_TRAP_BASE 32
38 // These are found in obj/cpustbl.c (generated by gencpu)
40 //extern const struct cputbl op_smalltbl_0_ff[]; /* 68040 */
41 //extern const struct cputbl op_smalltbl_1_ff[]; /* 68020 + 68881 */
42 //extern const struct cputbl op_smalltbl_2_ff[]; /* 68020 */
43 //extern const struct cputbl op_smalltbl_3_ff[]; /* 68010 */
44 extern const struct cputbl op_smalltbl_4_ff[]; /* 68000 */
45 extern const struct cputbl op_smalltbl_5_ff[]; /* 68000 slow but compatible. */
47 // Externs, supplied by the user...
48 //extern int irq_ack_handler(int);
50 // Function prototypes...
51 STATIC_INLINE void m68ki_check_interrupts(void);
52 void m68ki_exception_interrupt(uint32_t intLevel);
53 STATIC_INLINE uint32_t m68ki_init_exception(void);
54 STATIC_INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr);
55 unsigned long IllegalOpcode(uint32_t opcode);
56 void BuildCPUFunctionTable(void);
57 void m68k_set_irq2(unsigned int intLevel);
59 // Local "Global" vars
60 static int32_t initialCycles;
61 cpuop_func * cpuFunctionTable[65536];
63 // By virtue of the fact that m68k_set_irq() can be called asychronously by
64 // another thread, we need something along the lines of this:
65 static int checkForIRQToHandle = 0;
66 //static pthread_mutex_t executionLock = PTHREAD_MUTEX_INITIALIZER;
67 static int IRQLevelToHandle = 0;
70 #define ADD_CYCLES(A) m68ki_remaining_cycles += (A)
71 #define USE_CYCLES(A) m68ki_remaining_cycles -= (A)
72 #define SET_CYCLES(A) m68ki_remaining_cycles = A
73 #define GET_CYCLES() m68ki_remaining_cycles
74 #define USE_ALL_CYCLES() m68ki_remaining_cycles = 0
76 #define CPU_INT_LEVEL m68ki_cpu.int_level /* ASG: changed from CPU_INTS_PENDING */
77 #define CPU_INT_CYCLES m68ki_cpu.int_cycles /* ASG */
78 #define CPU_STOPPED m68ki_cpu.stopped
79 #define CPU_PREF_ADDR m68ki_cpu.pref_addr
80 #define CPU_PREF_DATA m68ki_cpu.pref_data
81 #define CPU_ADDRESS_MASK m68ki_cpu.address_mask
82 #define CPU_SR_MASK m68ki_cpu.sr_mask
88 void Dasm(uint32_t offset, uint32_t qt)
91 // back up a few instructions...
93 static char buffer[2048];//, mem[64];
94 int pc = offset, oldpc;
100 for(int j=0; j<64; j++)
101 mem[j^0x01] = jaguar_byte_read(pc + j);
103 pc += Dasm68000((char *)mem, buffer, 0);
104 WriteLog("%08X: %s\n", oldpc, buffer);//*/
106 pc += m68k_disassemble(buffer, pc, 0);//M68K_CPU_TYPE_68000);
107 // WriteLog("%08X: %s\n", oldpc, buffer);//*/
108 printf("%08X: %s\n", oldpc, buffer);//*/
115 void DumpRegisters(void)
121 printf("%s%i: %08X ", (i < 8 ? "D" : "A"), i & 0x7, regs.regs[i]);
130 void M68KSetHalt(void)
136 void M68KClearHalt(void)
142 void m68k_set_cpu_type(unsigned int type)
147 // Pulse the RESET line on the CPU
148 void m68k_pulse_reset(void)
150 static uint32_t emulation_initialized = 0;
152 // The first call to this function initializes the opcode handler jump table
153 if (!emulation_initialized)
156 m68ki_build_opcode_table();
157 m68k_set_int_ack_callback(NULL);
158 m68k_set_bkpt_ack_callback(NULL);
159 m68k_set_reset_instr_callback(NULL);
160 m68k_set_pc_changed_callback(NULL);
161 m68k_set_fc_callback(NULL);
162 m68k_set_instr_hook_callback(NULL);
164 // Build opcode handler table here...
167 BuildCPUFunctionTable();
169 emulation_initialized = 1;
172 // if (CPU_TYPE == 0) /* KW 990319 */
173 // m68k_set_cpu_type(M68K_CPU_TYPE_68000);
176 /* Clear all stop levels and eat up all remaining cycles */
180 /* Turn off tracing */
181 FLAG_T1 = FLAG_T0 = 0;
183 /* Interrupt mask to level 7 */
184 FLAG_INT_MASK = 0x0700;
187 /* Go to supervisor mode */
188 m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR);
190 /* Invalidate the prefetch queue */
191 #if M68K_EMULATE_PREFETCH
192 /* Set to arbitrary number since our first fetch is from 0 */
193 CPU_PREF_ADDR = 0x1000;
194 #endif /* M68K_EMULATE_PREFETCH */
196 /* Read the initial stack pointer and program counter */
198 REG_SP = m68ki_read_imm_32();
199 REG_PC = m68ki_read_imm_32();
203 regs.remainingCycles = 0;
206 regs.s = 1; // Supervisor mode ON
208 // Read initial SP and PC
209 m68k_areg(regs, 7) = m68k_read_memory_32(0);
210 m68k_setpc(m68k_read_memory_32(4));
211 refill_prefetch(m68k_getpc(), 0);
216 int m68k_execute(int num_cycles)
220 regs.remainingCycles = 0; // int32_t
221 regs.interruptCycles = 0; // uint32_t
227 /* Set our pool of clock cycles available */
228 SET_CYCLES(num_cycles);
229 m68ki_initial_cycles = num_cycles;
231 /* ASG: update cycles */
232 USE_CYCLES(CPU_INT_CYCLES);
235 /* Return point if we had an address error */
236 m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */
238 regs.remainingCycles = num_cycles;
239 /*int32_t*/ initialCycles = num_cycles;
241 regs.remainingCycles -= regs.interruptCycles;
242 regs.interruptCycles = 0;
245 /* Main loop. Keep going until we run out of clock cycles */
249 /* Set tracing accodring to T1. (T0 is done inside instruction) */
250 m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */
252 /* Set the address space for reads */
253 m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */
255 /* Call external hook to peek at CPU */
256 m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */
258 /* Record previous program counter */
261 /* Read an instruction and call its handler */
262 REG_IR = m68ki_read_imm_16();
263 m68ki_instruction_jump_table[REG_IR]();
264 USE_CYCLES(CYC_INSTRUCTION[REG_IR]);
266 /* Trace m68k_exception, if necessary */
267 m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
269 //Testing Hover Strike...
272 static int hitCount = 0;
273 static int inRoutine = 0;
276 //if (regs.pc == 0x80340A)
277 if (regs.pc == 0x803416)
282 printf("%i: $80340A start. A0=%08X, A1=%08X ", hitCount, regs.regs[8], regs.regs[9]);
284 else if (regs.pc == 0x803422)
287 printf("(%i instructions)\n", instSeen);
293 // AvP testing... (problem was: 32 bit addresses on 24 bit address cpu--FIXED)
297 if (regs.pc == 0x94BA)
303 if (regs.pc == 0x94C6)
306 // if (regs.regs[10] == 0xFFFFFFFF && go)
309 // printf("A2=-1, PC=%08X\n", regs.pc);
311 // Dasm(regs.pc, 130);
315 //94BA: 2468 0000 MOVEA.L (A0,$0000) == $0002328A, A2
316 //94BE: 200A MOVE.L A2, D0
317 //94C0: 6A02 BPL.B $94C4
318 //94C2: 2452 MOVEA.L (A2), A2 ; <--- HERE
319 //94C4: 4283 CLR.L D3
321 // pthread_mutex_lock(&executionLock);
322 if (checkForIRQToHandle)
324 checkForIRQToHandle = 0;
325 m68k_set_irq2(IRQLevelToHandle);
328 #ifdef M68K_HOOK_FUNCTION
329 M68KInstructionHook();
331 uint32_t opcode = get_iword(0);
332 //if ((opcode & 0xFFF8) == 0x31C0)
334 // printf("MOVE.W D%i, EA\n", opcode & 0x07);
336 int32_t cycles = (int32_t)(*cpuFunctionTable[opcode])(opcode);
337 regs.remainingCycles -= cycles;
338 // pthread_mutex_unlock(&executionLock);
340 //printf("Executed opcode $%04X (%i cycles)...\n", opcode, cycles);
342 // This is so our debugging code can break in on a dime.
343 // Otherwise, this is just extra slow down :-P
346 num_cycles = initialCycles - regs.remainingCycles;
347 regs.remainingCycles = 0; // int32_t
348 regs.interruptCycles = 0; // uint32_t
353 while (regs.remainingCycles > 0);
356 /* set previous PC to current PC for the next entry into the loop */
359 /* ASG: update cycles */
360 USE_CYCLES(CPU_INT_CYCLES);
363 /* return how many clocks we used */
364 return m68ki_initial_cycles - GET_CYCLES();
366 regs.remainingCycles -= regs.interruptCycles;
367 regs.interruptCycles = 0;
369 // Return # of clock cycles used
370 return initialCycles - regs.remainingCycles;
375 void m68k_set_irq(unsigned int intLevel)
377 // We need to check for stopped state as well...
380 m68k_set_irq2(intLevel);
384 // Since this can be called asynchronously, we need to fix it so that it
385 // doesn't fuck up the main execution loop.
386 IRQLevelToHandle = intLevel;
387 checkForIRQToHandle = 1;
391 /* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
392 void m68k_set_irq2(unsigned int intLevel)
394 // pthread_mutex_lock(&executionLock);
395 // printf("m68k_set_irq: Could not get the lock!!!\n");
397 int oldLevel = regs.intLevel;
398 regs.intLevel = intLevel;
400 // A transition from < 7 to 7 always interrupts (NMI)
401 // Note: Level 7 can also level trigger like a normal IRQ
402 if (oldLevel != 0x07 && regs.intLevel == 0x07)
403 m68ki_exception_interrupt(7); // Edge triggered level 7 (NMI)
405 m68ki_check_interrupts(); // Level triggered (IRQ)
407 // pthread_mutex_unlock(&executionLock);
411 // Check for interrupts
412 STATIC_INLINE void m68ki_check_interrupts(void)
415 if(CPU_INT_LEVEL > FLAG_INT_MASK)
416 m68ki_exception_interrupt(CPU_INT_LEVEL>>8);
418 if (regs.intLevel > regs.intmask)
419 m68ki_exception_interrupt(regs.intLevel);
424 // Service an interrupt request and start exception processing
425 void m68ki_exception_interrupt(uint32_t intLevel)
432 /* Turn off the stopped state */
433 CPU_STOPPED &= ~STOP_LEVEL_STOP;
435 /* If we are halted, don't do anything */
439 /* Acknowledge the interrupt */
440 vector = m68ki_int_ack(int_level);
442 /* Get the interrupt vector */
443 if(vector == M68K_INT_ACK_AUTOVECTOR)
444 /* Use the autovectors. This is the most commonly used implementation */
445 vector = EXCEPTION_INTERRUPT_AUTOVECTOR+int_level;
446 else if(vector == M68K_INT_ACK_SPURIOUS)
447 /* Called if no devices respond to the interrupt acknowledge */
448 vector = EXCEPTION_SPURIOUS_INTERRUPT;
449 else if(vector > 255)
451 M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
452 m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
456 /* Start exception processing */
457 sr = m68ki_init_exception();
459 /* Set the interrupt mask to the level of the one being serviced */
460 FLAG_INT_MASK = int_level<<8;
463 new_pc = m68ki_read_data_32((vector<<2) + REG_VBR);
465 /* If vector is uninitialized, call the uninitialized interrupt vector */
467 new_pc = m68ki_read_data_32((EXCEPTION_UNINITIALIZED_INTERRUPT<<2) + REG_VBR);
469 /* Generate a stack frame */
470 m68ki_stack_frame_0000(REG_PC, sr, vector);
472 if(FLAG_M && CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
474 /* Create throwaway frame */
475 m68ki_set_sm_flag(FLAG_S); /* clear M */
476 sr |= 0x2000; /* Same as SR in master stack frame except S is forced high */
477 m68ki_stack_frame_0001(REG_PC, sr, vector);
482 /* Defer cycle counting until later */
483 CPU_INT_CYCLES += CYC_EXCEPTION[vector];
485 #if !M68K_EMULATE_INT_ACK
486 /* Automatically clear IRQ if we are not using an acknowledge scheme */
488 #endif /* M68K_EMULATE_INT_ACK */
490 // Turn off the stopped state
494 //JLH: need to add halt state?
495 // prolly, for debugging/alpine mode... :-/
496 // but then again, this should be handled already by the main execution loop :-P
497 // If we are halted, don't do anything
501 // Acknowledge the interrupt (NOTE: This is a user supplied function!)
502 uint32_t vector = irq_ack_handler(intLevel);
504 // Get the interrupt vector
505 if (vector == M68K_INT_ACK_AUTOVECTOR)
506 // Use the autovectors. This is the most commonly used implementation
507 vector = EXCEPTION_INTERRUPT_AUTOVECTOR + intLevel;
508 else if (vector == M68K_INT_ACK_SPURIOUS)
509 // Called if no devices respond to the interrupt acknowledge
510 vector = EXCEPTION_SPURIOUS_INTERRUPT;
511 else if (vector > 255)
513 // M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
514 // m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
518 // Start exception processing
519 uint32_t sr = m68ki_init_exception();
521 // Set the interrupt mask to the level of the one being serviced
522 regs.intmask = intLevel;
525 extern int startM68KTracing;
526 if (startM68KTracing)
528 printf("IRQ: old PC=%06X, ", regs.pc);
533 uint32_t newPC = m68k_read_memory_32(vector << 2);
536 if (startM68KTracing)
538 printf("new PC=%06X, vector=%u, ", newPC, vector);
542 // If vector is uninitialized, call the uninitialized interrupt vector
544 newPC = m68k_read_memory_32(EXCEPTION_UNINITIALIZED_INTERRUPT << 2);
546 // Generate a stack frame
547 m68ki_stack_frame_3word(regs.pc, sr);
551 if (startM68KTracing)
553 printf("(PC=%06X)\n", regs.pc);
557 // Defer cycle counting until later
558 regs.interruptCycles += 56; // NOT ACCURATE-- !!! FIX !!!
559 // CPU_INT_CYCLES += CYC_EXCEPTION[vector];
564 // Initiate exception processing
565 STATIC_INLINE uint32_t m68ki_init_exception(void)
568 /* Save the old status register */
569 uint sr = m68ki_get_sr();
571 /* Turn off trace flag, clear pending traces */
572 FLAG_T1 = FLAG_T0 = 0;
574 /* Enter supervisor mode */
575 m68ki_set_s_flag(SFLAG_SET);
580 uint32_t sr = regs.sr; // Save old status register
581 regs.s = 1; // Set supervisor mode
588 // 3 word stack frame (68000 only)
589 STATIC_INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr)
596 m68k_areg(regs, 7) -= 4;
597 m68k_write_memory_32(m68k_areg(regs, 7), pc);
599 m68k_areg(regs, 7) -= 2;
600 m68k_write_memory_16(m68k_areg(regs, 7), sr);
605 unsigned int m68k_get_reg(void * context, m68k_register_t reg)
607 if (reg <= M68K_REG_A7)
608 return regs.regs[reg];
609 else if (reg == M68K_REG_PC)
611 else if (reg == M68K_REG_SR)
616 else if (reg == M68K_REG_SP)
617 return regs.regs[15];
623 void m68k_set_reg(m68k_register_t reg, unsigned int value)
625 if (reg <= M68K_REG_A7)
626 regs.regs[reg] = value;
627 else if (reg == M68K_REG_PC)
629 else if (reg == M68K_REG_SR)
634 else if (reg == M68K_REG_SP)
635 regs.regs[15] = value;
640 // Check if the instruction is a valid one
642 unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type)
644 instruction &= 0xFFFF;
646 if (cpuFunctionTable[instruction] == IllegalOpcode)
653 // Dummy functions, for now, until we prove the concept here. :-)
655 // Temp, while we're using the Musashi disassembler...
657 unsigned int m68k_disassemble(char * str_buff, unsigned int pc, unsigned int cpu_type)
663 int m68k_cycles_run(void) {} /* Number of cycles run so far */
664 int m68k_cycles_remaining(void) {} /* Number of cycles left */
665 //void m68k_modify_timeslice(int cycles) {} /* Modify cycles left */
666 //void m68k_end_timeslice(void) {} /* End timeslice now */
669 void m68k_modify_timeslice(int cycles)
671 regs.remainingCycles = cycles;
675 void m68k_end_timeslice(void)
678 m68ki_initial_cycles = GET_CYCLES();
681 initialCycles = regs.remainingCycles;
682 regs.remainingCycles = 0;
687 unsigned long IllegalOpcode(uint32_t opcode)
690 uint32_t pc = m68k_getpc ();
692 if ((opcode & 0xF000) == 0xF000)
694 Exception(0x0B, 0, M68000_EXC_SRC_CPU); // LineF exception...
697 else if ((opcode & 0xF000) == 0xA000)
699 Exception(0x0A, 0, M68000_EXC_SRC_CPU); // LineA exception...
704 write_log ("Illegal instruction: %04x at %08lx\n", opcode, (long)pc);
707 Exception(0x04, 0, M68000_EXC_SRC_CPU); // Illegal opcode exception...
712 void BuildCPUFunctionTable(void)
715 unsigned long opcode;
717 // We're only using the "fast" 68000 emulation here, not the "compatible"
718 // ("fast" doesn't throw exceptions, so we're using "compatible" now :-P)
720 const struct cputbl * tbl = (currprefs.cpu_compatible
721 ? op_smalltbl_5_ff : op_smalltbl_4_ff);
723 //let's try "compatible" and see what happens here...
724 // const struct cputbl * tbl = op_smalltbl_4_ff;
725 const struct cputbl * tbl = op_smalltbl_5_ff;
728 // Log_Printf(LOG_DEBUG, "Building CPU function table (%d %d %d).\n",
729 // currprefs.cpu_level, currprefs.cpu_compatible, currprefs.address_space_24);
731 // Set all instructions to Illegal...
732 for(opcode=0; opcode<65536; opcode++)
733 cpuFunctionTable[opcode] = IllegalOpcode;
735 // Move functions from compact table into our full function table...
736 for(i=0; tbl[i].handler!=NULL; i++)
737 cpuFunctionTable[tbl[i].opcode] = tbl[i].handler;
739 //JLH: According to readcpu.c, handler is set to -1 and never changes.
740 // Actually, it does read this crap in readcpu.c, do_merges() does it... :-P
741 // Again, seems like a build time thing could be done here...
743 for(opcode=0; opcode<65536; opcode++)
745 // if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > currprefs.cpu_level)
746 if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > 0)
749 if (table68k[opcode].handler != -1)
751 //printf("Relocate: $%04X->$%04X\n", table68k[opcode].handler, opcode);
752 cpuop_func * f = cpuFunctionTable[table68k[opcode].handler];
754 if (f == IllegalOpcode)
757 cpuFunctionTable[opcode] = f;