// Concurrency test, program A of a pair (run "gnoconcura & gnoconcurb" under
// gsh).  Proves two SEPARATE programs run CONCURRENTLY under GNO's preemptive
// scheduler via a shared-memory rendezvous that can ONLY complete if the two
// interleave: A announces itself (flagA), then spins waiting for B's flag.  If A
// ran to completion before B ever started, it would time out (0xDEAD).  Both
// reaching their rendezvous marker (A=0xAAAA, B=0xBBBB) means the scheduler
// actually time-sliced them.  Each also does real work (sumTo) to prove it is
// not a trivial spin.  Bank-2 markers are absolute physical RAM, shared across
// processes -- that is what makes the cross-process rendezvous observable.
//
//   0x025040  0001  flagA: A is alive
//   0x025044  AAAA  A saw B's flag (rendezvous) AND sumTo(100)==5050; else DEAD
#include <stdint.h>

#define M(a)   (*(volatile uint16_t *)(a))
#define FLAG_A 0x025040UL
#define FLAG_B 0x025042UL
#define RES_A  0x025044UL
#define LIMIT  100000000UL


static uint16_t sumTo(uint16_t n) {
    uint16_t acc;
    uint16_t i;
    acc = 0;
    for (i = 1; i <= n; i++) {
        acc = acc + i;
    }
    return acc;
}


int main(void) {
    unsigned long spin;
    uint16_t      saw;

    M(RES_A)  = 0xDEAD;
    M(FLAG_A) = 0x0001;                  // announce A

    saw = 0;
    for (spin = 0; spin < LIMIT; spin++) {
        if (M(FLAG_B) == 0x0001) {
            saw = 1;
            break;
        }
    }

    if (saw == 1 && sumTo(100) == 5050) {
        M(RES_A) = 0xAAAA;               // rendezvous + real work OK
    }
    M(0x025048UL) = 0xE0AA;              // A reached end (distinguishes timeout from crash)
    for (volatile unsigned long j = 0; j < 300000UL; j++) {
    }
    return 0;
}
