Skip to content

Commit 2bd9064

Browse files
committed
m680000 quirk fixes
Signed-off-by: Joseph Mattiello <[email protected]>
1 parent 6bf3a04 commit 2bd9064

2 files changed

Lines changed: 180 additions & 8 deletions

File tree

src/m68000/cpuemu.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14962,9 +14962,14 @@ unsigned long CPUFUNC(op_6101_4)(uint32_t opcode) /* BSR */
1496214962
unsigned long CPUFUNC(op_61ff_4)(uint32_t opcode) /* BSR */
1496314963
{
1496414964
OpcodeFamily = 54; CurrentInstrCycles = 18;
14965-
{{ int32_t src = get_ilong(2);
14966-
int32_t s = (int32_t)src + 2;
14967-
m68k_do_bsr(m68k_getpc() + 6, s);
14965+
{{ /* Atari Jaguar quirk: the 'aln' linker emits BSR with an 8-bit
14966+
* displacement of $FF (the 68020+ "long-form" escape) but writes the
14967+
* ABSOLUTE TARGET ADDRESS into the 32-bit displacement slot instead
14968+
* of a PC-relative displacement. Real 68000 hardware doesn't have
14969+
* BSR.L at all, so any $61FF we see in a Jaguar binary uses this
14970+
* convention. Treat the operand as an absolute jump target. */
14971+
uint32_t target = (uint32_t)get_ilong(2);
14972+
m68k_do_jsr(m68k_getpc() + 6, target);
1496814973
}}return 18;
1496914974
}
1497014975
unsigned long CPUFUNC(op_6200_4)(uint32_t opcode) /* Bcc */
@@ -46548,14 +46553,15 @@ return 18;
4654846553
unsigned long CPUFUNC(op_61ff_5)(uint32_t opcode) /* BSR */
4654946554
{
4655046555
OpcodeFamily = 54; CurrentInstrCycles = 18;
46551-
{{ int32_t src = get_ilong_prefetch(2);
46552-
int32_t s = (int32_t)src + 2;
46553-
if (src & 1) {
46556+
{{ /* See op_61ff_4: aln writes the absolute target address into the
46557+
* 32-bit displacement slot of BSR.L. Treat operand as absolute. */
46558+
uint32_t target = (uint32_t)get_ilong_prefetch(2);
46559+
if (target & 1) {
4655446560
last_addr_for_exception_3 = m68k_getpc() + 2;
46555-
last_fault_for_exception_3 = m68k_getpc() + s;
46561+
last_fault_for_exception_3 = target;
4655646562
last_op_for_exception_3 = opcode; Exception(3,0,M68000_EXC_SRC_CPU); goto endlabel2596;
4655746563
}
46558-
m68k_do_bsr(m68k_getpc() + 6, s);
46564+
m68k_do_jsr(m68k_getpc() + 6, target);
4655946565
fill_prefetch_0 ();
4656046566
}}endlabel2596: ;
4656146567
return 18;

src/m68000/m68kinterface.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
352512
unsigned 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

Comments
 (0)