]> Shamusworld >> Repos - virtualjaguar/blob - src/m68000/cpuextra.c
Added memory browser, fixed documentation, M68000 exception explanations.
[virtualjaguar] / src / m68000 / cpuextra.c
1 //
2 // Stuff that's neither inline or generated by gencpu.c, but depended upon by
3 // cpuemu.c.
4 //
5 // Originally part of UAE by Bernd Schmidt
6 // and released under the GPL v2 or later
7 //
8
9 #include "cpuextra.h"
10 #include "cpudefs.h"
11 #include "inlines.h"
12
13
14 uint16_t last_op_for_exception_3;               // Opcode of faulting instruction
15 uint32_t last_addr_for_exception_3;             // PC at fault time
16 uint32_t last_fault_for_exception_3;    // Address that generated the exception
17
18 int OpcodeFamily;                                               // Used by cpuemu.c...
19 int BusCyclePenalty = 0;                                // Used by cpuemu.c...
20 int CurrentInstrCycles;
21
22 struct regstruct regs;
23
24 //
25 // Make displacement effective address for 68000
26 //
27 uint32_t get_disp_ea_000(uint32_t base, uint32_t dp)
28 {
29         int reg = (dp >> 12) & 0x0F;
30         int32_t regd = regs.regs[reg];
31
32 #if 1
33         if ((dp & 0x800) == 0)
34                 regd = (int32_t)(int16_t)regd;
35
36         return base + (int8_t)dp + regd;
37 #else
38         /* Branch-free code... benchmark this again now that
39          * things are no longer inline.
40          */
41         int32_t regd16;
42         uint32_t mask;
43         mask = ((dp & 0x800) >> 11) - 1;
44         regd16 = (int32_t)(int16_t)regd;
45         regd16 &= mask;
46         mask = ~mask;
47         base += (int8_t)dp;
48         regd &= mask;
49         regd |= regd16;
50         return base + regd;
51 #endif
52 }
53
54 //
55 // Create the Status Register from the flags
56 //
57 void MakeSR(void)
58 {
59         regs.sr = ((regs.s << 13) | (regs.intmask << 8) | (GET_XFLG << 4)
60                 | (GET_NFLG << 3) | (GET_ZFLG << 2) | (GET_VFLG << 1) | GET_CFLG);
61 }
62
63 //
64 // Set up the flags from Status Register
65 //
66 void MakeFromSR(void)
67 {
68         int olds = regs.s;
69
70         regs.s = (regs.sr >> 13) & 1;
71         regs.intmask = (regs.sr >> 8) & 7;
72         SET_XFLG((regs.sr >> 4) & 1);
73         SET_NFLG((regs.sr >> 3) & 1);
74         SET_ZFLG((regs.sr >> 2) & 1);
75         SET_VFLG((regs.sr >> 1) & 1);
76         SET_CFLG(regs.sr & 1);
77
78         if (olds != regs.s)
79         {
80                 if (olds)
81                 {
82                         regs.isp = m68k_areg(regs, 7);
83                         m68k_areg(regs, 7) = regs.usp;
84                 }
85                 else
86                 {
87                         regs.usp = m68k_areg(regs, 7);
88                         m68k_areg(regs, 7) = regs.isp;
89                 }
90         }
91
92         /* Pending interrupts can occur again after a write to the SR: */
93 //JLH: is this needed?
94 //      set_special(SPCFLAG_DOINT);
95 }
96
97 //
98 // Rudimentary exception handling. This is really stripped down from what
99 // was in Hatari.
100 /*
101 NB: Seems that when an address exception occurs, it doesn't get handled properly
102     as per test1.cof. Need to figure out why it keeps going when it should wedge. :-P
103 */
104 //
105 // Handle exceptions. We need a special case to handle MFP exceptions
106 // on Atari ST, because it's possible to change the MFP's vector base
107 // and get a conflict with 'normal' cpu exceptions.
108 //
109 void Exception(int nr, uint32_t oldpc, int ExceptionSource)
110 {
111 char excNames[33][64] = {
112         "???", "???", "Bus Error", "Address Error",
113         "Illegal Instruction", "Zero Divide", "CHK", "TrapV",
114         "Privilege Violation", "Trace", "Line A", "Line F",
115         "???", "???", "Format Error", "Uninitialized Interrupt",
116         "???", "???", "???", "???",
117         "???", "???", "???", "???",
118         "Spurious/Autovector", "???", "???", "???",
119         "???", "???", "???", "???",
120         "Trap #"
121 };
122
123 printf("Exception #%i occurred! (%s)\n", nr, (nr < 32 ? excNames[nr] : (nr < 48 ? "Trap #" : "????")));
124 printf("Vector @ #%i = %08X\n", nr, m68k_read_memory_32(nr * 4));
125 //abort();
126         uint32_t currpc = m68k_getpc(), newpc;
127 printf("PC = $%08X\n", currpc);
128 printf("A0 = $%08X A1 = $%08X A2 = $%08X A3 = $%08X\n", m68k_areg(regs, 0), m68k_areg(regs, 1), m68k_areg(regs, 2), m68k_areg(regs, 3));
129 printf("A4 = $%08X A5 = $%08X A6 = $%08X A7 = $%08X\n", m68k_areg(regs, 4), m68k_areg(regs, 5), m68k_areg(regs, 6), m68k_areg(regs, 7));
130 printf("D0 = $%08X D1 = $%08X D2 = $%08X D3 = $%08X\n", m68k_dreg(regs, 0), m68k_dreg(regs, 1), m68k_dreg(regs, 2), m68k_dreg(regs, 3));
131 printf("D4 = $%08X D5 = $%08X D6 = $%08X D7 = $%08X\n", m68k_dreg(regs, 4), m68k_dreg(regs, 5), m68k_dreg(regs, 6), m68k_dreg(regs, 7));
132 printf("\n");
133
134 uint32_t disPC = currpc - 10;
135 char buffer[128];
136
137 do
138 {
139         uint32_t oldpc = disPC;
140         disPC += m68k_disassemble(buffer, disPC, 0);
141         printf("%s%08X: %s\n", (oldpc == currpc ? ">" : " "), oldpc, buffer);
142 }
143 while (disPC < (currpc + 10));
144
145 /*if( nr>=2 && nr<10 )  fprintf(stderr,"Exception (-> %i bombs)!\n",nr);*/
146
147         MakeSR();
148
149         // Change to supervisor mode if necessary
150         if (!regs.s)
151         {
152                 regs.usp = m68k_areg(regs, 7);
153                 m68k_areg(regs, 7) = regs.isp;
154                 regs.s = 1;
155         }
156
157         // Create 68000 style stack frame
158         m68k_areg(regs, 7) -= 4;                                // Push PC on stack
159         m68k_write_memory_32(m68k_areg(regs, 7), currpc);
160         m68k_areg(regs, 7) -= 2;                                // Push SR on stack
161         m68k_write_memory_16(m68k_areg(regs, 7), regs.sr);
162
163 //      LOG_TRACE(TRACE_CPU_EXCEPTION, "cpu exception %d currpc %x buspc %x newpc %x fault_e3 %x op_e3 %hx addr_e3 %x\n",
164 //      nr, currpc, BusErrorPC, get_long(4 * nr), last_fault_for_exception_3, last_op_for_exception_3, last_addr_for_exception_3);
165
166 #if 0
167         /* 68000 bus/address errors: */
168         if ((nr == 2 || nr == 3) && ExceptionSource == M68000_EXC_SRC_CPU)
169         {
170                 uint16_t specialstatus = 1;
171
172                 /* Special status word emulation isn't perfect yet... :-( */
173                 if (regs.sr & 0x2000)
174                         specialstatus |= 0x4;
175
176                 m68k_areg(regs, 7) -= 8;
177
178                 if (nr == 3)     /* Address error */
179                 {
180                         specialstatus |= (last_op_for_exception_3 & (~0x1F));   /* [NP] unused bits of specialstatus are those of the last opcode ! */
181                         put_word(m68k_areg(regs, 7), specialstatus);
182                         put_long(m68k_areg(regs, 7) + 2, last_fault_for_exception_3);
183                         put_word(m68k_areg(regs, 7) + 6, last_op_for_exception_3);
184                         put_long(m68k_areg(regs, 7) + 10, last_addr_for_exception_3);
185
186 //JLH: Not now...
187 #if 0
188                         if (bExceptionDebugging)
189                         {
190                                 fprintf(stderr,"Address Error at address $%x, PC=$%x\n", last_fault_for_exception_3, currpc);
191                                 DebugUI();
192                         }
193 #endif
194                 }
195                 else     /* Bus error */
196                 {
197                         specialstatus |= (get_word(BusErrorPC) & (~0x1F));      /* [NP] unused bits of special status are those of the last opcode ! */
198
199                         if (bBusErrorReadWrite)
200                                 specialstatus |= 0x10;
201
202                         put_word(m68k_areg(regs, 7), specialstatus);
203                         put_long(m68k_areg(regs, 7) + 2, BusErrorAddress);
204                         put_word(m68k_areg(regs, 7) + 6, get_word(BusErrorPC)); /* Opcode */
205
206                         /* [NP] PC stored in the stack frame is not necessarily pointing to the next instruction ! */
207                         /* FIXME : we should have a proper model for this, in the meantime we handle specific cases */
208                         if (get_word(BusErrorPC) == 0x21F8)                     /* move.l $0.w,$24.w (Transbeauce 2 loader) */
209                                 put_long(m68k_areg(regs, 7) + 10, currpc - 2);          /* correct PC is 2 bytes less than usual value */
210
211                         /* Check for double bus errors: */
212                         if (regs.spcflags & SPCFLAG_BUSERROR)
213                         {
214                                 fprintf(stderr, "Detected double bus error at address $%x, PC=$%lx => CPU halted!\n",
215                                 BusErrorAddress, (long)currpc);
216                                 unset_special(SPCFLAG_BUSERROR);
217
218                                 if (bExceptionDebugging)
219                                         DebugUI();
220                                 else
221                                         DlgAlert_Notice("Detected double bus error => CPU halted!\nEmulation needs to be reset.\n");
222
223                                 regs.intmask = 7;
224                                 m68k_setstopped(true);
225                                 return;
226                         }
227
228                         if (bExceptionDebugging && BusErrorAddress != 0xFF8A00)
229                         {
230                                 fprintf(stderr,"Bus Error at address $%x, PC=$%lx\n", BusErrorAddress, (long)currpc);
231                                 DebugUI();
232                         }
233                 }
234         }
235
236 //Not now...
237 #if 0
238         /* Set PC and flags */
239         if (bExceptionDebugging && get_long(4 * nr) == 0)
240         {
241                 write_log("Uninitialized exception handler #%i!\n", nr);
242                 DebugUI();
243         }
244 #endif
245
246         newpc = get_long(4 * nr);
247
248         if (newpc & 1)                          /* check new pc is odd */
249         {
250                 if (nr == 2 || nr == 3)                 /* address error during bus/address error -> stop emulation */
251                 {
252                         fprintf(stderr,"Address Error during exception 2/3, aborting new PC=$%x\n", newpc);
253                         DebugUI();
254                 }
255                 else
256                 {
257                         fprintf(stderr,"Address Error during exception, new PC=$%x\n", newpc);
258                         Exception(3, m68k_getpc(), M68000_EXC_SRC_CPU);
259                 }
260
261                 return;
262         }
263 #endif
264
265         m68k_setpc(m68k_read_memory_32(4 * nr));
266         fill_prefetch_0();
267         /* Handle trace flags depending on current state */
268 //JLH:no        exception_trace(nr);
269
270 #if 0
271         /* Handle exception cycles (special case for MFP) */
272 //      if (ExceptionSource == M68000_EXC_SRC_INT_MFP)
273 //      {
274 //              M68000_AddCycles(44 + 12);                      /* MFP interrupt, 'nr' can be in a different range depending on $fffa17 */
275 //      }
276 //      else
277         if (nr >= 24 && nr <= 31)
278         {
279 #if 0
280                 if (nr == 26)                           /* HBL */
281                 {
282                         /* store current cycle pos when then interrupt was received (see video.c) */
283                         LastCycleHblException = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
284                         M68000_AddCycles(44 + 12);              /* Video Interrupt */
285                 }
286                 else if (nr == 28)                      /* VBL */
287                         M68000_AddCycles(44 + 12);              /* Video Interrupt */
288                 else
289 #endif
290                         M68000_AddCycles(44 + 4);                       /* Other Interrupts */
291         }
292         else if (nr >= 32 && nr <= 47)
293         {
294                 M68000_AddCycles(34 - 4);                       /* Trap (total is 34, but cpuemu.c already adds 4) */
295         }
296         else switch(nr)
297         {
298                 case 2: M68000_AddCycles(50); break;    /* Bus error */
299                 case 3: M68000_AddCycles(50); break;    /* Address error */
300                 case 4: M68000_AddCycles(34); break;    /* Illegal instruction */
301                 case 5: M68000_AddCycles(38); break;    /* Div by zero */
302                 case 6: M68000_AddCycles(40); break;    /* CHK */
303                 case 7: M68000_AddCycles(34); break;    /* TRAPV */
304                 case 8: M68000_AddCycles(34); break;    /* Privilege violation */
305                 case 9: M68000_AddCycles(34); break;    /* Trace */
306                 case 10: M68000_AddCycles(34); break;   /* Line-A - probably wrong */
307                 case 11: M68000_AddCycles(34); break;   /* Line-F - probably wrong */
308                 default:
309                 /* FIXME: Add right cycles value for MFP interrupts and copro exceptions ... */
310                 if (nr < 64)
311                         M68000_AddCycles(4);                    /* Coprocessor and unassigned exceptions (???) */
312                 else
313                         M68000_AddCycles(44 + 12);              /* Must be a MFP or DSP interrupt */
314
315                 break;
316         }
317 #endif
318 }
319
320 /*
321  The routines below take dividend and divisor as parameters.
322  They return 0 if division by zero, or exact number of cycles otherwise.
323
324  The number of cycles returned assumes a register operand.
325  Effective address time must be added if memory operand.
326
327  For 68000 only (not 68010, 68012, 68020, etc).
328  Probably valid for 68008 after adding the extra prefetch cycle.
329
330
331  Best and worst cases are for register operand:
332  (Note the difference with the documented range.)
333
334
335  DIVU:
336
337  Overflow (always): 10 cycles.
338  Worst case: 136 cycles.
339  Best case: 76 cycles.
340
341
342  DIVS:
343
344  Absolute overflow: 16-18 cycles.
345  Signed overflow is not detected prematurely.
346
347  Worst case: 156 cycles.
348  Best case without signed overflow: 122 cycles.
349  Best case with signed overflow: 120 cycles
350  */
351
352 //
353 // DIVU
354 // Unsigned division
355 //
356 STATIC_INLINE int getDivu68kCycles_2 (uint32_t dividend, uint16_t divisor)
357 {
358         int mcycles;
359         uint32_t hdivisor;
360         int i;
361
362         if (divisor == 0)
363                 return 0;
364
365         // Overflow
366         if ((dividend >> 16) >= divisor)
367                 return (mcycles = 5) * 2;
368
369         mcycles = 38;
370         hdivisor = divisor << 16;
371
372         for(i=0; i<15; i++)
373         {
374                 uint32_t temp;
375                 temp = dividend;
376
377                 dividend <<= 1;
378
379                 // If carry from shift
380                 if ((int32_t)temp < 0)
381                         dividend -= hdivisor;
382                 else
383                 {
384                         mcycles += 2;
385
386                         if (dividend >= hdivisor)
387                         {
388                                 dividend -= hdivisor;
389                                 mcycles--;
390                         }
391                 }
392         }
393
394         return mcycles * 2;
395 }
396
397 // This is called by cpuemu.c
398 int getDivu68kCycles(uint32_t dividend, uint16_t divisor)
399 {
400         int v = getDivu68kCycles_2(dividend, divisor) - 4;
401 //      write_log ("U%d ", v);
402         return v;
403 }
404
405 //
406 // DIVS
407 // Signed division
408 //
409
410 STATIC_INLINE int getDivs68kCycles_2(int32_t dividend, int16_t divisor)
411 {
412         int mcycles;
413         uint32_t aquot;
414         int i;
415
416         if (divisor == 0)
417                 return 0;
418
419         mcycles = 6;
420
421         if (dividend < 0)
422                 mcycles++;
423
424         // Check for absolute overflow
425         if (((uint32_t)abs(dividend) >> 16) >= (uint16_t)abs(divisor))
426                 return (mcycles + 2) * 2;
427
428         // Absolute quotient
429         aquot = (uint32_t)abs(dividend) / (uint16_t)abs(divisor);
430
431         mcycles += 55;
432
433         if (divisor >= 0)
434         {
435                 if (dividend >= 0)
436                         mcycles--;
437                 else
438                         mcycles++;
439         }
440
441         // Count 15 msbits in absolute of quotient
442
443         for(i=0; i<15; i++)
444         {
445                 if ((int16_t)aquot >= 0)
446                         mcycles++;
447
448                 aquot <<= 1;
449         }
450
451         return mcycles * 2;
452 }
453
454 // This is called by cpuemu.c
455 int getDivs68kCycles(int32_t dividend, int16_t divisor)
456 {
457         int v = getDivs68kCycles_2(dividend, divisor) - 4;
458 //      write_log ("S%d ", v);
459         return v;
460 }