/*
 * atexit + exit + _exit.
 *
 *   atexit(fn)  — register fn to be called at normal termination (max 8).
 *   exit(code)  — run the atexit chain in LIFO, then ESTEX EXIT.
 *   _exit(code) — POSIX raw exit: skip the chain, go straight to ESTEX.
 *
 * These functions own termination entirely; crt0.s only does inline RST
 * 10h #41 when main returns without an explicit exit().  That path skips
 * the atexit chain — programs that need handlers should call exit() at
 * the bottom of main (or return through a wrapper).
 */

#include <stdlib.h>
#include <sprinter_exit.h>

#define ATEXIT_MAX  8

static void (*atexit_stack[ATEXIT_MAX])(void);
static int   atexit_top = 0;

int atexit(void (*fn)(void))
{
    if (atexit_top >= ATEXIT_MAX) {
        return -1;
    }
    atexit_stack[atexit_top++] = fn;
    return 0;
}

/* exit() — runs the chain, then performs the raw ESTEX EXIT. */
void exit(int code)
{
    while (atexit_top > 0) {
        void (*fn)(void) = atexit_stack[--atexit_top];
        if (fn) {
            fn();
        }
    }
    _exit(code);   /* falls into the inline-asm raw exit below */
}

/* _exit() — POSIX raw termination, no atexit chain. */
void _exit(int code) __naked
{
    (void)code;
    __asm
        ;; HL = code (single int arg).
        ld    a, l
        ld    b, a
        ld    c, #0x41        ; ESTEX EXIT
        rst   #0x10
        ;; Should not return.
1$:     halt
        jr    1$
    __endasm;
}
