#include #include #include #include #include #include "builtins.h" #include "dos2linux.h" #include "cpu.h" #include "emu.h" #include "int.h" #include "utilities.h" #include "zalloc.h" /* hope 2K is enough */ #define LOWMEM_POOL_SIZE 0x800 #define BUF_SIZE LOWMEM_POOL_SIZE static char scratch[BUF_SIZE]; static char scratch2[BUF_SIZE]; static char *builtin_name; unsigned char _osmajor; unsigned char _osminor; int com_errno; static MemPool mp; static char *lowmem_pool; int com_vsprintf(char *str, char *format, va_list ap) { char *s = str; int i, size; size = vsnprintf(scratch, BUF_SIZE, format, ap); for (i=0; i < size; i++) { if (scratch[i] == '\n') *s++ = '\r'; *s++ = scratch[i]; } *s = 0; return s - str; } int com_sprintf(char *str, char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = com_vsprintf(str, format, ap); va_end(ap); return ret; } int com_vfprintf(int dosfilefd, char *format, va_list ap) { int size; size = com_vsprintf(scratch2, format, ap); if (!size) return 0; return com_doswrite(dosfilefd, scratch2, size); } int com_vprintf(char *format, va_list ap) { return com_vfprintf(1, format, ap); } int com_fprintf(int dosfilefd, char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = com_vfprintf(dosfilefd, format, ap); va_end(ap); return ret; } int com_printf(char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = com_vfprintf(1, format, ap); va_end(ap); return ret; } int com_puts(char *s) { return com_printf("%s", s); } char *skip_white_and_delim(char *s, int delim) { while (*s && isspace(*s)) s++; if (*s == delim) s++; while (*s && isspace(*s)) s++; return s; } void call_msdos(void) { do_intr_call_back(0x21); } int com_doswrite(int dosfilefd, char *buf32, int size) { char *s; if (!size) return 0; com_errno = 8; s = lowmem_alloc(size); if (!s) return -1; memcpy(s, buf32, size); LWORD(ecx) = size; LWORD(ebx) = dosfilefd; LWORD(ds) = FP_SEG32(s); LWORD(edx) = FP_OFF32(s); LWORD(eax) = 0x4000; /* write handle */ call_msdos(); /* call MSDOS */ lowmem_free(s, size); if (LWORD(eflags) & CF) { com_errno = LWORD(eax); return -1; } return LWORD(eax); } int com_dosread(int dosfilefd, char *buf32, int size) { char *s; if (!size) return 0; com_errno = 8; s = lowmem_alloc(size); if (!s) return -1; LWORD(ecx) = size; LWORD(ebx) = dosfilefd; LWORD(ds) = FP_SEG32(s); LWORD(edx) = FP_OFF32(s); LWORD(eax) = 0x3f00; /* read handle */ call_msdos(); /* call MSDOS */ if (LWORD(eflags) & CF) { com_errno = LWORD(eax); lowmem_free(s, size); return -1; } memcpy(buf32, s, LWORD(eax)); lowmem_free(s, size); return LWORD(eax); } char *com_getenv(char *keyword) { struct PSP *psp = COM_PSP_ADDR; char *env = SEG2LINEAR(psp->envir_frame); char key[128]; int len; len = strlen(keyword); memcpy(key, keyword, len+1); strupr(key); while (*env) { if (!strncmp(key, env, len) && (env[len] == '=')) { return env + len + 1; } env += strlen(env) + 1; } return 0; } static int load_and_run_DOS_program(char *command, char *cmdline, int quit) { struct param4a { unsigned short envframe; FAR_PTR cmdline; FAR_PTR fcb1; FAR_PTR fcb2; } __attribute__((packed)); struct PSP *psp = COM_PSP_ADDR; char *cmd, *cmdl; struct param4a *pa4; pa4 = (struct param4a *)lowmem_alloc(sizeof(struct param4a)); if (!pa4) return -1; cmd = com_strdup(command); if (!cmd) { com_errno = 8; return -1; } cmdl = lowmem_alloc(256); if (!cmdl) { com_strfree(cmd); com_errno = 8; return -1; } if (!cmdline) cmdline = ""; snprintf(cmdl, 256, "%c %s\r", (char)(strlen(cmdline)+1), cmdline); /* prepare param block */ pa4->envframe = 0; // ctcb->envir_frame; pa4->cmdline = MK_FP(FP_SEG32(cmdl), FP_OFF32(cmdl)); pa4->fcb1 = MK_FP(FP_SEG32(psp->FCB1), FP_OFF32(psp->FCB1)); pa4->fcb2 = MK_FP(FP_SEG32(psp->FCB2), FP_OFF32(psp->FCB2)); LWORD(es) = FP_SEG32(pa4); LWORD(ebx) = FP_OFF32(pa4); /* path of programm to load */ LWORD(ds) = FP_SEG32(cmd); LWORD(edx) = FP_OFF32(cmd); LWORD(eax) = 0x4b00; if (quit) fake_call_to(BIOSSEG, 0xfff0); real_run_int(0x21); return 0; } int com_system(char *command, int quit) { char *program = com_getenv("COMSPEC"); char cmdline[256]; snprintf(cmdline, sizeof(cmdline), "/C %s", command); if (!program) program = "\\COMMAND.COM"; return load_and_run_DOS_program(program, cmdline, quit); } static char * com_dosallocmem(int size) { /* size in bytes */ LWORD(ebx) = (size +15) >> 4; HI(ax) = 0x48; call_msdos(); /* call MSDOS */ if (LWORD(eflags) & CF) { com_errno = LWORD(eax); error("lowmem_alloc failed for %i\n", size); return NULL; } return (char *)SEG2LINEAR(LWORD(eax)); } #ifdef NEED_DOSFREEMEM static void com_dosfreemem(char *p) { if (!p) { error("Attempt to free NULL\n"); return; } if (FP_OFF32(p)) { error("Attempt to free addr %p not from malloc\n", p); return; } LWORD(es) = FP_SEG32(p); HI(ax) = 0x49; call_msdos(); /* call MSDOS */ if (LWORD(eflags) & CF) { error("lowmem_free failed for %p\n", p); com_errno = LWORD(eax); } return; } #endif char * lowmem_alloc(int size) { char *ptr = znalloc(&mp, size); if (!ptr) { error("builtin %s OOM\n", builtin_name); leavedos(86); } if (size > 1024) { /* well, the lowmem heap is limited, let's be polite! */ error("builtin %s requests too much of a heap: 0x%x\n", builtin_name); } return ptr; } void lowmem_free(char *p, int size) { return zfree(&mp, p, size); } char * com_strdup(char *s) { struct lowstring *p; int len = strlen(s); if (len > 254) { error("lowstring too long: %i bytes. builtin: %s\n", len, builtin_name); len = 254; } p = (void *)lowmem_alloc(len + 1 + sizeof(struct lowstring)); if (!p) return 0; p->len = len; memcpy(p->s, s, len + 1); p->s[len] = 0; return p->s; } void com_strfree(char *s) { struct lowstring *p = (void *)(s - 1); lowmem_free((char *)p, p->len); } static int com_argparse(char *s, char **argvx, int maxarg) { int mode = 0; int argcx = 0; char delim = 0; char *p; p = strchr(s+1, '\r'); if (p && ((p - (s+1)) < 128)) { *p = 0; } else s[1+(unsigned char)s[0]] = 0; s++; /* transform: * dir/p to dir /p * cd\ to cd \ * cd.. to cd .. */ p = s + strcspn(s, "\\/. "); if (*p && *p != ' ' && (*p != '.' || (p[1] && strchr("\\/.", p[1])))) { memmove(p+1, p, s [-1] - (p - s) + 1/*NUL*/); *p = ' '; s[-1]++; /* update length */ } maxarg --; for ( ; *s; s++) { if (!mode) { if (*s > ' ') { mode = 1; switch (*s) { case '"': case '\'': delim = *s; mode = 2; argvx[argcx++] = s+1; break; default: argvx[argcx++] = s; break; } if (argcx >= maxarg) break; } } else if (mode == 1) { if (*s <= ' ') { mode = 0; *s = 0; } } else { if (*s == delim) { *s = 0; mode = 1; } } } argvx[argcx] = 0; return(argcx); } int com_dosgetdrive(void) { HI(ax) = 0x19; call_msdos(); /* call MSDOS */ return LO(ax); /* 0=A, 1=B, ... */ } int com_dossetdrive(int drive) { HI(ax) = 0x0e; LO(dx) = drive; /* 0=A, 1=B, ... */ call_msdos(); /* call MSDOS */ return LO(ax); /* number of available logical drives */ } int com_dossetcurrentdir(char *path) { /*struct com_starter_seg *ctcb = owntcb->params;*/ char *s = com_strdup(path); com_errno = 8; if (!s) return -1; HI(ax) = 0x3b; LWORD(ds) = FP_SEG32 (s) /*COM_SEG*/; LWORD(edx) = FP_OFF32 (s) /*COM_OFFS_OF(s)*/; call_msdos(); /* call MSDOS */ com_strfree(s); if (LWORD(eflags) & CF) return -1; return 0; } struct REGPACK regs_to_regpack(struct vm86_regs *regs) { struct REGPACK regpack; regpack.r_ax = regs->eax; regpack.r_bx = regs->ebx; regpack.r_cx = regs->ecx; regpack.r_dx = regs->edx; regpack.r_bp = regs->ebp; regpack.r_si = regs->esi; regpack.r_di = regs->edi; regpack.r_ds = regs->ds; regpack.r_es = regs->es; regpack.r_flags = regs->eflags; return regpack; } struct vm86_regs regpack_to_regs(struct REGPACK *regpack) { struct vm86_regs regs = _regs; regs.eax = regpack->r_ax; regs.ebx = regpack->r_bx; regs.ecx = regpack->r_cx; regs.edx = regpack->r_dx; regs.ebp = regpack->r_bp; regs.esi = regpack->r_si; regs.edi = regpack->r_di; regs.ds = regpack->r_ds; regs.es = regpack->r_es; regs.eflags = regpack->r_flags; return regs; } void com_intr(int intno, struct REGPACK *regpack) { struct vm86_regs saved_regs = _regs; _regs = regpack_to_regs(regpack); do_intr_call_back(intno); *regpack = regs_to_regpack(&_regs); _regs = saved_regs; } void dos_helper_r(struct REGPACK *regpack) { struct vm86_regs saved_regs = _regs; _regs = regpack_to_regs(regpack); dos_helper(); *regpack = regs_to_regpack(&_regs); _regs = saved_regs; } int com_int86x(int intno, union com_REGS *inregs, union com_REGS *outregs, struct SREGS *segregs) { struct vm86_regs saved_regs = _regs; LWORD(eax) = inregs->x.ax; LWORD(ebx) = inregs->x.bx; LWORD(ecx) = inregs->x.cx; LWORD(edx) = inregs->x.dx; LWORD(esi) = inregs->x.si; LWORD(edi) = inregs->x.di; LWORD(eflags) = inregs->x.flags; if (segregs) { LWORD(ds) = segregs->ds; LWORD(es) = segregs->es; } do_intr_call_back(intno); outregs->x.ax = LWORD(eax); outregs->x.bx = LWORD(ebx); outregs->x.cx = LWORD(ecx); outregs->x.dx = LWORD(edx); outregs->x.si = LWORD(esi); outregs->x.di = LWORD(edi); outregs->x.flags = LWORD(eflags); if (segregs) { segregs->ds = LWORD(ds); segregs->es = LWORD(es); } outregs->x.cflag = LWORD(eflags) & CF; _regs = saved_regs; return LWORD(eax); } int com_int86(int intno, union com_REGS *inregs, union com_REGS *outregs) { return com_int86x(intno, inregs, outregs, 0); } static struct com_program_entry *com_program_list = 0; static struct com_program_entry *find_com_program(char *name) { struct com_program_entry *com = com_program_list; while (com) { if (!strcmp(com->name, name)) return com; com = com->next; } return 0; } void register_com_program(char *name, com_program_type *program) { struct com_program_entry *com; if ((com = find_com_program(name)) == 0) { com = malloc(sizeof(struct com_program_entry)); if (!com) return; com->next = com_program_list; com_program_list = com; } com->name = name; com->program = program; } static char *com_getarg0(void) { char *env = SEG2LINEAR(COM_PSP_ADDR->envir_frame); return memchr(env, 1, 0x10000) + 2; } static void zpanic(const char *ctl, ...) { error(ctl); error("\nzalloc failure for builtin %s\n", builtin_name); leavedos(85); } int commands_plugin_inte6(void) { #define MAX_ARGS 63 char *args[MAX_ARGS + 1]; struct PSP *psp; struct MCB *mcb; struct com_program_entry *com; int argc; psp = COM_PSP_ADDR; mcb = SEG2LINEAR(COM_PSP_SEG - 1); if (!(lowmem_pool = com_dosallocmem(LOWMEM_POOL_SIZE))) { error("Unable to allocate memory pool\n"); error("You should upgrade generic.com, lredir.com and so on\n"); return 0; } zinitPool(&mp, "lowmem_pool", zpanic, znot, lowmem_pool, LOWMEM_POOL_SIZE); zclearPool(&mp); /* first parse commandline */ args[0] = strlower(com_strdup(com_getarg0())); argc = com_argparse(&psp->cmdline_len, &args[1], MAX_ARGS - 1) + 1; builtin_name = strlower(com_strdup(mcb->name)); builtin_name[8] = 0; /* now set the DOS version */ HI(ax) = 0x30; call_msdos(); _osmajor = LO(ax); _osminor = HI(ax); com = find_com_program(builtin_name); if (com) { com->program(argc, args); } else { error("inte6: unknown builtin: %s\n",builtin_name); } /* * NOTE: We cannot free the lowmem pool here or "unix -e" will fail. * We leave that responsibility to DOS - it will free the mem when * generic.S exits. */ return 1; }