]> Shamusworld >> Repos - virtualjaguar/blob - src/m68000/cpuextra.c
Tweaks for OP dumping. Hopefully removing all duplicate entries now.
[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 // Handle exceptions. We need a special case to handle MFP exceptions
102 // on Atari ST, because it's possible to change the MFP's vector base
103 // and get a conflict with 'normal' cpu exceptions.
104 //
105 void Exception(int nr, uint32_t oldpc, int ExceptionSource)
106 {
107 printf("Exception #%i occurred!\n", nr);
108 printf("Vector @ #%i = %08X\n", nr, m68k_read_memory_32(nr * 4));
109 //abort();
110         uint32_t currpc = m68k_getpc(), newpc;
111
112         /*if( nr>=2 && nr<10 )  fprintf(stderr,"Exception (-> %i bombs)!\n",nr);*/
113
114         MakeSR();
115
116         // Change to supervisor mode if necessary
117         if (!regs.s)
118         {
119                 regs.usp = m68k_areg(regs, 7);
120                 m68k_areg(regs, 7) = regs.isp;
121                 regs.s = 1;
122         }
123
124         // Create 68000 style stack frame
125         m68k_areg(regs, 7) -= 4;                                // Push PC on stack
126         m68k_write_memory_32(m68k_areg(regs, 7), currpc);
127         m68k_areg(regs, 7) -= 2;                                // Push SR on stack
128         m68k_write_memory_16(m68k_areg(regs, 7), regs.sr);
129
130 //      LOG_TRACE(TRACE_CPU_EXCEPTION, "cpu exception %d currpc %x buspc %x newpc %x fault_e3 %x op_e3 %hx addr_e3 %x\n",
131 //      nr, currpc, BusErrorPC, get_long(4 * nr), last_fault_for_exception_3, last_op_for_exception_3, last_addr_for_exception_3);
132
133 #if 0
134         /* 68000 bus/address errors: */
135         if ((nr == 2 || nr == 3) && ExceptionSource == M68000_EXC_SRC_CPU)
136         {
137                 uint16_t specialstatus = 1;
138
139                 /* Special status word emulation isn't perfect yet... :-( */
140                 if (regs.sr & 0x2000)
141                         specialstatus |= 0x4;
142
143                 m68k_areg(regs, 7) -= 8;
144
145                 if (nr == 3)     /* Address error */
146                 {
147                         specialstatus |= (last_op_for_exception_3 & (~0x1F));   /* [NP] unused bits of specialstatus are those of the last opcode ! */
148                         put_word(m68k_areg(regs, 7), specialstatus);
149                         put_long(m68k_areg(regs, 7) + 2, last_fault_for_exception_3);
150                         put_word(m68k_areg(regs, 7) + 6, last_op_for_exception_3);
151                         put_long(m68k_areg(regs, 7) + 10, last_addr_for_exception_3);
152
153 //JLH: Not now...
154 #if 0
155                         if (bExceptionDebugging)
156                         {
157                                 fprintf(stderr,"Address Error at address $%x, PC=$%x\n", last_fault_for_exception_3, currpc);
158                                 DebugUI();
159                         }
160 #endif
161                 }
162                 else     /* Bus error */
163                 {
164                         specialstatus |= (get_word(BusErrorPC) & (~0x1F));      /* [NP] unused bits of special status are those of the last opcode ! */
165
166                         if (bBusErrorReadWrite)
167                                 specialstatus |= 0x10;
168
169                         put_word(m68k_areg(regs, 7), specialstatus);
170                         put_long(m68k_areg(regs, 7) + 2, BusErrorAddress);
171                         put_word(m68k_areg(regs, 7) + 6, get_word(BusErrorPC)); /* Opcode */
172
173                         /* [NP] PC stored in the stack frame is not necessarily pointing to the next instruction ! */
174                         /* FIXME : we should have a proper model for this, in the meantime we handle specific cases */
175                         if (get_word(BusErrorPC) == 0x21F8)                     /* move.l $0.w,$24.w (Transbeauce 2 loader) */
176                                 put_long(m68k_areg(regs, 7) + 10, currpc - 2);          /* correct PC is 2 bytes less than usual value */
177
178                         /* Check for double bus errors: */
179                         if (regs.spcflags & SPCFLAG_BUSERROR)
180                         {
181                                 fprintf(stderr, "Detected double bus error at address $%x, PC=$%lx => CPU halted!\n",
182                                 BusErrorAddress, (long)currpc);
183                                 unset_special(SPCFLAG_BUSERROR);
184
185                                 if (bExceptionDebugging)
186                                         DebugUI();
187                                 else
188                                         DlgAlert_Notice("Detected double bus error => CPU halted!\nEmulation needs to be reset.\n");
189
190                                 regs.intmask = 7;
191                                 m68k_setstopped(true);
192                                 return;
193                         }
194
195                         if (bExceptionDebugging && BusErrorAddress != 0xFF8A00)
196                         {
197                                 fprintf(stderr,"Bus Error at address $%x, PC=$%lx\n", BusErrorAddress, (long)currpc);
198                                 DebugUI();
199                         }
200                 }
201         }
202
203 //Not now...
204 #if 0
205         /* Set PC and flags */
206         if (bExceptionDebugging && get_long(4 * nr) == 0)
207         {
208                 write_log("Uninitialized exception handler #%i!\n", nr);
209                 DebugUI();
210         }
211 #endif
212
213         newpc = get_long(4 * nr);
214
215         if (newpc & 1)                          /* check new pc is odd */
216         {
217                 if (nr == 2 || nr == 3)                 /* address error during bus/address error -> stop emulation */
218                 {
219                         fprintf(stderr,"Address Error during exception 2/3, aborting new PC=$%x\n", newpc);
220                         DebugUI();
221                 }
222                 else
223                 {
224                         fprintf(stderr,"Address Error during exception, new PC=$%x\n", newpc);
225                         Exception(3, m68k_getpc(), M68000_EXC_SRC_CPU);
226                 }
227
228                 return;
229         }
230 #endif
231
232         m68k_setpc(m68k_read_memory_32(4 * nr));
233         fill_prefetch_0();
234         /* Handle trace flags depending on current state */
235 //JLH:no        exception_trace(nr);
236
237 #if 0
238         /* Handle exception cycles (special case for MFP) */
239 //      if (ExceptionSource == M68000_EXC_SRC_INT_MFP)
240 //      {
241 //              M68000_AddCycles(44 + 12);                      /* MFP interrupt, 'nr' can be in a different range depending on $fffa17 */
242 //      }
243 //      else
244         if (nr >= 24 && nr <= 31)
245         {
246 #if 0
247                 if (nr == 26)                           /* HBL */
248                 {
249                         /* store current cycle pos when then interrupt was received (see video.c) */
250                         LastCycleHblException = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
251                         M68000_AddCycles(44 + 12);              /* Video Interrupt */
252                 }
253                 else if (nr == 28)                      /* VBL */
254                         M68000_AddCycles(44 + 12);              /* Video Interrupt */
255                 else
256 #endif
257                         M68000_AddCycles(44 + 4);                       /* Other Interrupts */
258         }
259         else if (nr >= 32 && nr <= 47)
260         {
261                 M68000_AddCycles(34 - 4);                       /* Trap (total is 34, but cpuemu.c already adds 4) */
262         }
263         else switch(nr)
264         {
265                 case 2: M68000_AddCycles(50); break;    /* Bus error */
266                 case 3: M68000_AddCycles(50); break;    /* Address error */
267                 case 4: M68000_AddCycles(34); break;    /* Illegal instruction */
268                 case 5: M68000_AddCycles(38); break;    /* Div by zero */
269                 case 6: M68000_AddCycles(40); break;    /* CHK */
270                 case 7: M68000_AddCycles(34); break;    /* TRAPV */
271                 case 8: M68000_AddCycles(34); break;    /* Privilege violation */
272                 case 9: M68000_AddCycles(34); break;    /* Trace */
273                 case 10: M68000_AddCycles(34); break;   /* Line-A - probably wrong */
274                 case 11: M68000_AddCycles(34); break;   /* Line-F - probably wrong */
275                 default:
276                 /* FIXME: Add right cycles value for MFP interrupts and copro exceptions ... */
277                 if (nr < 64)
278                         M68000_AddCycles(4);                    /* Coprocessor and unassigned exceptions (???) */
279                 else
280                         M68000_AddCycles(44 + 12);              /* Must be a MFP or DSP interrupt */
281
282                 break;
283         }
284 #endif
285 }
286
287 /*
288  The routines below take dividend and divisor as parameters.
289  They return 0 if division by zero, or exact number of cycles otherwise.
290
291  The number of cycles returned assumes a register operand.
292  Effective address time must be added if memory operand.
293
294  For 68000 only (not 68010, 68012, 68020, etc).
295  Probably valid for 68008 after adding the extra prefetch cycle.
296
297
298  Best and worst cases are for register operand:
299  (Note the difference with the documented range.)
300
301
302  DIVU:
303
304  Overflow (always): 10 cycles.
305  Worst case: 136 cycles.
306  Best case: 76 cycles.
307
308
309  DIVS:
310
311  Absolute overflow: 16-18 cycles.
312  Signed overflow is not detected prematurely.
313
314  Worst case: 156 cycles.
315  Best case without signed overflow: 122 cycles.
316  Best case with signed overflow: 120 cycles
317  */
318
319 //
320 // DIVU
321 // Unsigned division
322 //
323 STATIC_INLINE int getDivu68kCycles_2 (uint32_t dividend, uint16_t divisor)
324 {
325         int mcycles;
326         uint32_t hdivisor;
327         int i;
328
329         if (divisor == 0)
330                 return 0;
331
332         // Overflow
333         if ((dividend >> 16) >= divisor)
334                 return (mcycles = 5) * 2;
335
336         mcycles = 38;
337         hdivisor = divisor << 16;
338
339         for(i=0; i<15; i++)
340         {
341                 uint32_t temp;
342                 temp = dividend;
343
344                 dividend <<= 1;
345
346                 // If carry from shift
347                 if ((int32_t)temp < 0)
348                         dividend -= hdivisor;
349                 else
350                 {
351                         mcycles += 2;
352
353                         if (dividend >= hdivisor)
354                         {
355                                 dividend -= hdivisor;
356                                 mcycles--;
357                         }
358                 }
359         }
360
361         return mcycles * 2;
362 }
363
364 // This is called by cpuemu.c
365 int getDivu68kCycles(uint32_t dividend, uint16_t divisor)
366 {
367         int v = getDivu68kCycles_2(dividend, divisor) - 4;
368 //      write_log ("U%d ", v);
369         return v;
370 }
371
372 //
373 // DIVS
374 // Signed division
375 //
376
377 STATIC_INLINE int getDivs68kCycles_2(int32_t dividend, int16_t divisor)
378 {
379         int mcycles;
380         uint32_t aquot;
381         int i;
382
383         if (divisor == 0)
384                 return 0;
385
386         mcycles = 6;
387
388         if (dividend < 0)
389                 mcycles++;
390
391         // Check for absolute overflow
392         if (((uint32_t)abs(dividend) >> 16) >= (uint16_t)abs(divisor))
393                 return (mcycles + 2) * 2;
394
395         // Absolute quotient
396         aquot = (uint32_t)abs(dividend) / (uint16_t)abs(divisor);
397
398         mcycles += 55;
399
400         if (divisor >= 0)
401         {
402                 if (dividend >= 0)
403                         mcycles--;
404                 else
405                         mcycles++;
406         }
407
408         // Count 15 msbits in absolute of quotient
409
410         for(i=0; i<15; i++)
411         {
412                 if ((int16_t)aquot >= 0)
413                         mcycles++;
414
415                 aquot <<= 1;
416         }
417
418         return mcycles * 2;
419 }
420
421 // This is called by cpuemu.c
422 int getDivs68kCycles(int32_t dividend, int16_t divisor)
423 {
424         int v = getDivs68kCycles_2(dividend, divisor) - 4;
425 //      write_log ("S%d ", v);
426         return v;
427 }