Logo Search packages:      
Sourcecode: dosbox version File versions  Download package

dos.cpp

/*
 *  Copyright (C) 2002-2004  The DOSBox Team
 *
 *  This program 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 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* $Id: dos.cpp,v 1.77 2004/11/16 14:24:52 qbix79 Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "dosbox.h"
#include "bios.h"
#include "mem.h"
#include "callback.h"
#include "regs.h"
#include "dos_inc.h"
#include "setup.h"

DOS_Block dos;
DOS_InfoBlock dos_infoblock;

Bit8u dos_copybuf[0x10000];
static Bitu call_20,call_21,call_25,call_26,call_27,call_28,call_29;

void DOS_SetError(Bit16u code) {
      dos.errorcode=code;
}

#define DOSNAMEBUF 256
static Bitu DOS_21Handler(void) {
      if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah<0x6c)) {
            DOS_PSP psp(dos.psp());
            psp.SetStack(RealMake(SegValue(ss),reg_sp-18));
      }

      char name1[DOSNAMEBUF+1];
      char name2[DOSNAMEBUF+1];
      switch (reg_ah) {
      case 0x01:        /* Read character from STDIN, with echo */
            {     
                  Bit8u c;Bit16u n=1;
                  dos.echo=true;
                  DOS_ReadFile(STDIN,&c,&n);
                  reg_al=c;
                  dos.echo=false;
            }
            break;
      case 0x02:        /* Write character to STDOUT */
            {
                  Bit8u c=reg_dl;Bit16u n=1;
                  DOS_WriteFile(STDOUT,&c,&n);
            }
            break;
      case 0x03:        /* Read character from STDAUX */
      case 0x04:        /* Write Character to STDAUX */
      case 0x05:        /* Write Character to PRINTER */
            E_Exit("DOS:Unhandled call %02X",reg_ah);
            break;
      case 0x06:        /* Direct Console Output / Input */
            switch (reg_dl) {
            case 0xFF:  /* Input */
                  {     
//TODO Make this better according to standards
                        if (!DOS_GetSTDINStatus()) {
                              reg_al=0;
                              CALLBACK_SZF(true);
                              break;
                        }
                        Bit8u c;Bit16u n=1;
                        DOS_ReadFile(STDIN,&c,&n);
                        reg_al=c;
                        CALLBACK_SZF(false);
                        break;
                  }
            default:
                  {
                        Bit8u c=reg_dl;Bit16u n=1;
                        DOS_WriteFile(STDOUT,&c,&n);
                  }
                  break;
            };
            break;
      case 0x07:        /* Character Input, without echo */
            {
                        Bit8u c;Bit16u n=1;
                        DOS_ReadFile (STDIN,&c,&n);
                        reg_al=c;
                        break;
            };
      case 0x08:        /* Direct Character Input, without echo (checks for breaks officially :)*/
            {
                        Bit8u c;Bit16u n=1;
                        DOS_ReadFile (STDIN,&c,&n);
                        reg_al=c;
                        break;
            };
      case 0x09:        /* Write string to STDOUT */
            {     
                  Bit8u c;Bit16u n=1;
                  PhysPt buf=SegPhys(ds)+reg_dx;
                  while ((c=mem_readb(buf++))!='$') {
                        DOS_WriteFile(STDOUT,&c,&n);
                  }
            }
            break;
      case 0x0a:        /* Buffered Input */
            {
                  //TODO ADD Break checkin in STDIN but can't care that much for it
                  PhysPt data=SegPhys(ds)+reg_dx;
                  Bit8u free=mem_readb(data);
                  Bit8u read=0;Bit8u c;Bit16u n=1;
                  if (!free) break;
                  while (read<free) {
                        DOS_ReadFile(STDIN,&c,&n);
                        DOS_WriteFile(STDOUT,&c,&n);
                        mem_writeb(data+read+2,c);
                        if (c==13) 
                              break;
                        read++;
                  };
                  mem_writeb(data+1,read);
                  break;
            };
      case 0x0b:        /* Get STDIN Status */
            if (!DOS_GetSTDINStatus()) {reg_al=0x00;}
            else {reg_al=0xFF;}
            break;
      case 0x0c:        /* Flush Buffer and read STDIN call */
            {
                  switch (reg_al) {
                  case 0x1:
                  case 0x6:
                  case 0x7:
                  case 0x8:
                  case 0xa:
                        { 
                              Bit8u oldah=reg_ah;
                              reg_ah=reg_al;
                              DOS_21Handler();
                              reg_ah=oldah;
                        }
                        break;
                  default:
//                      LOG_ERROR("DOS:0C:Illegal Flush STDIN Buffer call %d",reg_al);
                        break;
                  }
            }
            break;
//TODO Find out the values for when reg_al!=0
//TODO Hope this doesn't do anything special
      case 0x0d:        /* Disk Reset */
//Sure let's reset a virtual disk
            break;      
      case 0x0e:        /* Select Default Drive */
            DOS_SetDefaultDrive(reg_dl);
            reg_al=DOS_DRIVES;
            break;
      case 0x0f:        /* Open File using FCB */
            if(DOS_FCBOpen(SegValue(ds),reg_dx)){
                  reg_al=0;
            }else{
                  reg_al=0xff;
            }
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x0f FCB-fileopen used, result:al=%d",reg_al);
            break;
      case 0x10:        /* Close File using FCB */
            if(DOS_FCBClose(SegValue(ds),reg_dx)){
                  reg_al=0;
            }else{
                  reg_al=0xff;
            }
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x10 FCB-fileclose used, result:al=%d",reg_al);
            break;
      case 0x11:        /* Find First Matching File using FCB */
            if(DOS_FCBFindFirst(SegValue(ds),reg_dx)) reg_al = 0x00;
            else reg_al = 0xFF;
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x11 FCB-FindFirst used, result:al=%d",reg_al);
            break;
      case 0x12:        /* Find Next Matching File using FCB */
            if(DOS_FCBFindNext(SegValue(ds),reg_dx)) reg_al = 0x00;
            else reg_al = 0xFF;
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x12 FCB-FindNext used, result:al=%d",reg_al);
            break;
      case 0x13:        /* Delete File using FCB */
            if (DOS_FCBDeleteFile(SegValue(ds),reg_dx)) reg_al = 0x00;
            else reg_al = 0xFF;
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Delete used, result:al=%d",reg_al);
            break;
      case 0x14:        /* Sequential read from FCB */
            reg_al = DOS_FCBRead(SegValue(ds),reg_dx,0);
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x14 FCB-Read used, result:al=%d",reg_al);
            break;
      case 0x15:        /* Sequential write to FCB */
            reg_al=DOS_FCBWrite(SegValue(ds),reg_dx,0);
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x15 FCB-Write used, result:al=%d",reg_al);
            break;
      case 0x16:        /* Create or truncate file using FCB */
            if (DOS_FCBCreate(SegValue(ds),reg_dx)) reg_al = 0x00;
            else reg_al = 0xFF;
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Create used, result:al=%d",reg_al);
            break;
      case 0x17:        /* Rename file using FCB */         
            if (DOS_FCBRenameFile(SegValue(ds),reg_dx)) reg_al = 0x00;
            else reg_al = 0xFF;
            break;
      case 0x1b:        /* Get allocation info for default drive */     
            if (!DOS_GetAllocationInfo(0,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
            break;
      case 0x1c:        /* Get allocation info for specific drive */
            if (!DOS_GetAllocationInfo(reg_dl,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
            break;
      case 0x21:        /* Read random record from FCB */
            reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,1,true);
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x21 FCB-Random read used, result:al=%d",reg_al);
            break;
      case 0x22:        /* Write random record to FCB */
            reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,1,true);
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x22 FCB-Random write used, result:al=%d",reg_al);
            break;
      case 0x23:        /* Get file size for FCB */
            if (DOS_FCBGetFileSize(SegValue(ds),reg_dx,reg_cx)) reg_al = 0x00;
            else reg_al = 0xFF;
            break;
      case 0x24:        /* Set Random Record number for FCB */
            DOS_FCBSetRandomRecord(SegValue(ds),reg_dx);
            break;
      case 0x27:        /* Random block read from FCB */
            reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,reg_cx,false);
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x27 FCB-Random(block) read used, result:al=%d",reg_al);
            break;
      case 0x28:        /* Random Block write to FCB */
            reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,reg_cx,false);
            LOG(LOG_FCB,LOG_NORMAL)("DOS:0x28 FCB-Random(block) write used, result:al=%d",reg_al);
            break;
      case 0x29:        /* Parse filename into FCB */
            {   
                  Bit8u difference;
                  char string[1024];
                  MEM_StrCopy(SegPhys(ds)+reg_si,string,1024);
                  reg_al=FCB_Parsename(SegValue(es),reg_di,reg_al ,string, &difference);
                  reg_si+=difference;
            }
            LOG(LOG_FCB,LOG_NORMAL)("DOS:29:FCB Parse Filename, result:al=%d",reg_al);
            break;
      case 0x19:        /* Get current default drive */
            reg_al=DOS_GetDefaultDrive();
            break;
      case 0x1a:        /* Set Disk Transfer Area Address */
            dos.dta(RealMakeSeg(ds,reg_dx));
            break;
      case 0x25:        /* Set Interrupt Vector */
            RealSetVec(reg_al,RealMakeSeg(ds,reg_dx));
            break;
      case 0x26:        /* Create new PSP */
            DOS_NewPSP(reg_dx,DOS_PSP(dos.psp()).GetSize());
            break;
      case 0x2a:        /* Get System Date */
            {
                  int a = (14 - dos.date.month)/12;
                  int y = dos.date.year - a;
                  int m = dos.date.month + 12*a - 2;
                  reg_al=(dos.date.day+y+(y/4)-(y/100)+(y/400)+(31*m)/12) % 7;
                  reg_cx=dos.date.year;
                  reg_dh=dos.date.month;
                  reg_dl=dos.date.day;
            }
            break;
      case 0x2b:        /* Set System Date */
            if (reg_cx<1980) { reg_al=0xff;break;}
            if ((reg_dh>12) || (reg_dh==0))     { reg_al=0xff;break;}
            if ((reg_dl>31) || (reg_dl==0))     { reg_al=0xff;break;}
            dos.date.year=reg_cx;
            dos.date.month=reg_dh;
            dos.date.day=reg_dl;
            reg_al=0;
            break;
      case 0x2c:        /* Get System Time */
//TODO Get time through bios calls date is fixed
            {
/*    Calculate how many miliseconds have passed */
                  Bitu ticks=5*mem_readd(BIOS_TIMER);
                  ticks = ((ticks / 59659u) << 16) + ((ticks % 59659u) << 16) / 59659u;
                  Bitu seconds=(ticks/100);
                  reg_ch=(Bit8u)(seconds/3600);
                  reg_cl=(Bit8u)((seconds % 3600)/60);
                  reg_dh=(Bit8u)(seconds % 60);
                  reg_dl=(Bit8u)(ticks % 100);
            }
            break;
      case 0x2d:        /* Set System Time */
            LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set System Time not supported");
            reg_al=0;   /* Noone is changing system time */
            break;
      case 0x2e:        /* Set Verify flag */
            dos.verify=(reg_al==1);
            break;
      case 0x2f:        /* Get Disk Transfer Area */
            SegSet16(es,RealSeg(dos.dta()));
            reg_bx=RealOff(dos.dta());
            break;
      case 0x30:        /* Get DOS Version */
            if (reg_al==0) reg_bh=0xFF;         /* Fake Microsoft DOS */
            if (reg_al==1) reg_bh=0x10;         /* DOS is in HMA */
            reg_al=dos.version.major;
            reg_ah=dos.version.minor;
            /* Serialnumber */
            reg_bl=0x00;
            reg_cx=0x0000;
            break;
      case 0x31:        /* Terminate and stay resident */
            //TODO First get normal files executing
            // Important: This service does not set the carry flag!
            DOS_ResizeMemory(dos.psp(),&reg_dx);
            DOS_Terminate(true);
            dos.return_code=reg_al; //Officially a field in the SDA
            dos.return_mode=RETURN_TSR;
            break;
        case 0x32: /* Get drive parameter block for specific drive */
            {     /* Officially a dpb should be returned as well. The disk detection part is implemented */
                  Bitu drive=reg_dl;if(!drive) drive=dos.current_drive;else drive--;
                  if(Drives[drive]) {
                        reg_al=0x00;
                        LOG(LOG_DOSMISC,LOG_ERROR)("Get drive parameter block.");   
                  } else {
                        reg_al=0xff;
                  }
            }
            break;
      case 0x33:        /* Extended Break Checking */
            switch (reg_al) {
                  case 0:reg_dl=dos.breakcheck;break;             /* Get the breakcheck flag */
                  case 1:dos.breakcheck=(reg_dl>0);break;         /* Set the breakcheck flag */
                  case 2:{bool old=dos.breakcheck;dos.breakcheck=(reg_dl>0);reg_dl=old;}break;
                  case 5:reg_dl=3;break;//TODO should be z                                /* Always boot from c: :) */
                  case 6:                                                                 /* Get true version number */
                        reg_bl=dos.version.major;
                        reg_bh=dos.version.minor;
                        reg_dl=dos.version.revision;
                        reg_dh=0x10;                                                /* Dos in HMA */
                        break;
                  default:
                        E_Exit("DOS:Illegal 0x33 Call %2X",reg_al);                             
            }
            break;
      case 0x34:        /* Get INDos Flag */
            SegSet16(es,DOS_SDA_SEG);
            reg_bx=DOS_SDA_OFS + 0x01;
            break;
      case 0x35:        /* Get interrupt vector */
            reg_bx=real_readw(0,((Bit16u)reg_al)*4);
            SegSet16(es,real_readw(0,((Bit16u)reg_al)*4+2));
            break;
      case 0x36:        /* Get Free Disk Space */
            {
                  Bit16u bytes,clusters,free;
                  Bit8u sectors;
                  if(DOS_GetFreeDiskSpace(reg_dl,&bytes,&sectors,&clusters,&free)) {
                        reg_ax=sectors;
                        reg_bx=free;
                        reg_cx=bytes;
                        reg_dx=clusters;
                  } else {
                        reg_ax=0xffff;
                  }
            }
            break;
      case 0x37:        /* Get/Set Switch char Get/Set Availdev thing */
//TODO      Give errors for these functions to see if anyone actually uses this shit-
            switch (reg_al) {
            case 0:
                   reg_al=0;reg_dl=0x2f;break;  /* always return '/' like dos 5.0+ */
            case 1:
                   reg_al=0;break;
            case 2:
                   reg_al=0;reg_dl=0x2f;break;
            case 3:
                   reg_al=0;break;
            };
            LOG(LOG_MISC,LOG_ERROR)("DOS:0x37:Call for not supported switchchar");
            break;
      case 0x38:                          /* Set Country Code */  
            if (reg_al==0) {        /* Get country specidic information */
                  PhysPt dest = SegPhys(ds)+reg_dx;
                  MEM_BlockWrite(dest,dos.tables.country,0x22);
                  reg_bx = 0x01;
                  CALLBACK_SCF(false);
                  break;
            } else {                      /* Set country code */
                  LOG(LOG_MISC,LOG_ERROR)("DOS:Setting country code not supported");
            }
            CALLBACK_SCF(true);
            break;
      case 0x39:        /* MKDIR Create directory */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if (DOS_MakeDir(name1)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x3a:        /* RMDIR Remove directory */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if  (DOS_RemoveDir(name1)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
                  LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode);
            }
            break;
      case 0x3b:        /* CHDIR Set current directory */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if  (DOS_ChangeDir(name1)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x3c:        /* CREATE Create of truncate file */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if (DOS_CreateFile(name1,reg_cx,&reg_ax)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x3d:        /* OPEN Open existing file */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if (DOS_OpenFile(name1,reg_al,&reg_ax)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x3e:        /* CLOSE Close file */
            if (DOS_CloseFile(reg_bx)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x3f:        /* READ Read from file or device */
            { 
                  Bit16u toread=reg_cx;
                  dos.echo=true;
                  if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) {
                        MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread);
                        reg_ax=toread;
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
                  dos.echo=false;
                  break;
            }
      case 0x40:                          /* WRITE Write to file or device */
            {
                  Bit16u towrite=reg_cx;
                  MEM_BlockRead(SegPhys(ds)+reg_dx,dos_copybuf,towrite);
                  if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) {
                        reg_ax=towrite;
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
                  break;
            };
      case 0x41:                          /* UNLINK Delete file */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if (DOS_UnlinkFile(name1)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x42:                          /* LSEEK Set current file position */
            {
                  Bit32u pos=(reg_cx<<16) + reg_dx;
                  if (DOS_SeekFile(reg_bx,&pos,reg_al)) {
                        reg_dx=(Bit16u)(pos >> 16);
                        reg_ax=(Bit16u)(pos & 0xFFFF);
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
                  break;
            }
      case 0x43:                          /* Get/Set file attributes */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            switch (reg_al)
            case 0x00:                    /* Get */
            {
                  if (DOS_GetFileAttr(name1,&reg_cx)) {
                        reg_ax=reg_cx; /* Undocumented */   
                        CALLBACK_SCF(false);
                  } else {
                        CALLBACK_SCF(true);
                        reg_ax=dos.errorcode;
                  }
                  break;
            case 0x01:                    /* Set */
                  LOG(LOG_MISC,LOG_ERROR)("DOS:Set File Attributes for %s not supported",name1);
                  if (DOS_SetFileAttr(name1,reg_cx)) {
                        CALLBACK_SCF(false);
                  } else {
                        CALLBACK_SCF(true);
                        reg_ax=dos.errorcode;
                  }
                  break;
            default:
                  LOG(LOG_MISC,LOG_ERROR)("DOS:0x43:Illegal subfunction %2X",reg_al);
                  CALLBACK_SCF(false);
                  break;
            }
            break;
      case 0x44:                          /* IOCTL Functions */
            if (DOS_IOCTL()) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x45:                          /* DUP Duplicate file handle */
            if (DOS_DuplicateEntry(reg_bx,&reg_ax)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x46:                          /* DUP2,FORCEDUP Force duplicate file handle */
            if (DOS_ForceDuplicateEntry(reg_bx,reg_ax)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x47:                          /* CWD Get current directory */
            if (DOS_GetCurrentDir(reg_dl,name1)) {
                  MEM_BlockWrite(SegPhys(ds)+reg_si,name1,strlen(name1)+1);   
                  reg_ax=0x0100;
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x48:                          /* Allocate memory */
            {
                  Bit16u size=reg_bx;Bit16u seg;
                  if (DOS_AllocateMemory(&seg,&size)) {
                        reg_ax=seg;
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        reg_bx=size;
                        CALLBACK_SCF(true);
                  }
                  break;
            }
      case 0x49:                          /* Free memory */
            if (DOS_FreeMemory(SegValue(es))) {
                  CALLBACK_SCF(false);
            } else {            
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x4a:                          /* Resize memory block */
            {
                  Bit16u size=reg_bx;
                  if (DOS_ResizeMemory(SegValue(es),&size)) {
                        reg_ax=SegValue(es);
                        CALLBACK_SCF(false);
                  } else {            
                        reg_ax=dos.errorcode;
                        reg_bx=size;
                        CALLBACK_SCF(true);
                  }
                  break;
            }
      case 0x4b:                          /* EXEC Load and/or execute program */
            { 
                  MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
                  LOG(LOG_EXEC,LOG_ERROR)("Execute %s %d",name1,reg_al);
                  if (!DOS_Execute(name1,SegPhys(es)+reg_bx,reg_al)) {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
            }
            break;
//TODO Check for use of execution state AL=5
      case 0x00:
            reg_ax=0x4c00;                      /* Terminate Program */
      case 0x4c:                          /* EXIT Terminate with return code */
              {
                  if (DOS_Terminate(false)) {
                        /* This can't ever return false normally */
                  } else {            
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
                  break;
            }
      case 0x4d:                          /* Get Return code */
            reg_al=dos.return_code;/* Officially read from SDA and clear when read */
            reg_ah=dos.return_mode;
            break;
      case 0x4e:                          /* FINDFIRST Find first matching file */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            if (DOS_FindFirst(name1,reg_cx)) {
                  CALLBACK_SCF(false);    
                  reg_ax=0;               /* Undocumented */
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            };
            break;             
      case 0x4f:                          /* FINDNEXT Find next matching file */
            if (DOS_FindNext()) {
                  CALLBACK_SCF(false);
                  /* reg_ax=0xffff;*/                 /* Undocumented */
                  reg_ax=0;                     /* Undocumented:Qbix Willy beamish */
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            };
            break;            
      case 0x50:                          /* Set current PSP */
            dos.psp(reg_bx);
            break;
      case 0x51:                          /* Get current PSP */
            reg_bx=dos.psp();
            break;
      case 0x52: {                        /* Get list of lists */
            RealPt addr=dos_infoblock.GetPointer();
            SegSet16(es,RealSeg(addr));
            reg_bx=RealOff(addr);
            LOG(LOG_DOSMISC,LOG_NORMAL)("Call is made for list of lists - let's hope for the best");
            break; }
//TODO Think hard how shit this is gonna be
//And will any game ever use this :)
      case 0x53:                          /* Translate BIOS parameter block to drive parameter block */
            E_Exit("Unhandled Dos 21 call %02X",reg_ah);
            break;
      case 0x54:                          /* Get verify flag */
            reg_al=dos.verify?1:0;
            break;
      case 0x55:                          /* Create Child PSP*/
            DOS_ChildPSP(reg_dx,reg_si);
            dos.psp(reg_dx);
            break;
      case 0x56:                          /* RENAME Rename file */
            MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
            MEM_StrCopy(SegPhys(es)+reg_di,name2,DOSNAMEBUF);
            if (DOS_Rename(name1,name2)) {
                  CALLBACK_SCF(false);                
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;            
      case 0x57:                          /* Get/Set File's Date and Time */
            if (reg_al==0x00) {
                  if (DOS_GetFileDate(reg_bx,&reg_cx,&reg_dx)) {
                        CALLBACK_SCF(false);
                  } else {
                        CALLBACK_SCF(true);
                  }
            } else if (reg_al==0x01) {
                  LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Set File Date Time Faked");
                  CALLBACK_SCF(false);          
            } else {
                  LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Unsupported subtion %X",reg_al);
            }
            break;
      case 0x58:                          /* Get/Set Memory allocation strategy */
            switch (reg_al) {
            case 0:                             /* Get Strategy */
                  reg_ax=DOS_GetMemAllocStrategy();
                  break;
            case 1:                             /* Set Strategy */
                  DOS_SetMemAllocStrategy(reg_bx);
                  break;
            case 2:                             /* Get UMB Link Status */
                  reg_ax=1;   // no UMB support 
                  CALLBACK_SCF(true);
                  break;
            case 3:                             /* Set UMB Link Status */
                  reg_ax=1;   // failure, no support
                  CALLBACK_SCF(true);
                  break;
            default:
                  LOG(LOG_DOSMISC,LOG_ERROR)("DOS:58:Not Supported Set//Get memory allocation call %X",reg_al);
            }
            break;
      case 0x59:                          /* Get Extended error information */
            reg_ax=dos.errorcode;
            reg_bh=0;   //Unkown error class
            reg_bl=1;   //Retry retry retry
            reg_ch=0;   //Unkown error locus
            break;
      case 0x5a:                          /* Create temporary file */
            {
                  Bit16u handle;
                  MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
                  if (DOS_CreateTempFile(name1,&handle)) {
                        reg_ax=handle;
                        MEM_BlockWrite(SegPhys(ds)+reg_dx,name1,strlen(name1)+1);
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
            }
            break;
      case 0x5b:                          /* Create new file */
            {
                  MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
                  Bit16u handle;
                  if (DOS_OpenFile(name1,0,&handle)) {
                        DOS_CloseFile(handle);
                        DOS_SetError(DOSERR_ACCESS_DENIED);
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                        break;
                  }
                  if (DOS_CreateFile(name1,reg_cx,&handle)) {
                        reg_ax=handle;
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
                  break;
            }
      case 0x5d:                          /* Network Functions */
            if(reg_al == 0x06) {
                  SegSet16(ds,DOS_SDA_SEG);
                  reg_si = DOS_SDA_OFS;
                  reg_cx = 0x80;  // swap if in dos
                  reg_dx = 0x1a;  // swap always
                  LOG(LOG_DOSMISC,LOG_ERROR)("Get SDA, Let's hope for the best!");
            }
            break;
      case 0x5f:                          /* Network redirection */
            reg_ax=0x0001;          //Failing it
            CALLBACK_SCF(true);
            break; 
      case 0x60:                          /* Canonicalize filename or path */
            MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF);
            if (DOS_Canonicalize(name1,name2)) {
                        MEM_BlockWrite(SegPhys(es)+reg_di,name2,strlen(name2)+1);   
                        CALLBACK_SCF(false);
                  } else {
                        reg_ax=dos.errorcode;
                        CALLBACK_SCF(true);
                  }
                  break;
      case 0x62:                          /* Get Current PSP Address */
            reg_bx=dos.psp();
            break;
      case 0x63:                          /* DOUBLE BYTE CHARACTER SET */
            if(reg_al == 0) {
                  SegSet16(ds,RealSeg(dos.tables.dcbs));
                  reg_si=RealOff(dos.tables.dcbs);          
                  reg_al = 0;
                  CALLBACK_SCF(false); //undocumented
            } else reg_al = 0xff; //Doesn't officially touch carry flag
            break;
      case 0x64:                          /* Set device driver lookahead flag */
            E_Exit("Unhandled Dos 21 call %02X",reg_ah);
            break;
      case 0x65:                          /* Get extented country information and a lot of other useless shit*/
            { /* Todo maybe fully support this for now we set it standard for USA */ 
                  LOG(LOG_DOSMISC,LOG_ERROR)("DOS:65:Extended country information call %X",reg_ax);
                  if(reg_cx < 0x05 ) {
                        DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
                        CALLBACK_SCF(true);
                        break;
                  }
                  PhysPt data=SegPhys(es)+reg_di;
                  switch (reg_al) {
                  case 1:
                        mem_writeb(data + 0x00,reg_al);
                        mem_writew(data + 0x01,0x26);
                        mem_writew(data + 0x03,1);
                        if(reg_cx > 0x06 ) mem_writew(data+0x05,0x01b5);
                        if(reg_cx > 0x08 ) {
                              Bitu amount = (reg_cx>=0x29)?0x22:(reg_cx-7);
                              MEM_BlockWrite(data + 0x07,dos.tables.country,amount);
                              reg_cx=(reg_cx>=0x29)?0x29:reg_cx;
                        }
                        CALLBACK_SCF(false);
                        break;
                  case 2:     // Get pointer to uppercase table
                  case 3: // Get pointer to lowercase table
                  case 4: // Get pointer to filename uppercase table
                  case 5: // Get pointer to filename terminator table
                  case 6: // Get pointer to collating sequence table
                  case 7: // Get pointer to double byte char set table
                        mem_writeb(data,reg_al);
                        mem_writed(data+1,dos.tables.dcbs); //used to be 0
                        reg_cx=5;
                        CALLBACK_SCF(false);
                        break;
                  default:
                        E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al); 
                  };
                  break;
            }
      case 0x66:                          /* Get/Set global code page table  */
            if (reg_al==1) {
                  LOG(LOG_DOSMISC,LOG_ERROR)("Getting global code page table");
                  reg_bx=reg_dx=437;
                  CALLBACK_SCF(false);
                  break;
            }
            LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Setting code page table is not supported");
            break;
      case 0x67:                          /* Set handle count */
            /* Weird call to increase amount of file handles needs to allocate memory if >20 */
            {
                  DOS_PSP psp(dos.psp());
                  psp.SetNumFiles(reg_bx);
                  CALLBACK_SCF(false);
                  break;
            };
      case 0x69:                          /* Get/Set disk serial number */
            {
                  switch(reg_al)          {
                  case 0x00:                    /* Get */
                        LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Get Disk serial number");
                        CALLBACK_SCF(true);
                        break;
                  case 0x01:
                        LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set Disk serial number");
                  default:
                        E_Exit("DOS:Illegal Get Serial Number call %2X",reg_al);
                  }     
                  break;
            } 
      case 0x6c:                          /* Extended Open/Create */
            MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF);
            if (DOS_OpenFileExtended(name1,reg_bx,reg_cx,reg_dx,&reg_ax,&reg_cx)) {
                  CALLBACK_SCF(false);
            } else {
                  reg_ax=dos.errorcode;
                  CALLBACK_SCF(true);
            }
            break;
      case 0x71:                          /* Unknown probably 4dos detection */
            reg_ax=0x7100;
            CALLBACK_SCF(true);
            LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Windows long file name support call %2X",reg_al);
            break;

    case 0x68:                  /* FFLUSH Commit file */
        CALLBACK_SCF(false);    //mirek
    case 0xE0:
    case 0x18:                      /* NULL Function for CP/M compatibility or Extended rename FCB */
    case 0x1d:                      /* NULL Function for CP/M compatibility or Extended rename FCB */
    case 0x1e:                      /* NULL Function for CP/M compatibility or Extended rename FCB */
    case 0x20:                      /* NULL Function for CP/M compatibility or Extended rename FCB */
    case 0x6b:                      /* NULL Function */
    case 0x61:                      /* UNUSED */
    case 0xEF:                  /* Used in Ancient Art Of War CGA */
      case 0x1f:                          /* Get drive parameter block for default drive */

      case 0x5c:                          /* FLOCK File region locking */
      case 0x5e:                          /* More Network Functions */
    default:
        LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Unhandled call %02X al=%02X. Set al to default of 0",reg_ah,reg_al);
        reg_al=0x00; /* default value */
            break;
      };
      return CBRET_NONE;
/* That's it now let's get it working */
};



static Bitu DOS_20Handler(void) {

      reg_ax=0x4c00;
      DOS_21Handler();
      return CBRET_NONE;
}

static Bitu DOS_27Handler(void) 
{
      // Terminate & stay resident
      Bit16u para = (reg_dx/16)+((reg_dx % 16)>0);
      if (DOS_ResizeMemory(dos.psp(),&para)) DOS_Terminate(true);
      return CBRET_NONE;
}
static Bitu DOS_25Handler(void) {
      if(Drives[reg_al]==0){
            reg_ax=0x8002;
            SETFLAGBIT(CF,true);
      }else{
            SETFLAGBIT(CF,false);
            if((reg_cx != 1) ||(reg_dx != 1))
                  LOG(LOG_DOSMISC,LOG_NORMAL)("int 25 called but not as diskdetection drive %X",reg_al);

         reg_ax=0;
      }
    return CBRET_NONE;
}
static Bitu DOS_26Handler(void) {
      LOG(LOG_DOSMISC,LOG_NORMAL)("int 26 called: hope for the best!");
      if(Drives[reg_al]==0){
            reg_ax=0x8002;
            SETFLAGBIT(CF,true);
      }else{
            SETFLAGBIT(CF,false);
            reg_ax=0;
      }
    return CBRET_NONE;
}
static Bitu DOS_28Handler(void) {
    return CBRET_NONE;
}

static Bitu DOS_29Handler(void) {
      static bool int29warn=false;
      if(!int29warn) { 
            LOG(LOG_DOSMISC,LOG_WARN)("Int 29 called. Redirecting to int 10:0x0e");
            int29warn=true;
      }
      reg_ah=0x0e;
      CALLBACK_RunRealInt(0x10);
      return CBRET_NONE;
}


void DOS_ShutDown(Section* sec)
{     
      for (Bit16u i=0;i<DOS_DRIVES;i++) delete Drives[i];
};


void DOS_Init(Section* sec) {
      call_20=CALLBACK_Allocate();
      CALLBACK_Setup(call_20,DOS_20Handler,CB_IRET,"DOS Int 20");
      RealSetVec(0x20,CALLBACK_RealPointer(call_20));

      call_21=CALLBACK_Allocate();
      CALLBACK_Setup(call_21,DOS_21Handler,CB_IRET_STI,"DOS Int 21");
      RealSetVec(0x21,CALLBACK_RealPointer(call_21));

      call_25=CALLBACK_Allocate();
      CALLBACK_Setup(call_25,DOS_25Handler,CB_RETF,"DOS Int 25");
      RealSetVec(0x25,CALLBACK_RealPointer(call_25));
      
      call_26=CALLBACK_Allocate();
      CALLBACK_Setup(call_26,DOS_26Handler,CB_RETF,"DOS Int 26");
      RealSetVec(0x26,CALLBACK_RealPointer(call_26));
      
      call_27=CALLBACK_Allocate();
      CALLBACK_Setup(call_27,DOS_27Handler,CB_IRET,"DOS Int 27");
      RealSetVec(0x27,CALLBACK_RealPointer(call_27));

      call_28=CALLBACK_Allocate();
      CALLBACK_Setup(call_28,DOS_28Handler,CB_IRET,"DOS Int 28");
      RealSetVec(0x28,CALLBACK_RealPointer(call_28));

      call_29=CALLBACK_Allocate();
      CALLBACK_Setup(call_29,DOS_29Handler,CB_IRET,"CON Output Int 29");
      RealSetVec(0x29,CALLBACK_RealPointer(call_29));

      DOS_SetupFiles();                                           /* Setup system File tables */
      DOS_SetupDevices();                                         /* Setup dos devices */
      DOS_SetupTables();
      DOS_SetupMemory();                                                /* Setup first MCB */
      DOS_SetupPrograms();
      DOS_SetupMisc();                                      /* Some additional dos interrupts */
      DOS_SetDefaultDrive(25);

      dos.version.major=5;
      dos.version.minor=0;

      /* Setup time and date */
      time_t curtime;struct tm *loctime;
      curtime = time (NULL);loctime = localtime (&curtime);

      dos.date.day=(Bit8u)loctime->tm_mday;
      dos.date.month=(Bit8u)loctime->tm_mon+1;
      dos.date.year=(Bit16u)loctime->tm_year+1900;
      Bit32u ticks=(Bit32u)((loctime->tm_hour*3600+loctime->tm_min*60+loctime->tm_sec)*18.2);
      mem_writed(BIOS_TIMER,ticks);

      /* shutdown function */
      sec->AddDestroyFunction(&DOS_ShutDown);   

}


Generated by  Doxygen 1.6.0   Back to index