1 //
2 // m68kinterface.c: Code interface to the UAE 68000 core and support code
3 //
4 // by James Hammons
5 // (C) 2011 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  10/28/2011  Created this file ;-)
12 //
13 
14 #include "m68kinterface.h"
15 //#include <pthread.h>
16 #include "cpudefs.h"
17 #include "inlines.h"
18 #include "cpuextra.h"
19 #include "readcpu.h"
20 
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
37 
38 // These are found in obj/cpustbl.c (generated by gencpu)
39 
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.  */
46 
47 // Externs, supplied by the user...
48 //extern int irq_ack_handler(int);
49 
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);
58 
59 // Local "Global" vars
60 static int32_t initialCycles;
61 cpuop_func * cpuFunctionTable[65536];
62 
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;
68 
69 #define CPU_DEBUG
70 
71 
Dasm(uint32_t offset,uint32_t qt)72 void Dasm(uint32_t offset, uint32_t qt)
73 {
74 #ifdef CPU_DEBUG
75 // back up a few instructions...
76 //offset -= 100;
77 	static char buffer[2048];//, mem[64];
78 	int pc = offset, oldpc;
79 	uint32_t i;
80 
81 	for(i=0; i<qt; i++)
82 	{
83 /*		oldpc = pc;
84 		for(int j=0; j<64; j++)
85 			mem[j^0x01] = jaguar_byte_read(pc + j);
86 
87 		pc += Dasm68000((char *)mem, buffer, 0);
88 		WriteLog("%08X: %s\n", oldpc, buffer);//*/
89 		oldpc = pc;
90 		pc += m68k_disassemble(buffer, pc, 0);//M68K_CPU_TYPE_68000);
91 //		WriteLog("%08X: %s\n", oldpc, buffer);//*/
92 		printf("%08X: %s\n", oldpc, buffer);//*/
93 	}
94 #endif
95 }
96 
97 
98 #ifdef CPU_DEBUG
DumpRegisters(void)99 void DumpRegisters(void)
100 {
101 	uint32_t i;
102 
103 	for(i=0; i<16; i++)
104 	{
105 		printf("%s%i: %08X ", (i < 8 ? "D" : "A"), i & 0x7, regs.regs[i]);
106 
107 		if ((i & 0x03) == 3)
108 			printf("\n");
109 	}
110 }
111 #endif
112 
113 
M68KDebugHalt(void)114 void M68KDebugHalt(void)
115 {
116 	regs.spcflags |= SPCFLAG_DEBUGGER;
117 }
118 
119 
M68KDebugResume(void)120 void M68KDebugResume(void)
121 {
122 	regs.spcflags &= ~SPCFLAG_DEBUGGER;
123 }
124 
125 
m68k_set_cpu_type(unsigned int type)126 void m68k_set_cpu_type(unsigned int type)
127 {
128 }
129 
130 
131 // Pulse the RESET line on the CPU
m68k_pulse_reset(void)132 void m68k_pulse_reset(void)
133 {
134 	static uint32_t emulation_initialized = 0;
135 
136 	// The first call to this function initializes the opcode handler jump table
137 	if (!emulation_initialized)
138 	{
139 		// Build opcode handler table here...
140 		read_table68k();
141 		do_merges();
142 		BuildCPUFunctionTable();
143 		emulation_initialized = 1;
144 	}
145 
146 //	if (CPU_TYPE == 0)	/* KW 990319 */
147 //		m68k_set_cpu_type(M68K_CPU_TYPE_68000);
148 
149 	regs.spcflags = 0;
150 	regs.stopped = 0;
151 	regs.remainingCycles = 0;
152 
153 	regs.intmask = 0x07;
154 	regs.s = 1;								// Supervisor mode ON
155 
156 	// Read initial SP and PC
157 	m68k_areg(regs, 7) = m68k_read_memory_32(0);
158 	m68k_setpc(m68k_read_memory_32(4));
159 	refill_prefetch(m68k_getpc(), 0);
160 }
161 
162 
m68k_execute(int num_cycles)163 int m68k_execute(int num_cycles)
164 {
165 	if (regs.stopped)
166 	{
167 		regs.remainingCycles = 0;	// int32_t
168 		regs.interruptCycles = 0;	// uint32_t
169 
170 		return num_cycles;
171 	}
172 
173 	regs.remainingCycles = num_cycles;
174 	/*int32_t*/ initialCycles = num_cycles;
175 
176 	regs.remainingCycles -= regs.interruptCycles;
177 	regs.interruptCycles = 0;
178 
179 	/* Main loop.  Keep going until we run out of clock cycles */
180 	do
181 	{
182       uint32_t opcode;
183       int32_t cycles;
184 
185 		// This is so our debugging code can break in on a dime.
186 		// Otherwise, this is just extra slow down :-P
187 		if (regs.spcflags & SPCFLAG_DEBUGGER)
188 		{
189 			// Not sure this is correct... :-P
190 			num_cycles = initialCycles - regs.remainingCycles;
191 			regs.remainingCycles = 0;	// int32_t
192 			regs.interruptCycles = 0;	// uint32_t
193 
194 			return num_cycles;
195 		}
196 		if (checkForIRQToHandle)
197 		{
198 			checkForIRQToHandle = 0;
199 			m68k_set_irq2(IRQLevelToHandle);
200 		}
201 
202 #ifdef M68K_HOOK_FUNCTION
203 		M68KInstructionHook();
204 #endif
205 		opcode = get_iword(0);
206 		cycles = (int32_t)(*cpuFunctionTable[opcode])(opcode);
207 		regs.remainingCycles -= cycles;
208 
209       //printf("Executed opcode $%04X (%i cycles)...\n", opcode, cycles);
210 	}
211 	while (regs.remainingCycles > 0);
212 
213 	regs.remainingCycles -= regs.interruptCycles;
214 	regs.interruptCycles = 0;
215 
216 	// Return # of clock cycles used
217 	return initialCycles - regs.remainingCycles;
218 }
219 
220 
m68k_set_irq(unsigned int intLevel)221 void m68k_set_irq(unsigned int intLevel)
222 {
223 	// We need to check for stopped state as well...
224 	if (regs.stopped)
225 	{
226 		m68k_set_irq2(intLevel);
227 		return;
228 	}
229 
230 	// Since this can be called asynchronously, we need to fix it so that it
231 	// doesn't fuck up the main execution loop.
232 	IRQLevelToHandle = intLevel;
233 	checkForIRQToHandle = 1;
234 }
235 
236 
237 /* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
m68k_set_irq2(unsigned int intLevel)238 void m68k_set_irq2(unsigned int intLevel)
239 {
240 //	pthread_mutex_lock(&executionLock);
241 //		printf("m68k_set_irq: Could not get the lock!!!\n");
242 
243 	int oldLevel = regs.intLevel;
244 	regs.intLevel = intLevel;
245 
246 	// A transition from < 7 to 7 always interrupts (NMI)
247 	// Note: Level 7 can also level trigger like a normal IRQ
248 	if (oldLevel != 0x07 && regs.intLevel == 0x07)
249 		m68ki_exception_interrupt(7);		// Edge triggered level 7 (NMI)
250 	else
251 		m68ki_check_interrupts();			// Level triggered (IRQ)
252 
253 //	pthread_mutex_unlock(&executionLock);
254 }
255 
256 
257 // Check for interrupts
m68ki_check_interrupts(void)258 static INLINE void m68ki_check_interrupts(void)
259 {
260 	if (regs.intLevel > regs.intmask)
261 		m68ki_exception_interrupt(regs.intLevel);
262 }
263 
264 
265 // Service an interrupt request and start exception processing
m68ki_exception_interrupt(uint32_t intLevel)266 void m68ki_exception_interrupt(uint32_t intLevel)
267 {
268    uint32_t vector, sr, newPC;
269 
270 	// Turn off the stopped state (N.B.: normal 68K behavior!)
271 	regs.stopped = 0;
272 
273 //JLH: need to add halt state?
274 // prolly, for debugging/alpine mode... :-/
275 // but then again, this should be handled already by the main execution loop :-P
276 	// If we are halted, don't do anything
277 //	if (regs.stopped)
278 //		return;
279 
280 	// Acknowledge the interrupt (NOTE: This is a user supplied function!)
281 	vector = irq_ack_handler(intLevel);
282 
283 	// Get the interrupt vector
284 	if (vector == M68K_INT_ACK_AUTOVECTOR)
285 		// Use the autovectors.  This is the most commonly used implementation
286 		vector = EXCEPTION_INTERRUPT_AUTOVECTOR + intLevel;
287 	else if (vector == M68K_INT_ACK_SPURIOUS)
288 		// Called if no devices respond to the interrupt acknowledge
289 		vector = EXCEPTION_SPURIOUS_INTERRUPT;
290 	else if (vector > 255)
291 	{
292 //		M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
293 //			 m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
294 		return;
295 	}
296 
297 	// Start exception processing
298 	sr = m68ki_init_exception();
299 
300 	// Set the interrupt mask to the level of the one being serviced
301 	regs.intmask = intLevel;
302 
303 	// Get the new PC
304 	newPC = m68k_read_memory_32(vector << 2);
305 
306 	// If vector is uninitialized, call the uninitialized interrupt vector
307 	if (newPC == 0)
308 		newPC = m68k_read_memory_32(EXCEPTION_UNINITIALIZED_INTERRUPT << 2);
309 
310 	// Generate a stack frame
311 	m68ki_stack_frame_3word(regs.pc, sr);
312 
313 	m68k_setpc(newPC);
314 
315 	// Defer cycle counting until later
316 	regs.interruptCycles += 56;	// NOT ACCURATE-- !!! FIX !!!
317 //	CPU_INT_CYCLES += CYC_EXCEPTION[vector];
318 }
319 
320 
321 // Initiate exception processing
m68ki_init_exception(void)322 static INLINE uint32_t m68ki_init_exception(void)
323 {
324    uint32_t sr;
325 
326 	MakeSR();
327 	sr = regs.sr;					// Save old status register
328 	regs.s = 1;								// Set supervisor mode
329 
330 	return sr;
331 }
332 
333 
334 // 3 word stack frame (68000 only)
m68ki_stack_frame_3word(uint32_t pc,uint32_t sr)335 static INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr)
336 {
337 	// Push PC on stack:
338 	m68k_areg(regs, 7) -= 4;
339 	m68k_write_memory_32(m68k_areg(regs, 7), pc);
340 	// Push SR on stack:
341 	m68k_areg(regs, 7) -= 2;
342 	m68k_write_memory_16(m68k_areg(regs, 7), sr);
343 }
344 
345 
m68k_get_reg(void * context,m68k_register_t reg)346 unsigned int m68k_get_reg(void * context, m68k_register_t reg)
347 {
348 	if (reg <= M68K_REG_A7)
349 		return regs.regs[reg];
350 	else if (reg == M68K_REG_PC)
351 		return regs.pc;
352 	else if (reg == M68K_REG_SR)
353 	{
354 		MakeSR();
355 		return regs.sr;
356 	}
357 	else if (reg == M68K_REG_SP)
358 		return regs.regs[15];
359 
360 	return 0;
361 }
362 
363 
m68k_set_reg(m68k_register_t reg,unsigned int value)364 void m68k_set_reg(m68k_register_t reg, unsigned int value)
365 {
366 	if (reg <= M68K_REG_A7)
367 		regs.regs[reg] = value;
368 	else if (reg == M68K_REG_PC)
369 		regs.pc = value;
370 	else if (reg == M68K_REG_SR)
371 	{
372 		regs.sr = value;
373 		MakeFromSR();
374 	}
375 	else if (reg == M68K_REG_SP)
376 		regs.regs[15] = value;
377 }
378 
379 
380 //
381 // Check if the instruction is a valid one
382 //
m68k_is_valid_instruction(unsigned int instruction,unsigned int cpu_type)383 unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type)
384 {
385 	instruction &= 0xFFFF;
386 
387 	if (cpuFunctionTable[instruction] == IllegalOpcode)
388 		return 0;
389 
390 	return 1;
391 }
392 
393 
394 // Dummy functions, for now, until we prove the concept here. :-)
395 
m68k_cycles_run(void)396 int m68k_cycles_run(void) { return 0; }              /* Number of cycles run so far */
m68k_cycles_remaining(void)397 int m68k_cycles_remaining(void) { return 0; }        /* Number of cycles left */
398 
m68k_modify_timeslice(int cycles)399 void m68k_modify_timeslice(int cycles)
400 {
401 	regs.remainingCycles = cycles;
402 }
403 
404 
m68k_end_timeslice(void)405 void m68k_end_timeslice(void)
406 {
407 	initialCycles = regs.remainingCycles;
408 	regs.remainingCycles = 0;
409 }
410 
411 
IllegalOpcode(uint32_t opcode)412 unsigned long IllegalOpcode(uint32_t opcode)
413 {
414 	if ((opcode & 0xF000) == 0xF000)
415 	{
416 		Exception(0x0B, 0, M68000_EXC_SRC_CPU);	// LineF exception...
417 		return 4;
418 	}
419 	else if ((opcode & 0xF000) == 0xA000)
420 	{
421 		Exception(0x0A, 0, M68000_EXC_SRC_CPU);	// LineA exception...
422 		return 4;
423 	}
424 
425 	Exception(0x04, 0, M68000_EXC_SRC_CPU);		// Illegal opcode exception...
426 	return 4;
427 }
428 
429 
BuildCPUFunctionTable(void)430 void BuildCPUFunctionTable(void)
431 {
432 	int i;
433 	unsigned long opcode;
434 
435 	// We're only using the "fast" 68000 emulation here, not the "compatible"
436 	// ("fast" doesn't throw exceptions, so we're using "compatible" now :-P)
437    //let's try "compatible" and see what happens here...
438 	const struct cputbl * tbl = op_smalltbl_5_ff;
439 
440 	// Set all instructions to Illegal...
441 	for(opcode=0; opcode<65536; opcode++)
442 		cpuFunctionTable[opcode] = IllegalOpcode;
443 
444 	// Move functions from compact table into our full function table...
445 	for(i=0; tbl[i].handler!=NULL; i++)
446 		cpuFunctionTable[tbl[i].opcode] = tbl[i].handler;
447 
448 //JLH: According to readcpu.c, handler is set to -1 and never changes.
449 // Actually, it does read this crap in readcpu.c, do_merges() does it... :-P
450 // Again, seems like a build time thing could be done here...
451 	for(opcode=0; opcode<65536; opcode++)
452 	{
453 		if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > 0)
454 			continue;
455 
456 		if (table68k[opcode].handler != -1)
457 		{
458 			cpuop_func * f = cpuFunctionTable[table68k[opcode].handler];
459 
460 			if (f == IllegalOpcode)
461 				abort();
462 
463 			cpuFunctionTable[opcode] = f;
464 		}
465 	}
466 }
467