| /* |
| * (c) 1993, 1994 Erik Bos |
| */ |
| |
| #include <time.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/file.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <utime.h> |
| #include <ctype.h> |
| #include "dos_fs.h" |
| #include "windows.h" |
| #include "msdos.h" |
| #include "registers.h" |
| #include "ldt.h" |
| #include "task.h" |
| #include "options.h" |
| #include "miscemu.h" |
| #include "stddebug.h" |
| #include "debug.h" |
| #ifdef __svr4__ |
| /* SVR4 DOESNTdo locking the same way must implement properly */ |
| #define LOCK_EX 0 |
| #define LOCK_SH 1 |
| #define LOCK_NB 8 |
| #endif |
| /* Define the drive parameter block, as used by int21/1F |
| * and int21/32. This table can be accessed through the |
| * global 'dpb' pointer, which points into the local dos |
| * heap. |
| */ |
| struct DPB |
| { |
| BYTE drive_num; /* 0=A, etc. */ |
| BYTE unit_num; /* Drive's unit number (?) */ |
| WORD sector_size; /* Sector size in bytes */ |
| BYTE high_sector; /* Highest sector in a cluster */ |
| BYTE shift; /* Shift count (?) */ |
| WORD reserved; /* Number of reserved sectors at start */ |
| BYTE num_FAT; /* Number of FATs */ |
| WORD dir_entries; /* Number of root dir entries */ |
| WORD first_data; /* First data sector */ |
| WORD high_cluster; /* Highest cluster number */ |
| WORD sectors_in_FAT; /* Number of sectors per FAT */ |
| WORD start_dir; /* Starting sector of first dir */ |
| DWORD driver_head; /* Address of device driver header (?) */ |
| BYTE media_ID; /* Media ID */ |
| BYTE access_flag; /* Prev. accessed flag (0=yes,0xFF=no) */ |
| DWORD next; /* Pointer to next DPB in list */ |
| WORD free_search; /* Free cluster search start */ |
| WORD free_clusters; /* Number of free clusters (0xFFFF=unknown) */ |
| }; |
| |
| WORD CodePage = 437; |
| struct DPB *dpb; |
| DWORD dpbsegptr; |
| |
| struct DosHeap { |
| BYTE InDosFlag; |
| BYTE mediaID; |
| BYTE biosdate[8]; |
| struct DPB dpb; |
| }; |
| static struct DosHeap *heap; |
| static WORD DosHeapHandle; |
| |
| WORD sharing_retries = 3; /* number of retries at sharing violation */ |
| WORD sharing_pause = 1; /* pause between retries */ |
| |
| extern char TempDirectory[]; |
| |
| static int Error(int e, int class, int el) |
| { |
| return DOS_Error(e,class,el); |
| } |
| |
| |
| BYTE *GetCurrentDTA(void) |
| { |
| TDB *pTask = (TDB *)GlobalLock( GetCurrentTask() ); |
| return (BYTE *)PTR_SEG_TO_LIN( pTask->dta ); |
| } |
| |
| |
| void ChopOffWhiteSpace(char *string) |
| { |
| int length; |
| |
| for (length = strlen(string) ; length ; length--) |
| if (string[length] == ' ') |
| string[length] = '\0'; |
| } |
| |
| static void CreateBPB(int drive, BYTE *data) |
| { |
| if (drive > 1) { |
| setword(data, 512); |
| data[2] = 2; |
| setword(&data[3], 0); |
| data[5] = 2; |
| setword(&data[6], 240); |
| setword(&data[8], 64000); |
| data[0x0a] = 0xf8; |
| setword(&data[0x0b], 40); |
| setword(&data[0x0d], 56); |
| setword(&data[0x0f], 2); |
| setword(&data[0x11], 0); |
| setword(&data[0x1f], 800); |
| data[0x21] = 5; |
| setword(&data[0x22], 1); |
| } else { /* 1.44mb */ |
| setword(data, 512); |
| data[2] = 2; |
| setword(&data[3], 0); |
| data[5] = 2; |
| setword(&data[6], 240); |
| setword(&data[8], 2880); |
| data[0x0a] = 0xf8; |
| setword(&data[0x0b], 6); |
| setword(&data[0x0d], 18); |
| setword(&data[0x0f], 2); |
| setword(&data[0x11], 0); |
| setword(&data[0x1f], 80); |
| data[0x21] = 7; |
| setword(&data[0x22], 2); |
| } |
| } |
| |
| static void GetFreeDiskSpace(struct sigcontext_struct *context) |
| { |
| int drive; |
| long size,available; |
| |
| if (DL_reg(context) == 0) drive = DOS_GetDefaultDrive(); |
| else drive = DL_reg(context) - 1; |
| |
| if (!DOS_ValidDrive(drive)) { |
| Error(InvalidDrive, EC_MediaError , EL_Disk); |
| AX_reg(context) = 0xffff; |
| return; |
| } |
| |
| if (!DOS_GetFreeSpace(drive, &size, &available)) { |
| Error(GeneralFailure, EC_MediaError , EL_Disk); |
| AX_reg(context) = 0xffff; |
| return; |
| } |
| |
| AX_reg(context) = (drive < 2) ? 1 : 64; /* 64 for hard-disks, 1 for diskettes */ |
| CX_reg(context) = 512; |
| |
| BX_reg(context) = (available / (CX_reg(context) * AX_reg(context))); |
| DX_reg(context) = (size / (CX_reg(context) * AX_reg(context))); |
| Error (0,0,0); |
| } |
| |
| static void GetDriveAllocInfo(struct sigcontext_struct *context) |
| { |
| int drive; |
| long size, available; |
| |
| if (DL_reg(context) == 0) drive = DOS_GetDefaultDrive(); |
| else drive = DL_reg(context) - 1; |
| |
| if (!DOS_ValidDrive(drive)) |
| { |
| AX_reg(context) = 4; |
| CX_reg(context) = 512; |
| DX_reg(context) = 0; |
| Error (InvalidDrive, EC_MediaError, EL_Disk); |
| return; |
| } |
| |
| if (!DOS_GetFreeSpace(drive, &size, &available)) { |
| Error(GeneralFailure, EC_MediaError , EL_Disk); |
| AX_reg(context) = 0xffff; |
| return; |
| } |
| |
| AX_reg(context) = (drive < 2) ? 1 : 64; /* 64 for hard-disks, 1 for diskettes */ |
| CX_reg(context) = 512; |
| DX_reg(context) = (size / (CX_reg(context) * AX_reg(context))); |
| |
| heap->mediaID = 0xf0; |
| |
| DS_reg(context) = DosHeapHandle; |
| BX_reg(context) = (int)&heap->mediaID - (int)heap; |
| Error (0,0,0); |
| } |
| |
| static void GetDrivePB(struct sigcontext_struct *context, int drive) |
| { |
| if(!DOS_ValidDrive(drive)) |
| { |
| Error (InvalidDrive, EC_MediaError, EL_Disk); |
| AX_reg(context) = 0x00ff; |
| } |
| else |
| { |
| dprintf_int(stddeb, "int21: GetDrivePB not fully implemented.\n"); |
| |
| /* FIXME: I have no idea what a lot of this information should |
| * say or whether it even really matters since we're not allowing |
| * direct block access. However, some programs seem to depend on |
| * getting at least _something_ back from here. The 'next' pointer |
| * does worry me, though. Should we have a complete table of |
| * separate DPBs per drive? Probably, but I'm lazy. :-) -CH |
| */ |
| dpb->drive_num = dpb->unit_num = drive; /* The same? */ |
| dpb->sector_size = 512; |
| dpb->high_sector = 1; |
| dpb->shift = drive < 2 ? 0 : 6; /* 6 for HD, 0 for floppy */ |
| dpb->reserved = 0; |
| dpb->num_FAT = 1; |
| dpb->dir_entries = 2; |
| dpb->first_data = 2; |
| dpb->high_cluster = 64000; |
| dpb->sectors_in_FAT = 1; |
| dpb->start_dir = 1; |
| dpb->driver_head = 0; |
| dpb->media_ID = (drive > 1) ? 0xF8 : 0xF0; |
| dpb->access_flag = 0; |
| dpb->next = 0; |
| dpb->free_search = 0; |
| dpb->free_clusters = 0xFFFF; /* unknown */ |
| |
| AL_reg(context) = 0x00; |
| DS_reg(context) = SELECTOROF(dpbsegptr); |
| BX_reg(context) = OFFSETOF(dpbsegptr); |
| } |
| } |
| |
| static void ReadFile(struct sigcontext_struct *context) |
| { |
| char *ptr; |
| int size; |
| |
| /* can't read from stdout / stderr */ |
| if ((BX_reg(context) == 1) || (BX_reg(context) == 2)) { |
| Error (InvalidHandle, EL_Unknown, EC_Unknown); |
| AX_reg(context) = InvalidHandle; |
| SET_CFLAG(context); |
| dprintf_int(stddeb, "int21: read (%d, void *, 0x%x) = EBADF\n", |
| BX_reg(context), CX_reg(context)); |
| return; |
| } |
| |
| ptr = PTR_SEG_OFF_TO_LIN (DS_reg(context),DX_reg(context)); |
| if (BX_reg(context) == 0) { |
| *ptr = EOF; |
| Error (0,0,0); |
| AX_reg(context) = 1; |
| RESET_CFLAG(context); |
| dprintf_int(stddeb, "int21: read (%d, void *, 0x%x) = EOF\n", |
| BX_reg(context), CX_reg(context)); |
| return; |
| } else { |
| size = read(BX_reg(context), ptr, CX_reg(context)); |
| dprintf_int(stddeb,"int21: read(%d, %04x:%04x, 0x%x) = 0x%x\n", |
| BX_reg(context), DS_reg(context), DX_reg(context), |
| CX_reg(context), size ); |
| if (size == -1) { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| Error (0,0,0); |
| AX_reg(context) = size; |
| RESET_CFLAG(context); |
| } |
| } |
| |
| static void WriteFile(struct sigcontext_struct *context) |
| { |
| char *ptr; |
| int x,size; |
| |
| ptr = PTR_SEG_OFF_TO_LIN (DS_reg(context),DX_reg(context)); |
| |
| if (BX_reg(context) == 0) { |
| Error (InvalidHandle, EC_Unknown, EL_Unknown); |
| EAX_reg(context) = InvalidHandle; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| if (BX_reg(context) < 3) { |
| for (x = 0;x != CX_reg(context);x++) { |
| dprintf_int(stddeb, "%c", *ptr++); |
| } |
| fflush(stddeb); |
| |
| Error (0,0,0); |
| AX_reg(context) = CX_reg(context); |
| RESET_CFLAG(context); |
| } else { |
| /* well, this function already handles everything we need */ |
| size = _lwrite(BX_reg(context),ptr,CX_reg(context)); |
| if (size == -1) { /* HFILE_ERROR == -1 */ |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| Error (0,0,0); |
| AX_reg(context) = size; |
| RESET_CFLAG(context); |
| } |
| } |
| |
| static void SeekFile(struct sigcontext_struct *context) |
| { |
| off_t status, fileoffset; |
| |
| switch (AL_reg(context)) |
| { |
| case 1: fileoffset = SEEK_CUR; |
| break; |
| case 2: fileoffset = SEEK_END; |
| break; |
| default: |
| case 0: fileoffset = SEEK_SET; |
| break; |
| } |
| status = lseek(BX_reg(context), (CX_reg(context) << 16) + DX_reg(context), fileoffset); |
| |
| dprintf_int (stddeb, "int21: seek (%d, 0x%x, %d) = 0x%lx\n", |
| BX_reg(context), (CX_reg(context) << 16) + DX_reg(context), AL_reg(context), status); |
| |
| if (status == -1) |
| { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| Error (0,0,0); |
| AX_reg(context) = LOWORD(status); |
| DX_reg(context) = HIWORD(status); |
| RESET_CFLAG(context); |
| } |
| |
| static void ioctlGetDeviceInfo(struct sigcontext_struct *context) |
| { |
| |
| dprintf_int (stddeb, "int21: ioctl (%d, GetDeviceInfo)\n", BX_reg(context)); |
| |
| switch (BX_reg(context)) |
| { |
| case 0: |
| case 1: |
| case 2: |
| DX_reg(context) = 0x80d0 | (1 << (BX_reg(context) != 0)); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| { |
| struct stat sbuf; |
| |
| if (fstat(BX_reg(context), &sbuf) < 0) |
| { |
| INT_BARF( context, 0x21 ); |
| SET_CFLAG(context); |
| return; |
| } |
| |
| DX_reg(context) = 0x0943; |
| /* bits 0-5 are current drive |
| * bit 6 - file has NOT been written..FIXME: correct? |
| * bit 8 - generate int24 if no diskspace on write/ read past end of file |
| * bit 11 - media not removable |
| */ |
| } |
| } |
| RESET_CFLAG(context); |
| } |
| |
| static void ioctlGenericBlkDevReq(struct sigcontext_struct *context) |
| { |
| BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context)); |
| int drive; |
| |
| if (BL_reg(context) == 0) drive = DOS_GetDefaultDrive(); |
| else drive = BL_reg(context) - 1; |
| |
| if (!DOS_ValidDrive(drive)) |
| { |
| Error( FileNotFound, EC_NotFound, EL_Disk ); |
| AX_reg(context) = FileNotFound; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| if (CH_reg(context) != 0x08) |
| { |
| INT_BARF( context, 0x21 ); |
| return; |
| } |
| switch (CL_reg(context)) { |
| case 0x60: /* get device parameters */ |
| /* used by w4wgrp's winfile */ |
| memset(dataptr, 0, 0x26); |
| dataptr[0] = 0x04; |
| dataptr[6] = 0; /* media type */ |
| if (drive > 1) |
| { |
| dataptr[1] = 0x05; /* fixed disk */ |
| setword(&dataptr[2], 0x01); /* non removable */ |
| setword(&dataptr[4], 0x300); /* # of cylinders */ |
| } |
| else |
| { |
| dataptr[1] = 0x07; /* block dev, floppy */ |
| setword(&dataptr[2], 0x02); /* removable */ |
| setword(&dataptr[4], 80); /* # of cylinders */ |
| } |
| CreateBPB(drive, &dataptr[7]); |
| RESET_CFLAG(context); |
| return; |
| default: |
| INT_BARF( context, 0x21 ); |
| } |
| } |
| |
| static void GetSystemDate(struct sigcontext_struct *context) |
| { |
| struct tm *now; |
| time_t ltime; |
| |
| ltime = time(NULL); |
| now = localtime(<ime); |
| |
| CX_reg(context) = now->tm_year + 1900; |
| DX_reg(context) = ((now->tm_mon + 1) << 8) | now->tm_mday; |
| AX_reg(context) = now->tm_wday; |
| } |
| |
| static void GetSystemTime(struct sigcontext_struct *context) |
| { |
| struct tm *now; |
| struct timeval tv; |
| time_t seconds; |
| |
| gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */ |
| seconds = tv.tv_sec; |
| now = localtime(&seconds); |
| |
| CX_reg(context) = (now->tm_hour<<8) | now->tm_min; |
| DX_reg(context) = (now->tm_sec<<8) | tv.tv_usec/10000; |
| /* Note hundredths of seconds */ |
| } |
| |
| static void GetExtendedErrorInfo(struct sigcontext_struct *context) |
| { |
| AX_reg(context) = ExtendedError; |
| BX_reg(context) = (ErrorClass << 8) | Action; |
| CH_reg(context) = ErrorLocus; |
| } |
| |
| static void CreateFile(struct sigcontext_struct *context) |
| { |
| int handle; |
| handle = open(DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context), |
| DX_reg(context))), |
| O_CREAT | O_TRUNC | O_RDWR, 0666 ); |
| if (handle == -1) { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| Error (0,0,0); |
| AX_reg(context) = handle; |
| RESET_CFLAG(context); |
| } |
| |
| void OpenExistingFile(struct sigcontext_struct *context) |
| { |
| int handle; |
| int mode; |
| int lock; |
| |
| switch (AX_reg(context) & 0x0007) |
| { |
| case 0: |
| mode = O_RDONLY; |
| break; |
| |
| case 1: |
| mode = O_WRONLY; |
| break; |
| |
| default: |
| mode = O_RDWR; |
| break; |
| } |
| |
| if ((handle = open(DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))), |
| mode)) == -1) |
| { |
| if( Options.allowReadOnly ) |
| handle = open( DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))), |
| O_RDONLY ); |
| if( handle == -1 ) |
| { |
| dprintf_int (stddeb, "int21: open (%s, %d) = -1\n", |
| DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))), mode); |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| } |
| |
| dprintf_int (stddeb, "int21: open (%s, %d) = %d\n", |
| DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context), |
| DX_reg(context))), mode, handle); |
| |
| switch (AX_reg(context) & 0x0070) |
| { |
| case 0x00: /* compatability mode */ |
| case 0x40: /* DENYNONE */ |
| lock = -1; |
| break; |
| |
| case 0x30: /* DENYREAD */ |
| dprintf_int(stddeb, |
| "OpenExistingFile (%s): DENYREAD changed to DENYALL\n", |
| (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))); |
| case 0x10: /* DENYALL */ |
| lock = LOCK_EX; |
| break; |
| |
| case 0x20: /* DENYWRITE */ |
| lock = LOCK_SH; |
| break; |
| |
| default: |
| lock = -1; |
| } |
| |
| if (lock != -1) |
| { |
| |
| int result,retries=sharing_retries; |
| { |
| #ifdef __svr4__ |
| printf("Should call flock and needs porting to lockf\n"); |
| result = 0; |
| retries = 0; |
| #else |
| result = flock(handle, lock | LOCK_NB); |
| #endif |
| if ( retries && (!result) ) |
| { |
| int i; |
| for(i=0;i<32768*((int)sharing_pause);i++) |
| result++; /* stop the optimizer */ |
| for(i=0;i<32768*((int)sharing_pause);i++) |
| result--; |
| } |
| } |
| while( (!result) && (!(retries--)) ); |
| |
| if(result) |
| { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| close(handle); |
| SET_CFLAG(context); |
| return; |
| } |
| |
| } |
| |
| Error (0,0,0); |
| AX_reg(context) = handle; |
| RESET_CFLAG(context); |
| } |
| |
| static void CloseFile(struct sigcontext_struct *context) |
| { |
| dprintf_int (stddeb, "int21: close (%d)\n", BX_reg(context)); |
| |
| if (_lclose( BX_reg(context) )) |
| { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| Error (0,0,0); |
| AX_reg(context) = NoError; |
| RESET_CFLAG(context); |
| } |
| |
| static void RenameFile(struct sigcontext_struct *context) |
| { |
| char *newname, *oldname; |
| |
| /* FIXME: should not rename over an existing file */ |
| dprintf_int(stddeb,"int21: renaming %s to %s\n", |
| (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)), |
| (char *)PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context))); |
| |
| oldname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context), |
| DX_reg(context)) ); |
| newname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(ES_reg(context), |
| DI_reg(context)) ); |
| |
| rename( oldname, newname); |
| RESET_CFLAG(context); |
| } |
| |
| |
| static void MakeDir(struct sigcontext_struct *context) |
| { |
| char *dirname; |
| |
| dprintf_int(stddeb,"int21: makedir %s\n", (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) ); |
| |
| if ((dirname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context), |
| DX_reg(context)) ))== NULL) { |
| Error( CanNotMakeDir, EC_AccessDenied, EL_Disk ); |
| AX_reg(context) = CanNotMakeDir; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| if ((mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == -1) && errno!=EEXIST) { |
| Error( CanNotMakeDir, EC_AccessDenied, EL_Disk ); |
| AX_reg(context) = CanNotMakeDir; |
| SET_CFLAG(context); |
| return; |
| } |
| RESET_CFLAG(context); |
| } |
| |
| static void ChangeDir(struct sigcontext_struct *context) |
| { |
| int drive; |
| char *dirname = PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)); |
| drive = DOS_GetDefaultDrive(); |
| dprintf_int(stddeb,"int21: changedir %s\n", dirname); |
| if (dirname != NULL && dirname[1] == ':') { |
| drive = toupper(dirname[0]) - 'A'; |
| dirname += 2; |
| } |
| if (!DOS_ChangeDir(drive, dirname)) |
| { |
| SET_CFLAG(context); |
| AX_reg(context)=0x03; |
| } |
| } |
| |
| static void RemoveDir(struct sigcontext_struct *context) |
| { |
| char *dirname; |
| |
| dprintf_int(stddeb,"int21: removedir %s\n", (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) ); |
| |
| if ((dirname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) ))== NULL) { |
| Error( PathNotFound, EC_NotFound, EL_Disk ); |
| AX_reg(context) = PathNotFound; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| /* |
| if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) { |
| AL = CanNotRemoveCwd; |
| SET_CFLAG(context); |
| } |
| */ |
| if (rmdir(dirname) == -1) { |
| Error( AccessDenied, EC_AccessDenied, EL_Disk ); |
| AX_reg(context) = AccessDenied; |
| SET_CFLAG(context); |
| return; |
| } |
| RESET_CFLAG(context); |
| } |
| |
| static void FindNext(struct sigcontext_struct *context) |
| { |
| struct dosdirent *dp; |
| struct tm *t; |
| BYTE *dta = GetCurrentDTA(); |
| |
| memcpy(&dp, dta+0x11, sizeof(dp)); |
| |
| dprintf_int(stddeb, "int21: FindNext, dta %p, dp %p\n", dta, dp); |
| do { |
| if ((dp = DOS_readdir(dp)) == NULL) { |
| Error(NoMoreFiles, EC_MediaError , EL_Disk); |
| AX_reg(context) = NoMoreFiles; |
| SET_CFLAG(context); |
| return; |
| } |
| } /* while (*(dta + 0x0c) != dp->attribute);*/ |
| while ( ( dp->search_attribute & dp->attribute) != dp->attribute); |
| |
| *(dta + 0x15) = dp->attribute; |
| setword(&dta[0x0d], dp->entnum); |
| |
| t = localtime(&(dp->filetime)); |
| setword(&dta[0x16], (t->tm_hour << 11) + (t->tm_min << 5) + |
| (t->tm_sec / 2)); /* time */ |
| setword(&dta[0x18], ((t->tm_year - 80) << 9) + (t->tm_mon << 5) + |
| (t->tm_mday)); /* date */ |
| setdword(&dta[0x1a], dp->filesize); |
| strncpy(dta + 0x1e, dp->filename, 12); |
| *(dta + 0x1e + 13) = 0; |
| AnsiUpper(dta+0x1e); |
| |
| AX_reg(context) = 0; |
| RESET_CFLAG(context); |
| |
| dprintf_int(stddeb, "int21: FindNext -- (%s) index=%d size=%ld\n", dp->filename, dp->entnum, dp->filesize); |
| } |
| |
| static void FindFirst(struct sigcontext_struct *context) |
| { |
| BYTE drive, *path = PTR_SEG_OFF_TO_LIN(DS_reg(context), |
| DX_reg(context)); |
| struct dosdirent *dp; |
| |
| BYTE *dta = GetCurrentDTA(); |
| |
| dprintf_int(stddeb, "int21: FindFirst path = %s\n", path); |
| |
| if ((*path)&&(path[1] == ':')) { |
| drive = (islower(*path) ? toupper(*path) : *path) - 'A'; |
| |
| if (!DOS_ValidDrive(drive)) { |
| Error(InvalidDrive, EC_MediaError , EL_Disk); |
| AX_reg(context) = InvalidDrive; |
| SET_CFLAG(context); |
| return; |
| } |
| } else |
| drive = DOS_GetDefaultDrive(); |
| |
| *dta = drive; |
| memset(dta + 1 , '?', 11); |
| *(dta + 0x0c) = ECX_reg(context) & (FA_LABEL | FA_DIREC); |
| |
| if ((dp = DOS_opendir(path)) == NULL) { |
| Error(PathNotFound, EC_MediaError, EL_Disk); |
| AX_reg(context) = FileNotFound; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| dp->search_attribute = ECX_reg(context) & (FA_LABEL | FA_DIREC); |
| memcpy(dta + 0x11, &dp, sizeof(dp)); |
| FindNext(context); |
| |
| } |
| |
| static void GetFileDateTime(struct sigcontext_struct *context) |
| { |
| struct stat filestat; |
| struct tm *now; |
| |
| fstat( BX_reg(context), &filestat ); |
| now = localtime (&filestat.st_mtime); |
| |
| CX_reg(context) = ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2); |
| DX_reg(context) = (((now->tm_year + 1900 - 1980) * 0x200) + |
| (now->tm_mon * 0x20) + now->tm_mday); |
| |
| RESET_CFLAG(context); |
| } |
| |
| static void SetFileDateTime(struct sigcontext_struct *context) |
| { |
| char *filename; |
| struct utimbuf filetime; |
| |
| /* FIXME: Argument isn't the name of the file in DS:DX, |
| but the file handle in BX */ |
| filename = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context), |
| DX_reg(context)) ); |
| |
| filetime.actime = 0L; |
| filetime.modtime = filetime.actime; |
| |
| utime(filename, &filetime); |
| RESET_CFLAG(context); |
| } |
| |
| static void CreateTempFile(struct sigcontext_struct *context) |
| { |
| char temp[256]; |
| int handle; |
| static int counter = 0; |
| |
| sprintf(temp,"%s\\win%d.%03d",TempDirectory,(int) getpid(), counter); |
| counter = (counter + 1) % 1000; |
| |
| dprintf_int(stddeb,"CreateTempFile %s\n",temp); |
| |
| handle = open(DOS_GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR, 0666); |
| |
| if (handle == -1) { |
| Error( WriteProtected, EC_AccessDenied, EL_Disk ); |
| AX_reg(context) = WriteProtected; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| strcpy(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)), temp); |
| |
| AX_reg(context) = handle; |
| RESET_CFLAG(context); |
| } |
| |
| static void CreateNewFile(struct sigcontext_struct *context) |
| { |
| int handle; |
| |
| if ((handle = open(DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) ), |
| O_CREAT | O_EXCL | O_RDWR, 0666)) == -1) |
| { |
| Error( WriteProtected, EC_AccessDenied, EL_Disk ); |
| AX_reg(context) = WriteProtected; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| AX_reg(context) = handle; |
| RESET_CFLAG(context); |
| } |
| |
| static void GetCurrentDirectory(struct sigcontext_struct *context) |
| { |
| int drive; |
| |
| if (DL_reg(context) == 0) drive = DOS_GetDefaultDrive(); |
| else drive = DL_reg(context) - 1; |
| |
| if (!DOS_ValidDrive(drive)) |
| { |
| Error( InvalidDrive, EC_NotFound, EL_Disk ); |
| AX_reg(context) = InvalidDrive; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| strcpy(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context)), |
| DOS_GetCurrentDir(drive) ); |
| RESET_CFLAG(context); |
| } |
| |
| static void GetDiskSerialNumber(struct sigcontext_struct *context) |
| { |
| int drive; |
| BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context)); |
| DWORD serialnumber; |
| |
| if (BL_reg(context) == 0) drive = DOS_GetDefaultDrive(); |
| else drive = BL_reg(context) - 1; |
| |
| if (!DOS_ValidDrive(drive)) |
| { |
| Error( InvalidDrive, EC_NotFound, EL_Disk ); |
| AX_reg(context) = InvalidDrive; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| DOS_GetSerialNumber(drive, &serialnumber); |
| |
| setword(dataptr, 0); |
| setdword(&dataptr[2], serialnumber); |
| strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8); |
| strncpy(dataptr + 0x11, "FAT16 ", 8); |
| |
| AX_reg(context) = 0; |
| RESET_CFLAG(context); |
| } |
| |
| static void SetDiskSerialNumber(struct sigcontext_struct *context) |
| { |
| int drive; |
| BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context)); |
| DWORD serialnumber; |
| |
| if (BL_reg(context) == 0) drive = DOS_GetDefaultDrive(); |
| else drive = BL_reg(context) - 1; |
| |
| if (!DOS_ValidDrive(drive)) |
| { |
| Error( InvalidDrive, EC_NotFound, EL_Disk ); |
| AX_reg(context) = InvalidDrive; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) + |
| (dataptr[4] << 24); |
| |
| DOS_SetSerialNumber(drive, serialnumber); |
| AX_reg(context) = 1; |
| RESET_CFLAG(context); |
| } |
| |
| static void DumpFCB(BYTE *fcb) |
| { |
| int x, y; |
| |
| fcb -= 7; |
| |
| for (y = 0; y !=2 ; y++) { |
| for (x = 0; x!=15;x++) |
| dprintf_int(stddeb, "%02x ", *fcb++); |
| dprintf_int(stddeb,"\n"); |
| } |
| } |
| |
| /* microsoft's programmers should be shot for using CP/M style int21 |
| calls in Windows for Workgroup's winfile.exe */ |
| |
| static void FindFirstFCB(struct sigcontext_struct *context) |
| { |
| BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context)); |
| struct fcb *standard_fcb; |
| struct fcb *output_fcb; |
| int drive; |
| char path[12]; |
| |
| BYTE *dta = GetCurrentDTA(); |
| |
| DumpFCB( fcb ); |
| |
| if ((*fcb) == 0xff) |
| { |
| standard_fcb = (struct fcb *)(fcb + 7); |
| output_fcb = (struct fcb *)(dta + 7); |
| *dta = 0xff; |
| } |
| else |
| { |
| standard_fcb = (struct fcb *)fcb; |
| output_fcb = (struct fcb *)dta; |
| } |
| |
| if (standard_fcb->drive) |
| { |
| drive = standard_fcb->drive - 1; |
| if (!DOS_ValidDrive(drive)) |
| { |
| Error (InvalidDrive, EC_MediaError, EL_Disk); |
| AX_reg(context) = 0xff; |
| return; |
| } |
| } |
| else |
| drive = DOS_GetDefaultDrive(); |
| |
| output_fcb->drive = drive; |
| |
| if (*(fcb) == 0xff) |
| { |
| if (*(fcb+6) & FA_LABEL) /* return volume label */ |
| { |
| *(dta+6) = FA_LABEL; |
| memset(&output_fcb->name, ' ', 11); |
| if (DOS_GetVolumeLabel(drive) != NULL) |
| { |
| strncpy(output_fcb->name, DOS_GetVolumeLabel(drive), 11); |
| AX_reg(context) = 0x00; |
| return; |
| } |
| } |
| } |
| |
| strncpy(output_fcb->name, standard_fcb->name, 11); |
| if (*fcb == 0xff) |
| *(dta+6) = ( *(fcb+6) & (!FA_DIREC)); |
| |
| sprintf(path,"%c:*.*",drive+'A'); |
| if ((output_fcb->directory = DOS_opendir(path))==NULL) |
| { |
| Error (PathNotFound, EC_MediaError, EL_Disk); |
| AX_reg(context) = 0xff; |
| return; |
| } |
| |
| } |
| |
| |
| static void DeleteFileFCB(struct sigcontext_struct *context) |
| { |
| BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context)); |
| int drive; |
| struct dosdirent *dp; |
| char temp[256], *ptr; |
| |
| DumpFCB( fcb ); |
| |
| if (*fcb) |
| drive = *fcb - 1; |
| else |
| drive = DOS_GetDefaultDrive(); |
| |
| strcpy(temp, DOS_GetCurrentDir(drive)); |
| strcat(temp, "\\"); |
| strncat(temp, fcb + 1, 8); |
| ChopOffWhiteSpace(temp); |
| strncat(temp, fcb + 9, 3); |
| ChopOffWhiteSpace(temp); |
| |
| if ((dp = DOS_opendir(temp)) == NULL) { |
| Error(InvalidDrive, EC_MediaError , EL_Disk); |
| AX_reg(context) = 0xff; |
| return; |
| } |
| |
| strcpy(temp, DOS_GetCurrentDir(drive) ); |
| strcat(temp, "\\"); |
| |
| ptr = temp + strlen(temp); |
| |
| while (DOS_readdir(dp) != NULL) |
| { |
| strcpy(ptr, dp->filename); |
| dprintf_int(stddeb, "int21: delete file %s\n", temp); |
| /* unlink(DOS_GetUnixFileName(temp)); */ |
| } |
| DOS_closedir(dp); |
| AX_reg(context) = 0; |
| } |
| |
| static void RenameFileFCB(struct sigcontext_struct *context) |
| { |
| BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context)); |
| int drive; |
| struct dosdirent *dp; |
| char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr; |
| |
| DumpFCB( fcb ); |
| |
| if (*fcb) |
| drive = *fcb - 1; |
| else |
| drive = DOS_GetDefaultDrive(); |
| |
| strcpy(temp, DOS_GetCurrentDir(drive)); |
| strcat(temp, "\\"); |
| strncat(temp, fcb + 1, 8); |
| ChopOffWhiteSpace(temp); |
| strncat(temp, fcb + 9, 3); |
| ChopOffWhiteSpace(temp); |
| |
| if ((dp = DOS_opendir(temp)) == NULL) { |
| Error(InvalidDrive, EC_MediaError , EL_Disk); |
| AX_reg(context) = 0xff; |
| return; |
| } |
| |
| strcpy(oldname, DOS_GetCurrentDir(drive) ); |
| strcat(oldname, "\\"); |
| oldnameptr = oldname + strlen(oldname); |
| |
| strcpy(newname, DOS_GetCurrentDir(drive) ); |
| strcat(newname, "\\"); |
| newnameptr = newname + strlen(newname); |
| |
| while (DOS_readdir(dp) != NULL) |
| { |
| strcpy(oldnameptr, dp->filename); |
| strcpy(newnameptr, fcb + 1); |
| dprintf_int(stddeb, "int21: renamefile %s -> %s\n", |
| oldname, newname); |
| } |
| DOS_closedir(dp); |
| AX_reg(context) = 0; |
| } |
| |
| |
| |
| static void fLock (struct sigcontext_struct * context) |
| { |
| struct flock f; |
| int result,retries=sharing_retries; |
| |
| f.l_start = MAKELONG(DX_reg(context),CX_reg(context)); |
| f.l_len = MAKELONG(DI_reg(context),SI_reg(context)); |
| f.l_whence = 0; |
| f.l_pid = 0; |
| |
| switch ( AX_reg(context) & 0xff ) |
| { |
| case 0x00: /* LOCK */ |
| f.l_type = F_WRLCK; |
| break; |
| |
| case 0x01: /* UNLOCK */ |
| f.l_type = F_UNLCK; |
| break; |
| |
| default: |
| AX_reg(context) = 0x0001; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| { |
| result = fcntl(BX_reg(context),F_SETLK,&f); |
| if ( retries && (!result) ) |
| { |
| int i; |
| for(i=0;i<32768*((int)sharing_pause);i++) |
| result++; /* stop the optimizer */ |
| for(i=0;i<32768*((int)sharing_pause);i++) |
| result--; |
| } |
| } |
| while( (!result) && (!(retries--)) ); |
| |
| if(result) |
| { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| Error (0,0,0); |
| RESET_CFLAG(context); |
| } |
| |
| |
| static void GetFileAttribute (struct sigcontext_struct * context) |
| { |
| char *filename = PTR_SEG_OFF_TO_LIN (DS_reg(context),DX_reg(context)); |
| struct stat s; |
| int res; |
| |
| res = stat(DOS_GetUnixFileName(filename), &s); |
| if (res==-1) |
| { |
| errno_to_doserr(); |
| AX_reg(context) = ExtendedError; |
| SET_CFLAG(context); |
| return; |
| } |
| |
| CX_reg(context) = 0; |
| if (S_ISDIR(s.st_mode)) |
| CX_reg(context) |= 0x10; |
| if ((S_IWRITE & s.st_mode) != S_IWRITE) |
| CX_reg(context) |= 0x01; |
| |
| dprintf_int (stddeb, "int21: GetFileAttributes (%s) = 0x%x\n", |
| filename, CX_reg(context) ); |
| |
| RESET_CFLAG(context); |
| Error (0,0,0); |
| } |
| |
| |
| extern void LOCAL_PrintHeap (WORD ds); |
| |
| /*********************************************************************** |
| * DOS3Call (KERNEL.102) |
| */ |
| void DOS3Call( struct sigcontext_struct context ) |
| { |
| int drive; |
| |
| if (AH_reg(&context) == 0x59) |
| { |
| GetExtendedErrorInfo(&context); |
| return; |
| } |
| else |
| { |
| Error (0,0,0); |
| switch(AH_reg(&context)) |
| { |
| case 0x00: /* TERMINATE PROGRAM */ |
| TASK_KillCurrentTask( 0 ); |
| break; |
| |
| case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */ |
| case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */ |
| case 0x03: /* READ CHARACTER FROM STDAUX */ |
| case 0x04: /* WRITE CHARACTER TO STDAUX */ |
| case 0x05: /* WRITE CHARACTER TO PRINTER */ |
| case 0x06: /* DIRECT CONSOLE IN/OUTPUT */ |
| case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */ |
| case 0x08: /* CHARACTER INPUT WITHOUT ECHO */ |
| case 0x09: /* WRITE STRING TO STANDARD OUTPUT */ |
| case 0x0a: /* BUFFERED INPUT */ |
| case 0x0b: /* GET STDIN STATUS */ |
| case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */ |
| case 0x0f: /* OPEN FILE USING FCB */ |
| case 0x10: /* CLOSE FILE USING FCB */ |
| case 0x12: /* FIND NEXT MATCHING FILE USING FCB */ |
| case 0x14: /* SEQUENTIAL READ FROM FCB FILE */ |
| case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */ |
| case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */ |
| case 0x21: /* READ RANDOM RECORD FROM FCB FILE */ |
| case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */ |
| case 0x23: /* GET FILE SIZE FOR FCB */ |
| case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */ |
| case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */ |
| case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */ |
| case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */ |
| case 0x29: /* PARSE FILENAME INTO FCB */ |
| case 0x2e: /* SET VERIFY FLAG */ |
| INT_BARF( &context, 0x21 ); |
| break; |
| |
| case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER |
| "SWITCHAR" - SET SWITCH CHARACTER |
| "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */ |
| case 0x54: /* GET VERIFY FLAG */ |
| INT_BARF( &context, 0x21 ); |
| break; |
| |
| case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */ |
| case 0x1d: |
| case 0x1e: |
| case 0x20: |
| case 0x6b: /* NULL FUNCTION */ |
| AL_reg(&context) = 0; |
| break; |
| |
| case 0x5c: /* "FLOCK" - RECORD LOCKING */ |
| fLock(&context); |
| break; |
| |
| case 0x0d: /* DISK BUFFER FLUSH */ |
| RESET_CFLAG(&context); /* dos 6+ only */ |
| break; |
| |
| case 0x0e: /* SELECT DEFAULT DRIVE */ |
| if (!DOS_ValidDrive(DL_reg(&context))) |
| Error (InvalidDrive, EC_MediaError, EL_Disk); |
| else |
| { |
| DOS_SetDefaultDrive(DL_reg(&context)); |
| Error(0,0,0); |
| } |
| AL_reg(&context) = MAX_DOS_DRIVES; |
| break; |
| |
| case 0x11: /* FIND FIRST MATCHING FILE USING FCB */ |
| FindFirstFCB(&context); |
| break; |
| |
| case 0x13: /* DELETE FILE USING FCB */ |
| DeleteFileFCB(&context); |
| break; |
| |
| case 0x17: /* RENAME FILE USING FCB */ |
| RenameFileFCB(&context); |
| break; |
| |
| case 0x19: /* GET CURRENT DEFAULT DRIVE */ |
| AL_reg(&context) = DOS_GetDefaultDrive(); |
| Error (0,0,0); |
| break; |
| |
| case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */ |
| { |
| TDB *pTask = (TDB *)GlobalLock( GetCurrentTask() ); |
| pTask->dta = MAKELONG( DX_reg(&context), DS_reg(&context) ); |
| dprintf_int(stddeb, "int21: Set DTA: %08lx\n", pTask->dta); |
| } |
| break; |
| |
| case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */ |
| DL_reg(&context) = 0; |
| GetDriveAllocInfo(&context); |
| break; |
| |
| case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */ |
| GetDriveAllocInfo(&context); |
| break; |
| |
| case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */ |
| GetDrivePB(&context, DOS_GetDefaultDrive()); |
| break; |
| |
| case 0x25: /* SET INTERRUPT VECTOR */ |
| INT_SetHandler( AL_reg(&context), |
| MAKELONG( DX_reg(&context), DS_reg(&context) ) ); |
| break; |
| |
| case 0x2a: /* GET SYSTEM DATE */ |
| GetSystemDate(&context); |
| break; |
| |
| case 0x2b: /* SET SYSTEM DATE */ |
| fprintf( stdnimp, "SetSystemDate(%02d/%02d/%04d): not allowed\n", |
| DL_reg(&context), DH_reg(&context), CX_reg(&context) ); |
| AL_reg(&context) = 0; /* Let's pretend we succeeded */ |
| break; |
| |
| case 0x2c: /* GET SYSTEM TIME */ |
| GetSystemTime(&context); |
| break; |
| |
| case 0x2d: /* SET SYSTEM TIME */ |
| fprintf( stdnimp, "SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n", |
| CH_reg(&context), CL_reg(&context), |
| DH_reg(&context), DL_reg(&context) ); |
| AL_reg(&context) = 0; /* Let's pretend we succeeded */ |
| break; |
| |
| case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */ |
| { |
| TDB *pTask = (TDB *)GlobalLock( GetCurrentTask() ); |
| ES_reg(&context) = SELECTOROF( pTask->dta ); |
| BX_reg(&context) = OFFSETOF( pTask->dta ); |
| } |
| break; |
| |
| case 0x30: /* GET DOS VERSION */ |
| AX_reg(&context) = DOSVERSION; |
| BX_reg(&context) = 0x0012; /* 0x123456 is Wine's serial # */ |
| CX_reg(&context) = 0x3456; |
| break; |
| |
| case 0x31: /* TERMINATE AND STAY RESIDENT */ |
| INT_BARF( &context, 0x21 ); |
| break; |
| |
| case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */ |
| GetDrivePB(&context, (DL_reg(&context) == 0) ? |
| (DOS_GetDefaultDrive()) : (DL_reg(&context)-1)); |
| break; |
| |
| case 0x33: /* MULTIPLEXED */ |
| switch (AL_reg(&context)) |
| { |
| case 0x00: /* GET CURRENT EXTENDED BREAK STATE */ |
| DL_reg(&context) = 0; |
| break; |
| |
| case 0x01: /* SET EXTENDED BREAK STATE */ |
| break; |
| |
| case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/ |
| DL_reg(&context) = 0; |
| break; |
| |
| case 0x05: /* GET BOOT DRIVE */ |
| DL_reg(&context) = 2; |
| /* c: is Wine's bootdrive */ |
| break; |
| |
| case 0x06: /* GET TRUE VERSION NUMBER */ |
| BX_reg(&context) = DOSVERSION; |
| DX_reg(&context) = 0x00; |
| break; |
| |
| default: |
| INT_BARF( &context, 0x21 ); |
| break; |
| } |
| break; |
| |
| case 0x34: /* GET ADDRESS OF INDOS FLAG */ |
| ES_reg(&context) = DosHeapHandle; |
| BX_reg(&context) = (int)&heap->InDosFlag - (int)heap; |
| break; |
| |
| case 0x35: /* GET INTERRUPT VECTOR */ |
| { |
| SEGPTR addr = INT_GetHandler( AL_reg(&context) ); |
| ES_reg(&context) = SELECTOROF(addr); |
| BX_reg(&context) = OFFSETOF(addr); |
| } |
| break; |
| |
| case 0x36: /* GET FREE DISK SPACE */ |
| GetFreeDiskSpace(&context); |
| break; |
| |
| case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */ |
| AX_reg(&context) = 0x02; /* no country support available */ |
| SET_CFLAG(&context); |
| break; |
| |
| case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */ |
| MakeDir(&context); |
| break; |
| |
| case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */ |
| RemoveDir(&context); |
| break; |
| |
| case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */ |
| ChangeDir(&context); |
| break; |
| |
| case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */ |
| CreateFile(&context); |
| break; |
| |
| case 0x3d: /* "OPEN" - OPEN EXISTING FILE */ |
| OpenExistingFile(&context); |
| break; |
| |
| case 0x3e: /* "CLOSE" - CLOSE FILE */ |
| CloseFile(&context); |
| break; |
| |
| case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */ |
| ReadFile(&context); |
| break; |
| |
| case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */ |
| WriteFile(&context); |
| break; |
| |
| case 0x41: /* "UNLINK" - DELETE FILE */ |
| if (unlink( DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(&context),DX_reg(&context))) ) == -1) { |
| errno_to_doserr(); |
| AX_reg(&context) = ExtendedError; |
| SET_CFLAG(&context); |
| break; |
| } |
| Error(0,0,0); |
| RESET_CFLAG(&context); |
| break; |
| |
| case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */ |
| SeekFile(&context); |
| break; |
| |
| case 0x43: /* FILE ATTRIBUTES */ |
| switch (AL_reg(&context)) |
| { |
| case 0x00: |
| GetFileAttribute(&context); |
| break; |
| case 0x01: |
| RESET_CFLAG(&context); |
| break; |
| } |
| break; |
| |
| case 0x44: /* IOCTL */ |
| switch (AL_reg(&context)) |
| { |
| case 0x00: |
| ioctlGetDeviceInfo(&context); |
| break; |
| |
| case 0x01: |
| |
| break; |
| case 0x08: /* Check if drive is removable. */ |
| drive = BL_reg(&context) ? (BL_reg(&context) - 1) |
| : DOS_GetDefaultDrive(); |
| if(!DOS_ValidDrive(drive)) |
| { |
| Error( InvalidDrive, EC_NotFound, EL_Disk ); |
| AX_reg(&context) = InvalidDrive; |
| SET_CFLAG(&context); |
| } |
| else |
| { |
| if (drive > 1) |
| AX_reg(&context) = 1; /* not removable */ |
| else |
| AX_reg(&context) = 0; /* removable */ |
| RESET_CFLAG(&context); |
| } |
| break; |
| |
| case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */ |
| drive = BL_reg(&context) ? (BL_reg(&context) - 1) |
| : DOS_GetDefaultDrive(); |
| if(!DOS_ValidDrive(drive)) |
| { |
| Error( InvalidDrive, EC_NotFound, EL_Disk ); |
| AX_reg(&context) = InvalidDrive; |
| SET_CFLAG(&context); |
| } |
| else |
| { |
| DX_reg(&context) = (1<<9) | (1<<12) | (1<<15); |
| RESET_CFLAG(&context); |
| } |
| break; |
| case 0x0a: /* check if handle (BX) is remote */ |
| /* returns DX, bit 15 set if remote, bit 14 set if date/time |
| * not set on close |
| */ |
| DX_reg(&context) = 0; |
| RESET_CFLAG(&context); |
| break; |
| case 0x0b: /* SET SHARING RETRY COUNT */ |
| if (!CX_reg(&context)) |
| { |
| AX_reg(&context) = 1; |
| SET_CFLAG(&context); |
| break; |
| } |
| sharing_pause = CX_reg(&context); |
| if (!DX_reg(&context)) |
| sharing_retries = DX_reg(&context); |
| RESET_CFLAG(&context); |
| break; |
| |
| case 0x0d: |
| ioctlGenericBlkDevReq(&context); |
| break; |
| |
| case 0x0F: /* Set logical drive mapping */ |
| /* FIXME: Not implemented at the moment, always returns error |
| */ |
| fprintf(stdnimp,"Attempt to map drive %02x\n",BL_reg(&context)); |
| AX_reg(&context) = 0x0001; /* invalid function */ |
| SET_CFLAG(&context); |
| break; |
| |
| default: |
| INT_BARF( &context, 0x21 ); |
| break; |
| } |
| break; |
| |
| case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */ |
| if ((AX_reg(&context) = dup(BX_reg(&context))) == 0xffff) |
| { |
| errno_to_doserr(); |
| AX_reg(&context) = ExtendedError; |
| SET_CFLAG(&context); |
| } |
| else RESET_CFLAG(&context); |
| break; |
| |
| case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */ |
| if (dup2( BX_reg(&context), CX_reg(&context) ) == -1) |
| { |
| errno_to_doserr(); |
| AX_reg(&context) = ExtendedError; |
| SET_CFLAG(&context); |
| } |
| else RESET_CFLAG(&context); |
| break; |
| |
| case 0x47: /* "CWD" - GET CURRENT DIRECTORY */ |
| GetCurrentDirectory(&context); |
| AX_reg(&context) = 0x0100; |
| /* intlist: many Microsoft products for Windows rely on this */ |
| break; |
| |
| case 0x48: /* ALLOCATE MEMORY */ |
| case 0x49: /* FREE MEMORY */ |
| case 0x4a: /* RESIZE MEMORY BLOCK */ |
| INT_BARF( &context, 0x21 ); |
| break; |
| |
| case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */ |
| WinExec( PTR_SEG_OFF_TO_LIN(DS_reg(&context),DX_reg(&context)), |
| SW_NORMAL ); |
| break; |
| |
| case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */ |
| TASK_KillCurrentTask( AL_reg(&context) ); |
| break; |
| |
| case 0x4d: /* GET RETURN CODE */ |
| AX_reg(&context) = NoError; /* normal exit */ |
| break; |
| |
| case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */ |
| FindFirst(&context); |
| break; |
| |
| case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */ |
| FindNext(&context); |
| break; |
| |
| case 0x51: /* GET PSP ADDRESS */ |
| case 0x62: /* GET PSP ADDRESS */ |
| /* FIXME: should we return the original DOS PSP upon */ |
| /* Windows startup ? */ |
| BX_reg(&context) = GetCurrentPDB(); |
| break; |
| |
| case 0x52: /* "SYSVARS" - GET LIST OF LISTS */ |
| ES_reg(&context) = 0x0; |
| BX_reg(&context) = 0x0; |
| INT_BARF( &context, 0x21 ); |
| break; |
| |
| case 0x56: /* "RENAME" - RENAME FILE */ |
| RenameFile(&context); |
| break; |
| |
| case 0x57: /* FILE DATE AND TIME */ |
| switch (AL_reg(&context)) |
| { |
| case 0x00: |
| GetFileDateTime(&context); |
| break; |
| case 0x01: |
| SetFileDateTime(&context); |
| break; |
| } |
| break; |
| |
| case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */ |
| switch (AL_reg(&context)) |
| { |
| case 0x00: |
| AX_reg(&context) = 1; |
| break; |
| case 0x02: |
| AX_reg(&context) = 0; |
| break; |
| case 0x01: |
| case 0x03: |
| break; |
| } |
| RESET_CFLAG(&context); |
| break; |
| |
| case 0x5a: /* CREATE TEMPORARY FILE */ |
| CreateTempFile(&context); |
| break; |
| |
| case 0x5b: /* CREATE NEW FILE */ |
| CreateNewFile(&context); |
| break; |
| |
| case 0x5d: /* NETWORK */ |
| case 0x5e: |
| /* network software not installed */ |
| AX_reg(&context) = NoNetwork; |
| SET_CFLAG(&context); |
| break; |
| |
| case 0x5f: /* NETWORK */ |
| switch (AL_reg(&context)) |
| { |
| case 0x07: /* ENABLE DRIVE */ |
| if (!DOS_EnableDrive(DL_reg(&context))) |
| { |
| Error(InvalidDrive, EC_MediaError , EL_Disk); |
| AX_reg(&context) = InvalidDrive; |
| SET_CFLAG(&context); |
| break; |
| } |
| else |
| { |
| RESET_CFLAG(&context); |
| break; |
| } |
| case 0x08: /* DISABLE DRIVE */ |
| if (!DOS_DisableDrive(DL_reg(&context))) |
| { |
| Error(InvalidDrive, EC_MediaError , EL_Disk); |
| AX_reg(&context) = InvalidDrive; |
| SET_CFLAG(&context); |
| break; |
| } |
| else |
| { |
| RESET_CFLAG(&context); |
| break; |
| } |
| default: |
| /* network software not installed */ |
| AX_reg(&context) = NoNetwork; |
| SET_CFLAG(&context); |
| break; |
| } |
| break; |
| |
| case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */ |
| strncpy(PTR_SEG_OFF_TO_LIN(ES_reg(&context),DI_reg(&context)), |
| PTR_SEG_OFF_TO_LIN(DS_reg(&context),SI_reg(&context)), |
| strlen(PTR_SEG_OFF_TO_LIN(DS_reg(&context),SI_reg(&context))) & 0x7f); |
| RESET_CFLAG(&context); |
| break; |
| |
| case 0x61: /* UNUSED */ |
| case 0x63: /* UNUSED */ |
| case 0x64: /* OS/2 DOS BOX */ |
| case 0x65: /* GET EXTENDED COUNTRY INFORMATION */ |
| INT_BARF( &context, 0x21 ); |
| break; |
| |
| case 0x66: /* GLOBAL CODE PAGE TABLE */ |
| switch (AL_reg(&context)) |
| { |
| case 0x01: |
| DX_reg(&context) = BX_reg(&context) = CodePage; |
| RESET_CFLAG(&context); |
| break; |
| case 0x02: |
| CodePage = BX_reg(&context); |
| RESET_CFLAG(&context); |
| break; |
| } |
| break; |
| |
| case 0x67: /* SET HANDLE COUNT */ |
| RESET_CFLAG(&context); |
| break; |
| |
| case 0x68: /* "FFLUSH" - COMMIT FILE */ |
| case 0x6a: /* COMMIT FILE */ |
| fsync( BX_reg(&context) ); |
| RESET_CFLAG(&context); |
| break; |
| |
| case 0x69: /* DISK SERIAL NUMBER */ |
| switch (AL_reg(&context)) |
| { |
| case 0x00: |
| GetDiskSerialNumber(&context); |
| break; |
| case 0x01: |
| SetDiskSerialNumber(&context); |
| break; |
| } |
| break; |
| |
| case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */ |
| break; |
| |
| case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */ |
| break; |
| |
| default: |
| INT_BARF( &context, 0x21 ); |
| break; |
| } |
| } |
| dprintf_int(stddeb,"ret21: AX %04x, BX %04x, CX %04x, DX %04x, " |
| "SI %04x, DI %04x, DS %04x, ES %04x EFL %08lx\n", |
| AX_reg(&context), BX_reg(&context), CX_reg(&context), |
| DX_reg(&context), SI_reg(&context), DI_reg(&context), |
| DS_reg(&context), ES_reg(&context), EFL_reg(&context)); |
| } |
| |
| |
| void INT21_Init(void) |
| { |
| if ((DosHeapHandle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0) |
| { |
| fprintf( stderr, "INT21_Init: Out of memory\n"); |
| exit(1); |
| } |
| heap = (struct DosHeap *) GlobalLock(DosHeapHandle); |
| |
| dpb = &heap->dpb; |
| dpbsegptr = MAKELONG( (int)&heap->dpb - (int)heap, DosHeapHandle ); |
| heap->InDosFlag = 0; |
| strcpy(heap->biosdate, "01/01/80"); |
| } |