/*
 * tcheck.c — реализация микро-фреймворка (см. tcheck.h).
 *
 * Всё пишется в tc_result — раннер читает эту структуру из ОЗУ.  Ничего
 * не инициализируем явно: crt0_ucsim зануляет _DATA/_BSS, и magic ставится
 * первым же tc_begin (правило проекта про статики — memory
 * sdcc_static_storage_gotcha).
 */
#include "tcheck.h"

tc_result_t tc_result;
const char *tc_curr;

static void tc_putc(char c)
{
    if (tc_result.logn < TC_LOGSZ - 1)
        tc_result.log[tc_result.logn++] = (uint8_t)c;
}

static void tc_puts(const char *s)
{
    while (*s) tc_putc(*s++);
}

static void tc_puthex(uint16_t v)
{
    const char *d = "0123456789ABCDEF";
    tc_puts(" (0x");
    tc_putc(d[(v >> 12) & 15]); tc_putc(d[(v >> 8) & 15]);
    tc_putc(d[(v >> 4) & 15]);  tc_putc(d[v & 15]);
    tc_putc(')');
}

/* Знаковое десятичное: значения тестов маленькие, но делаем честно для
 * всего диапазона int16_t. */
static void tc_putd(int16_t v)
{
    char buf[7];
    uint8_t n = 0;
    uint16_t u;
    if (v < 0) { tc_putc('-'); u = (uint16_t)(-v); }
    else       { u = (uint16_t)v; }
    do { buf[n++] = (char)('0' + (u % 10u)); u /= 10u; } while (u);
    while (n) tc_putc(buf[--n]);
}

void tc_begin(const char *name)
{
    tc_result.magic = TC_MAGIC;
    tc_curr = name;
}

static void tc_fail_head(const char *expr, uint16_t line)
{
    tc_result.failed++;
    tc_puts("FAIL ");
    tc_puts(tc_curr ? tc_curr : "?");
    tc_puts(":");
    tc_putd((int16_t)line);
    tc_puts("  ");
    tc_puts(expr);
}

void tc_check(int16_t got, int16_t want, const char *expr, uint16_t line)
{
    tc_result.magic = TC_MAGIC;
    tc_result.total++;
    if (got == want) return;
    tc_fail_head(expr, line);
    /* Печатаем и десятичное, и hex: сравнение идёт как int16_t, поэтому
     * беззнаковые величины (сид PRNG, координаты > 32767) в десятичном
     * виде выглядят отрицательными и сбивают с толку. */
    tc_puts(" = ");
    tc_putd(got);
    tc_puthex((uint16_t)got);
    tc_puts(", ждали ");
    tc_putd(want);
    tc_puthex((uint16_t)want);
    tc_putc('\n');
}

void tc_checkp(const void *got, const void *want, const char *expr, uint16_t line)
{
    tc_result.magic = TC_MAGIC;
    tc_result.total++;
    if (got == want) return;
    tc_fail_head(expr, line);
    tc_puts(" — указатели различаются\n");
}
