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, 1);//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 int M68KGetCurrentOpcodeFamily(void)
132 return (OpcodeFamily
);
136 // Get M68K debug halt status
137 int M68KDebugHaltStatus(void)
139 return (regs
.spcflags
& SPCFLAG_DEBUGGER
);
144 int M68KDebugHalt(void)
146 return (regs
.spcflags
|= SPCFLAG_DEBUGGER
);
151 void M68KDebugResume(void)
153 regs
.spcflags
&= ~SPCFLAG_DEBUGGER
;
157 void m68k_set_cpu_type(unsigned int type
)
162 // Pulse the RESET line on the CPU
163 void m68k_pulse_reset(void)
165 static uint32_t emulation_initialized
= 0;
167 // The first call to this function initializes the opcode handler jump table
168 if (!emulation_initialized
)
171 m68ki_build_opcode_table();
172 m68k_set_int_ack_callback(NULL
);
173 m68k_set_bkpt_ack_callback(NULL
);
174 m68k_set_reset_instr_callback(NULL
);
175 m68k_set_pc_changed_callback(NULL
);
176 m68k_set_fc_callback(NULL
);
177 m68k_set_instr_hook_callback(NULL
);
179 // Build opcode handler table here...
182 BuildCPUFunctionTable();
184 emulation_initialized
= 1;
187 // if (CPU_TYPE == 0) /* KW 990319 */
188 // m68k_set_cpu_type(M68K_CPU_TYPE_68000);
191 /* Clear all stop levels and eat up all remaining cycles */
195 /* Turn off tracing */
196 FLAG_T1
= FLAG_T0
= 0;
198 /* Interrupt mask to level 7 */
199 FLAG_INT_MASK
= 0x0700;
202 /* Go to supervisor mode */
203 m68ki_set_sm_flag(SFLAG_SET
| MFLAG_CLEAR
);
205 /* Invalidate the prefetch queue */
206 #if M68K_EMULATE_PREFETCH
207 /* Set to arbitrary number since our first fetch is from 0 */
208 CPU_PREF_ADDR
= 0x1000;
209 #endif /* M68K_EMULATE_PREFETCH */
211 /* Read the initial stack pointer and program counter */
213 REG_SP
= m68ki_read_imm_32();
214 REG_PC
= m68ki_read_imm_32();
217 checkForIRQToHandle
= 0;
220 regs
.remainingCycles
= 0;
223 regs
.s
= 1; // Supervisor mode ON
225 // Read initial SP and PC
226 m68k_areg(regs
, 7) = m68k_read_memory_32(0);
227 m68k_setpc(m68k_read_memory_32(4));
228 refill_prefetch(m68k_getpc(), 0);
233 int m68k_execute(int num_cycles
)
237 regs
.remainingCycles
= 0; // int32_t
238 regs
.interruptCycles
= 0; // uint32_t
244 /* Set our pool of clock cycles available */
245 SET_CYCLES(num_cycles
);
246 m68ki_initial_cycles
= num_cycles
;
248 /* ASG: update cycles */
249 USE_CYCLES(CPU_INT_CYCLES
);
252 /* Return point if we had an address error */
253 m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */
255 regs
.remainingCycles
= num_cycles
;
256 /*int32_t*/ initialCycles
= num_cycles
;
258 regs
.remainingCycles
-= regs
.interruptCycles
;
259 regs
.interruptCycles
= 0;
262 /* Main loop. Keep going until we run out of clock cycles */
265 // This is so our debugging code can break in on a dime.
266 // Otherwise, this is just extra slow down :-P
267 if (regs
.spcflags
& SPCFLAG_DEBUGGER
)
269 // Not sure this is correct... :-P
270 num_cycles
= initialCycles
- regs
.remainingCycles
;
271 regs
.remainingCycles
= 0; // int32_t
272 regs
.interruptCycles
= 0; // uint32_t
277 /* Set tracing accodring to T1. (T0 is done inside instruction) */
278 m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */
280 /* Set the address space for reads */
281 m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */
283 /* Call external hook to peek at CPU */
284 m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */
286 /* Record previous program counter */
289 /* Read an instruction and call its handler */
290 REG_IR
= m68ki_read_imm_16();
291 m68ki_instruction_jump_table
[REG_IR
]();
292 USE_CYCLES(CYC_INSTRUCTION
[REG_IR
]);
294 /* Trace m68k_exception, if necessary */
295 m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
297 //Testing Hover Strike...
300 static int hitCount
= 0;
301 static int inRoutine
= 0;
304 //if (regs.pc == 0x80340A)
305 if (regs
.pc
== 0x803416)
310 printf("%i: $80340A start. A0=%08X, A1=%08X ", hitCount
, regs
.regs
[8], regs
.regs
[9]);
312 else if (regs
.pc
== 0x803422)
315 printf("(%i instructions)\n", instSeen
);
321 // AvP testing... (problem was: 32 bit addresses on 24 bit address cpu--FIXED)
325 if (regs
.pc
== 0x94BA)
331 if (regs
.pc
== 0x94C6)
334 // if (regs.regs[10] == 0xFFFFFFFF && go)
337 // printf("A2=-1, PC=%08X\n", regs.pc);
339 // Dasm(regs.pc, 130);
343 //94BA: 2468 0000 MOVEA.L (A0,$0000) == $0002328A, A2
344 //94BE: 200A MOVE.L A2, D0
345 //94C0: 6A02 BPL.B $94C4
346 //94C2: 2452 MOVEA.L (A2), A2 ; <--- HERE
347 //94C4: 4283 CLR.L D3
349 // pthread_mutex_lock(&executionLock);
350 if (checkForIRQToHandle
)
352 checkForIRQToHandle
= 0;
353 m68k_set_irq2(IRQLevelToHandle
);
356 #ifdef M68K_HOOK_FUNCTION
357 M68KInstructionHook();
359 uint32_t opcode
= get_iword(0);
360 //if ((opcode & 0xFFF8) == 0x31C0)
362 // printf("MOVE.W D%i, EA\n", opcode & 0x07);
365 if (regs
.spcflags
& SPCFLAG_DEBUGGER
)
371 cycles
= (int32_t)(*cpuFunctionTable
[opcode
])(opcode
);
373 regs
.remainingCycles
-= cycles
;
374 // pthread_mutex_unlock(&executionLock);
376 //printf("Executed opcode $%04X (%i cycles)...\n", opcode, cycles);
379 while (regs
.remainingCycles
> 0);
382 /* set previous PC to current PC for the next entry into the loop */
385 /* ASG: update cycles */
386 USE_CYCLES(CPU_INT_CYCLES
);
389 /* return how many clocks we used */
390 return m68ki_initial_cycles
- GET_CYCLES();
392 regs
.remainingCycles
-= regs
.interruptCycles
;
393 regs
.interruptCycles
= 0;
395 // Return # of clock cycles used
396 return initialCycles
- regs
.remainingCycles
;
401 void m68k_set_irq(unsigned int intLevel
)
403 // We need to check for stopped state as well...
406 m68k_set_irq2(intLevel
);
410 // Since this can be called asynchronously, we need to fix it so that it
411 // doesn't fuck up the main execution loop.
412 IRQLevelToHandle
= intLevel
;
413 checkForIRQToHandle
= 1;
417 /* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
418 void m68k_set_irq2(unsigned int intLevel
)
420 // pthread_mutex_lock(&executionLock);
421 // printf("m68k_set_irq: Could not get the lock!!!\n");
423 int oldLevel
= regs
.intLevel
;
424 regs
.intLevel
= intLevel
;
426 // A transition from < 7 to 7 always interrupts (NMI)
427 // Note: Level 7 can also level trigger like a normal IRQ
428 if (oldLevel
!= 0x07 && regs
.intLevel
== 0x07)
429 m68ki_exception_interrupt(7); // Edge triggered level 7 (NMI)
431 m68ki_check_interrupts(); // Level triggered (IRQ)
433 // pthread_mutex_unlock(&executionLock);
437 // Check for interrupts
438 STATIC_INLINE
void m68ki_check_interrupts(void)
441 if(CPU_INT_LEVEL
> FLAG_INT_MASK
)
442 m68ki_exception_interrupt(CPU_INT_LEVEL
>>8);
444 if (regs
.intLevel
> regs
.intmask
)
445 m68ki_exception_interrupt(regs
.intLevel
);
450 // Service an interrupt request and start exception processing
451 void m68ki_exception_interrupt(uint32_t intLevel
)
458 /* Turn off the stopped state */
459 CPU_STOPPED
&= ~STOP_LEVEL_STOP
;
461 /* If we are halted, don't do anything */
465 /* Acknowledge the interrupt */
466 vector
= m68ki_int_ack(int_level
);
468 /* Get the interrupt vector */
469 if(vector
== M68K_INT_ACK_AUTOVECTOR
)
470 /* Use the autovectors. This is the most commonly used implementation */
471 vector
= EXCEPTION_INTERRUPT_AUTOVECTOR
+int_level
;
472 else if(vector
== M68K_INT_ACK_SPURIOUS
)
473 /* Called if no devices respond to the interrupt acknowledge */
474 vector
= EXCEPTION_SPURIOUS_INTERRUPT
;
475 else if(vector
> 255)
477 M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE
"%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
478 m68ki_cpu_names
[CPU_TYPE
], ADDRESS_68K(REG_PC
), vector
));
482 /* Start exception processing */
483 sr
= m68ki_init_exception();
485 /* Set the interrupt mask to the level of the one being serviced */
486 FLAG_INT_MASK
= int_level
<<8;
489 new_pc
= m68ki_read_data_32((vector
<<2) + REG_VBR
);
491 /* If vector is uninitialized, call the uninitialized interrupt vector */
493 new_pc
= m68ki_read_data_32((EXCEPTION_UNINITIALIZED_INTERRUPT
<<2) + REG_VBR
);
495 /* Generate a stack frame */
496 m68ki_stack_frame_0000(REG_PC
, sr
, vector
);
498 if(FLAG_M
&& CPU_TYPE_IS_EC020_PLUS(CPU_TYPE
))
500 /* Create throwaway frame */
501 m68ki_set_sm_flag(FLAG_S
); /* clear M */
502 sr
|= 0x2000; /* Same as SR in master stack frame except S is forced high */
503 m68ki_stack_frame_0001(REG_PC
, sr
, vector
);
508 /* Defer cycle counting until later */
509 CPU_INT_CYCLES
+= CYC_EXCEPTION
[vector
];
511 #if !M68K_EMULATE_INT_ACK
512 /* Automatically clear IRQ if we are not using an acknowledge scheme */
514 #endif /* M68K_EMULATE_INT_ACK */
516 // Turn off the stopped state (N.B.: normal 68K behavior!)
519 //JLH: need to add halt state?
520 // prolly, for debugging/alpine mode... :-/
521 // but then again, this should be handled already by the main execution loop :-P
522 // If we are halted, don't do anything
526 // Acknowledge the interrupt (NOTE: This is a user supplied function!)
527 uint32_t vector
= irq_ack_handler(intLevel
);
529 // Get the interrupt vector
530 if (vector
== M68K_INT_ACK_AUTOVECTOR
)
531 // Use the autovectors. This is the most commonly used implementation
532 vector
= EXCEPTION_INTERRUPT_AUTOVECTOR
+ intLevel
;
533 else if (vector
== M68K_INT_ACK_SPURIOUS
)
534 // Called if no devices respond to the interrupt acknowledge
535 vector
= EXCEPTION_SPURIOUS_INTERRUPT
;
536 else if (vector
> 255)
538 // M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
539 // m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
543 // Start exception processing
544 uint32_t sr
= m68ki_init_exception();
546 // Set the interrupt mask to the level of the one being serviced
547 regs
.intmask
= intLevel
;
550 extern int startM68KTracing
;
551 if (startM68KTracing
)
553 printf("IRQ: old PC=%06X, ", regs
.pc
);
558 uint32_t newPC
= m68k_read_memory_32(vector
<< 2);
561 if (startM68KTracing
)
563 printf("new PC=%06X, vector=%u, ", newPC
, vector
);
567 // If vector is uninitialized, call the uninitialized interrupt vector
569 newPC
= m68k_read_memory_32(EXCEPTION_UNINITIALIZED_INTERRUPT
<< 2);
571 // Generate a stack frame
572 m68ki_stack_frame_3word(regs
.pc
, sr
);
576 if (startM68KTracing
)
578 printf("(PC=%06X)\n", regs
.pc
);
582 // Defer cycle counting until later
583 regs
.interruptCycles
+= 56; // NOT ACCURATE-- !!! FIX !!!
584 // CPU_INT_CYCLES += CYC_EXCEPTION[vector];
589 // Initiate exception processing
590 STATIC_INLINE
uint32_t m68ki_init_exception(void)
593 /* Save the old status register */
594 uint sr
= m68ki_get_sr();
596 /* Turn off trace flag, clear pending traces */
597 FLAG_T1
= FLAG_T0
= 0;
599 /* Enter supervisor mode */
600 m68ki_set_s_flag(SFLAG_SET
);
605 uint32_t sr
= regs
.sr
; // Save old status register
606 regs
.s
= 1; // Set supervisor mode
613 // 3 word stack frame (68000 only)
614 STATIC_INLINE
void m68ki_stack_frame_3word(uint32_t pc
, uint32_t sr
)
621 m68k_areg(regs
, 7) -= 4;
622 m68k_write_memory_32(m68k_areg(regs
, 7), pc
);
624 m68k_areg(regs
, 7) -= 2;
625 m68k_write_memory_16(m68k_areg(regs
, 7), sr
);
630 unsigned int m68k_get_reg(void * context
, m68k_register_t reg
)
632 if (reg
<= M68K_REG_A7
)
633 return regs
.regs
[reg
];
634 else if (reg
== M68K_REG_PC
)
636 else if (reg
== M68K_REG_SR
)
641 else if (reg
== M68K_REG_SP
)
642 return regs
.regs
[15];
648 void m68k_set_reg(m68k_register_t reg
, unsigned int value
)
650 if (reg
<= M68K_REG_A7
)
651 regs
.regs
[reg
] = value
;
652 else if (reg
== M68K_REG_PC
)
654 else if (reg
== M68K_REG_SR
)
659 else if (reg
== M68K_REG_SP
)
660 regs
.regs
[15] = value
;
665 // Check if the instruction is a valid one
667 unsigned int m68k_is_valid_instruction(unsigned int instruction
, unsigned int cpu_type
)
669 instruction
&= 0xFFFF;
671 if (cpuFunctionTable
[instruction
] == IllegalOpcode
)
678 // Dummy functions, for now, until we prove the concept here. :-)
680 // Temp, while we're using the Musashi disassembler...
682 unsigned int m68k_disassemble(char * str_buff
, unsigned int pc
, unsigned int cpu_type
)
688 int m68k_cycles_run(void) {} /* Number of cycles run so far */
689 int m68k_cycles_remaining(void) {} /* Number of cycles left */
690 //void m68k_modify_timeslice(int cycles) {} /* Modify cycles left */
691 //void m68k_end_timeslice(void) {} /* End timeslice now */
694 void m68k_modify_timeslice(int cycles
)
696 regs
.remainingCycles
= cycles
;
700 void m68k_end_timeslice(void)
703 m68ki_initial_cycles
= GET_CYCLES();
706 initialCycles
= regs
.remainingCycles
;
707 regs
.remainingCycles
= 0;
712 unsigned long IllegalOpcode(uint32_t opcode
)
715 uint32_t pc
= m68k_getpc ();
717 if ((opcode
& 0xF000) == 0xF000)
719 Exception(0x0B, 0, M68000_EXC_SRC_CPU
); // LineF exception...
722 else if ((opcode
& 0xF000) == 0xA000)
724 Exception(0x0A, 0, M68000_EXC_SRC_CPU
); // LineA exception...
729 write_log ("Illegal instruction: %04x at %08lx\n", opcode
, (long)pc
);
732 Exception(0x04, 0, M68000_EXC_SRC_CPU
); // Illegal opcode exception...
737 void BuildCPUFunctionTable(void)
740 unsigned long opcode
;
742 // We're only using the "fast" 68000 emulation here, not the "compatible"
743 // ("fast" doesn't throw exceptions, so we're using "compatible" now :-P)
745 const struct cputbl
* tbl
= (currprefs
.cpu_compatible
746 ? op_smalltbl_5_ff
: op_smalltbl_4_ff
);
748 //let's try "compatible" and see what happens here...
749 // const struct cputbl * tbl = op_smalltbl_4_ff;
750 const struct cputbl
* tbl
= op_smalltbl_5_ff
;
753 // Log_Printf(LOG_DEBUG, "Building CPU function table (%d %d %d).\n",
754 // currprefs.cpu_level, currprefs.cpu_compatible, currprefs.address_space_24);
756 // Set all instructions to Illegal...
757 for(opcode
=0; opcode
<65536; opcode
++)
758 cpuFunctionTable
[opcode
] = IllegalOpcode
;
760 // Move functions from compact table into our full function table...
761 for(i
=0; tbl
[i
].handler
!=NULL
; i
++)
762 cpuFunctionTable
[tbl
[i
].opcode
] = tbl
[i
].handler
;
764 //JLH: According to readcpu.c, handler is set to -1 and never changes.
765 // Actually, it does read this crap in readcpu.c, do_merges() does it... :-P
766 // Again, seems like a build time thing could be done here...
768 for(opcode
=0; opcode
<65536; opcode
++)
770 // if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > currprefs.cpu_level)
771 if (table68k
[opcode
].mnemo
== i_ILLG
|| table68k
[opcode
].clev
> 0)
774 if (table68k
[opcode
].handler
!= -1)
776 //printf("Relocate: $%04X->$%04X\n", table68k[opcode].handler, opcode);
777 cpuop_func
* f
= cpuFunctionTable
[table68k
[opcode
].handler
];
779 if (f
== IllegalOpcode
)
782 cpuFunctionTable
[opcode
] = f
;