]> Shamusworld >> Repos - virtualjaguar/blob - src/m68000/m68kinterface.c
c6e81021be9e833283dd4266d9f2dec4b4ed3ed7
[virtualjaguar] / src / m68000 / m68kinterface.c
1 //
2 // m68kinterface.c: Code interface to the UAE 68000 core and support code
3 //
4 // by James L. Hammons
5 // (C) 2011 Underground Software
6 //
7 // JLH = James L. 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 "cpudefs.h"
16 #include "inlines.h"
17 #include "cpuextra.h"
18 #include "readcpu.h"
19
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
58 // Local "Global" vars
59 static int32_t initialCycles;
60 cpuop_func * cpuFunctionTable[65536];
61
62
63 #if 0
64 #define ADD_CYCLES(A)    m68ki_remaining_cycles += (A)
65 #define USE_CYCLES(A)    m68ki_remaining_cycles -= (A)
66 #define SET_CYCLES(A)    m68ki_remaining_cycles = A
67 #define GET_CYCLES()     m68ki_remaining_cycles
68 #define USE_ALL_CYCLES() m68ki_remaining_cycles = 0
69
70 #define CPU_INT_LEVEL    m68ki_cpu.int_level /* ASG: changed from CPU_INTS_PENDING */
71 #define CPU_INT_CYCLES   m68ki_cpu.int_cycles /* ASG */
72 #define CPU_STOPPED      m68ki_cpu.stopped
73 #define CPU_PREF_ADDR    m68ki_cpu.pref_addr
74 #define CPU_PREF_DATA    m68ki_cpu.pref_data
75 #define CPU_ADDRESS_MASK m68ki_cpu.address_mask
76 #define CPU_SR_MASK      m68ki_cpu.sr_mask
77 #endif
78
79 #define CPU_DEBUG
80 void Dasm(uint32_t offset, uint32_t qt)
81 {
82 #ifdef CPU_DEBUG
83         static char buffer[2048];//, mem[64];
84         int pc = offset, oldpc;
85         uint32_t i;
86
87         for(i=0; i<qt; i++)
88         {
89 /*              oldpc = pc;
90                 for(int j=0; j<64; j++)
91                         mem[j^0x01] = jaguar_byte_read(pc + j);
92
93                 pc += Dasm68000((char *)mem, buffer, 0);
94                 WriteLog("%08X: %s\n", oldpc, buffer);//*/
95                 oldpc = pc;
96                 pc += m68k_disassemble(buffer, pc, 0);//M68K_CPU_TYPE_68000);
97 //              WriteLog("%08X: %s\n", oldpc, buffer);//*/
98                 printf("%08X: %s\n", oldpc, buffer);//*/
99         }
100 #endif
101 }
102
103
104 void m68k_set_cpu_type(unsigned int type)
105 {
106 }
107
108 // Pulse the RESET line on the CPU
109 void m68k_pulse_reset(void)
110 {
111         static uint32_t emulation_initialized = 0;
112
113         // The first call to this function initializes the opcode handler jump table
114         if (!emulation_initialized)
115         {
116 #if 0
117                 m68ki_build_opcode_table();
118                 m68k_set_int_ack_callback(NULL);
119                 m68k_set_bkpt_ack_callback(NULL);
120                 m68k_set_reset_instr_callback(NULL);
121                 m68k_set_pc_changed_callback(NULL);
122                 m68k_set_fc_callback(NULL);
123                 m68k_set_instr_hook_callback(NULL);
124 #else
125                 // Build opcode handler table here...
126                 read_table68k();
127                 do_merges();
128                 BuildCPUFunctionTable();
129 #endif
130                 emulation_initialized = 1;
131         }
132
133 //      if (CPU_TYPE == 0)      /* KW 990319 */
134 //              m68k_set_cpu_type(M68K_CPU_TYPE_68000);
135
136 #if 0
137         /* Clear all stop levels and eat up all remaining cycles */
138         CPU_STOPPED = 0;
139         SET_CYCLES(0);
140
141         /* Turn off tracing */
142         FLAG_T1 = FLAG_T0 = 0;
143         m68ki_clear_trace();
144         /* Interrupt mask to level 7 */
145         FLAG_INT_MASK = 0x0700;
146         /* Reset VBR */
147         REG_VBR = 0;
148         /* Go to supervisor mode */
149         m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR);
150
151         /* Invalidate the prefetch queue */
152 #if M68K_EMULATE_PREFETCH
153         /* Set to arbitrary number since our first fetch is from 0 */
154         CPU_PREF_ADDR = 0x1000;
155 #endif /* M68K_EMULATE_PREFETCH */
156
157         /* Read the initial stack pointer and program counter */
158         m68ki_jump(0);
159         REG_SP = m68ki_read_imm_32();
160         REG_PC = m68ki_read_imm_32();
161         m68ki_jump(REG_PC);
162 #else
163         regs.stopped = 0;
164         regs.remainingCycles = 0;
165         
166         regs.intmask = 0x07;
167         regs.s = 1;                                                             // Supervisor mode ON
168
169         // Read initial SP and PC
170         m68k_areg(regs, 7) = m68k_read_memory_32(0);
171         m68k_setpc(m68k_read_memory_32(4));
172         refill_prefetch(m68k_getpc(), 0);
173 #endif
174 }
175
176 int m68k_execute(int num_cycles)
177 {
178 #if 0
179         /* Make sure we're not stopped */
180         if (CPU_STOPPED)
181         {
182                 /* We get here if the CPU is stopped or halted */
183                 SET_CYCLES(0);
184                 CPU_INT_CYCLES = 0;
185
186                 return num_cycles;
187         }
188 #else
189         if (regs.stopped)
190         {
191                 regs.remainingCycles = 0;       // int32_t
192                 regs.interruptCycles = 0;       // uint32_t
193
194                 return num_cycles;
195         }
196 #endif
197
198 #if 0
199         /* Set our pool of clock cycles available */
200         SET_CYCLES(num_cycles);
201         m68ki_initial_cycles = num_cycles;
202
203         /* ASG: update cycles */
204         USE_CYCLES(CPU_INT_CYCLES);
205         CPU_INT_CYCLES = 0;
206
207         /* Return point if we had an address error */
208         m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */
209 #else
210         regs.remainingCycles = num_cycles;
211         /*int32_t*/ initialCycles = num_cycles;
212         
213         regs.remainingCycles -= regs.interruptCycles;
214         regs.interruptCycles = 0;
215 #endif
216
217         /* Main loop.  Keep going until we run out of clock cycles */
218         do
219         {
220 #if 0
221                 /* Set tracing accodring to T1. (T0 is done inside instruction) */
222                 m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */
223
224                 /* Set the address space for reads */
225                 m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */
226
227                 /* Call external hook to peek at CPU */
228                 m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */
229
230                 /* Record previous program counter */
231                 REG_PPC = REG_PC;
232
233                 /* Read an instruction and call its handler */
234                 REG_IR = m68ki_read_imm_16();
235                 m68ki_instruction_jump_table[REG_IR]();
236                 USE_CYCLES(CYC_INSTRUCTION[REG_IR]);
237
238                 /* Trace m68k_exception, if necessary */
239                 m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
240 #else
241 //Testing Hover Strike...
242 #if 0
243 //Dasm(regs.pc, 1);
244 static int hitCount = 0;
245 static int inRoutine = 0;
246 static int instSeen;
247
248 //if (regs.pc == 0x80340A)
249 if (regs.pc == 0x803416)
250 {
251         hitCount++;
252         inRoutine = 1;
253         instSeen = 0;
254         printf("%i: $80340A start. A0=%08X, A1=%08X ", hitCount, regs.regs[8], regs.regs[9]);
255 }
256 else if (regs.pc == 0x803422)
257 {
258         inRoutine = 0;
259         printf("(%i instructions)\n", instSeen);
260 }
261
262 if (inRoutine)
263         instSeen++;
264 #endif
265                 uint32_t opcode = get_iword(0);
266 //if ((opcode & 0xFFF8) == 0x31C0)
267 //{
268 //      printf("MOVE.W D%i, EA\n", opcode & 0x07);
269 //}
270                 int32_t cycles = (int32_t)(*cpuFunctionTable[opcode])(opcode);
271                 regs.remainingCycles -= cycles;
272 //printf("Executed opcode $%04X (%i cycles)...\n", opcode, cycles);
273 #endif
274         }
275 #if 0
276         while (GET_CYCLES() > 0);
277 #else
278         while (regs.remainingCycles > 0);
279 #endif
280
281 #if 0
282         /* set previous PC to current PC for the next entry into the loop */
283         REG_PPC = REG_PC;
284
285         /* ASG: update cycles */
286         USE_CYCLES(CPU_INT_CYCLES);
287         CPU_INT_CYCLES = 0;
288
289         /* return how many clocks we used */
290         return m68ki_initial_cycles - GET_CYCLES();
291 #else
292         regs.remainingCycles -= regs.interruptCycles;
293         regs.interruptCycles = 0;
294
295         // Return # of clock cycles used
296         return initialCycles - regs.remainingCycles;
297 #endif
298 }
299
300 /* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
301 void m68k_set_irq(unsigned int intLevel)
302 {
303 #if 0
304         uint old_level = CPU_INT_LEVEL;
305         CPU_INT_LEVEL = int_level << 8;
306
307         /* A transition from < 7 to 7 always interrupts (NMI) */
308         /* Note: Level 7 can also level trigger like a normal IRQ */
309         if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700)
310                 m68ki_exception_interrupt(7); /* Edge triggered level 7 (NMI) */
311         else
312                 m68ki_check_interrupts(); /* Level triggered (IRQ) */
313 #else
314         int oldLevel = regs.intLevel;
315         regs.intLevel = intLevel;
316
317         // A transition from < 7 to 7 always interrupts (NMI)
318         // Note: Level 7 can also level trigger like a normal IRQ
319         if (oldLevel != 0x07 && regs.intLevel == 0x07)
320                 m68ki_exception_interrupt(7);           // Edge triggered level 7 (NMI)
321         else
322                 m68ki_check_interrupts();                       // Level triggered (IRQ)
323 #endif
324 }
325
326 // Check for interrupts
327 STATIC_INLINE void m68ki_check_interrupts(void)
328 {
329 #if 0
330         if(CPU_INT_LEVEL > FLAG_INT_MASK)
331                 m68ki_exception_interrupt(CPU_INT_LEVEL>>8);
332 #else
333         if (regs.intLevel > regs.intmask)
334                 m68ki_exception_interrupt(regs.intLevel);
335 #endif
336 }
337
338 // Service an interrupt request and start exception processing
339 void m68ki_exception_interrupt(uint32_t intLevel)
340 {
341 #if 0
342         uint vector;
343         uint sr;
344         uint new_pc;
345
346         /* Turn off the stopped state */
347         CPU_STOPPED &= ~STOP_LEVEL_STOP;
348
349         /* If we are halted, don't do anything */
350         if(CPU_STOPPED)
351                 return;
352
353         /* Acknowledge the interrupt */
354         vector = m68ki_int_ack(int_level);
355
356         /* Get the interrupt vector */
357         if(vector == M68K_INT_ACK_AUTOVECTOR)
358                 /* Use the autovectors.  This is the most commonly used implementation */
359                 vector = EXCEPTION_INTERRUPT_AUTOVECTOR+int_level;
360         else if(vector == M68K_INT_ACK_SPURIOUS)
361                 /* Called if no devices respond to the interrupt acknowledge */
362                 vector = EXCEPTION_SPURIOUS_INTERRUPT;
363         else if(vector > 255)
364         {
365                 M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
366                          m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
367                 return;
368         }
369
370         /* Start exception processing */
371         sr = m68ki_init_exception();
372
373         /* Set the interrupt mask to the level of the one being serviced */
374         FLAG_INT_MASK = int_level<<8;
375
376         /* Get the new PC */
377         new_pc = m68ki_read_data_32((vector<<2) + REG_VBR);
378
379         /* If vector is uninitialized, call the uninitialized interrupt vector */
380         if(new_pc == 0)
381                 new_pc = m68ki_read_data_32((EXCEPTION_UNINITIALIZED_INTERRUPT<<2) + REG_VBR);
382
383         /* Generate a stack frame */
384         m68ki_stack_frame_0000(REG_PC, sr, vector);
385
386         if(FLAG_M && CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
387         {
388                 /* Create throwaway frame */
389                 m68ki_set_sm_flag(FLAG_S);      /* clear M */
390                 sr |= 0x2000; /* Same as SR in master stack frame except S is forced high */
391                 m68ki_stack_frame_0001(REG_PC, sr, vector);
392         }
393
394         m68ki_jump(new_pc);
395
396         /* Defer cycle counting until later */
397         CPU_INT_CYCLES += CYC_EXCEPTION[vector];
398
399 #if !M68K_EMULATE_INT_ACK
400         /* Automatically clear IRQ if we are not using an acknowledge scheme */
401         CPU_INT_LEVEL = 0;
402 #endif /* M68K_EMULATE_INT_ACK */
403 #else
404         // Turn off the stopped state
405         regs.stopped = 0;
406
407 //JLH: need to add halt state?
408         // If we are halted, don't do anything
409 //      if (CPU_STOPPED)
410 //              return;
411
412         // Acknowledge the interrupt (NOTE: This is a user supplied function!)
413         uint32_t vector = irq_ack_handler(intLevel);
414
415         // Get the interrupt vector
416         if (vector == M68K_INT_ACK_AUTOVECTOR)
417                 // Use the autovectors.  This is the most commonly used implementation
418                 vector = EXCEPTION_INTERRUPT_AUTOVECTOR + intLevel;
419         else if (vector == M68K_INT_ACK_SPURIOUS)
420                 // Called if no devices respond to the interrupt acknowledge
421                 vector = EXCEPTION_SPURIOUS_INTERRUPT;
422         else if (vector > 255)
423         {
424 //              M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
425 //                       m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
426                 return;
427         }
428
429         // Start exception processing
430         uint32_t sr = m68ki_init_exception();
431
432         // Set the interrupt mask to the level of the one being serviced
433         regs.intmask = intLevel;
434
435         // Get the new PC
436         uint32_t newPC = m68k_read_memory_32(vector << 2);
437
438         // If vector is uninitialized, call the uninitialized interrupt vector
439         if (newPC == 0)
440                 newPC = m68k_read_memory_32(EXCEPTION_UNINITIALIZED_INTERRUPT << 2);
441
442         // Generate a stack frame
443         m68ki_stack_frame_3word(regs.pc, sr);
444
445         m68k_setpc(newPC);
446
447         // Defer cycle counting until later
448         regs.interruptCycles += 56;     // NOT ACCURATE-- !!! FIX !!!
449 //      CPU_INT_CYCLES += CYC_EXCEPTION[vector];
450 #endif
451 }
452
453 // Initiate exception processing
454 STATIC_INLINE uint32_t m68ki_init_exception(void)
455 {
456 #if 0
457         /* Save the old status register */
458         uint sr = m68ki_get_sr();
459
460         /* Turn off trace flag, clear pending traces */
461         FLAG_T1 = FLAG_T0 = 0;
462         m68ki_clear_trace();
463         /* Enter supervisor mode */
464         m68ki_set_s_flag(SFLAG_SET);
465
466         return sr;
467 #else
468         MakeSR();
469         uint32_t sr = regs.sr;                                  // Save old status register
470         regs.s = 1;                                                             // Set supervisor mode
471
472         return sr;
473 #endif
474 }
475
476 // 3 word stack frame (68000 only)
477 STATIC_INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr)
478 {
479 #if 0
480         m68ki_push_32(pc);
481         m68ki_push_16(sr);
482 #else
483         // Push PC on stack:
484         m68k_areg(regs, 7) -= 4;
485         m68k_write_memory_32(m68k_areg(regs, 7), pc);
486         // Push SR on stack:
487         m68k_areg(regs, 7) -= 2;
488         m68k_write_memory_16(m68k_areg(regs, 7), sr);
489 #endif
490 }
491
492 unsigned int m68k_get_reg(void * context, m68k_register_t reg)
493 {
494         if (reg <= M68K_REG_A7)
495                 return regs.regs[reg];
496         else if (reg == M68K_REG_PC)
497                 return regs.pc;
498         else if (reg == M68K_REG_SR)
499         {
500                 MakeSR();
501                 return regs.sr;
502         }
503         else if (reg == M68K_REG_SP)
504                 return regs.regs[15];
505
506         return 0;
507 }
508
509 void m68k_set_reg(m68k_register_t reg, unsigned int value)
510 {
511         if (reg <= M68K_REG_A7)
512                 regs.regs[reg] = value;
513         else if (reg == M68K_REG_PC)
514                 regs.pc = value;
515         else if (reg == M68K_REG_SR)
516         {
517                 regs.sr = value;
518                 MakeFromSR();
519         }
520         else if (reg == M68K_REG_SP)
521                 regs.regs[15] = value;
522 }
523
524 //
525 // Check if the instruction is a valid one
526 //
527 unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type)
528 {
529         instruction &= 0xFFFF;
530
531         if (cpuFunctionTable[instruction] == IllegalOpcode)
532                 return 0;
533
534         return 1;
535 }
536
537 // Dummy functions, for now, until we prove the concept here. :-)
538
539 // Temp, while we're using the Musashi disassembler...
540 #if 0
541 unsigned int m68k_disassemble(char * str_buff, unsigned int pc, unsigned int cpu_type)
542 {
543         return 0;
544 }
545 #endif
546
547 int m68k_cycles_run(void) {}              /* Number of cycles run so far */
548 int m68k_cycles_remaining(void) {}        /* Number of cycles left */
549 void m68k_modify_timeslice(int cycles) {} /* Modify cycles left */
550 //void m68k_end_timeslice(void) {}          /* End timeslice now */
551
552 void m68k_end_timeslice(void)
553 {
554 #if 0
555         m68ki_initial_cycles = GET_CYCLES();
556         SET_CYCLES(0);
557 #else
558         initialCycles = regs.remainingCycles;
559         regs.remainingCycles = 0;
560 #endif
561 }
562
563
564 unsigned long IllegalOpcode(uint32_t opcode)
565 {
566 #if 0
567         uint32_t pc = m68k_getpc ();
568 #endif
569         if ((opcode & 0xF000) == 0xF000)
570         {
571                 Exception(0x0B, 0, M68000_EXC_SRC_CPU); // LineF exception...
572                 return 4;
573         }
574         else if ((opcode & 0xF000) == 0xA000)
575         {
576                 Exception(0x0A, 0, M68000_EXC_SRC_CPU); // LineA exception...
577                 return 4;
578         }
579
580 #if 0
581         write_log ("Illegal instruction: %04x at %08lx\n", opcode, (long)pc);
582 #endif
583
584         Exception(0x04, 0, M68000_EXC_SRC_CPU);         // Illegal opcode exception...
585         return 4;
586 }
587
588
589 void BuildCPUFunctionTable(void)
590 {
591         int i;
592         unsigned long opcode;
593
594         // We're only using the "fast" 68000 emulation here, not the "compatible"
595         // ("fast" doesn't throw exceptions, so we're using "compatible" now :-P)
596 #if 0
597         const struct cputbl * tbl = (currprefs.cpu_compatible
598                 ? op_smalltbl_5_ff : op_smalltbl_4_ff);
599 #else
600 //let's try "compatible" and see what happens here...
601 //      const struct cputbl * tbl = op_smalltbl_4_ff;
602         const struct cputbl * tbl = op_smalltbl_5_ff;
603 #endif
604
605 //      Log_Printf(LOG_DEBUG, "Building CPU function table (%d %d %d).\n",
606 //              currprefs.cpu_level, currprefs.cpu_compatible, currprefs.address_space_24);
607
608         // Set all instructions to Illegal...
609         for(opcode=0; opcode<65536; opcode++)
610                 cpuFunctionTable[opcode] = IllegalOpcode;
611
612         // Move functions from compact table into our full function table...
613         for(i=0; tbl[i].handler!=NULL; i++)
614                 cpuFunctionTable[tbl[i].opcode] = tbl[i].handler;
615
616 //JLH: According to readcpu.c, handler is set to -1 and never changes.
617 // Actually, it does read this crap in readcpu.c, do_merges() does it... :-P
618 // Again, seems like a build time thing could be done here...
619 #if 1
620         for(opcode=0; opcode<65536; opcode++)
621         {
622 //              if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > currprefs.cpu_level)
623                 if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > 0)
624                         continue;
625
626                 if (table68k[opcode].handler != -1)
627                 {
628 //printf("Relocate: $%04X->$%04X\n", table68k[opcode].handler, opcode);
629                         cpuop_func * f = cpuFunctionTable[table68k[opcode].handler];
630
631                         if (f == IllegalOpcode)
632                                 abort();
633
634                         cpuFunctionTable[opcode] = f;
635                 }
636         }
637 #endif
638 }