@@ -349,8 +349,174 @@ void m68k_end_timeslice(void)
349349}
350350
351351
352+ /* Read a 32-bit operand from any addressable EA the wrapper code uses.
353+ * The Removers/aln-built Jaguar binaries we care about always reach MULL/DIVL
354+ * with the EA = data-register-direct (mode 0). Other modes (immediate,
355+ * abs.W/L, (An), etc.) are emulated for completeness. */
356+ static int read_long_ea (uint32_t opcode , uint32_t * out )
357+ {
358+ uint32_t mode = (opcode >> 3 ) & 0x7 ;
359+ uint32_t reg = opcode & 0x7 ;
360+ uint32_t ea ;
361+
362+ switch (mode )
363+ {
364+ case 0 : /* Dn */
365+ * out = m68k_dreg (regs , reg );
366+ return 0 ;
367+ case 1 : /* An */
368+ * out = m68k_areg (regs , reg );
369+ return 0 ;
370+ case 2 : /* (An) */
371+ * out = m68k_read_memory_32 (m68k_areg (regs , reg ));
372+ return 0 ;
373+ case 5 : /* (d16,An) */
374+ {
375+ int16_t d = (int16_t )get_iword (4 );
376+ * out = m68k_read_memory_32 (m68k_areg (regs , reg ) + d );
377+ return 2 ;
378+ }
379+ case 7 :
380+ switch (reg )
381+ {
382+ case 0 : /* (xxx).W */
383+ ea = (int32_t )(int16_t )get_iword (4 );
384+ * out = m68k_read_memory_32 (ea );
385+ return 2 ;
386+ case 1 : /* (xxx).L */
387+ ea = (uint32_t )get_ilong (4 );
388+ * out = m68k_read_memory_32 (ea );
389+ return 4 ;
390+ case 4 : /* #imm */
391+ * out = (uint32_t )get_ilong (4 );
392+ return 4 ;
393+ }
394+ break ;
395+ }
396+ return -1 ;
397+ }
398+
399+ /* Emulate the 68020+ MULL / DIVL instructions on a 68000-only core.
400+ * The Removers Library + m68k-atari-mint-gcc toolchain emits these for
401+ * 32x32 multiply and divide; without them, our binaries hard-hang inside
402+ * libgcc helpers. Returns 1 if handled, 0 to fall through to a true illegal
403+ * exception. */
404+ static int handle_68020_mull_divl (uint32_t opcode )
405+ {
406+ uint32_t base = opcode & 0xFFC0 ;
407+ uint16_t ext ;
408+ uint32_t src ;
409+ int extra ;
410+
411+ if (base != 0x4C00 && base != 0x4C40 )
412+ return 0 ;
413+
414+ ext = (uint16_t )get_iword (2 );
415+ if (ext & 0x83F8 )
416+ return 0 ; /* reserved bits set — not a clean MULL/DIVL */
417+
418+ extra = read_long_ea (opcode , & src );
419+ if (extra < 0 )
420+ return 0 ;
421+
422+ {
423+ uint32_t Dl = (ext >> 12 ) & 0x7 ;
424+ uint32_t Dh = ext & 0x7 ;
425+ int sz = (ext >> 10 ) & 0x1 ; /* 0=32-bit, 1=64-bit */
426+ int sg = (ext >> 11 ) & 0x1 ; /* 0=unsigned, 1=signed */
427+
428+ if (base == 0x4C00 ) /* MULL */
429+ {
430+ uint32_t a = m68k_dreg (regs , Dl );
431+ uint32_t b = src ;
432+ if (sz == 0 )
433+ {
434+ uint32_t r = a * b ;
435+ m68k_dreg (regs , Dl ) = r ;
436+ SET_NFLG (r >> 31 );
437+ SET_ZFLG (r == 0 );
438+ SET_VFLG (0 ); SET_CFLG (0 );
439+ }
440+ else
441+ {
442+ uint64_t prod ;
443+ if (sg )
444+ prod = (uint64_t )((int64_t )(int32_t )a * (int64_t )(int32_t )b );
445+ else
446+ prod = (uint64_t )a * (uint64_t )b ;
447+ m68k_dreg (regs , Dl ) = (uint32_t )prod ;
448+ m68k_dreg (regs , Dh ) = (uint32_t )(prod >> 32 );
449+ SET_NFLG ((prod >> 63 ) & 1 );
450+ SET_ZFLG (prod == 0 );
451+ SET_VFLG (0 ); SET_CFLG (0 );
452+ }
453+ }
454+ else /* DIVL */
455+ {
456+ uint32_t divisor = src ;
457+ if (divisor == 0 )
458+ {
459+ m68k_incpc (2 + extra );
460+ Exception (0x05 , 0 , M68000_EXC_SRC_CPU );
461+ return 1 ;
462+ }
463+ if (sz == 0 )
464+ {
465+ uint32_t a = m68k_dreg (regs , Dl );
466+ if (sg )
467+ {
468+ int32_t q = (int32_t )a / (int32_t )divisor ;
469+ int32_t r = (int32_t )a % (int32_t )divisor ;
470+ m68k_dreg (regs , Dl ) = (uint32_t )q ;
471+ if (Dh != Dl ) m68k_dreg (regs , Dh ) = (uint32_t )r ;
472+ SET_NFLG (q < 0 ); SET_ZFLG (q == 0 );
473+ }
474+ else
475+ {
476+ uint32_t q = a / divisor ;
477+ uint32_t r = a % divisor ;
478+ m68k_dreg (regs , Dl ) = q ;
479+ if (Dh != Dl ) m68k_dreg (regs , Dh ) = r ;
480+ SET_NFLG (q >> 31 ); SET_ZFLG (q == 0 );
481+ }
482+ }
483+ else /* 64-bit dividend in Dh:Dl */
484+ {
485+ uint64_t dividend = ((uint64_t )m68k_dreg (regs , Dh ) << 32 )
486+ | (uint64_t )m68k_dreg (regs , Dl );
487+ if (sg )
488+ {
489+ int64_t q = (int64_t )dividend / (int32_t )divisor ;
490+ int64_t r = (int64_t )dividend % (int32_t )divisor ;
491+ m68k_dreg (regs , Dl ) = (uint32_t )q ;
492+ m68k_dreg (regs , Dh ) = (uint32_t )r ;
493+ SET_NFLG (q < 0 ); SET_ZFLG (q == 0 );
494+ }
495+ else
496+ {
497+ uint64_t q = dividend / divisor ;
498+ uint64_t r = dividend % divisor ;
499+ m68k_dreg (regs , Dl ) = (uint32_t )q ;
500+ m68k_dreg (regs , Dh ) = (uint32_t )r ;
501+ SET_NFLG ((q >> 63 ) & 1 ); SET_ZFLG (q == 0 );
502+ }
503+ }
504+ SET_VFLG (0 ); SET_CFLG (0 );
505+ }
506+ }
507+
508+ m68k_incpc (4 + extra );
509+ return 1 ;
510+ }
511+
352512unsigned long IllegalOpcode (uint32_t opcode )
353513{
514+ if ((opcode & 0xFF80 ) == 0x4C00 )
515+ {
516+ if (handle_68020_mull_divl (opcode ))
517+ return 40 ;
518+ }
519+
354520 if ((opcode & 0xF000 ) == 0xF000 )
355521 {
356522 Exception (0x0B , 0 , M68000_EXC_SRC_CPU ); // LineF exception...
0 commit comments