/*
 * kern.c - C-side experimental NetWare/DOS request wrapper
 *
 * This does NOT replace kern.asm.  It adds separate functions so we can test
 * register/segment handling and inspect the last call from DOS DEBUG or from
 * a small debug command later.
 *
 * Existing assembler function stays:
 *   int Net_Call(unsigned int func, void *req, void *repl);
 *
 * New test functions:
 *   int Net_Call_C(unsigned int ax, void *req, void *repl);
 *   int Net_Call_CX(unsigned int ax, unsigned int bx,
 *                   unsigned int cx, unsigned int dx,
 *                   void *req, void *repl);
 *   void Net_Call_C_Dump(void);
 */

#include <stdio.h>
#include <string.h>
#include <dos.h>

#include "net.h"

#if defined(__WATCOMC__)
#define KERN_C_CALL _Cdecl
#else
#define KERN_C_CALL
#endif


typedef struct {
  unsigned int in_ax;
  unsigned int in_bx;
  unsigned int in_cx;
  unsigned int in_dx;
  unsigned int in_si;
  unsigned int in_di;
  unsigned int in_ds;
  unsigned int in_es;

  unsigned int out_ax;
  unsigned int out_bx;
  unsigned int out_cx;
  unsigned int out_dx;
  unsigned int out_si;
  unsigned int out_di;
  unsigned int out_flags;

  void far *req_ptr;
  void far *repl_ptr;

  int rc;
} NET_CALL_C_DEBUG;

NET_CALL_C_DEBUG Net_Call_C_Last;

/*
 * Same call shape as kern.asm Net_Call, but implemented in C.
 *
 * This is meant for OLD known-working calls first:
 *   Net_Call_C(0xE200, ...)
 *   Net_Call_C(0xE300, ...)
 *
 * Do not use it blindly for NCP87 yet; the previous F257 test hung through
 * the old wrapper, so we will first test old calls and register layout.
 */
int KERN_C_CALL Net_Call_C(unsigned int ax, void *req, void *repl)
{
  return Net_Call_CX(ax, 0, 0, 0, req, repl);
}

/*
 * Extended variant with BX/CX/DX settable.  This lets us experiment without
 * changing kern.asm every time.
 */
int KERN_C_CALL Net_Call_CX(unsigned int ax, unsigned int bx,
                unsigned int cx, unsigned int dx,
                void *req, void *repl)
{
  union REGS inregs;
  union REGS outregs;
  struct SREGS segregs;

  memset(&inregs, 0, sizeof(inregs));
  memset(&outregs, 0, sizeof(outregs));
  memset(&segregs, 0, sizeof(segregs));
  memset(&Net_Call_C_Last, 0, sizeof(Net_Call_C_Last));

  inregs.x.ax = ax;
  inregs.x.bx = bx;
  inregs.x.cx = cx;
  inregs.x.dx = dx;

  inregs.x.si = FP_OFF(req);
  inregs.x.di = FP_OFF(repl);
  segregs.ds  = FP_SEG(req);
  segregs.es  = FP_SEG(repl);

  Net_Call_C_Last.in_ax = inregs.x.ax;
  Net_Call_C_Last.in_bx = inregs.x.bx;
  Net_Call_C_Last.in_cx = inregs.x.cx;
  Net_Call_C_Last.in_dx = inregs.x.dx;
  Net_Call_C_Last.in_si = inregs.x.si;
  Net_Call_C_Last.in_di = inregs.x.di;
  Net_Call_C_Last.in_ds = segregs.ds;
  Net_Call_C_Last.in_es = segregs.es;
  Net_Call_C_Last.req_ptr  = req;
  Net_Call_C_Last.repl_ptr = repl;

  int86x(0x21, &inregs, &outregs, &segregs);

  Net_Call_C_Last.out_ax = outregs.x.ax;
  Net_Call_C_Last.out_bx = outregs.x.bx;
  Net_Call_C_Last.out_cx = outregs.x.cx;
  Net_Call_C_Last.out_dx = outregs.x.dx;
  Net_Call_C_Last.out_si = outregs.x.si;
  Net_Call_C_Last.out_di = outregs.x.di;
  Net_Call_C_Last.out_flags = outregs.x.cflag;
  /* kern_wasm.asm clears AH before returning, so return AL only. */
  Net_Call_C_Last.rc = outregs.x.ax & 0x00ff;

  return Net_Call_C_Last.rc;
}

/*
 * Text dump for quick testing.  Later we can expose this through DEBUG.EXE
 * as "debug netcall" or similar.
 */
void KERN_C_CALL Net_Call_C_Dump(void)
{
  fprintf(stdout, "NETCALLC in : AX=%04X BX=%04X CX=%04X DX=%04X DS:SI=%04X:%04X ES:DI=%04X:%04X\n",
          Net_Call_C_Last.in_ax,
          Net_Call_C_Last.in_bx,
          Net_Call_C_Last.in_cx,
          Net_Call_C_Last.in_dx,
          Net_Call_C_Last.in_ds,
          Net_Call_C_Last.in_si,
          Net_Call_C_Last.in_es,
          Net_Call_C_Last.in_di);

  fprintf(stdout, "NETCALLC out: AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X CF=%04X RC=%d\n",
          Net_Call_C_Last.out_ax,
          Net_Call_C_Last.out_bx,
          Net_Call_C_Last.out_cx,
          Net_Call_C_Last.out_dx,
          Net_Call_C_Last.out_si,
          Net_Call_C_Last.out_di,
          Net_Call_C_Last.out_flags,
          Net_Call_C_Last.rc);
}

/*
 * Optional helper for DEBUG-style commands that do not want formatted output.
 * idx:
 *   0 in_ax      1 in_bx      2 in_cx      3 in_dx
 *   4 in_ds      5 in_si      6 in_es      7 in_di
 *   8 out_ax     9 out_bx    10 out_cx    11 out_dx
 *  12 out_si    13 out_di    14 out_flags 15 rc
 */
UI KERN_C_CALL Net_Call_C_GetDebug(UI idx)
{
  switch (idx) {
    case 0:  return Net_Call_C_Last.in_ax;
    case 1:  return Net_Call_C_Last.in_bx;
    case 2:  return Net_Call_C_Last.in_cx;
    case 3:  return Net_Call_C_Last.in_dx;
    case 4:  return Net_Call_C_Last.in_ds;
    case 5:  return Net_Call_C_Last.in_si;
    case 6:  return Net_Call_C_Last.in_es;
    case 7:  return Net_Call_C_Last.in_di;
    case 8:  return Net_Call_C_Last.out_ax;
    case 9:  return Net_Call_C_Last.out_bx;
    case 10: return Net_Call_C_Last.out_cx;
    case 11: return Net_Call_C_Last.out_dx;
    case 12: return Net_Call_C_Last.out_si;
    case 13: return Net_Call_C_Last.out_di;
    case 14: return Net_Call_C_Last.out_flags;
    case 15: return (unsigned int)Net_Call_C_Last.rc;
  }
  return 0xffff;
}
