/* ======================================================================== */
/*  EMU_LINK                                                                */
/*  Allows programs running inside the emulator to invoke emulator-specific */
/*  functionality.  jzIntv performs this task via the SIN instruction.      */
/*                                                                          */
/*  The process is simple:                                                  */
/*   -- Program loads $4A5A into R0 as a magic signature                    */
/*   -- Program loads EMU_LINK call # into R1                               */
/*   -- Program puts whatever arguments in R2..R5                           */
/*   -- Program issues SIN                                                  */
/*   -- Emulator checks R0 for signature and R1 for call #                  */
/*       -- If no signature, treat SIN as NOP                               */
/*       -- If signature OK but call # invalid, set C=1, R0=FFFF            */
/*       -- If signature OK and call # valid, set C=0 and execute call      */
/*   -- Callee executes, returning pass/fail in C, result in R0..R5.        */
/*                                                                          */
/*  Programs can quickly detect EMU_LINK support like so:                   */
/*   -- R0 = $4A5A, R1 = 0, SIN                                             */
/*   -- If C=0, R0=0, then EMU_LINK supported                               */
/*   -- Otherwise, it's not.                                                */
/* ======================================================================== */

#include "config.h"
#include "periph/periph.h"
#include "cp1600/cp1600.h"
#include "cp1600/emu_link.h"


static emu_link_api_t *emu_link_api = NULL;
static int emu_link_api_cnt = 0, emu_link_api_alloc = 0;

/* ======================================================================== */
/*  EMU_LINK_PING -- Simple API for presence detect.                        */
/* ======================================================================== */
int emu_link_ping(cp1600_t *cpu, int *fail)
{
    (void)cpu;
    *fail = 0;
    return 0;
}

/* ======================================================================== */
/*  EMU_LINK_INIT -- Initialize EMU_LINK subsystem.                         */
/* ======================================================================== */
int emu_link_init(void)
{
    emu_link_api        = calloc(16, sizeof(emu_link_api_t));
    emu_link_api_cnt    = 1;
    emu_link_api_alloc  = 16;

    if (!emu_link_api)
    {
        fprintf(stderr, "emu_link: Out of memory\n");
        return -1;
    }

    emu_link_api[0]     = emu_link_ping;

    return 0;
}

/* ======================================================================== */
/*  EMU_LINK_REGISTER -- Register an API with EMU_LINK                      */
/* ======================================================================== */
int emu_link_register(emu_link_api_t fn, int callno)
{
    int old_alloc = emu_link_api_alloc;
    int i;

    while (emu_link_api_alloc <= callno)
        emu_link_api_alloc <<= 1;

    emu_link_api = realloc(emu_link_api, 
                           emu_link_api_alloc * sizeof(emu_link_api_t));

    if (!emu_link_api)
    {
        fprintf(stderr, "emu_link: Out of memory\n");
        return -1;
    }

    for (i = old_alloc; i < emu_link_api_alloc; i++)
        emu_link_api[i] = NULL;

    if (emu_link_api[callno] != NULL)
        fprintf(stderr, "emu_link: Warning: API %d reassigned\n", callno);
    
    emu_link_api[callno] = fn;

    if (callno > emu_link_api_cnt)
        emu_link_api_cnt = callno;

    return 0;
}

/* ======================================================================== */
/*  EMU_LINK_DISPATCH -- Dispatch to an EMU_LINK API                        */
/* ======================================================================== */
void emu_link_dispatch(cp1600_t *cpu)
{
    int fail = 0;

    if (cpu->r[0] != 0x4A5A)
        return;

    //dprintf(("\nEMU-LINK %.4X %.4X %.4X\n", cpu->r[1], cpu->r[2], cpu->r[3]));
    if (cpu->r[1] > emu_link_api_cnt ||
        emu_link_api[cpu->r[1]] == NULL)
    {
        //dprintf(("EMU-LINK Invalid API\n"));
        cpu->C = 1;
        cpu->r[0] = 0xFFFF;
        return;
    }
    
    cpu->r[0] = emu_link_api[cpu->r[1]](cpu, &fail);
    //dprintf(("EMU-LINK Result:  %.4X %d\n", cpu->r[0], fail));

    cpu->C = fail != 0;
    return;
}

/* ======================================================================== */
/*  This program is free software; you can redistribute it and/or modify    */
/*  it under the terms of the GNU General Public License as published by    */
/*  the Free Software Foundation; either version 2 of the License, or       */
/*  (at your option) any later version.                                     */
/*                                                                          */
/*  This program is distributed in the hope that it will be useful,         */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
/*  General Public License for more details.                                */
/*                                                                          */
/*  You should have received a copy of the GNU General Public License       */
/*  along with this program; if not, write to the Free Software             */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               */
/* ======================================================================== */
/*                   Copyright (c) 2005, Joseph Zbiciak                     */
/* ======================================================================== */

