/* nwtests.c 20-May-96 */

/****************************************************************
 * (C)opyright (C) 1993,1996  Martin Stover, Marburg, Germany   *
 ****************************************************************/

#include "net.h"


static int tests_same_arg(char *a, char *b)
{
  while (*a || *b) {
    int ca = *a++;
    int cb = *b++;
    if (ca >= 'a' && ca <= 'z') ca -= 32;
    if (cb >= 'a' && cb <= 'z') cb -= 32;
    if (ca != cb) return(0);
  }
  return(1);
}

static void tests_usage(void)
{
  fprintf(stdout, "Usage: TESTS [OLD|NETCALL|E300|NCPF2 [file]|NWREQ87 [file]]\n");
}

static int tests_netcall(void)
{
  unsigned char req[4];
  unsigned char repl[4];
  int asm_rc;
  int c_rc;

  memset(req, 0, sizeof(req));
  memset(repl, 0, sizeof(repl));

  fprintf(stdout, "TEST Net_Call ASM vs C\n");
  fprintf(stdout, "Call: INT 21h AH=19h get current drive\n");
  fprintf(stdout, "Expected: AL=0 for A:, 1 for B:, 2 for C:, ...\n\n");

  asm_rc = Net_Call(0x1900, req, repl);
  fprintf(stdout, "ASM Net_Call(1900h) rc=%04X drive=%c:\n",
          asm_rc, 'A' + (asm_rc & 0xff));

  c_rc = Net_Call_C(0x1900, req, repl);
  fprintf(stdout, "C   Net_Call_C(1900h) rc=%04X drive=%c:\n",
          c_rc, 'A' + (c_rc & 0xff));

  Net_Call_C_Dump();

  if ((asm_rc & 0xff) == (c_rc & 0xff))
    fprintf(stdout, "\nNETCALL C basic register test OK\n");
  else
    fprintf(stdout, "\nNETCALL C basic register test FAILED\n");

  return(0);
}

static int tests_old(int argc, char *argv[], int mode)
{

  int level     = ncp_17_02(NWCONN, 6);
  int dirhandle = alloc_temp_dir_handle(0, "SYS:", 'd', NULL);
  int result    = -1;
  uint8  *path  = (argc < 2) ? "SYS:\\TMP" : argv[1];
  if (dirhandle > -1) {
    result = ncp_16_02(dirhandle, "SYSTEM/", NULL, NULL, NULL, NULL);
    result = ncp_16_02(dirhandle, "SYSTEM", NULL, NULL, NULL, NULL);
  }
  fprintf(stdout, "dirhandle=%d, result=%d\n", dirhandle, result);
  result = redir_device_drive(0x4, "u:", path);
  fprintf(stdout, "redir path=%s, result=%d\n", path, result);

  path="Q1";
  result = redir_device_drive(0x3, "LPT1", path);
  fprintf(stdout, "redir path=%s, result=%d\n", path, result);

  {
    int k =-1;
    uint8 devname[20];
    uint8 remotename[130];
    int  devicetyp;
    while ((result = list_redir(++k, &devicetyp, devname, remotename)) > -1){
       fprintf(stdout, "index=%d, dev=%s(%d), %s result=%d\n",
            k, devname, devicetyp, remotename, result);
    }
  }
  if (level > -1) (void) ncp_17_02(NWCONN, level);
  return(0);
}

static void tests_dump_bytes(char *title, unsigned char *p, int len)
{
  int i;
  fprintf(stdout, "%s", title);
  for (i = 0; i < len; i++)
    fprintf(stdout, " %02X", p[i]);
  fprintf(stdout, "\n");
}

static int tests_netcall_e300(void)
{
  struct {
    uint16 len;
    uint8  func;
  } req_asm, req_c;

  struct {
    uint16 len;
    uint8  data[16];
  } repl_asm, repl_c;

  int asm_rc;
  int c_rc;

  memset(&req_asm, 0, sizeof(req_asm));
  memset(&req_c, 0, sizeof(req_c));
  memset(&repl_asm, 0, sizeof(repl_asm));
  memset(&repl_c, 0, sizeof(repl_c));

  /*
   * NCP 17/17 Get Login Encryption Key.
   * This is a known safe bindery/login helper call used by LOGIN/PASSWD code.
   * Request via old DOS NetWare API AX=E300.
   */
  req_asm.len = 1;
  req_asm.func = 0x17;
  repl_asm.len = 8;

  req_c.len = 1;
  req_c.func = 0x17;
  repl_c.len = 8;

  fprintf(stdout, "TEST Net_Call ASM vs C E300/NCP17/17\n");
  fprintf(stdout, "Call: Get Login Encryption Key\n\n");

  asm_rc = Net_Call(0xE300, &req_asm, &repl_asm);
  fprintf(stdout, "ASM Net_Call(E300h) rc=%04X\n", asm_rc);
  tests_dump_bytes("ASM key:", repl_asm.data, 8);

  c_rc = Net_Call_C(0xE300, &req_c, &repl_c);
  fprintf(stdout, "C   Net_Call_C(E300h) rc=%04X\n", c_rc);
  tests_dump_bytes("C   key:", repl_c.data, 8);

  Net_Call_C_Dump();

  if ((asm_rc & 0xff) == (c_rc & 0xff))
    fprintf(stdout, "\nNETCALL C E300 return test OK\n");
  else
    fprintf(stdout, "\nNETCALL C E300 return test FAILED\n");

  /*
   * Keys can differ per request, so do not require byte-identical keys.
   * We only compare return status and check that both calls returned 8 bytes.
   */
  return(0);
}


static void tests_put_word_lh(uint8 *p, uint16 v)
{
  p[0] = (uint8)(v & 0xff);
  p[1] = (uint8)((v >> 8) & 0xff);
}

static void tests_put_dword_lh(uint8 *p, uint32 v)
{
  p[0] = (uint8)(v & 0xff);
  p[1] = (uint8)((v >> 8) & 0xff);
  p[2] = (uint8)((v >> 16) & 0xff);
  p[3] = (uint8)((v >> 24) & 0xff);
}

static uint32 tests_get_dword_lh(uint8 *p)
{
  return((uint32)p[0] |
         ((uint32)p[1] << 8) |
         ((uint32)p[2] << 16) |
         ((uint32)p[3] << 24));
}

static int tests_get_current_drive(void)
{
  REGS regs;

  regs.h.ah = 0x19;
  int86(0x21, &regs, &regs);
  return((int)regs.h.al);
}

static int tests_current_conn_dhandle(uint8 *connid, uint8 *dhandle)
{
  uint8 flags = 0;
  int drive;

  drive = tests_get_current_drive();

  if (get_drive_info((uint8)drive, connid, dhandle, &flags)) {
    fprintf(stdout, "NCPF2DBG get_drive_info failed drive=%c:\n", 'A' + drive);
    return(-1);
  }

  fprintf(stdout, "NCPF2DBG drive=%c: connid=%u dhandle=%u flags=%02X\n",
          'A' + drive, *connid, *dhandle, flags);

  if (!*connid || (flags & 0x80)) {
    fprintf(stdout, "NCPF2DBG current drive is not a network drive\n");
    return(-1);
  }

  return(0);
}

static int tests_add_handle_path(uint8 *p, uint8 dhandle, char *name)
{
  int nlen;

  nlen = strlen(name);
  if (nlen > 255) nlen = 255;

  *p++ = dhandle;
  tests_put_dword_lh(p, 0L); p += 4;
  *p++ = 0;                 /* dirstyle = short dir handle */
  *p++ = 1;                 /* one component */
  *p++ = (uint8)nlen;
  memcpy(p, name, nlen);
  p += nlen;

  return(1 + 4 + 1 + 1 + 1 + nlen);
}

static int tests_ncpf2_read_one(char *name, UI cx_mode)
{
  struct {
    uint8  data[320];
  } req;
  struct {
    uint8  data[256];
  } repl;

  uint8 *p;
  uint8 connid = 0;
  uint8 dhandle = 0;
  UI cx;
  int hlen;
  int rc;
  uint32 attr;

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  memset(&req, 0, sizeof(req));
  memset(&repl, 0, sizeof(repl));

  p = req.data;
  *p++ = 6;                  /* NCP87 subfunction 6: obtain file/subdir info */
  *p++ = 0;                  /* source namespace DOS */
  *p++ = 0;                  /* target namespace DOS */
  tests_put_word_lh(p, 0x0006); p += 2;        /* SA_ALL */
  tests_put_dword_lh(p, 0x00000004UL); p += 4; /* RIM_ATTRIBUTES */
  hlen = tests_add_handle_path(p, dhandle, name);
  p += hlen;

  if (cx_mode == 0)
    cx = (UI)(p - req.data);       /* most likely: request length */
  else
    cx = sizeof(repl.data);        /* alternate: reply buffer size */

  fprintf(stdout, "NCPF2DBG name=%s func=57 conn=%u cx=%u dx=%u mode=%u req.len=%u repl.max=%u\n",
          name, connid, cx, (UI)sizeof(repl.data), cx_mode, (UI)(p - req.data), (UI)sizeof(repl.data));
  tests_dump_bytes("NCPF2DBG req:", req.data, (p - req.data) > 64 ? 64 : (int)(p - req.data));

  rc = Net_Call_F2_C(0x57, cx, (UI)sizeof(repl.data), req.data, repl.data);

  fprintf(stdout, "NCPF2DBG rc=%04X\n", rc);
  tests_dump_bytes("NCPF2DBG repl:", repl.data, 64);
  Net_Call_C_Dump();

  attr = tests_get_dword_lh(repl.data);
  fprintf(stdout, "NCPF2DBG attr[0]=%08lX\n", attr);

  return(0);
}

static int tests_ncpf2(int argc, char *argv[])
{
  char *name = "LOGIN.EXE";
  UI mode = 0;

  if (argc > 2)
    name = argv[2];
  if (argc > 3 && tests_same_arg(argv[3], "REPLY"))
    mode = 1;

  fprintf(stdout, "TEST NCPF2 AH=F2/AL=57 NCP87 obtain RIM_ATTRIBUTES\n");
  fprintf(stdout, "Default CX is request length. Add REPLY to use reply size.\n");
  fprintf(stdout, "This is read-only, but still experimental.\n\n");

  return tests_ncpf2_read_one(name, mode);
}


static void tests_build_nwreq87_path(uint8 *buf, uint8 dhandle,
                                     char *name, UI *out_len)
{
  uint8 *p;
  int nlen;

  memset(buf, 0, 300);

  p = buf;
  nlen = strlen(name);
  if (nlen > 255) nlen = 255;

  /*
   * Same compact handle/path payload we used before:
   *   handle, dirbase, dirstyle, component-count, len, name
   * The new part in this test is not the path itself; it is that we now
   * reproduce NWCREQUEST's fragmented request and full reply size.
   */
  *p++ = dhandle;
  tests_put_dword_lh(p, 0L); p += 4;
  *p++ = 0;                 /* dirstyle = short directory handle */
  *p++ = 1;                 /* one path component */
  *p++ = (uint8)nlen;
  memcpy(p, name, nlen);
  p += nlen;

  *out_len = (UI)(p - buf);
}

static UI tests_build_nwreq87s6_flat(uint8 *req,
                                     uint8 *path,
                                     UI path_len)
{
  uint8 *p;

  memset(req, 0, 400);
  p = req;

  /*
   * DeveloperNet ncpdos16 87s6.c builds two request fragments:
   *   frag 1 length 9:
   *     subfn=6, srcNS, dstNS, searchAttrs, returnInfoMask
   *   frag 2:
   *     packed handle/path structure
   *
   * NWCREQUEST for NETX/Shell concatenates those request fragments before
   * calling NWCSHELLREQ.
   */
  *p++ = 6;                         /* NCP87 subfunction 6 */
  *p++ = 0;                         /* source namespace DOS */
  *p++ = 0;                         /* target namespace DOS */
  tests_put_word_lh(p, 0x0006); p += 2;       /* SA_ALL */
  tests_put_dword_lh(p, 0x00000004UL); p += 4;/* RIM_ATTRIBUTES */

  memcpy(p, path, path_len);
  p += path_len;

  return (UI)(p - req);
}

static int tests_nwreq87(int argc, char *argv[])
{
  char *name = "LOGIN.EXE";
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 path[300];
  uint8 req[400];
  uint8 repl[0x180];
  uint8 dummy[8];
  UI path_len;
  UI req_len;
  int rc;
  uint32 a0;
  uint32 a4;
  uint32 a4d;

  if (argc > 2)
    name = argv[2];

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  tests_build_nwreq87_path(path, dhandle, name, &path_len);
  req_len = tests_build_nwreq87s6_flat(req, path, path_len);

  memset(repl, 0, sizeof(repl));
  memset(dummy, 0, sizeof(dummy));

  fprintf(stdout, "TEST NWREQ87 shell-style NCP87/S6 for %s\n", name);
  fprintf(stdout, "connid=%u dhandle=%u path.len=%u req.len=%u repl.len=%u\n",
          connid, dhandle, path_len, req_len, (UI)0x014d);
  tests_dump_bytes("NWREQ87 path:", path, path_len > 48 ? 48 : path_len);
  tests_dump_bytes("NWREQ87 req :", req, req_len > 64 ? 64 : req_len);

  /*
   * Reproduce the NETX/Shell path in clndos16 dreq.c:
   *   first AH=F0/DX=conn via NWCSHELLREQ
   *   then AH=F2/AL=function with DS:SI=request, CX=requestLen,
   *        ES:DI=reply, DX=sum(replyFragLengths).
   *
   * For 87s6.c reply fragments are 0x4d + 0x100 = 0x014d.
   */
  fprintf(stdout, "NWREQ87 init F000 DX=conn\n");
  rc = Net_Call_F2X_C(0xF000, 0, 0, (UI)connid, dummy, repl);
  fprintf(stdout, "NWREQ87 init rc=%04X\n", rc);
  Net_Call_C_Dump();

  fprintf(stdout, "NWREQ87 call F257 CX=req.len DX=014D\n");
  rc = Net_Call_F2X_C(0xF257, 0, req_len, 0x014d, req, repl);
  fprintf(stdout, "NWREQ87 rc=%04X\n", rc);
  Net_Call_C_Dump();

  tests_dump_bytes("NWREQ87 repl[0..63]:", repl, 64);
  tests_dump_bytes("NWREQ87 repl[4d..8c]:", repl + 0x4d, 64);

  a0 = tests_get_dword_lh(repl);
  a4 = tests_get_dword_lh(repl + 4);
  a4d = tests_get_dword_lh(repl + 0x4d);

  fprintf(stdout, "NWREQ87 dword[0]=%08lX dword[4]=%08lX dword[4d]=%08lX\n",
          a0, a4, a4d);

  return(0);
}


typedef struct {
  uint16 off;
  uint16 seg;
  uint16 len;
} TEST_NWFRAG16;

static void tests_set_frag(TEST_NWFRAG16 *f, void *ptr, UI len)
{
  f->off = FP_OFF(ptr);
  f->seg = FP_SEG(ptr);
  f->len = (uint16)len;
}

static int tests_nwreq87vlm(int argc, char *argv[])
{
  char *name = "LOGIN.EXE";
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 path[300];
  uint8 hdr[16];
  uint8 repl[0x180];
  TEST_NWFRAG16 reqfrags[2];
  TEST_NWFRAG16 replfrags[2];
  UI path_len;
  int rc;
  uint32 a0;
  uint32 a4d;

  if (argc > 2)
    name = argv[2];

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  tests_build_nwreq87_path(path, dhandle, name, &path_len);

  memset(hdr, 0, sizeof(hdr));
  memset(repl, 0, sizeof(repl));
  memset(reqfrags, 0, sizeof(reqfrags));
  memset(replfrags, 0, sizeof(replfrags));

  /*
   * ncpdos16 87s6.c request frag #1, length 9.
   */
  hdr[0] = 6;                                      /* subfunction */
  hdr[1] = 0;                                      /* src namespace DOS */
  hdr[2] = 0;                                      /* dst namespace DOS */
  tests_put_word_lh(hdr + 3, 0x0006);              /* SA_ALL */
  tests_put_dword_lh(hdr + 5, 0x00000004UL);       /* RIM_ATTRIBUTES */

  tests_set_frag(&reqfrags[0], hdr, 9);
  tests_set_frag(&reqfrags[1], path, path_len);

  /*
   * ncpdos16 87s6.c reply frags:
   *   local entry-info block 0x4d
   *   caller output extension 0x100
   */
  tests_set_frag(&replfrags[0], repl, 0x4d);
  tests_set_frag(&replfrags[1], repl + 0x4d, 0x100);

  fprintf(stdout, "TEST NWREQ87VLM NCP87/S6 for %s\n", name);
  fprintf(stdout, "connid=%u dhandle=%u path.len=%u\n", connid, dhandle, path_len);
  tests_dump_bytes("VLM hdr :", hdr, 9);
  tests_dump_bytes("VLM path:", path, path_len > 48 ? 48 : path_len);

  fprintf(stdout, "VLM call: AX=0057 BX=0002 CX=conn DX=0002 p1=6 p2=20 p3=0\n");

  rc = Net_Call_VLM_Raw(0x0057, 2, (UI)connid, 2,
                        reqfrags, replfrags,
                        6, 0x20, 0);

  fprintf(stdout, "NWREQ87VLM rc=%04X\n", rc);
  tests_dump_bytes("VLM repl[0..63]:", repl, 64);
  tests_dump_bytes("VLM repl[4d..8c]:", repl + 0x4d, 64);

  a0 = tests_get_dword_lh(repl);
  a4d = tests_get_dword_lh(repl + 0x4d);
  fprintf(stdout, "NWREQ87VLM dword[0]=%08lX dword[4d]=%08lX\n", a0, a4d);

  return(0);
}


static UI tests_build_nwreq87_path_multi(uint8 *buf, uint8 dhandle,
                                         UI dirbase, uint8 style,
                                         int count,
                                         char *c1, char *c2, char *c3)
{
  uint8 *p;
  int l;

  memset(buf, 0, 300);
  p = buf;

  *p++ = dhandle;
  tests_put_dword_lh(p, (uint32)dirbase); p += 4;
  *p++ = style;
  *p++ = (uint8)count;

  if (count > 0 && c1) {
    l = strlen(c1); if (l > 255) l = 255;
    *p++ = (uint8)l; memcpy(p, c1, l); p += l;
  }
  if (count > 1 && c2) {
    l = strlen(c2); if (l > 255) l = 255;
    *p++ = (uint8)l; memcpy(p, c2, l); p += l;
  }
  if (count > 2 && c3) {
    l = strlen(c3); if (l > 255) l = 255;
    *p++ = (uint8)l; memcpy(p, c3, l); p += l;
  }

  return (UI)(p - buf);
}


static void tests_wait_key(void)
{
  fprintf(stdout, "\nStrike any key when ready . . . ");
  fflush(stdout);
  getch();
  fprintf(stdout, "\n");
  fflush(stdout);
}

static int tests_nwreq87vlm_one(char *label, uint8 connid,
                                uint8 *hdr, uint8 *path, UI path_len)
{
  uint8 repl[0x180];
  TEST_NWFRAG16 reqfrags[2];
  TEST_NWFRAG16 replfrags[2];
  int rc;
  uint32 a0;
  uint32 a4d;

  memset(repl, 0, sizeof(repl));
  memset(reqfrags, 0, sizeof(reqfrags));
  memset(replfrags, 0, sizeof(replfrags));

  tests_set_frag(&reqfrags[0], hdr, 9);
  tests_set_frag(&reqfrags[1], path, path_len);
  tests_set_frag(&replfrags[0], repl, 0x4d);
  tests_set_frag(&replfrags[1], repl + 0x4d, 0x100);

  fprintf(stdout, "\nCASE %s\n", label);
  tests_dump_bytes("PATH:", path, path_len > 64 ? 64 : path_len);

  rc = Net_Call_VLM_Raw(0x0057, 2, (UI)connid, 2,
                        reqfrags, replfrags,
                        6, 0x20, 0);

  fprintf(stdout, "RC=%04X\n", rc);
  tests_dump_bytes("REPL0:", repl, 32);
  tests_dump_bytes("REPL4D:", repl + 0x4d, 32);
  a0 = tests_get_dword_lh(repl);
  a4d = tests_get_dword_lh(repl + 0x4d);
  fprintf(stdout, "DW0=%08lX DW4D=%08lX\n", a0, a4d);

  tests_wait_key();

  return rc;
}

static int tests_nwreq87vlmmatrix(int argc, char *argv[])
{
  char *name = "LOGIN.EXE";
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 hdr[16];
  uint8 path[300];
  UI len;

  (void)argc;
  (void)argv;

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  memset(hdr, 0, sizeof(hdr));
  hdr[0] = 6;
  hdr[1] = 0;
  hdr[2] = 0;
  tests_put_word_lh(hdr + 3, 0x0006);
  tests_put_dword_lh(hdr + 5, 0x00000004UL);

  fprintf(stdout, "TEST NWREQ87VLMMATRIX NCP87/S6\n");
  fprintf(stdout, "connid=%u dhandle=%u file=%s\n", connid, dhandle, name);
  tests_dump_bytes("HDR:", hdr, 9);

  /*
   * We know the VLM transport is reached.  RC=81 now likely means our
   * packed handle/path is wrong for NCP87.  Try the common handle/path
   * variants:
   *
   * current mapped handle + file
   * current mapped handle + PUBLIC/file
   * root handle 0 + PUBLIC/file
   * style 1 variants in case dirstyle differs
   */
  len = tests_build_nwreq87_path_multi(path, dhandle, 0, 0, 1,
                                       "LOGIN.EXE", 0, 0);
  tests_nwreq87vlm_one("h=dhandle style=0 LOGIN.EXE", connid, hdr, path, len);

  len = tests_build_nwreq87_path_multi(path, dhandle, 0, 0, 2,
                                       "PUBLIC", "LOGIN.EXE", 0);
  tests_nwreq87vlm_one("h=dhandle style=0 PUBLIC/LOGIN.EXE", connid, hdr, path, len);

  len = tests_build_nwreq87_path_multi(path, 0, 0, 0, 2,
                                       "PUBLIC", "LOGIN.EXE", 0);
  tests_nwreq87vlm_one("h=0 style=0 PUBLIC/LOGIN.EXE", connid, hdr, path, len);

  len = tests_build_nwreq87_path_multi(path, dhandle, 0, 1, 1,
                                       "LOGIN.EXE", 0, 0);
  tests_nwreq87vlm_one("h=dhandle style=1 LOGIN.EXE", connid, hdr, path, len);

  len = tests_build_nwreq87_path_multi(path, 0, 0, 1, 2,
                                       "PUBLIC", "LOGIN.EXE", 0);
  tests_nwreq87vlm_one("h=0 style=1 PUBLIC/LOGIN.EXE", connid, hdr, path, len);

  return(0);
}


static int tests_nwreq87vlmcase(int argc, char *argv[])
{
  int which = 1;
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 hdr[16];
  uint8 path[300];
  UI len;

  if (argc > 2)
    which = atoi(argv[2]);

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  memset(hdr, 0, sizeof(hdr));
  hdr[0] = 6;
  hdr[1] = 0;
  hdr[2] = 0;
  tests_put_word_lh(hdr + 3, 0x0006);
  tests_put_dword_lh(hdr + 5, 0x00000004UL);

  fprintf(stdout, "TEST NWREQ87VLMCASE %d\n", which);
  fprintf(stdout, "connid=%u dhandle=%u\n", connid, dhandle);
  tests_dump_bytes("HDR:", hdr, 9);

  switch (which) {
    case 1:
      len = tests_build_nwreq87_path_multi(path, dhandle, 0, 0, 1,
                                           "LOGIN.EXE", 0, 0);
      return tests_nwreq87vlm_one("1 h=dhandle style=0 LOGIN.EXE", connid, hdr, path, len);

    case 2:
      len = tests_build_nwreq87_path_multi(path, dhandle, 0, 0, 2,
                                           "PUBLIC", "LOGIN.EXE", 0);
      return tests_nwreq87vlm_one("2 h=dhandle style=0 PUBLIC/LOGIN.EXE", connid, hdr, path, len);

    case 3:
      len = tests_build_nwreq87_path_multi(path, 0, 0, 0, 2,
                                           "PUBLIC", "LOGIN.EXE", 0);
      return tests_nwreq87vlm_one("3 h=0 style=0 PUBLIC/LOGIN.EXE", connid, hdr, path, len);

    case 4:
      len = tests_build_nwreq87_path_multi(path, dhandle, 0, 1, 1,
                                           "LOGIN.EXE", 0, 0);
      return tests_nwreq87vlm_one("4 h=dhandle style=1 LOGIN.EXE", connid, hdr, path, len);

    case 5:
      len = tests_build_nwreq87_path_multi(path, 0, 0, 1, 2,
                                           "PUBLIC", "LOGIN.EXE", 0);
      return tests_nwreq87vlm_one("5 h=0 style=1 PUBLIC/LOGIN.EXE", connid, hdr, path, len);
  }

  fprintf(stdout, "Valid cases: 1..5\n");
  return(1);
}


static void tests_set_frag_alt_len_off_seg(uint8 *buf, void *ptr, UI len)
{
  tests_put_word_lh(buf + 0, (uint16)len);
  tests_put_word_lh(buf + 2, (uint16)FP_OFF(ptr));
  tests_put_word_lh(buf + 4, (uint16)FP_SEG(ptr));
}

static void tests_nwreq87vlmreg_help(void)
{
  fprintf(stdout, "Usage: TESTS NWREQ87VLMREG n\n");
  fprintf(stdout, "n=1..20, one VLM register/fragment-layout case\n");
  fprintf(stdout, "1-10  frag layout off,seg,len\n");
  fprintf(stdout, "11-20 frag layout len,off,seg\n");
  fprintf(stdout, "odd=stack 6,20,0  even=stack 0,20,6\n");
  fprintf(stdout, "reg maps repeat every 2 cases:\n");
  fprintf(stdout, "  A AX=57 BX=reqs CX=conn DX=reps\n");
  fprintf(stdout, "  B AX=57 BX=conn CX=reqs DX=reps\n");
  fprintf(stdout, "  C AX=57 BX=reqs CX=reps DX=conn\n");
  fprintf(stdout, "  D AX=57 BX=conn CX=reps DX=reqs\n");
  fprintf(stdout, "  E AX=57 BX=reps CX=conn DX=reqs\n");
}

static int tests_nwreq87vlmreg(int argc, char *argv[])
{
  int which = 1;
  int layout_alt;
  int local_case;
  int stack_rev;
  int regmap;

  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 hdr[16];
  uint8 path[300];
  uint8 repl[0x180];
  UI path_len;

  TEST_NWFRAG16 frags_req_std[2];
  TEST_NWFRAG16 frags_rep_std[2];
  uint8 frags_req_alt[12];
  uint8 frags_rep_alt[12];
  void *reqfragp;
  void *repfragp;

  UI ax = 0x0057;
  UI bx = 0;
  UI cx = 0;
  UI dx = 0;
  UI p1 = 6;
  UI p2 = 0x20;
  UI p3 = 0;
  int rc;
  uint32 dw0;
  uint32 dw4d;

  if (argc < 3) {
    tests_nwreq87vlmreg_help();
    return(1);
  }

  which = atoi(argv[2]);
  if (which < 1 || which > 20) {
    tests_nwreq87vlmreg_help();
    return(1);
  }

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  memset(hdr, 0, sizeof(hdr));
  hdr[0] = 6;
  hdr[1] = 0;
  hdr[2] = 0;
  tests_put_word_lh(hdr + 3, 0x0006);
  tests_put_dword_lh(hdr + 5, 0x00000004UL);

  path_len = tests_build_nwreq87_path_multi(path, dhandle, 0, 0, 1,
                                            "LOGIN.EXE", 0, 0);

  memset(repl, 0, sizeof(repl));
  memset(frags_req_std, 0, sizeof(frags_req_std));
  memset(frags_rep_std, 0, sizeof(frags_rep_std));
  memset(frags_req_alt, 0, sizeof(frags_req_alt));
  memset(frags_rep_alt, 0, sizeof(frags_rep_alt));

  tests_set_frag(&frags_req_std[0], hdr, 9);
  tests_set_frag(&frags_req_std[1], path, path_len);
  tests_set_frag(&frags_rep_std[0], repl, 0x4d);
  tests_set_frag(&frags_rep_std[1], repl + 0x4d, 0x100);

  tests_set_frag_alt_len_off_seg(frags_req_alt + 0, hdr, 9);
  tests_set_frag_alt_len_off_seg(frags_req_alt + 6, path, path_len);
  tests_set_frag_alt_len_off_seg(frags_rep_alt + 0, repl, 0x4d);
  tests_set_frag_alt_len_off_seg(frags_rep_alt + 6, repl + 0x4d, 0x100);

  layout_alt = (which > 10);
  local_case = layout_alt ? which - 10 : which;
  stack_rev = ((local_case & 1) == 0);
  regmap = (local_case + 1) / 2; /* 1..5 */

  if (layout_alt) {
    reqfragp = frags_req_alt;
    repfragp = frags_rep_alt;
  } else {
    reqfragp = frags_req_std;
    repfragp = frags_rep_std;
  }

  if (stack_rev) {
    p1 = 0;
    p2 = 0x20;
    p3 = 6;
  }

  switch (regmap) {
    case 1:
      bx = 2;       cx = connid; dx = 2;
      break;
    case 2:
      bx = connid;  cx = 2;      dx = 2;
      break;
    case 3:
      bx = 2;       cx = 2;      dx = connid;
      break;
    case 4:
      bx = connid;  cx = 2;      dx = 2;
      break;
    case 5:
      bx = 2;       cx = connid; dx = 2;
      break;
  }

  /*
   * Cases 2 and 4 share registers but differ stack order.
   * Cases 1 and 9 share registers but let us repeat with same stack/table
   * for sanity.  Keep explicit output so we know exactly what was run.
   */

  fprintf(stdout, "TEST NWREQ87VLMREG case=%d\n", which);
  fprintf(stdout, "layout=%s stack=%s regmap=%d\n",
          layout_alt ? "len,off,seg" : "off,seg,len",
          stack_rev ? "0,20,6" : "6,20,0",
          regmap);
  fprintf(stdout, "connid=%u dhandle=%u path_len=%u\n", connid, dhandle, path_len);
  fprintf(stdout, "AX=%04X BX=%04X CX=%04X DX=%04X p1=%04X p2=%04X p3=%04X\n",
          ax, bx, cx, dx, p1, p2, p3);
  tests_dump_bytes("HDR :", hdr, 9);
  tests_dump_bytes("PATH:", path, path_len > 48 ? 48 : path_len);

  rc = Net_Call_VLM_Raw(ax, bx, cx, dx, reqfragp, repfragp, p1, p2, p3);

  fprintf(stdout, "RC=%04X\n", rc);
  tests_dump_bytes("REPL0 :", repl, 32);
  tests_dump_bytes("REPL4D:", repl + 0x4d, 32);
  dw0 = tests_get_dword_lh(repl);
  dw4d = tests_get_dword_lh(repl + 0x4d);
  fprintf(stdout, "DW0=%08lX DW4D=%08lX\n", dw0, dw4d);

  return(0);
}


static int tests_nwreq87vlmconn_one(UI conn_value, UI label_value)
{
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 hdr[16];
  uint8 path[300];
  uint8 repl[0x180];
  TEST_NWFRAG16 reqfrags[2];
  TEST_NWFRAG16 replfrags[2];
  UI path_len;
  int rc;
  uint32 dw0;
  uint32 dw4d;

  if (tests_current_conn_dhandle(&connid, &dhandle))
    return(1);

  memset(hdr, 0, sizeof(hdr));
  hdr[0] = 6;
  hdr[1] = 0;
  hdr[2] = 0;
  tests_put_word_lh(hdr + 3, 0x0006);
  tests_put_dword_lh(hdr + 5, 0x00000004UL);

  path_len = tests_build_nwreq87_path_multi(path, dhandle, 0, 0, 1,
                                            "LOGIN.EXE", 0, 0);

  memset(repl, 0, sizeof(repl));
  memset(reqfrags, 0, sizeof(reqfrags));
  memset(replfrags, 0, sizeof(replfrags));

  tests_set_frag(&reqfrags[0], hdr, 9);
  tests_set_frag(&reqfrags[1], path, path_len);
  tests_set_frag(&replfrags[0], repl, 0x4d);
  tests_set_frag(&replfrags[1], repl + 0x4d, 0x100);

  fprintf(stdout, "TEST NWREQ87VLMCONN value=%u label=%u\n",
          conn_value, label_value);
  fprintf(stdout, "drive connid=%u dhandle=%u path_len=%u\n",
          connid, dhandle, path_len);
  fprintf(stdout, "VLM call: AX=0057 BX=0002 CX=%04X DX=0002 p1=0006 p2=0020 p3=0000\n",
          conn_value);
  tests_dump_bytes("HDR :", hdr, 9);
  tests_dump_bytes("PATH:", path, path_len > 48 ? 48 : path_len);

  rc = Net_Call_VLM_Raw(0x0057, 2, conn_value, 2,
                        reqfrags, replfrags,
                        6, 0x20, 0);

  fprintf(stdout, "RC=%04X\n", rc);
  tests_dump_bytes("REPL0 :", repl, 32);
  tests_dump_bytes("REPL4D:", repl + 0x4d, 32);

  dw0 = tests_get_dword_lh(repl);
  dw4d = tests_get_dword_lh(repl + 0x4d);
  fprintf(stdout, "DW0=%08lX DW4D=%08lX\n", dw0, dw4d);

  return(rc);
}

static int tests_nwreq87vlmconn(int argc, char *argv[])
{
  UI value;

  if (argc < 3) {
    fprintf(stdout, "Usage: TESTS NWREQ87VLMCONN value\n");
    fprintf(stdout, "Examples:\n");
    fprintf(stdout, "  TESTS NWREQ87VLMCONN 0\n");
    fprintf(stdout, "  TESTS NWREQ87VLMCONN 1\n");
    fprintf(stdout, "  TESTS NWREQ87VLMCONN 2\n");
    fprintf(stdout, "Hex is not parsed; use decimal values.\n");
    return(1);
  }

  value = (UI)atoi(argv[2]);
  return tests_nwreq87vlmconn_one(value, value);
}

static int tests_nwreq87vlmconnmatrix(void)
{
  UI values[10];
  int i;
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 flags = 0;
  int drive;

  drive = tests_get_current_drive();
  get_drive_info((uint8)drive, &connid, &dhandle, &flags);

  /*
   * Paged matrix: one screen per value.
   * Start with likely connection-reference candidates.
   */
  values[0] = 0;
  values[1] = 1;
  values[2] = 2;
  values[3] = 3;
  values[4] = (UI)connid;
  values[5] = (UI)dhandle;
  values[6] = (UI)drive;
  values[7] = (UI)(drive + 1);
  values[8] = 0x31;   /* low byte observed in AH=DC CX=3130 */
  values[9] = 0x3130; /* AH=DC returned CX */

  fprintf(stdout, "TEST NWREQ87VLMCONNMATRIX paged\n");
  fprintf(stdout, "current drive=%c: connid=%u dhandle=%u flags=%02X\n",
          'A' + drive, connid, dhandle, flags);
  tests_wait_key();

  for (i = 0; i < 10; i++) {
    tests_nwreq87vlmconn_one(values[i], (UI)i);
    if (i != 9)
      tests_wait_key();
  }

  return(0);
}


static UI tests_build_novell_handle_path(uint8 *buf, uint8 dhandle,
                                         uint16 dirbase, uint8 style,
                                         int count,
                                         char *c1, char *c2, char *c3)
{
  uint8 *p;
  int l;
  UI used;

  /*
   * DeveloperNet fillhan.o:
   *   memset(pathStruct, 0, 0x133)
   *   if handle != 0:
   *     byte[5] = 0
   *     word[1] = handle
   *     word[3] = 0
   *   else:
   *     byte[5] = 0xff
   *
   * _NWGETCOMPATHSTRUCTLENGTH reads component count at offset 6 and returns
   *   7 + sum(1 + componentLen)
   *
   * ncpdos16 87s6.c sends frag2 from struct start with length stored at +13c.
   */
  memset(buf, 0, 0x140);

  if (dhandle) {
    tests_put_word_lh(buf + 1, (uint16)dhandle);
    tests_put_word_lh(buf + 3, dirbase);
    buf[5] = style;
  } else {
    buf[5] = 0xff;
  }

  p = buf + 6;
  *p++ = (uint8)count;

  if (count > 0 && c1) {
    l = strlen(c1); if (l > 255) l = 255;
    *p++ = (uint8)l; memcpy(p, c1, l); p += l;
  }
  if (count > 1 && c2) {
    l = strlen(c2); if (l > 255) l = 255;
    *p++ = (uint8)l; memcpy(p, c2, l); p += l;
  }
  if (count > 2 && c3) {
    l = strlen(c3); if (l > 255) l = 255;
    *p++ = (uint8)l; memcpy(p, c3, l); p += l;
  }

  used = (UI)(p - buf);
  tests_put_word_lh(buf + 0x13c, used);
  return used;
}

static int tests_nwreq87vlmfull_one(char *label, uint8 connid,
                                    uint8 *hdr, uint8 *path_struct)
{
  uint8 repl[0x180];
  TEST_NWFRAG16 reqfrags[2];
  TEST_NWFRAG16 replfrags[2];
  UI path_len;
  int rc;
  uint32 dw0;
  uint32 dw4d;

  path_len = (UI)(path_struct[0x13c] | ((UI)path_struct[0x13d] << 8));

  memset(repl, 0, sizeof(repl));
  memset(reqfrags, 0, sizeof(reqfrags));
  memset(replfrags, 0, sizeof(replfrags));

  tests_set_frag(&reqfrags[0], hdr, 9);
  tests_set_frag(&reqfrags[1], path_struct, path_len);
  tests_set_frag(&replfrags[0], repl, 0x4d);
  tests_set_frag(&replfrags[1], repl + 0x4d, 0x100);

  fprintf(stdout, "\nCASE %s\n", label);
  fprintf(stdout, "path_len=%u\n", path_len);
  tests_dump_bytes("PATH:", path_struct, path_len > 64 ? 64 : path_len);

  rc = Net_Call_VLM_Raw(0x0057, 2, (UI)connid, 2,
                        reqfrags, replfrags,
                        6, 0x20, 0);

  fprintf(stdout, "RC=%04X\n", rc);
  tests_dump_bytes("REPL0 :", repl, 32);
  tests_dump_bytes("REPL4D:", repl + 0x4d, 32);
  dw0 = tests_get_dword_lh(repl);
  dw4d = tests_get_dword_lh(repl + 0x4d);
  fprintf(stdout, "DW0=%08lX DW4D=%08lX\n", dw0, dw4d);
  tests_wait_key();

  return rc;
}

static int tests_nwreq87vlmfull(void)
{
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 flags = 0;
  int drive;
  uint8 hdr[16];
  uint8 path[0x140];

  drive = tests_get_current_drive();
  if (get_drive_info((uint8)drive, &connid, &dhandle, &flags)) {
    fprintf(stdout, "get_drive_info failed\n");
    return(1);
  }

  memset(hdr, 0, sizeof(hdr));
  hdr[0] = 6;
  hdr[1] = 0;
  hdr[2] = 0;
  tests_put_word_lh(hdr + 3, 0x0006);
  tests_put_dword_lh(hdr + 5, 0x00000004UL);

  fprintf(stdout, "TEST NWREQ87VLMFULL Novell handle/path struct\n");
  fprintf(stdout, "drive=%c: connid=%u dhandle=%u flags=%02X\n",
          'A' + drive, connid, dhandle, flags);
  tests_dump_bytes("HDR:", hdr, 9);

  tests_build_novell_handle_path(path, dhandle, 0, 0, 1,
                                 "LOGIN.EXE", 0, 0);
  tests_nwreq87vlmfull_one("1 dhandle LOGIN.EXE", connid, hdr, path);

  tests_build_novell_handle_path(path, dhandle, 0, 0, 2,
                                 "PUBLIC", "LOGIN.EXE", 0);
  tests_nwreq87vlmfull_one("2 dhandle PUBLIC/LOGIN.EXE", connid, hdr, path);

  tests_build_novell_handle_path(path, 0, 0, 0, 2,
                                 "PUBLIC", "LOGIN.EXE", 0);
  tests_nwreq87vlmfull_one("3 handle0 PUBLIC/LOGIN.EXE", connid, hdr, path);

  return(0);
}


static uint16 tests_get_word_lh(uint8 *p)
{
  return (uint16)(p[0] | ((uint16)p[1] << 8));
}

static uint32 tests_get_dword_lh2(uint8 *p)
{
  return ((uint32)p[0] |
          ((uint32)p[1] << 8) |
          ((uint32)p[2] << 16) |
          ((uint32)p[3] << 24));
}

static int tests_c32type(void)
{
  uint8 out[32];
  int rc;
  uint16 ax, bx, cx, dx, si, dsreg, esreg;
  uint32 esi, ecx;

  memset(out, 0, sizeof(out));

  fprintf(stdout, "TEST Client32/NIOS probe ESI/ECX\n");
  fprintf(stdout, "16-bit OMF, manual 386 stores after INT 2F AX=D8C1\n\n");

  rc = C32_LoadNios_Probe(0xD8C1, out);

  ax    = tests_get_word_lh(out + 0);
  esi   = tests_get_dword_lh2(out + 2);
  ecx   = tests_get_dword_lh2(out + 6);
  bx    = tests_get_word_lh(out + 10);
  cx    = tests_get_word_lh(out + 12);
  dx    = tests_get_word_lh(out + 14);
  si    = tests_get_word_lh(out + 16);
  dsreg = tests_get_word_lh(out + 18);
  esreg = tests_get_word_lh(out + 20);

  fprintf(stdout, "C32 probe rc=%04X\n", rc);
  fprintf(stdout, "INT2F D8C1 result AX=%04X\n", ax);
  fprintf(stdout, "ESI=%08lX ECX=%08lX\n", esi, ecx);
  fprintf(stdout, "BX=%04X CX=%04X DX=%04X SI=%04X\n", bx, cx, dx, si);
  fprintf(stdout, "DS=%04X ES=%04X\n", dsreg, esreg);
  tests_dump_bytes("RAW:", out, 22);

  if (ax == 0)
    fprintf(stdout, "\nClient32/NIOS D8C1 reports success.\n");
  else
    fprintf(stdout, "\nClient32/NIOS D8C1 did not report success.\n");

  return(0);
}


static void tests_c32get_one(char *name)
{
  uint8 out[32];
  uint16 load_ax;
  uint32 esi, ecx;
  uint16 ret_ax, ret_dx;

  memset(out, 0, sizeof(out));
  C32_GetFunc_Probe(name, out);

  load_ax = tests_get_word_lh(out + 0);
  esi     = tests_get_dword_lh2(out + 2);
  ecx     = tests_get_dword_lh2(out + 6);
  ret_ax  = tests_get_word_lh(out + 10);
  ret_dx  = tests_get_word_lh(out + 12);

  fprintf(stdout, "\nFUNC %s\n", name);
  fprintf(stdout, "Load AX=%04X ESI=%08lX ECX=%08lX\n", load_ax, esi, ecx);
  fprintf(stdout, "Resolver returned DX:AX=%04X:%04X\n", ret_dx, ret_ax);
  tests_dump_bytes("RAW:", out, 18);
}

static int tests_c32getfunc(void)
{
  fprintf(stdout, "TEST C32GETFUNC\n");
  fprintf(stdout, "Resolve Client32 function addresses via NIOS resolver\n");

  tests_c32get_one("CLIENT32GetVersion");
  tests_c32get_one("COMPATNcpRequestReply");
  tests_c32get_one("CONNOpenByReference");
  tests_c32get_one("CONNClose");

  return(0);
}


static int tests_c32callver2(void)
{
  uint8 out[32];
  uint16 load_ax;
  uint16 res_off, res_seg;
  uint16 tramp_off, tramp_seg;
  uint16 fn_off, fn_seg;
  uint16 call_ax, call_dx, flags;

  memset(out, 0, sizeof(out));

  fprintf(stdout, "TEST C32CALLVER2\n");
  fprintf(stdout, "Call CLIENT32GetVersion through ECX NIOS trampoline\n\n");

  C32_CallVersion_Nios_Probe(out);

  load_ax   = tests_get_word_lh(out + 0);
  res_off   = tests_get_word_lh(out + 2);
  res_seg   = tests_get_word_lh(out + 4);
  tramp_off = tests_get_word_lh(out + 6);
  tramp_seg = tests_get_word_lh(out + 8);
  fn_off    = tests_get_word_lh(out + 10);
  fn_seg    = tests_get_word_lh(out + 12);
  call_ax   = tests_get_word_lh(out + 14);
  call_dx   = tests_get_word_lh(out + 16);
  flags     = tests_get_word_lh(out + 18);

  fprintf(stdout, "Load AX=%04X\n", load_ax);
  fprintf(stdout, "Resolver=%04X:%04X Trampoline=%04X:%04X\n",
          res_seg, res_off, tramp_seg, tramp_off);
  fprintf(stdout, "Function=%04X:%04X\n", fn_seg, fn_off);
  fprintf(stdout, "Call returned DX:AX=%04X:%04X FLAGS=%04X\n",
          call_dx, call_ax, flags);
  tests_dump_bytes("RAW:", out, 20);

  if (load_ax == 0 && (fn_off || fn_seg))
    fprintf(stdout, "\nNIOS trampoline path resolved successfully.\n");
  return(0);
}


static int tests_c32mapflat(void)
{
  uint8 out[32];
  char sample[32];
  uint16 load_ax, esi_off, esi_seg;
  uint16 map_lo, map_hi;
  uint16 unl_ax, unl_dx;

  strcpy(sample, "C32MAPFLAT");
  memset(out, 0, sizeof(out));

  fprintf(stdout, "TEST C32MAPFLAT\n");
  fprintf(stdout, "Probe d32wrap __MapLockFlat/__UnlockFlat via NIOS resolver\n");
  fprintf(stdout, "sample ptr=%04X:%04X len=%u text=%s\n",
          FP_SEG(sample), FP_OFF(sample), (UI)strlen(sample) + 1, sample);

  C32_MapLock_Probe(sample, (UI)strlen(sample) + 1, out);

  load_ax = tests_get_word_lh(out + 0);
  esi_off = tests_get_word_lh(out + 2);
  esi_seg = tests_get_word_lh(out + 4);
  map_lo  = tests_get_word_lh(out + 6);
  map_hi  = tests_get_word_lh(out + 8);
  unl_ax  = tests_get_word_lh(out + 10);
  unl_dx  = tests_get_word_lh(out + 12);

  fprintf(stdout, "Load AX=%04X Resolver=%04X:%04X\n",
          load_ax, esi_seg, esi_off);
  fprintf(stdout, "MapLock returned flat=%04X:%04X\n", map_hi, map_lo);
  fprintf(stdout, "Unlock returned DX:AX=%04X:%04X\n", unl_dx, unl_ax);
  tests_dump_bytes("RAW:", out, 14);

  if (load_ax == 0 && (map_lo || map_hi))
    fprintf(stdout, "\nMapLockFlat appears to work.\n");
  else
    fprintf(stdout, "\nMapLockFlat did not return a flat pointer.\n");

  return(0);
}


static int tests_nwreq87c32raw_one(UI connLo, UI connHi, UI label)
{
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 flags = 0;
  int drive;
  uint8 hdr[16];
  uint8 path[0x140];
  uint8 rep0[0x60];
  uint8 rep1[0x110];
  uint8 out[32];
  UI path_len;
  uint16 load_ax, res_off, res_seg, tramp_off, tramp_seg, fn_off, fn_seg;
  uint16 ret_ax, ret_dx, act_lo, act_hi;

  drive = tests_get_current_drive();
  get_drive_info((uint8)drive, &connid, &dhandle, &flags);

  memset(hdr, 0, sizeof(hdr));
  hdr[0] = 6;
  hdr[1] = 0;
  hdr[2] = 0;
  tests_put_word_lh(hdr + 3, 0x0006);
  tests_put_dword_lh(hdr + 5, 0x00000004UL);

  path_len = tests_build_novell_handle_path(path, dhandle, 0, 0, 1,
                                            "LOGIN.EXE", 0, 0);

  memset(rep0, 0, sizeof(rep0));
  memset(rep1, 0, sizeof(rep1));
  memset(out, 0, sizeof(out));

  fprintf(stdout, "\nTEST NWREQ87C32RAW label=%u conn32=%04X:%04X\n",
          label, connHi, connLo);
  fprintf(stdout, "drive=%c: connid=%u dhandle=%u flags=%02X path_len=%u\n",
          'A' + drive, connid, dhandle, flags, path_len);
  tests_dump_bytes("HDR :", hdr, 9);
  tests_dump_bytes("PATH:", path, path_len > 48 ? 48 : path_len);

  C32_NCP87_Raw_Probe(connLo, connHi,
                      hdr, 9,
                      path, path_len,
                      rep0, 0x4d,
                      rep1, 0x100,
                      out);

  load_ax   = tests_get_word_lh(out + 0);
  res_off   = tests_get_word_lh(out + 2);
  res_seg   = tests_get_word_lh(out + 4);
  tramp_off = tests_get_word_lh(out + 6);
  tramp_seg = tests_get_word_lh(out + 8);
  fn_off    = tests_get_word_lh(out + 10);
  fn_seg    = tests_get_word_lh(out + 12);
  ret_ax    = tests_get_word_lh(out + 14);
  ret_dx    = tests_get_word_lh(out + 16);
  act_lo    = tests_get_word_lh(out + 18);
  act_hi    = tests_get_word_lh(out + 20);

  fprintf(stdout, "Load AX=%04X Resolver=%04X:%04X Tramp=%04X:%04X\n",
          load_ax, res_seg, res_off, tramp_seg, tramp_off);
  fprintf(stdout, "Function COMPAT=%04X:%04X\n", fn_seg, fn_off);
  fprintf(stdout, "Return DX:AX=%04X:%04X actual=%04X:%04X\n",
          ret_dx, ret_ax, act_hi, act_lo);
  tests_dump_bytes("OUT :", out, 22);
  tests_dump_bytes("REP0:", rep0, 64);
  tests_dump_bytes("REP1:", rep1, 64);

  return(0);
}

static int tests_nwreq87c32raw(int argc, char *argv[])
{
  UI lo = 0;
  UI hi = 0;

  if (argc > 2)
    lo = (UI)atoi(argv[2]);
  if (argc > 3)
    hi = (UI)atoi(argv[3]);

  return tests_nwreq87c32raw_one(lo, hi, 0);
}

static int tests_nwreq87c32matrix(void)
{
  UI vals[6];
  int i;

  vals[0] = 0;
  vals[1] = 1;
  vals[2] = 2;
  vals[3] = 3;
  vals[4] = 0x31;
  vals[5] = 0x3130;

  fprintf(stdout, "TEST NWREQ87C32MATRIX paged\n");
  tests_wait_key();

  for (i = 0; i < 6; i++) {
    tests_nwreq87c32raw_one(vals[i], 0, (UI)i);
    if (i != 5)
      tests_wait_key();
  }
  return(0);
}



static void tests_set_reg_word(uint8 *r, int off, UI val)
{
  tests_put_word_lh(r + off, (uint16)val);
}

static UI tests_get_reg_word(uint8 *r, int off)
{
  return (UI)(r[off] | ((UI)r[off + 1] << 8));
}

static void tests_dump_vlm_regs(char *title, uint8 *r)
{
  fprintf(stdout, "%s SI=%04X DS=%04X DI=%04X ES=%04X AX=%04X BX=%04X CX=%04X DX=%04X\n",
          title,
          tests_get_reg_word(r, 0),
          tests_get_reg_word(r, 2),
          tests_get_reg_word(r, 4),
          tests_get_reg_word(r, 6),
          tests_get_reg_word(r, 8),
          tests_get_reg_word(r, 10),
          tests_get_reg_word(r, 12),
          tests_get_reg_word(r, 14));
}

static int tests_c32primref(void)
{
  uint8 regs[16];
  int rc;
  UI primref;

  memset(regs, 0, sizeof(regs));

  /*
   * DeveloperNet dgtprmci.o / NWCGETPRIMCONNREF:
   *
   *   regblk.AX = 1
   *   NWCVLMREQ(flags=0, regblk, p1=4, p2=43h, p3=0)
   *   if rc==0:
   *     primaryConnRef = regblk.CX
   */
  tests_set_reg_word(regs, 8, 1);  /* AX */

  fprintf(stdout, "TEST C32PRIMREF / NWCGETPRIMCONNREF VLM step\n");
  tests_dump_vlm_regs("REG in :", regs);
  fprintf(stdout, "Call NWCVLMREQ flags=0 p1=0004 p2=0043 p3=0000\n");

  rc = Net_Call_NWCVLMREQ(0, regs, 0x0004, 0x0043, 0);

  fprintf(stdout, "NWCVLMREQ rc=%04X\n", rc);
  tests_dump_vlm_regs("REG out:", regs);

  primref = tests_get_reg_word(regs, 12); /* CX */
  fprintf(stdout, "primary connection reference candidate CX=%04X decimal=%u\n",
          primref, primref);

  if (rc == 0 && primref != 0) {
    fprintf(stdout, "\nNext test:\n");
    fprintf(stdout, "  TESTS NWREQ87C32RAW %u 0\n", primref);
  }

  return(0);
}


int func_tests(int argc, char *argv[], int mode)
{
  if (argc >= 2) {
    if (tests_same_arg(argv[1], "NETCALL"))
      return tests_netcall();

    if (tests_same_arg(argv[1], "E300") || tests_same_arg(argv[1], "NETCALLE300"))
      return tests_netcall_e300();

    if (tests_same_arg(argv[1], "C32TYPE"))
      return tests_c32type();

    if (tests_same_arg(argv[1], "C32GETFUNC"))
      return tests_c32getfunc();

    if (tests_same_arg(argv[1], "C32CALLVER2"))
      return tests_c32callver2();

    if (tests_same_arg(argv[1], "C32MAPFLAT"))
      return tests_c32mapflat();

    if (tests_same_arg(argv[1], "NWREQ87C32RAW"))
      return tests_nwreq87c32raw(argc, argv);

    if (tests_same_arg(argv[1], "C32PRIMREF"))
      return tests_c32primref();

    if (tests_same_arg(argv[1], "NWREQ87C32MATRIX"))
      return tests_nwreq87c32matrix();

    if (tests_same_arg(argv[1], "NWREQ87"))
      return tests_nwreq87(argc, argv);

    if (tests_same_arg(argv[1], "NWREQ87VLM"))
      return tests_nwreq87vlm(argc, argv);

    if (tests_same_arg(argv[1], "NWREQ87VLMMATRIX"))
      return tests_nwreq87vlmmatrix(argc, argv);

    if (tests_same_arg(argv[1], "NWREQ87VLMCASE"))
      return tests_nwreq87vlmcase(argc, argv);

    if (tests_same_arg(argv[1], "NWREQ87VLMCONN"))
      return tests_nwreq87vlmconn(argc, argv);

    if (tests_same_arg(argv[1], "NWREQ87VLMCONNMATRIX"))
      return tests_nwreq87vlmconnmatrix();

    if (tests_same_arg(argv[1], "NWREQ87VLMFULL"))
      return tests_nwreq87vlmfull();

    if (tests_same_arg(argv[1], "NWREQ87VLMREG"))
      return tests_nwreq87vlmreg(argc, argv);

    if (tests_same_arg(argv[1], "NCPF2"))
      return tests_ncpf2(argc, argv);

    if (tests_same_arg(argv[1], "OLD"))
      return tests_old(argc - 1, argv + 1, mode);

    if (tests_same_arg(argv[1], "/?") || tests_same_arg(argv[1], "-?") ||
        tests_same_arg(argv[1], "?")) {
      tests_usage();
      return(0);
    }
  }

  /*
   * Default and unknown arguments keep the historical nwtests.c behavior.
   */
  return tests_old(argc, argv, mode);
}

