Commit | Line | Data |
---|---|---|
cf76e892 JPM |
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 |