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