// Confirm the &global blocker for forked children: forming a 32-bit &global
// pointer reads DP $BE for the bank byte, and a forked child's fresh DP has $BE
// uninitialized (crt0 sets it only for the main thread).  So a child that takes
// the address of a global and dereferences it through a real pointer should read
// from the WRONG bank -> garbage (or crash), unless/until a fork thunk sets $BE.
// Passing &gCanary through a noinline fn forces a genuine 32-bit pointer (not a
// near/absolute access the compiler could fold).
//
//   0x025060  0001  child entered
//   0x025062  xxxx  value read back through &gCanary (0x1234 == $BE correct)
//   0x025064  E0DD  child reached end (no crash)
//   0x025066  5050  parent reaped the child
#include <stdint.h>

extern int  fork(void *subr);
extern int  wait(int *status);
extern __attribute__((noreturn)) void _exit(int status);

#define M(a)  (*(volatile uint16_t *)(a))
#define BAD   0xDEAD

static volatile uint16_t gCanary = 0x1234;


static uint16_t __attribute__((noinline)) deref16(volatile uint16_t *p) {
    return *p;                           // deref a full 32-bit pointer
}


static void child(void) {
    uint16_t v;
    M(0x025060UL) = 0x0001;
    v = deref16((volatile uint16_t *)&gCanary);   // &global -> uses $BE bank byte
    M(0x025062UL) = v;                   // 0x1234 if $BE right; else garbage
    M(0x025064UL) = 0xE0DD;
    _exit(0);
}


int main(void) {
    int pid;
    int st;

    M(0x025060UL) = BAD;
    M(0x025062UL) = BAD;
    M(0x025064UL) = BAD;
    M(0x025066UL) = BAD;

    pid = fork((void *)child);
    if (pid > 0) {
        wait(&st);
        M(0x025066UL) = 0x5050;
    }
    for (volatile unsigned long j = 0; j < 300000UL; j++) {
    }
    return 0;
}
