diff --git a/kern.c b/kern.c index 3d3aa77..31ab335 100644 --- a/kern.c +++ b/kern.c @@ -179,6 +179,51 @@ int KERN_C_CALL Net_Call_F2_C(unsigned int function, return (int)(ret_ax & 0x00ff); } +/* + * Fully generic INT 21h register test wrapper. + * Used for reproducing the NWCREQUEST/NWCSHELLREQ register calls seen in + * DeveloperNet 1997 clndos16.lib. + */ +int KERN_C_CALL Net_Call_F2X_C(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; + unsigned int ret_ax; + + memset(&inregs, 0, sizeof(inregs)); + memset(&outregs, 0, sizeof(outregs)); + memset(&segregs, 0, sizeof(segregs)); + net_call_c_clear(); + + 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_save_in(&inregs, &segregs, req, repl); + + int86x(0x21, &inregs, &outregs, &segregs); + + ret_ax = outregs.x.ax & 0x00ff; + if (ret_ax) + ret_ax |= 0x8900; + + net_call_c_save_out(&outregs, ret_ax); + + return (int)(ret_ax & 0x00ff); +} + + 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", diff --git a/kern.h b/kern.h index f6eae43..aa1aa86 100644 --- a/kern.h +++ b/kern.h @@ -18,6 +18,8 @@ extern int KERN_CALL Net_Call_CX(UI func, UI bx, UI cx, UI dx, void *req, void *repl); extern int KERN_CALL Net_Call_F2_C(UI function, UI req_len, UI repl_len, void *req, void *repl); +extern int KERN_CALL Net_Call_F2X_C(UI ax, UI bx, UI cx, UI dx, + void *req, void *repl); extern void KERN_CALL Net_Call_C_Dump(void); extern UI KERN_CALL Net_Call_C_GetDebug(UI idx); diff --git a/nwtests.c b/nwtests.c index 292736f..5175474 100644 --- a/nwtests.c +++ b/nwtests.c @@ -21,7 +21,7 @@ static int tests_same_arg(char *a, char *b) static void tests_usage(void) { - fprintf(stdout, "Usage: TESTS [OLD|NETCALL|E300|NCPF2 [file] [REPLY]]\n"); + fprintf(stdout, "Usage: TESTS [OLD|NETCALL|E300|NCPF2 [file]|NWREQ87 [file]]\n"); } static int tests_netcall(void) @@ -300,6 +300,132 @@ static int tests_ncpf2(int argc, char *argv[]) } +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); +} + + int func_tests(int argc, char *argv[], int mode) { if (argc >= 2) { @@ -309,6 +435,9 @@ int func_tests(int argc, char *argv[], int mode) if (tests_same_arg(argv[1], "E300") || tests_same_arg(argv[1], "NETCALLE300")) return tests_netcall_e300(); + if (tests_same_arg(argv[1], "NWREQ87")) + return tests_nwreq87(argc, argv); + if (tests_same_arg(argv[1], "NCPF2")) return tests_ncpf2(argc, argv);