/* m68k-amigaos-gcc: an input bound to a0 is not materialised when its value is
 * a compile-time constant, if a0 is also an output operand of the same asm.
 *
 * This is what the NDK inline macros (LP7NR and friends) expand to, so it hits
 * every library call that takes a constant pointer in an address register --
 * BltPattern(rp, NULL, ...) passes whatever a0 happened to hold.
 *
 *   m68k-amigaos-gcc -O2 -m68020 -S -o - a0-repro.c
 *
 * gcc 6.5.0b:  both functions emit an a0 load     (sub.l a0,a0 / move.l ...,a0)
 * gcc 13.4+:   call_const has no write to a0 at all
 */
typedef short WORD;
typedef unsigned short UWORD;
typedef unsigned char *PLANEPTR;
struct RastPort;

extern void *GfxBase;

#define CALL(mask_expr)                                                       \
    ({                                                                        \
        struct RastPort *_v1 = (rp);                                          \
        const PLANEPTR _v2 = (mask_expr);                                     \
        register int _d0 __asm("d0");                                         \
        register int _d1 __asm("d1");                                         \
        register int _a0 __asm("a0");                                         \
        register int _a1 __asm("a1");                                         \
        register struct RastPort *_n1 __asm("a1") = _v1;                      \
        register const PLANEPTR _n2 __asm("a0") = _v2;                        \
        register void *const _bn __asm("a6") = (GfxBase);                     \
        __asm volatile("jsr a6@(-0x138:W)"                                    \
                       : "=r"(_d0), "=r"(_d1), "=r"(_a0), "=r"(_a1)           \
                       : "r"(_bn), "rf"(_n1), "rf"(_n2)                       \
                       : "fp0", "fp1", "cc", "memory");                       \
    })

/* Constant argument: a0 is never written. */
void call_const(struct RastPort *rp)
{
    CALL(0L);
}

/* Variable argument: a0 is loaded correctly. */
void call_var(struct RastPort *rp, PLANEPTR mask)
{
    CALL(mask);
}

/* Same constant argument, but the a0 variable is not const-qualified. */
void call_const_nonconst_var(struct RastPort *rp)
{
    register int _d0 __asm("d0");
    register int _d1 __asm("d1");
    register struct RastPort *_n1 __asm("a1") = rp;
    register PLANEPTR _n2 __asm("a0") = 0L;
    register void *const _bn __asm("a6") = GfxBase;

    __asm volatile("jsr a6@(-0x138:W)"
                   : "=r"(_d0), "=r"(_d1)
                   : "r"(_bn), "rf"(_n1), "rf"(_n2)
                   : "fp0", "fp1", "cc", "memory");
}

/* Same constant argument with a plain "r" constraint instead of "rf". */
void call_const_plain_r(struct RastPort *rp)
{
    struct RastPort *_v1 = rp;
    const PLANEPTR _v2 = 0L;
    register int _d0 __asm("d0");
    register int _d1 __asm("d1");
    register struct RastPort *_n1 __asm("a1") = _v1;
    register const PLANEPTR _n2 __asm("a0") = _v2;
    register void *const _bn __asm("a6") = GfxBase;

    __asm volatile("jsr a6@(-0x138:W)"
                   : "=r"(_d0), "=r"(_d1)
                   : "r"(_bn), "r"(_n1), "r"(_n2)
                   : "fp0", "fp1", "cc", "memory");
}

/* Same constant argument, but a0 is not also an output operand. */
void call_const_no_a0_output(struct RastPort *rp)
{
    struct RastPort *_v1 = rp;
    const PLANEPTR _v2 = 0L;
    register int _d0 __asm("d0");
    register int _d1 __asm("d1");
    register struct RastPort *_n1 __asm("a1") = _v1;
    register const PLANEPTR _n2 __asm("a0") = _v2;
    register void *const _bn __asm("a6") = GfxBase;

    __asm volatile("jsr a6@(-0x138:W)"
                   : "=r"(_d0), "=r"(_d1)
                   : "r"(_bn), "rf"(_n1), "rf"(_n2)
                   : "fp0", "fp1", "cc", "memory");
}
