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