FreeDOS/kernel/inthndlr.c

2077 lines
55 KiB
C
Raw Normal View History

/****************************************************************/
/* */
/* inthndlr.c */
/* */
/* Interrupt Handler and Function dispatcher for Kernel */
/* */
/* Copyright (c) 1995 */
/* Pasquale J. Villani */
/* All Rights Reserved */
/* */
/* This file is part of DOS-C. */
/* */
/* DOS-C 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, or (at your option) any later version. */
/* */
/* DOS-C 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 DOS-C; see the file COPYING. If not, */
/* write to the Free Software Foundation, 675 Mass Ave, */
/* Cambridge, MA 02139, USA. */
/* */
/****************************************************************/
#define MAIN
#include "portab.h"
#include "globals.h"
#include "nls.h"
#ifdef VERSION_STRINGS
BYTE *RcsId =
"$Id$";
#endif
#ifdef TSC
STATIC VOID StartTrace(VOID);
static bTraceNext = FALSE;
#endif
#if 0 /* Very suspicious, passing structure by value??
Deactivated -- 2000/06/16 ska */
/* Special entry for far call into the kernel */
#pragma argsused
VOID FAR int21_entry(iregs UserRegs)
{
int21_handler(UserRegs);
}
#endif
/* Structures needed for int 25 / int 26 */
struct HugeSectorBlock {
ULONG blkno;
WORD nblks;
BYTE FAR *buf;
};
/* Normal entry. This minimizes user stack usage by avoiding local */
/* variables needed for the rest of the handler. */
/* this here works on the users stack !! and only very few functions
are allowed */
VOID ASMCFUNC int21_syscall(iregs FAR * irp)
{
Int21AX = irp->AX;
switch (irp->AH)
{
/* Set Interrupt Vector */
case 0x25:
setvec(irp->AL, (intvec)MK_FP(irp->DS, irp->DX));
break;
/* DosVars - get/set dos variables */
case 0x33:
switch (irp->AL)
{
/* Set Ctrl-C flag; returns dl = break_ena */
case 0x01:
break_ena = irp->DL & 1;
/* fall through */
/* Get Ctrl-C flag */
case 0x00:
irp->DL = break_ena;
break;
case 0x02: /* andrew schulman: get/set extended control break */
{
UBYTE tmp = break_ena;
break_ena = irp->DL & 1;
irp->DL = tmp;
}
break;
/* Get Boot Drive */
case 0x05:
irp->DL = BootDrive;
break;
/* Get (real) DOS-C version */
case 0x06:
irp->BL = os_major;
irp->BH = os_minor;
irp->DL = rev_number;
irp->DH = version_flags; /* bit3:runs in ROM,bit 4: runs in HMA */
break;
case 0x03: /* DOS 7 does not set AL */
case 0x07: /* neither here */
default: /* set AL=0xFF as error, NOT carry */
irp->AL = 0xff;
break;
/* set FreeDOS returned version for int 21.30 from BX */
case 0xfc: /* 0xfc ... 0xff are FreeDOS extensions */
os_setver_major = irp->BL;
os_setver_minor = irp->BH;
break;
/* Toggle DOS-C rdwrblock trace dump */
#ifdef DEBUG
case 0xfd:
bDumpRdWrParms = !bDumpRdWrParms;
break;
#endif
/* Toggle DOS-C syscall trace dump */
#ifdef DEBUG
case 0xfe:
bDumpRegs = !bDumpRegs;
break;
#endif
/* Get DOS-C release string pointer */
case 0xff:
irp->DX = FP_SEG(os_release);
irp->AX = FP_OFF(os_release);
}
break;
/* Get Interrupt Vector */
case 0x35:
{
intvec p = getvec(irp->AL);
irp->ES = FP_SEG(p);
irp->BX = FP_OFF(p);
break;
}
/* Set PSP */
case 0x50:
cu_psp = irp->BX;
break;
/* Get PSP */
case 0x51:
/* UNDOCUMENTED: return current psp */
case 0x62:
irp->BX = cu_psp;
/* Normal DOS function - DO NOT ARRIVE HERE */
/* default: */
}
}
#ifdef WITHFAT32
/* DOS 7.0+ FAT32 extended functions */
int int21_fat32(lregs *r)
{
COUNT rc;
switch (r->AL)
{
/* Get extended drive parameter block */
case 0x02:
{
struct dpb FAR *dpb;
struct xdpbdata FAR *xddp;
if (r->CX < sizeof(struct xdpbdata))
return DE_INVLDBUF;
dpb = GetDriveDPB(r->DL, &rc);
if (rc != SUCCESS)
return rc;
/* hazard: no error checking! */
flush_buffers(dpb->dpb_unit);
dpb->dpb_flags = M_CHANGED; /* force reread of drive BPB/DPB */
if (media_check(dpb) < 0)
return DE_INVLDDRV;
xddp = MK_FP(r->ES, r->DI);
fmemcpy(&xddp->xdd_dpb, dpb, sizeof(struct dpb));
xddp->xdd_dpbsize = sizeof(struct dpb);
/* if it doesn't look like an extended DPB, fill in those fields */
if (!ISFAT32(dpb) && dpb->dpb_xsize != dpb->dpb_size)
{
xddp->xdd_dpb.dpb_nfreeclst_un.dpb_nfreeclst_st.dpb_nfreeclst_hi =
(dpb->dpb_nfreeclst == 0xFFFF ? 0xFFFF : 0);
dpb16to32(&xddp->xdd_dpb);
xddp->xdd_dpb.dpb_xfatsize = dpb->dpb_fatsize;
xddp->xdd_dpb.dpb_xcluster = (dpb->dpb_cluster == 0xFFFF ?
0xFFFFFFFFuL : dpb->dpb_cluster);
}
break;
}
/* Get extended free drive space */
case 0x03:
{
struct xfreespace FAR *xfsp = MK_FP(r->ES, r->DI);
if (r->CX < sizeof(struct xfreespace))
return DE_INVLDBUF;
rc = DosGetExtFree(MK_FP(r->DS, r->DX), xfsp);
if (rc != SUCCESS)
return rc;
xfsp->xfs_datasize = sizeof(struct xfreespace);
xfsp->xfs_version.actual = 0;
break;
}
/* Set DPB to use for formatting */
case 0x04:
{
struct xdpbforformat FAR *xdffp = MK_FP(r->ES, r->DI);
struct dpb FAR *dpb;
if (r->CX < sizeof(struct xdpbforformat))
{
return DE_INVLDBUF;
}
dpb = GetDriveDPB(r->DL, &rc);
if (rc != SUCCESS)
return rc;
xdffp->xdff_datasize = sizeof(struct xdpbforformat);
xdffp->xdff_version.actual = 0;
switch ((UWORD) xdffp->xdff_function)
{
case 0x00:
{
ULONG nfreeclst = xdffp->xdff_f.setdpbcounts.nfreeclst;
ULONG cluster = xdffp->xdff_f.setdpbcounts.cluster;
if (ISFAT32(dpb))
{
if ((dpb->dpb_xfsinfosec == 0xffff
&& (nfreeclst != 0 || cluster != 0))
|| nfreeclst == 1 || nfreeclst > dpb->dpb_xsize
|| cluster == 1 || cluster > dpb->dpb_xsize)
{
return DE_INVLDPARM;
}
dpb->dpb_xnfreeclst = nfreeclst;
dpb->dpb_xcluster = cluster;
write_fsinfo(dpb);
}
else
{
if ((unsigned)nfreeclst == 1 || (unsigned)nfreeclst > dpb->dpb_size ||
(unsigned)cluster == 1 || (unsigned)cluster > dpb->dpb_size)
{
return DE_INVLDPARM;
}
dpb->dpb_nfreeclst = (UWORD)nfreeclst;
dpb->dpb_cluster = (UWORD)cluster;
}
break;
}
case 0x01:
{
ddt *pddt = getddt(r->DL);
fmemcpy(&pddt->ddt_bpb, xdffp->xdff_f.rebuilddpb.bpbp,
sizeof(bpb));
}
case 0x02:
{
rebuild_dpb:
/* hazard: no error checking! */
flush_buffers(dpb->dpb_unit);
dpb->dpb_flags = M_CHANGED;
if (media_check(dpb) < 0)
return DE_INVLDDRV;
break;
}
case 0x03:
{
struct buffer FAR *bp;
bpb FAR *bpbp;
DWORD newmirroring =
xdffp->xdff_f.setmirroring.newmirroring;
if (newmirroring != -1
&& (ISFAT32(dpb)
&& (newmirroring & ~(0xf | 0x80))))
{
return DE_INVLDPARM;
}
xdffp->xdff_f.setmirroring.oldmirroring =
(ISFAT32(dpb) ? dpb->dpb_xflags : 0);
if (newmirroring != -1 && ISFAT32(dpb))
{
bp = getblock(1, dpb->dpb_unit);
bp->b_flag &= ~(BFR_DATA | BFR_DIR | BFR_FAT);
bp->b_flag |= BFR_VALID | BFR_DIRTY;
bpbp = (bpb FAR *) & bp->b_buffer[BT_BPB];
bpbp->bpb_xflags = (UWORD)newmirroring;
}
goto rebuild_dpb;
}
case 0x04:
{
struct buffer FAR *bp;
bpb FAR *bpbp;
DWORD rootclst = xdffp->xdff_f.setroot.newrootclst;
if (!ISFAT32(dpb)
|| (rootclst != -1
&& (rootclst == 1
|| (ULONG)rootclst > dpb->dpb_xsize)))
{
return DE_INVLDPARM;
}
xdffp->xdff_f.setroot.oldrootclst = dpb->dpb_xrootclst;
if (rootclst != -1)
{
bp = getblock(1, dpb->dpb_unit);
bp->b_flag &= ~(BFR_DATA | BFR_DIR | BFR_FAT);
bp->b_flag |= BFR_VALID | BFR_DIRTY;
bpbp = (bpb FAR *) & bp->b_buffer[BT_BPB];
bpbp->bpb_xrootclst = rootclst;
}
goto rebuild_dpb;
}
}
break;
}
/* Extended absolute disk read/write */
/* TODO(vlp) consider using of the 13-14th bits of SI */
case 0x05:
{
struct HugeSectorBlock FAR *SectorBlock =
(struct HugeSectorBlock FAR *)MK_FP(r->DS, r->BX);
UBYTE mode;
/* bit 0 of SI is 0 read / 1 write, bits 13/14 indicate a type: */
/* 0 any, 1 fat, 2 dir, 3 file. Type is mostly for "write hints" */
if (r->CX != 0xffff || (r->SI & ~0x6001))
{
return DE_INVLDPARM;
}
if (r->DL > lastdrive || r->DL == 0)
return -0x207;
if ((r->SI & 1) == 0) /* while uncommon, reads CAN have type hints */
mode = DSKREADINT25;
else
mode = DSKWRITEINT26;
r->AX =
dskxfer(r->DL - 1, SectorBlock->blkno, SectorBlock->buf,
SectorBlock->nblks, mode);
if (mode == DSKWRITEINT26)
if (r->AX == 0)
setinvld(r->DL - 1);
if (r->AX > 0)
return -0x20c;
break;
}
default:
return DE_INVLDFUNC;
}
return SUCCESS;
}
#endif
VOID ASMCFUNC int21_service(iregs FAR * r)
{
COUNT rc;
long lrc;
lregs lr; /* 8 local registers (ax, bx, cx, dx, si, di, ds, es) */
#define FP_DS_DX (MK_FP(lr.DS, lr.DX))
#define FP_ES_DI (MK_FP(lr.ES, lr.DI))
#define CLEAR_CARRY_FLAG() r->FLAGS &= ~FLG_CARRY
#define SET_CARRY_FLAG() r->FLAGS |= FLG_CARRY
((psp FAR *) MK_FP(cu_psp, 0))->ps_stack = (BYTE FAR *) r;
fmemcpy(&lr, r, sizeof(lregs) - 4);
lr.DS = r->DS;
lr.ES = r->ES;
dispatch:
#ifdef DEBUG
if (bDumpRegs)
{
fmemcpy(&error_regs, user_r, sizeof(iregs));
printf("System call (21h): %02x\n", user_r->AX);
dump_regs = TRUE;
dump();
}
#endif
if ((lr.AH >= 0x38 && lr.AH <= 0x4F) || (lr.AH >= 0x56 && lr.AH <= 0x5c) ||
(lr.AH >= 0x5e && lr.AH <= 0x60) || (lr.AH >= 0x65 && lr.AH <= 0x6a) ||
lr.AH == 0x6c)
{
CLEAR_CARRY_FLAG();
if (lr.AH != 0x59)
CritErrCode = SUCCESS;
}
/* Clear carry by default for these functions */
/* We force clear the near fnodes,
On a normal int21h entry these are unused, so should already
be clear, thus this code is effectively redundant.
However when re-entering int21h, such as a device driver
(e.g. shsufdrv), the fnodes may contain the values of the
in progress call; as long as the driver/tsr causing the
reentrancey saves and restores the SDA across the call then
any changes we do (ie clearing these) should go unnoticed
and if the cause of the reentrancy does not save/restore the
SDA then the caller should expected the unexpected.
Failure to do this will at a minimal result in the extra
output indicating fnodes not 0 (and possibly clearing at end
of call) when the re-entered int21h call completes, but could
result in the re-entered call to fail with no free near fnodes
if both are already in use by the original int21h call (?bug 1879?).
See also PATCH TE 5 jul 04 explanation at end
*/
if (/*ErrorMode && */lr.AH > 0x0c && lr.AH != 0x30 && lr.AH != 0x59)
{
/*if (ErrorMode)*/ ErrorMode = 0;
fnode[0].f_count = 0; /* don't panic - THEY ARE unused !! */
fnode[1].f_count = 0;
}
/* Check for Ctrl-Break */
if (break_ena || (lr.AH >= 1 && lr.AH <= 5) || (lr.AH >= 8 && lr.AH <= 0x0b))
check_handle_break(&syscon);
/* The dispatch handler */
switch (lr.AH)
{
/* int 21h common error handler */
case 0x64:
goto error_invalid;
/* case 0x00: --> Simulate a DOS-4C-00 */
/* Read Keyboard with Echo */
case 0x01:
DOS_01:
lr.AL = read_char_stdin(TRUE);
write_char_stdout(lr.AL);
break;
/* Display Character */
case 0x02:
DOS_02:
lr.AL = lr.DL;
write_char_stdout(lr.AL);
break;
/* Auxiliary Input */
case 0x03:
{
int sft_idx = get_sft_idx(STDAUX);
lr.AL = read_char(sft_idx, sft_idx, TRUE);
break;
}
/* Auxiliary Output */
case 0x04:
write_char(lr.DL, get_sft_idx(STDAUX));
break;
/* Print Character */
case 0x05:
write_char(lr.DL, get_sft_idx(STDPRN));
break;
/* Direct Console I/O */
case 0x06:
DOS_06:
if (lr.DL != 0xff)
goto DOS_02;
lr.AL = 0x00;
r->FLAGS |= FLG_ZERO;
if (StdinBusy()) {
DosIdle_int();
break;
}
r->FLAGS &= ~FLG_ZERO;
/* fall through */
/* Direct Console Input */
case 0x07:
DOS_07:
lr.AL = read_char_stdin(FALSE);
break;
/* Read Keyboard Without Echo */
case 0x08:
DOS_08:
lr.AL = read_char_stdin(TRUE);
break;
/* Display String */
case 0x09:
{
unsigned char c;
unsigned char FAR *bp = FP_DS_DX;
while ((c = *bp++) != '$')
write_char_stdout(c);
lr.AL = c;
}
break;
/* Buffered Keyboard Input */
case 0x0a:
DOS_0A:
read_line(get_sft_idx(STDIN), get_sft_idx(STDOUT), FP_DS_DX);
break;
/* Check Stdin Status */
case 0x0b:
lr.AL = 0xFF;
if (StdinBusy())
lr.AL = 0x00;
break;
/* Flush Buffer, Read Keyboard */
case 0x0c:
{
struct dhdr FAR *dev = sft_to_dev(get_sft(STDIN));
if (dev)
con_flush(&dev);
switch (lr.AL)
{
case 0x01: goto DOS_01;
case 0x06: goto DOS_06;
case 0x07: goto DOS_07;
case 0x08: goto DOS_08;
case 0x0a: goto DOS_0A;
}
lr.AL = 0x00;
break;
}
/* Reset Drive */
case 0x0d:
flush();
break;
/* Set Default Drive */
case 0x0e:
lr.AL = DosSelectDrv(lr.DL);
break;
case 0x0f:
lr.AL = FcbOpen(FP_DS_DX, O_FCB | O_LEGACY | O_OPEN | O_RDWR);
break;
case 0x10:
lr.AL = FcbClose(FP_DS_DX);
break;
case 0x11:
lr.AL = FcbFindFirstNext(FP_DS_DX, TRUE);
break;
case 0x12:
lr.AL = FcbFindFirstNext(FP_DS_DX, FALSE);
break;
case 0x13:
lr.AL = FcbDelete(FP_DS_DX);
break;
case 0x14:
/* FCB read */
lr.AL = FcbReadWrite(FP_DS_DX, 1, XFR_READ);
break;
case 0x15:
/* FCB write */
lr.AL = FcbReadWrite(FP_DS_DX, 1, XFR_WRITE);
break;
case 0x16:
lr.AL = FcbOpen(FP_DS_DX, O_FCB | O_LEGACY | O_CREAT | O_TRUNC | O_RDWR);
break;
case 0x17:
lr.AL = FcbRename(FP_DS_DX);
break;
default:
#ifdef DEBUG
printf("Unsupported INT21 AH = 0x%x, AL = 0x%x.\n", lr.AH, lr.AL);
#endif
/* Fall through. */
/* CP/M compatibility functions */
case 0x18:
case 0x1d:
case 0x1e:
case 0x20:
#ifndef TSC
case 0x61:
#endif
case 0x6b:
lr.AL = 0;
break;
/* Get Default Drive */
case 0x19:
lr.AL = default_drive;
break;
/* Set DTA */
case 0x1a:
dta = FP_DS_DX;
break;
/* Get Default Drive Data */
case 0x1b:
lr.DL = 0;
/* fall through */
/* Get Drive Data */
case 0x1c:
{
BYTE FAR *p;
if ((p = FatGetDrvData(lr.DL, &lr.AL, &lr.CX, &lr.DX)) != NULL)
{
lr.DS = FP_SEG(p);
lr.BX = FP_OFF(p);
}
else
lr.AL = 0xff; /* return 0xff on invalid drive */
}
break;
/* Get default DPB */
/* case 0x1f: see case 0x32 */
/* Random read using FCB: fields not updated
(XFR_RANDOM should not be used here) */
case 0x21:
lr.AL = FcbRandomIO(FP_DS_DX, XFR_READ);
break;
/* Random write using FCB */
case 0x22:
lr.AL = FcbRandomIO(FP_DS_DX, XFR_WRITE);
break;
/* Get file size in records using FCB */
case 0x23:
lr.AL = FcbGetFileSize(FP_DS_DX);
break;
/* Set random record field in FCB */
case 0x24:
FcbSetRandom(FP_DS_DX);
break;
/* Set Interrupt Vector */
/* case 0x25: handled above (re-entrant) */
/* Dos Create New Psp */
case 0x26:
new_psp(lr.DX, r->CS);
break;
/* Read random record(s) using FCB */
case 0x27:
lr.AL = FcbRandomBlockIO(FP_DS_DX, &lr.CX, XFR_READ | XFR_FCB_RANDOM);
break;
/* Write random record(s) using FCB */
case 0x28:
lr.AL = FcbRandomBlockIO(FP_DS_DX, &lr.CX, XFR_WRITE | XFR_FCB_RANDOM);
break;
/* Parse File Name */
case 0x29:
lr.SI = FcbParseFname(&lr.AL, MK_FP(lr.DS, lr.SI), FP_ES_DI);
break;
/* Get Date */
case 0x2a:
lr.AL = DosGetDate((struct dosdate *)&lr.CX);
break;
/* Set Date */
case 0x2b:
lr.AL = DosSetDate ((struct dosdate*)&lr.CX) == SUCCESS ? 0 : 0xFF;
break;
/* Get Time */
case 0x2c:
DosGetTime((struct dostime *)&lr.CL);
break;
/* Set Time */
case 0x2d:
lr.AL = DosSetTime ((struct dostime*)&lr.CX) == SUCCESS ? 0 : 0xFF;
break;
/* Set verify flag */
case 0x2e:
verify_ena = lr.AL & 1;
break;
/* Get DTA */
case 0x2f:
lr.BX = FP_OFF(dta);
lr.ES = FP_SEG(dta);
break;
/* Get (editable) DOS Version */
case 0x30:
lr.AL = os_setver_major;
lr.AH = os_setver_minor;
lr.BH = OEM_ID;
lr.BL = REVISION_SEQ;
lr.CX = 0; /* do not set this to a serial number!
32RTM won't like non-zero values */
if (ReturnAnyDosVersionExpected)
{
/* TE for testing purpose only and NOT
to be documented:
return programs, who ask for version == XX.YY
exactly this XX.YY.
this makes most MS programs more happy.
*/
UBYTE FAR *retp = MK_FP(r->cs, r->ip);
if (retp[0] == 0x3d && /* cmp ax, xxyy */
(retp[3] == 0x75 || retp[3] == 0x74)) /* je/jne error */
{
lr.AL = retp[1];
lr.AH = retp[2];
}
else if (retp[0] == 0x86 && /* xchg al,ah */
retp[1] == 0xc4 && retp[2] == 0x3d && /* cmp ax, xxyy */
(retp[5] == 0x75 || retp[5] == 0x74)) /* je/jne error */
{
lr.AL = retp[4];
lr.AH = retp[3];
}
}
break;
/* Keep Program (Terminate and stay resident) */
case 0x31:
DosMemChange(cu_psp, lr.DX < 6 ? 6 : lr.DX, 0);
return_code = lr.AL | 0x300;
tsr = TRUE;
return_user();
break;
/* Get default BPB */
case 0x1f:
/* Get DPB */
case 0x32:
/* r->DL is NOT changed by MS 6.22 */
/* INT21/32 is documented to reread the DPB */
{
int drv = (lr.DL == 0 || lr.AH == 0x1f) ? default_drive : lr.DL - 1;
struct dpb FAR *dpb = get_dpb(drv);
if (dpb == NULL)
{
CritErrCode = -DE_INVLDDRV;
lr.AL = 0xFF;
break;
}
/* hazard: no error checking! */
flush_buffers(dpb->dpb_unit);
dpb->dpb_flags = M_CHANGED; /* force flush and reread of drive BPB/DPB */
#ifdef WITHFAT32
if (media_check(dpb) < 0 || ISFAT32(dpb))
#else
if (media_check(dpb) < 0)
#endif
{
lr.AL = 0xff;
CritErrCode = -DE_INVLDDRV;
break;
}
lr.DS = FP_SEG(dpb);
lr.BX = FP_OFF(dpb);
lr.AL = 0;
}
break;
/*
case 0x33:
see int21_syscall
*/
/* Get InDOS flag */
case 0x34:
lr.BX = FP_OFF(&InDOS);
lr.ES = FP_SEG(&InDOS);
break;
/* Get Interrupt Vector */
/* case 0x35: handled above (reentrant) */
/* Dos Get Disk Free Space */
case 0x36:
lr.AX = DosGetFree(lr.DL, &lr.BX, &lr.CX, &lr.DX);
break;
/* Undocumented Get/Set Switchar */
case 0x37:
switch (lr.AL)
{
/* Get switch character */
case 0x00:
lr.DL = switchar;
lr.AL = 0x00;
break;
/* Set switch character */
case 0x01:
switchar = lr.DL;
lr.AL = 0x00;
break;
default:
goto error_invalid;
}
break;
/* Get/Set Country Info */
case 0x38:
{
UWORD cntry = lr.AL;
if (cntry == 0xff)
cntry = lr.BX;
if (0xffff == lr.DX)
{
/* Set Country Code */
rc = DosSetCountry(cntry);
}
else
{
if (cntry == 0)
cntry--;
/* Get Country Information */
rc = DosGetCountryInformation(cntry, FP_DS_DX);
if (rc >= SUCCESS)
{
/* HACK FIXME */
if (cntry == (UWORD) - 1)
cntry = 1;
/* END OF HACK */
lr.AX = lr.BX = cntry;
}
}
goto short_check;
}
/* Dos Create Directory */
case 0x39:
/* Dos Remove Directory */
case 0x3a:
rc = DosMkRmdir(FP_DS_DX, lr.AH);
goto short_check;
/* Dos Change Directory */
case 0x3b:
rc = DosChangeDir(FP_DS_DX);
goto short_check;
/* Dos Create File */
case 0x3c:
lrc = DosOpen(FP_DS_DX, O_LEGACY | O_RDWR | O_CREAT | O_TRUNC, lr.CL);
goto long_check;
/* Dos Open */
case 0x3d:
lrc = DosOpen(FP_DS_DX, O_LEGACY | O_OPEN | lr.AL, 0);
goto long_check;
/* Dos Close */
case 0x3e:
rc = DosClose(lr.BX);
goto short_check;
/* Dos Read */
case 0x3f:
lrc = DosRead(lr.BX, lr.CX, FP_DS_DX);
goto long_check;
/* Dos Write */
case 0x40:
lrc = DosWrite(lr.BX, lr.CX, FP_DS_DX);
goto long_check;
/* Dos Delete File */
case 0x41:
rc = DosDelete((BYTE FAR *) FP_DS_DX, D_ALL);
goto short_check;
/* Dos Seek */
case 0x42:
if (lr.AL > 2)
goto error_invalid;
lrc = DosSeek(lr.BX, (LONG)((((ULONG) (lr.CX)) << 16) | lr.DX), lr.AL);
if (lrc == -1)
{
lrc = DE_INVLDHNDL;
}
else
{
lr.DX = (UWORD)(lrc >> 16);
lrc = (UWORD) lrc;
}
goto long_check;
/* Get/Set File Attributes */
case 0x43:
switch (lr.AL)
{
case 0x00:
rc = DosGetFattr((BYTE FAR *) FP_DS_DX);
if (rc >= SUCCESS)
lr.CX = rc;
break;
case 0x01:
rc = DosSetFattr((BYTE FAR *) FP_DS_DX, lr.CX);
lr.AX = lr.CX;
break;
default:
goto error_invalid;
}
goto short_check;
/* Device I/O Control */
case 0x44:
rc = DosDevIOctl(&lr); /* can set critical error code! */
if (rc < SUCCESS)
{
lr.AX = -rc;
if (rc != DE_DEVICE && rc != DE_ACCESS)
CritErrCode = lr.AX;
goto error_carry;
}
break;
/* Duplicate File Handle */
case 0x45:
lrc = DosDup(lr.BX);
goto long_check;
/* Force Duplicate File Handle */
case 0x46:
rc = DosForceDup(lr.BX, lr.CX);
goto short_check;
/* Get Current Directory */
case 0x47:
rc = DosGetCuDir(lr.DL, MK_FP(lr.DS, lr.SI));
lr.AX = 0x0100; /*jpp: from interrupt list */
goto short_check;
/* Allocate memory */
case 0x48:
if ((rc = DosMemAlloc(lr.BX, mem_access_mode, &lr.AX, &lr.BX)) < 0)
{
DosMemLargest(&lr.BX);
if (DosMemCheck() != SUCCESS)
panic("MCB chain corrupted");
goto error_exit;
}
lr.AX++; /* DosMemAlloc() returns seg of MCB rather than data */
break;
/* Free memory */
case 0x49:
if ((rc = DosMemFree(lr.ES - 1)) < SUCCESS)
{
if (DosMemCheck() != SUCCESS)
panic("MCB chain corrupted");
goto error_exit;
}
break;
/* Set memory block size */
case 0x4a:
if (DosMemCheck() != SUCCESS)
panic("before 4a: MCB chain corrupted");
if ((rc = DosMemChange(lr.ES, lr.BX, &lr.BX)) < 0)
{
#if 0
if (cu_psp == lr.ES)
{
psp FAR *p = MK_FP(cu_psp, 0);
p->ps_size = lr.BX + cu_psp;
}
#endif
if (DosMemCheck() != SUCCESS)
panic("after 4a: MCB chain corrupted");
goto error_exit;
}
lr.AX = lr.ES; /* Undocumented MS-DOS behaviour expected by BRUN45! */
break;
/* Load and Execute Program */
case 0x4b:
break_flg = FALSE;
rc = DosExec(lr.AL, MK_FP(lr.ES, lr.BX), FP_DS_DX);
goto short_check;
/* Terminate Program */
case 0x00:
lr.AX = 0x4c00;
/* End Program */
case 0x4c:
if (((psp FAR *)MK_FP(cu_psp, 0))->ps_parent == cu_psp)
break;
tsr = FALSE;
rc = 0;
if (ErrorMode)
{
ErrorMode = FALSE;
rc = 0x200;
}
else if (break_flg)
{
break_flg = FALSE;
rc = 0x100;
}
return_code = lr.AL | rc;
if (DosMemCheck() != SUCCESS)
panic("MCB chain corrupted");
#ifdef TSC
StartTrace();
#endif
return_user();
break;
/* Get Child-program Return Value */
case 0x4d:
lr.AX = return_code;
/* needs to be cleared (RBIL) */
return_code = 0;
break;
/* Dos Find First */
case 0x4e:
/* dta for this call is set on entry. This */
/* needs to be changed for new versions. */
rc = DosFindFirst(lr.CX, FP_DS_DX);
lr.AX = 0;
goto short_check;
/* Dos Find Next */
case 0x4f:
/* dta for this call is set on entry. This */
/* needs to be changed for new versions. */
rc = DosFindNext();
lr.AX = 0;
goto short_check;
/*
case 0x50:
case 0x51:
see int21_syscall
*/
/* ************UNDOCUMENTED************************************* */
/* Get List of Lists */
case 0x52:
lr.BX = FP_OFF(&DPBp);
lr.ES = FP_SEG(&DPBp);
break;
case 0x53:
/* DOS 2+ internal - TRANSLATE BIOS PARAMETER BLOCK TO DRIVE PARAM BLOCK */
bpb_to_dpb((bpb FAR *) MK_FP(lr.DS, lr.SI),
(struct dpb FAR *)MK_FP(lr.ES, r->BP)
#ifdef WITHFAT32
, (lr.CX == 0x4558 && lr.DX == 0x4152)
#endif
);
break;
/* Get verify state */
case 0x54:
lr.AL = verify_ena;
break;
/* ************UNDOCUMENTED************************************* */
/* Dos Create New Psp & set p_size */
case 0x55:
child_psp(lr.DX, cu_psp, lr.SI);
cu_psp = lr.DX;
break;
/* Dos Rename */
case 0x56:
rc = DosRename(FP_DS_DX, FP_ES_DI);
goto short_check;
/* Get/Set File Date and Time */
case 0x57:
switch (lr.AL)
{
case 0x00:
rc = DosGetFtime((COUNT) lr.BX, /* Handle */
&lr.DX, /* FileDate */
&lr.CX); /* FileTime */
break;
case 0x01:
rc = DosSetFtime((COUNT) lr.BX, /* Handle */
(date) lr.DX, /* FileDate */
(time) lr.CX); /* FileTime */
break;
default:
goto error_invalid;
}
goto short_check;
/* Get/Set Allocation Strategy */
case 0x58:
switch (lr.AL)
{
case 0x00:
lr.AL = mem_access_mode;
lr.AH = 0;
break;
case 0x01:
if (lr.BL > LAST_FIT_U || /* 0x82 */
(lr.BL & FIT_MASK) > LAST_FIT) /* 0x3f, 0x02 */
goto error_invalid;
mem_access_mode = lr.BL;
break;
case 0x02:
lr.AL = uppermem_link;
break;
case 0x03:
if (uppermem_root != 0xffff) /* always error if not exists */
{
DosUmbLink(lr.BX);
break;
}
/* else fall through */
default:
goto error_invalid;
#ifdef DEBUG
case 0xff:
show_chain();
break;
#endif
}
break;
/* Get Extended Error */
case 0x59:
lr.AX = CritErrCode;
lr.CH = CritErrLocus;
lr.BH = CritErrClass;
lr.BL = CritErrAction;
lr.DI = FP_OFF(CritErrDev);
lr.ES = FP_SEG(CritErrDev);
break;
/* Create Temporary File */
case 0x5a:
lrc = DosMkTmp(FP_DS_DX, lr.CX);
goto long_check;
/* Create New File */
case 0x5b:
lrc = DosOpen(FP_DS_DX, O_LEGACY | O_RDWR | O_CREAT, lr.CX);
goto long_check;
/* /// Added for SHARE. - Ron Cemer */
/* Lock/unlock file access */
case 0x5c:
rc = DosLockUnlock
(lr.BX, ((unsigned long)lr.CX << 16) | lr.DX,
((unsigned long)lr.SI << 16) | lr.DI, lr.AL != 0);
if (rc != SUCCESS)
goto error_exit;
break;
/* /// End of additions for SHARE. - Ron Cemer */
/* UNDOCUMENTED: server, share.exe and sda function */
case 0x5d:
switch (lr.AL)
{
/* Remote Server Call */
case 0x00:
fmemcpy(&lr, FP_DS_DX, sizeof(lr));
goto dispatch;
case 0x06:
lr.DS = FP_SEG(internal_data);
lr.SI = FP_OFF(internal_data);
lr.CX = swap_indos - internal_data;
lr.DX = swap_always - internal_data;
CLEAR_CARRY_FLAG();
break;
case 0x07:
case 0x08:
case 0x09:
rc = remote_printredir(lr.DX, Int21AX);
CLEAR_CARRY_FLAG();
if (rc != SUCCESS)
goto error_exit;
break;
/* Set Extended Error */
case 0x0a:
{
lregs far *er = FP_DS_DX;
CritErrCode = er->AX;
CritErrDev = MK_FP(er->ES, er->DI);
CritErrLocus = er->CH;
CritErrClass = er->BH;
CritErrAction = er->BL;
CLEAR_CARRY_FLAG();
break;
}
default:
CritErrCode = SUCCESS;
goto error_invalid;
}
break;
case 0x5e:
switch (lr.AL)
{
case 0x00:
lr.CX = get_machine_name(FP_DS_DX);
break;
case 0x01:
set_machine_name(FP_DS_DX, lr.CX);
break;
default:
rc = (int)network_redirector_mx(REM_PRINTSET, &lr, (void *)Int21AX);
goto short_check;
}
break;
case 0x5f:
if (lr.AL == 7 || lr.AL == 8)
{
if (lr.DL < lastdrive)
{
struct cds FAR *cdsp = CDSp + lr.DL;
if (FP_OFF(cdsp->cdsDpb)) /* letter of physical drive? */
{
cdsp->cdsFlags &= ~CDSPHYSDRV;
if (lr.AL == 7)
cdsp->cdsFlags |= CDSPHYSDRV;
break;
}
}
rc = DE_INVLDDRV;
goto error_exit;
}
else
{
rc = (int)network_redirector_mx(REM_DOREDIRECT, &lr, (void *)Int21AX);
/* the remote function manipulates *r directly !,
so we should not copy lr to r here */
if (rc != SUCCESS)
{
CritErrCode = -rc; /* Maybe set */
SET_CARRY_FLAG();
}
r->AX = -rc; /* okay because we use real_exit */
goto real_exit;
}
case 0x60: /* TRUENAME */
rc = DosTruename(MK_FP(lr.DS, lr.SI), adjust_far(FP_ES_DI));
lr.AX = rc;
goto short_check;
#ifdef TSC
/* UNDOCUMENTED: no-op */
/* */
/* DOS-C: tsc support */
case 0x61:
#ifdef DEBUG
switch (lr.AL)
{
case 0x01:
bTraceNext = TRUE;
break;
case 0x02:
bDumpRegs = FALSE;
break;
}
#endif
lr.AL = 0x00;
break;
#endif
/* UNDOCUMENTED: return current psp
case 0x62: is in int21_syscall
lr.BX = cu_psp;
break;
*/
/* UNDOCUMENTED: Double byte and korean tables */
case 0x63:
{
#if 0
/* not really supported, but will pass. */
lr.AL = 0x00; /*jpp: according to interrupt list */
/*Bart: fails for PQDI and WATCOM utilities:
use the above again */
#endif
switch (lr.AL)
{
case 0:
lr.DS = FP_SEG(&nlsDBCSHardcoded);
lr.SI = FP_OFF(&nlsDBCSHardcoded);
break;
case 1: /* set Korean Hangul input method to DL 0/1 */
lr.AL = 0xff; /* flag error (AL would be 0 if okay) */
break;
case 2: /* get Korean Hangul input method setting to DL */
lr.AL = 0xff; /* flag error, do not set DL */
break;
default: /* is this the proper way to handle invalid AL? */
rc = -1;
goto error_exit;
}
break;
}
/*
case 0x64:
see above (invalid)
*/
/* Extended country info */
case 0x65:
switch (lr.AL)
{
case 0x20: /* upcase single character */
lr.DL = DosUpChar(lr.DL);
break;
case 0x21: /* upcase memory area */
DosUpMem(FP_DS_DX, lr.CX);
break;
case 0x22: /* upcase ASCIZ */
DosUpString(FP_DS_DX);
break;
case 0xA0: /* upcase single character of filenames */
lr.DL = DosUpFChar(lr.DL);
break;
case 0xA1: /* upcase memory area of filenames */
DosUpFMem(FP_DS_DX, lr.CX);
break;
case 0xA2: /* upcase ASCIZ of filenames */
DosUpFString(FP_DS_DX);
break;
case 0x23: /* check Yes/No response */
lr.AX = DosYesNo(lr.DL);
break;
default:
#ifdef NLS_DEBUG
if ((rc = DosGetData(lr.AL, lr.BX, lr.DX, lr.CX, FP_ES_DI)) < 0)
{
printf("DosGetData() := %d\n", rc);
goto error_exit;
}
printf("DosGetData() returned successfully\n");
break;
#else
rc = DosGetData(lr.AL, lr.BX, lr.DX, lr.CX, FP_ES_DI);
goto short_check;
#endif
}
break;
/* Code Page functions */
case 0x66:
switch (lr.AL)
{
case 1:
rc = DosGetCodepage(&lr.BX, &lr.DX);
break;
case 2:
rc = DosSetCodepage(lr.BX, lr.DX);
break;
default:
goto error_invalid;
}
if (rc != SUCCESS)
goto error_exit;
break;
/* Set Max file handle count */
case 0x67:
rc = SetJFTSize(lr.BX);
goto short_check;
/* Flush file buffer -- COMMIT FILE. */
case 0x68:
case 0x6a:
rc = DosCommit(lr.BX);
goto short_check;
/* Get/Set Serial Number */
case 0x69:
rc = (lr.BL == 0 ? default_drive : lr.BL - 1);
if (lr.AL < 2)
{
if (get_cds(rc) == NULL)
{
rc = DE_INVLDDRV;
goto error_exit;
}
if (get_dpb(rc) != NULL)
{
UWORD saveCX = lr.CX;
lr.CX = lr.AL == 0 ? 0x0866 : 0x0846;
lr.AL = 0x0d;
rc = DosDevIOctl(&lr);
lr.CX = saveCX;
goto short_check;
}
}
goto error_invalid;
/*
case 0x6a: see case 0x68
case 0x6b: dummy func: return AL=0
*/
/* Extended Open-Creat, not fully functional. (bits 4,5,6 of BH) */
case 0x6c:
/* high nibble must be <= 1, low nibble must be <= 2 */
if ((lr.DL & 0xef) > 0x2)
goto error_invalid;
lrc = DosOpen(MK_FP(lr.DS, lr.SI),
(lr.BX & 0x70ff) | ((lr.DL & 3) << 8) |
((lr.DL & 0x10) << 6), lr.CL);
if (lrc >= SUCCESS)
/* action */
lr.CX = (UWORD)(lrc >> 16);
goto long_check;
/* case 0x6d and above not implemented : see default; return AL=0 */
#ifdef WITHFAT32
/* LFN functions - fail with "function not supported" error code */
case 0x71:
lr.AL = 00;
goto error_carry;
/* DOS 7.0+ FAT32 extended functions */
case 0x73:
CLEAR_CARRY_FLAG();
CritErrCode = SUCCESS;
rc = int21_fat32(&lr);
goto short_check;
#endif
#ifdef WITHLFNAPI
/* FreeDOS LFN helper API functions */
case 0x74:
{
switch (lr.AL)
{
/* Allocate LFN inode */
case 0x01:
rc = lfn_allocate_inode();
break;
/* Free LFN inode */
case 0x02:
rc = lfn_free_inode(lr.BX);
break;
/* Setup LFN inode */
case 0x03:
rc = lfn_setup_inode(lr.BX, ((ULONG)lr.CX << 16) | lr.DX, ((ULONG)lr.SI << 16) | lr.DI);
break;
/* Create LFN entries */
case 0x04:
rc = lfn_create_entries(lr.BX, (lfn_inode_ptr)FP_DS_DX);
break;
/* Read next LFN */
case 0x05:
rc = lfn_dir_read(lr.BX, (lfn_inode_ptr)FP_DS_DX);
break;
/* Write SFN pointed by LFN inode */
case 0x06:
rc = lfn_dir_write(lr.BX);
break;
default:
goto error_invalid;
}
lr.AX = rc;
CLEAR_CARRY_FLAG();
goto short_check;
}
#endif
}
goto exit_dispatch;
long_check:
if (lrc >= SUCCESS)
{
lr.AX = (UWORD)lrc;
goto exit_dispatch;
}
rc = (int)lrc;
short_check:
if (rc < SUCCESS)
goto error_exit;
goto exit_dispatch;
error_invalid:
rc = DE_INVLDFUNC;
error_exit:
lr.AX = -rc;
if (CritErrCode == SUCCESS)
CritErrCode = lr.AX; /* Maybe set */
error_carry:
SET_CARRY_FLAG();
exit_dispatch:
fmemcpy(r, &lr, sizeof(lregs) - 4); /* copy lr -> r but exclude flags */
r->DS = lr.DS;
r->ES = lr.ES;
real_exit:;
/* PATCH !! TE 5 JUL 04
what happened:
Application does FindFirst("I:\*.*");
this fails, and causes Int24
this sets ErrorMode, and calls Int24
Application decides NOT to return to DOS,
but instead pop the stack and return to itself
(this is legal; see RBIL/INT 24 description
a) now the alloc()'ed fnode[0] never gets free()'ed
b) errormode NEVER gets set back to 0 unyil exit()
I have NO idea how real DOS handles this;
the appended patch cures the worst symptoms
*/
if (fnode[0].f_count != 0 ||
fnode[1].f_count != 0 )
{
if (ErrorMode == 0)
put_string("near_fnodes not 0"); /* panic ?? */
fnode[0].f_count = 0; /* don't panic - THEY ARE unused !! */
fnode[1].f_count = 0;
}
/* PATCH !! END TE 5 JUL 04 */
#ifdef DEBUG
if (bDumpRegs)
{
fmemcpy(&error_regs, user_r, sizeof(iregs));
dump_regs = TRUE;
dump();
}
#endif
}
#if 0
/* No kernel INT-23 handler required no longer -- 1999/04/15 ska */
/* ctrl-Break handler */
#pragma argsused
VOID INRPT FAR int23_handler(int es, int ds, int di, int si, int bp,
int sp, int bx, int dx, int cx, int ax,
int ip, int cs, int flags)
{
tsr = FALSE;
return_mode = 1;
return_code = -1;
mod_sto(CTL_C);
DosMemCheck();
#ifdef TSC
StartTrace();
#endif
return_user();
}
#endif
struct int25regs {
UWORD es, ds;
UWORD di, si, bp, sp;
UWORD bx, dx, cx, ax;
UWORD flags, ip, cs;
};
/*
this function is called from an assembler wrapper function
*/
VOID ASMCFUNC int2526_handler(WORD mode, struct int25regs FAR * r)
{
ULONG blkno;
UWORD nblks;
BYTE FAR *buf;
UBYTE drv;
if (mode == 0x26)
mode = DSKWRITEINT26;
else
mode = DSKREADINT25;
drv = r->ax;
if (drv >= lastdrive)
{
r->ax = 0x201;
SET_CARRY_FLAG();
return;
}
#ifdef WITHFAT32
{
struct dpb FAR *dpbp = get_dpb(drv);
if (dpbp != NULL && ISFAT32(dpbp))
{
r->ax = 0x207;
SET_CARRY_FLAG();
return;
}
}
#endif
nblks = r->cx;
blkno = r->dx;
buf = MK_FP(r->ds, r->bx);
if (nblks == 0xFFFF)
{
/*struct HugeSectorBlock FAR *lb = MK_FP(r->ds, r->bx); */
blkno = ((struct HugeSectorBlock FAR *)buf)->blkno;
nblks = ((struct HugeSectorBlock FAR *)buf)->nblks;
buf = ((struct HugeSectorBlock FAR *)buf)->buf;
}
InDOS++;
r->ax = dskxfer(drv, blkno, buf, nblks, mode);
CLEAR_CARRY_FLAG();
if (r->ax != 0)
{
SET_CARRY_FLAG();
if (mode == DSKWRITEINT26)
setinvld(drv);
}
--InDOS;
}
/*
VOID int25_handler(struct int25regs FAR * r) { int2526_handler(DSKREAD,r); }
VOID int26_handler(struct int25regs FAR * r) { int2526_handler(DSKWRITE,r); }
*/
#ifdef TSC
STATIC VOID StartTrace(VOID)
{
if (bTraceNext)
{
#ifdef DEBUG
bDumpRegs = TRUE;
#endif
bTraceNext = FALSE;
}
#ifdef DEBUG
else
bDumpRegs = FALSE;
#endif
}
#endif
/* this function is called from an assembler wrapper function
and serves the internal dos calls - int2f/12xx and int2f/4a01,4a02.
*/
struct int2f12regs {
#ifdef I386
#ifdef __WATCOMC__
/* UWORD gs, fs; ** GS/FS are protected through SI/DI */
#else
UWORD high_edx,
#ifdef _MSC_VER
high_ecx,
#else /* __BORLANDC__ */
high_ebx,
#endif
high_eax;
#endif
#endif
UWORD es, ds;
UWORD di, si, bp;
xreg b, d, c, a;
UWORD ip, cs, flags;
UWORD callerARG1; /* used if called from INT2F/12 */
};
/* WARNING: modifications in `r' are used outside of int2F_12_handler()
* On input r.AX==0x12xx, 0x4A01 or 0x4A02
*/
VOID ASMCFUNC int2F_12_handler(struct int2f12regs r)
{
COUNT rc;
long lrc;
if (r.AH == 0x4a)
{
size_t size = 0, offs = 0xffff;
r.ES = offs;
if (FP_SEG(firstAvailableBuf) == offs) /* HMA present? */
{
offs = FP_OFF(firstAvailableBuf);
size = ~offs; /* BX for query HMA */
if (r.AL == 0x02) /* allocate HMA space */
{
if (r.BX < size)
size = r.BX;
AllocateHMASpace(offs, offs+size);
firstAvailableBuf += size;
}
}
r.DI = offs;
r.BX = size;
return;
}
switch (r.AL)
{
case 0x00: /* installation check */
r.AL = 0xff;
break;
case 0x03: /* get DOS data segment */
r.DS = FP_SEG(&nul_dev);
break;
case 0x06: /* invoke critical error */
/* code, drive number, error, device header */
r.AL = CriticalError(r.callerARG1 >> 8,
(r.callerARG1 & (EFLG_CHAR << 8)) ? 0 :
r.callerARG1 & 0xff, r.DI, MK_FP(r.BP, r.SI));
break;
case 0x08: /* decrease SFT reference count */
{
sft FAR *p = MK_FP(r.ES, r.DI);
r.AX = p->sft_count;
if (--p->sft_count == 0)
--p->sft_count;
}
break;
case 0x0c: /* perform "device open" for device, set owner for FCB */
if (lpCurSft->sft_flags & SFT_FDEVICE)
{
request rq;
rq.r_unit = 0;
rq.r_status = 0;
rq.r_command = C_OPEN;
rq.r_length = sizeof(request);
execrh((request FAR *) & rq, lpCurSft->sft_dev);
}
/* just do it always, not just for FCBs */
lpCurSft->sft_psp = cu_psp;
break;
case 0x0d: /* get dos date/time */
r.AX = dos_getdate();
r.DX = dos_gettime();
break;
case 0x11: /* normalise ASCIIZ filename */
{
char c;
char FAR *s = MK_FP(r.DS, r.SI);
char FAR *t = MK_FP(r.ES, r.DI);
do
{
c = *s++;
/* uppercase character */
/* for now, ASCII only because nls.c cannot handle DS!=SS */
if (c >= 'a' && c <= 'z')
c -= 'a' - 'A';
else if (c == '/')
c = '\\';
*t++ = c;
}
while (c);
break;
}
case 0x12: /* get length of asciiz string */
r.CX = fstrlen(MK_FP(r.ES, r.DI)) + 1;
break;
case 0x13:
/* uppercase character */
/* for now, ASCII only because nls.c cannot handle DS!=SS */
r.AL = (unsigned char)r.callerARG1;
if (r.AL >= 'a' && r.AL <= 'z')
r.AL -= 'a' - 'A';
break;
case 0x16:
/* get address of system file table entry - used by NET.EXE
BX system file table entry number ( such as returned from 2F/1220)
returns
ES:DI pointer to SFT entry
BX relative entry number within SFT */
{
int rel_idx = idx_to_sft_(r.BX);
if (rel_idx == -1)
{
r.FLAGS |= FLG_CARRY;
break;
}
r.FLAGS &= ~FLG_CARRY;
r.BX = rel_idx;
r.ES = FP_SEG(lpCurSft);
r.DI = FP_OFF(lpCurSft);
break;
}
case 0x17: /* get current directory structure for drive - used by NET.EXE
STACK: drive (0=A:,1=B,...)
; returns
; CF set if error
; DS:SI pointer to CDS for drive
;
; called like
; push 2 (c-drive)
; mov ax,1217
; int 2f
;
; probable use: get sizeof(CDSentry)
*/
{
struct cds FAR *cdsp = get_cds(r.callerARG1 & 0xff);
if (cdsp == NULL)
{
r.FLAGS |= FLG_CARRY;
break;
}
r.DS = FP_SEG(cdsp);
r.SI = FP_OFF(cdsp);
r.FLAGS &= ~FLG_CARRY;
break;
}
case 0x18: /* get caller's registers */
r.DS = FP_SEG(user_r);
r.SI = FP_OFF(user_r);
break;
case 0x1b: /* #days in February - valid until 2099. */
r.AL = (r.CL & 3) ? 28 : 29;
break;
case 0x20: /* get job file table entry */
{
psp FAR *p = MK_FP(cu_psp, 0);
unsigned char FAR *idx;
if (r.BX >= p->ps_maxfiles)
{
r.AL = -DE_INVLDHNDL;
r.FLAGS |= FLG_CARRY;
break;
}
idx = &p->ps_filetab[r.BX];
r.FLAGS &= ~FLG_CARRY;
r.ES = FP_SEG(idx);
r.DI = FP_OFF(idx);
}
break;
case 0x21: /* truename */
DosTruename(MK_FP(r.DS, r.SI), MK_FP(r.ES, r.DI));
break;
case 0x23: /* check if character device */
{
struct dhdr FAR *dhp;
dhp = IsDevice((BYTE FAR *) DirEntBuffer.dir_name);
if (dhp == NULL)
{
r.FLAGS |= FLG_CARRY;
break;
}
r.BH = dhp->dh_attr;
r.FLAGS &= ~FLG_CARRY;
}
break;
case 0x25: /* get length of asciiz string */
r.CX = fstrlen(MK_FP(r.DS, r.SI)) + 1;
break;
case 0x26: /* open file */
r.FLAGS &= ~FLG_CARRY;
CritErrCode = SUCCESS;
lrc = DosOpen(MK_FP(r.DS, r.DX), O_LEGACY | O_OPEN | r.CL, 0);
goto long_check;
case 0x27: /* close file */
r.FLAGS &= ~FLG_CARRY;
CritErrCode = SUCCESS;
rc = DosClose(r.BX);
goto short_check;
case 0x28: /* move file pointer */
/*
* RBIL says: "sets user stack frame pointer to dummy buffer,
* moves BP to AX, performs LSEEK, and restores frame pointer"
* We obviously don't do it like that. Does this do any harm?! --L.G.
*/
r.FLAGS &= ~FLG_CARRY;
CritErrCode = SUCCESS;
if (r.BP < 0x4200 || r.BP > 0x4202)
goto error_invalid;
#if 0
{
sft FAR *s = get_sft(r.BX);
if ((rc = _SftSeek(s, MK_ULONG(r.CX, r.DX), r.BP & 0xff)) >= SUCCESS)
{
r.DX = hiword (s->sft_posit);
r.AX = loword (s->sft_posit);
}
}
goto short_check;
#else
goto error_invalid;
#endif
case 0x29: /* read from file */
r.FLAGS &= ~FLG_CARRY;
CritErrCode = SUCCESS;
lrc = DosRead(r.BX, r.CX, MK_FP(r.DS, r.DX));
goto long_check;
case 0x2a: /* Set FastOpen but does nothing. */
r.FLAGS &= ~FLG_CARRY;
break;
case 0x2c: /* added by James Tabor For Zip Drives
Return Null Device Pointer */
/* by UDOS+RBIL: get header of SECOND device driver in device chain,
omitting the NUL device TE */
r.BX = FP_SEG(nul_dev.dh_next);
r.AX = FP_OFF(nul_dev.dh_next);
break;
case 0x2e: /* GET or SET error table addresse - ignored
called by MS debug with DS != DOSDS, printf
doesn't work!! */
break;
case 0x2f:
if (r.DX)
{
os_setver_major = r.DL;
os_setver_minor = r.DH;
}
else
{
os_setver_major = os_major;
os_setver_minor = os_minor;
}
break;
default:
if (r.AL <= 0x31)
{
put_string("unimplemented internal dos function INT2F/12");
put_unsigned(r.AL, 16, 2);
put_string("\n");
r.FLAGS |= FLG_CARRY;
}
}
return;
long_check:
if (lrc >= SUCCESS)
{
r.AX = (UWORD)lrc;
return;
}
rc = (int)lrc;
short_check:
if (rc < SUCCESS)
goto error_exit;
return;
error_invalid:
rc = DE_INVLDFUNC;
error_exit:
r.AX = -rc;
if (CritErrCode == SUCCESS)
CritErrCode = r.AX; /* Maybe set */
r.FLAGS |= FLG_CARRY;
}
/*
* 2000/09/04 Brian Reifsnyder
* Modified interrupts 0x25 & 0x26 to return more accurate error codes.
*/