// Definitive preemptive-concurrency test via fork() ping-pong.  A forked GNO
// thread (child) and the parent alternate ownership of a shared "turn" word in
// bank 2: the parent advances it on EVEN turns, the child on ODD.  Each must
// WAIT for the other to hand over the turn, ROUNDS times.  This completes ONLY
// if GNO preemptively time-slices the two threads -- if they ran sequentially,
// whichever went first would spin forever at round 2 waiting for a turn the
// other can't give.  No program-load latency and no gsh job control to muddy it
// (unlike the two-separate-programs path): fork is in-process and immediate.
//
// The child stays within the leaf-only fork contract: a register loop + absolute
// bank-2 loads/stores + _exit (no calls, libcalls, fn-pointers, or &global).
//
//   0x025050  ROUNDS*2 (0x0014)  final turn value if the ping-pong completed
//   0x025052  5050  parent finished all ROUNDS turns (else DEAD = stuck/no sched)
//   0x025054  CCCC  child  finished all ROUNDS turns (else DEAD = never scheduled)
#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 TURN   0x025050UL
#define RES_P  0x025052UL
#define RES_C  0x025054UL
#define ROUNDS 10


// Child (leaf): take the ODD turns.  Wait until TURN is odd, then bump it even.
static void child(void) {
    uint16_t k;
    for (k = 0; k < ROUNDS; k++) {
        while ((M(TURN) & 1) == 0) {
        }
        M(TURN) = M(TURN) + 1;
    }
    M(RES_C) = 0xCCCC;
    _exit(0);
}


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

    M(TURN)  = 0x0000;                   // even -> parent goes first
    M(RES_P) = 0xDEAD;
    M(RES_C) = 0xDEAD;

    pid = fork((void *)child);
    if (pid <= 0) {
        M(RES_P) = 0xF00D;               // fork failed outright
    } else {
        for (k = 0; k < ROUNDS; k++) {
            while ((M(TURN) & 1) == 1) {
            }
            M(TURN) = M(TURN) + 1;
        }
        M(RES_P) = 0x5050;               // parent completed its turns
        wait(&st);
    }
    for (volatile unsigned long j = 0; j < 300000UL; j++) {
    }
    return 0;
}
