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