home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 8 Other / 08-Other.zip / cpem8010.zip / bdos.cc next >
C/C++ Source or Header  |  1995-02-16  |  31KB  |  1,047 lines

  1. //     $Id: bdos.cc 1.7 1995/02/16 22:54:59 rg Exp $
  2. //      Copyright (c) 1994 by R. Griech
  3. //
  4. //  BDOS Simulation
  5. //  ---------------
  6. //  Trial of a CP/M 2.2 and partially 3.0 emulation.  This emu seems to run very
  7. //  nice for many compilers (turbo pascal 3.0, turbo modula 1.0, another modula2-
  8. //  compiler, the slr180-assembler, m80-assembler, strukta-preprocessor, some
  9. //  debuggers and corresponding tools).  Maybe some system tools like pip and stat
  10. //  will not work, but who cares...
  11. //
  12. //  possible problem areas
  13. //  ----------------------
  14. //  - the simulation is based mostly on 2.2.  Anyway there are some other functions
  15. //    taken from CP/M3.0 for proper working of some proggies (e.g. DSD 'needs' the
  16. //    system time (IMO pseudo copy protection))
  17. //  - the simulation is not complete
  18. //  - things not simulated:
  19. //    - there is no multi sector counting for disk I/O (CP/M3.0 feature)
  20. //    - no file attribute handling
  21. //  - the CP/M BDOS can't be simulated completely by this OS;  this is because
  22. //    - CP/M doesn't have any file handles and thus the FCBs can be moved around
  23. //      in memory, thrown away and so on, without any problems for deleting/
  24. //      renaming etc files.
  25. //    - thus the simulations close is a NOOP.  On other specific operations, the 
  26. //      simulation tries to close internally hold FCBs;  this might lead into trouble,
  27. //      because (may be) there are some ambiguities between the found (thru search
  28. //      algo) and the intended files.
  29. //      Closing NOOP is required for files that are e.g. twice opened and then
  30. //      one of the FCBs is closed...
  31. //  - the mapping of drives and pathes and so on might be ambigious (e.g. the path
  32. //    of the calling OS-shell has priority)
  33. //  - wildcard features are not fully supported, e.g you can't find deleted
  34. //    directory entries (SearchFirst/Next)
  35. //  - SearchFirst/Next don't reflect a CP/M drive (remember dir-extensions);
  36. //    also they are much more tolerant according to the call order (it is
  37. //    possible to do some other BDOS file calls intermediately);
  38. //    the return dir-block is always zero
  39. //  - CON/LPT as files are not allowed (because they can't be 'lseek'ed, but
  40. //    nevertheless not caught
  41. //  - sequential access after random access is limited to 2MByte (???)
  42. //  - information about disk allocation is not reliable.  Instead a default
  43. //    configuration is returned, i.e. there are some KB free on the current drive
  44. //    but the existing files are NOT mirrored into the allocation map
  45. //  - the write protect functions (28/29) have no effect
  46. //
  47. //  real bugs
  48. //  ---------
  49. //  - the FCB->Hnd mapping is not very clear (even to me)
  50. //  - some CP/M functions are real dummies (these thingies can be caught thru
  51. //    the -NT option :-))
  52. //
  53. //  versions
  54. //  --------
  55. //  040494  after changing the FCB detection (for equality) from the FCB address
  56. //          to the FCB contents, turbo-M2 seems to run without any problems
  57. //  110494  SearchForFirst/Next are now handled thru opendir() et al.
  58. //          Former version used _fnexplode(), which also found subdirs (not so
  59. //          nice)
  60. //  020594  introduced an LRU scheme for the open FCBs.  This was due to problems
  61. //          with RSX, that allows only 20 (?) open files.
  62. //
  63.  
  64.  
  65.  
  66. #include <stdio.h>
  67. #include <stdlib.h>
  68. #include <sys/types.h>
  69. #include <conio.h>
  70. #include <ctype.h>
  71. #include <dirent.h>
  72. #include <fcntl.h>
  73. #include <io.h>
  74. #include <string.h>
  75. #include <time.h>
  76. #include <Strng.h>
  77. #include "glob.h"
  78.  
  79.  
  80.  
  81. #define DBP_ADR (BDOS_BEG+0x18)     /* 17 Bytes Länge */
  82. #define ALO_ADR (BDOS_BEG+0x40)     /* 30 Bytes Länge */
  83.  
  84.  
  85.  
  86. static unsigned short DmaAdr;
  87. static unsigned short User;
  88. static unsigned short DefDrive;     // 0=A:,...
  89. static unsigned short WriteProtMap;
  90. static DIR *SearchDir = NULL;
  91. static FCB SearchPat;
  92. static unsigned long LruCnt = 0;
  93. static int OpenFcbs = 0;            // number of currently open FCBs
  94.  
  95. struct {
  96.    int Hnd;
  97.    char Name[12];
  98.    String OsPath;
  99.    int FMode;
  100.    unsigned long LruCnt;
  101. } FcbHndMap[MAX_FCBS+1];
  102.  
  103. #define FCB_FREE     -1
  104. #define FCB_CLOSED   -2
  105. #define FCB_MAXOPEN  10            // max number of concurrently open files
  106.                    // ~7 more required ?
  107.  
  108. #define GET_FCB(A) ((FCB *)(Mem+A))
  109. #define SET_EXIT_REGS16 { r.b.A = r.b.L;  r.b.B = r.b.H; }
  110.  
  111.  
  112.  
  113. static unsigned char ConSts( void )
  114. {
  115.    REGS rr;
  116.    rr.w.PC = BIOS_BEG+0x06;  //CONST
  117.    Bios( &rr );
  118.    return( rr.b.A );
  119. }   // ConSts
  120.  
  121.  
  122.  
  123. static unsigned char ConIn( void )
  124. {
  125.    REGS rr;
  126.    rr.w.PC = BIOS_BEG+0x09;  // CONIN
  127.    Bios( &rr );
  128.    return( rr.b.A );
  129. }   // ConIn
  130.  
  131.  
  132.  
  133. static void ConOut( unsigned char ch )
  134. {
  135.    REGS rr;
  136.    rr.w.PC = BIOS_BEG+0x0c;  // CONOUT
  137.    rr.b.C  = ch;
  138.    Bios( &rr );
  139. }   // ConOut
  140.  
  141.  
  142.  
  143. static void ConOutS( char *s )
  144. {
  145.    while (*s)
  146.       ConOut( *(s++) );
  147. }   // ConOutS
  148.  
  149.  
  150.  
  151. static String FcbToString( FCB *fcb )
  152. //
  153. //  konvertiert einen FCB in einen String (großgeschrieben)
  154. //
  155. {
  156.    String FName;
  157.    int i;
  158.    char f[sizeof(fcb->f)+1];
  159.    char t[sizeof(fcb->t)+1];
  160.  
  161.    for (i = 0;  i < sizeof(fcb->f);  i++)
  162.       f[i] = toupper((fcb->f[i]) & 0x7f);
  163.    f[i] = 0;
  164.    for (i = 0;  i < sizeof(fcb->t);  i++)
  165.       t[i] = toupper((fcb->t[i]) & 0x7f);
  166.    t[i] = 0;
  167.  
  168.    //
  169.    //  es wird jeweils bis zum ersten Blank weggeworfen, d.h.
  170.    //  in den Dateinamen können keine Blanks vorkommen !!!
  171.    //
  172.    FName = String(fcb->dr + '@') + ":";
  173.    FName += String(f);
  174.    FName = FName.at( Regex("[^ ]*") );
  175.    if (String(t) == String("COM"))
  176.       FName += String(".CPM");
  177.    else
  178.    if (String(t) != String("   ")) {
  179.       FName += "." + String(t);
  180.       FName = FName.at( Regex("[^ ]*") );
  181.    }
  182.  
  183.    if (fcb->dr > 16  &&  fcb->dr != '?') {
  184.       fprintf( DIAG,"\r\n*** Fatal in FcbToString:  Drive > 16 (%s)\r\n",
  185.               (char *)FName );
  186.       exit( 3 );
  187.    }
  188.    return( FName );
  189. }   // FcbToString
  190.  
  191.  
  192.  
  193. static void CloseLruFcb( void )
  194. {
  195.    int i;
  196.    int minlru, minndx;
  197.    int first = 1;
  198.  
  199.    if (OpenFcbs >= FCB_MAXOPEN) {
  200.       for (i = 0;  i < MAX_FCBS;  i++) {
  201.      if (FcbHndMap[i].Hnd != FCB_FREE  &&  FcbHndMap[i].Hnd != FCB_CLOSED) {
  202.         if (first) {
  203.            first  = 0;
  204.            minlru = FcbHndMap[i].LruCnt;
  205.            minndx = i;
  206.         }
  207.         else if (minlru > FcbHndMap[i].LruCnt) {
  208.            minlru = FcbHndMap[i].LruCnt;
  209.            minndx = i;
  210.         }
  211.      }
  212.       }
  213. #ifdef DEBUG
  214.       if (opt_BsCalls)
  215.      fprintf( DIAG,"temp-lru closing of '%s' (Hnd=%d)\r\n",
  216.           (char *)FcbHndMap[minndx].OsPath,FcbHndMap[minndx].Hnd );
  217. #endif
  218.       close( FcbHndMap[minndx].Hnd );
  219.       FcbHndMap[minndx].Hnd = FCB_CLOSED;
  220.       --OpenFcbs;
  221.    }
  222. }   // CloseLruFcb
  223.  
  224.  
  225.  
  226. //
  227. //
  228. //  search a specific FCB in FcbHndMap
  229. //  returns index into the FcbHndMap or -1 if not found
  230. //  returns not, if fatal is set and the FCB is not found
  231. //
  232. static int SearchFcb( FCB *fcb, int fatal )
  233. {
  234.    int i;
  235.    char Nam[sizeof(FcbHndMap[0].Name)];
  236.  
  237.    memcpy( Nam,fcb,sizeof(Nam) );
  238.    if (Nam[0] == 0)
  239.       Nam[0] = DefDrive + 1;
  240.  
  241.    for (i = 0;  FcbHndMap[i].Hnd == FCB_FREE  ||
  242.            memcmp(FcbHndMap[i].Name,Nam,sizeof(Nam)) != 0;  i++) {
  243.       if (i >= MAX_FCBS) {
  244.          if (fatal) {
  245.             fprintf( DIAG,"\r\n*** Fatal in SearchFcb:  not found (%s)\r\n",
  246.             (char *)FcbToString(fcb));
  247.             exit( 3 );
  248.          }
  249.          return( -1 );
  250.       }
  251.    }
  252.    if (FcbHndMap[i].Hnd == FCB_FREE)
  253.       return( -1 );
  254.    
  255.    if (FcbHndMap[i].Hnd == FCB_CLOSED) {
  256.       CloseLruFcb();
  257.       FcbHndMap[i].Hnd = open( FcbHndMap[i].OsPath,FcbHndMap[i].FMode );
  258.       ++OpenFcbs;
  259. #ifdef DEBUG
  260.       if (opt_BsCalls)
  261.      fprintf( DIAG,"reopening '%s' (Hnd=%d)\r\n",(char *)FcbHndMap[i].OsPath,
  262.           FcbHndMap[i].Hnd );
  263. #endif
  264.    }
  265.    FcbHndMap[i].LruCnt = ++LruCnt;
  266.    return( i );
  267. }   // SearchFcb
  268.  
  269.  
  270.  
  271. static void MakeNewFcb( FCB *fcb, int Hnd, String OsPath, int FMode )
  272. {
  273.    int FreeFcbHnd;
  274.  
  275.    CloseLruFcb();
  276.  
  277.    for (FreeFcbHnd = 0;  FcbHndMap[FreeFcbHnd].Hnd != FCB_FREE;  FreeFcbHnd++) {
  278.       if (FreeFcbHnd >= MAX_FCBS) {
  279.      fprintf( DIAG,"\r\n*** Fatal in MakeNewFcb:  no more FCBs free\r\n" );
  280.      exit( 3 );
  281.       }
  282.    }
  283.    memcpy( FcbHndMap[FreeFcbHnd].Name,fcb,sizeof(FcbHndMap[0].Name) );
  284.    if (fcb->dr == 0)
  285.       FcbHndMap[FreeFcbHnd].Name[0] = DefDrive + 1;
  286.    FcbHndMap[FreeFcbHnd].Hnd    = Hnd;
  287.    FcbHndMap[FreeFcbHnd].OsPath = OsPath;
  288.    FcbHndMap[FreeFcbHnd].FMode  = FMode;
  289.    FcbHndMap[FreeFcbHnd].LruCnt = ++LruCnt;
  290.    ++OpenFcbs;
  291. }   // MakeNewFcb
  292.  
  293.  
  294.  
  295. static void ImplicitClose( FCB *fcb )
  296. {
  297.    int FreeFcbHnd = SearchFcb( fcb,0 );
  298.  
  299.    if (FreeFcbHnd >= 0) {
  300. #ifdef DEBUG
  301.       if (opt_BsCalls)
  302.          fprintf( DIAG,"close implicitly '%s' (Hnd=%d)\r\n",
  303.           (char *)FcbHndMap[FreeFcbHnd].OsPath, FcbHndMap[FreeFcbHnd].Hnd );
  304. #endif
  305.       if (FcbHndMap[FreeFcbHnd].Hnd != FCB_CLOSED) {
  306.      close( FcbHndMap[FreeFcbHnd].Hnd );
  307.      --OpenFcbs;
  308.       }
  309.       FcbHndMap[FreeFcbHnd].Hnd = FCB_FREE;
  310.    }
  311. }   // ImplicitClose
  312.  
  313.  
  314.  
  315. //
  316. //
  317. //  implicit close for FCBs containing wildcards (delete file)
  318. //
  319. static void ImplicitCloseWild( FCB *fcb )
  320. {
  321.    int i,j;
  322.    int Match;
  323.    char Nam[sizeof(FcbHndMap[0].Name)];
  324.  
  325.    memcpy( Nam,fcb,sizeof(Nam) );
  326.    if (Nam[0] == 0)
  327.       Nam[0] = DefDrive + 1;
  328.  
  329.    for (i = 0;  i < MAX_FCBS;  i++) {
  330.       if (FcbHndMap[i].Hnd != FCB_FREE  &&  Nam[0] == FcbHndMap[i].Name[0]) {
  331.      Match = 1;
  332.      for (j = 1;  j < sizeof(FcbHndMap[0].Name);  j++) {
  333.         if (Nam[j] != '?'  &&  Nam[j] != FcbHndMap[i].Name[j]) {
  334.            Match = 0;
  335.            break;
  336.         }
  337.      }
  338.      if (Match) {
  339. #ifdef DEBUG
  340.         if (opt_BsCalls)
  341.            fprintf( DIAG,"close implicitly wild '%s' (Hnd=%d)\r\n",
  342.                (char *)FcbHndMap[i].OsPath,FcbHndMap[i].Hnd);
  343. #endif
  344.         if (FcbHndMap[i].Hnd != FCB_CLOSED) {
  345.            close( FcbHndMap[i].Hnd );
  346.            --OpenFcbs;
  347.         }
  348.         FcbHndMap[i].Hnd = FCB_FREE;
  349.      }
  350.       }
  351.    }
  352. }   // ImplicitCloseWild
  353.  
  354.  
  355.  
  356. static inline int fexist( char *path )
  357. {
  358.    int Hnd = open( path,O_RDONLY );
  359.    if (Hnd < 0)
  360.       return( 0 );
  361.    close( Hnd );
  362.    return( 1 );
  363. }   // fexist
  364.  
  365.  
  366.  
  367. static String _SearchPath( int drive, String fn, int Search )
  368. {
  369.    char path[_MAX_PATH];
  370.  
  371.    if (drive == '@')
  372.       drive = DefDrive;
  373.    else
  374.       drive -= 'A';
  375.  
  376.    if (drive == StartDrive) {
  377.       _makepath( path,NULL,StartPath,(char *)fn,NULL );
  378.       if ( !Search)
  379.          return( String(path) );
  380. #ifdef DEBUG
  381.       if (opt_BsCalls)
  382.          fprintf( DIAG,"open:  trying '%s'\r\n",path );
  383. #endif
  384.       if (fexist(path))
  385.          return( String(path) );
  386.    }
  387.  
  388.    _makepath( path,NULL,CpmMap[User][drive],(char *)fn,NULL );
  389.    if ( !Search)
  390.       return( String(path) );
  391. #ifdef DEBUG
  392.    if (opt_BsCalls)
  393.       fprintf( DIAG,"open:  trying '%s'\r\n",path );
  394. #endif
  395.    if (fexist(path))
  396.       return( String(path) );
  397.  
  398.    if (User != 0) {
  399. #ifdef DEBUG
  400.       if (opt_BsCalls)
  401.          fprintf( DIAG,"open:  trying '%s'\r\n",path );
  402. #endif
  403.       _makepath( path,NULL,CpmMap[0][drive],(char *)fn,NULL );
  404.       if (fexist(path))
  405.          return( String(path) );
  406.    }
  407.    return( "" );
  408. }   // _SearchPath
  409.  
  410.  
  411.  
  412. static String SearchPath( String fn, int Search )
  413. //
  414. //  Datei suchen.
  415. //  Die ersten beiden Buchstaben von 'fn' sind immer das Laufwerk.
  416. //  Folgendermaßen wird gesucht:
  417. //  - suche User,Laufwerk
  418. //  - suche User=0,Laufwerk
  419. //  - suche über CPEMPATH
  420. //  Wird im Startlaufwerk gesucht, so wird als erstes im Startver-
  421. //  zeichnis gesucht...
  422. //  Wird nix gefunden, so wird "" zurückgegeben, sonst der komplette Name
  423. //  'Search' entscheidet über die Operation:
  424. //  0 - Filenamen nur entsprechend Laufwerk erweitern (für MAKE, DELETE)
  425. //  1 - File versuchen zu öffnen, mit Suchpfad (für OPEN)
  426. //  andere Werte reserviert
  427. //
  428. {
  429.    String sn;
  430.    int dr = fn[0];
  431.    fn = fn.after(1);
  432.  
  433.    sn = _SearchPath(dr,fn,Search);
  434.    if (sn != String(""))
  435.       return( sn );
  436.  
  437.    if (Search) {
  438.       char path[_MAX_PATH], opath[_MAX_PATH];
  439.  
  440.       strcpy( path,(char *)fn);
  441.       _searchenv( path,ARG_PATH,opath );
  442. #ifdef DEBUG
  443.       if (opt_BsCalls)
  444.          fprintf( DIAG,"open _searchenv found: '%s'\r\n",opath );
  445. #endif
  446.       return( String(opath) );
  447.    }
  448.    return( String("") );
  449. }   // SearchPath
  450.  
  451.  
  452.  
  453. static void IncCr( FCB *fcb )
  454. {
  455.    int x = fcb->cr;
  456.  
  457.    ++x;
  458.    if (x >= 128) {
  459.       x = 0;
  460.       ++(fcb->ex);
  461.    }
  462.    fcb->cr = x;
  463. }   // IncCr
  464.  
  465.  
  466.  
  467. static int SearchPatOk( FCB *pat, dirent *DirEnt )
  468. {
  469.    int i;
  470.    char fname[_MAX_FNAME], fext[_MAX_EXT];
  471.    FCB name;
  472.  
  473.    String FName( DirEnt->d_name,DirEnt->d_namlen );
  474.    FName.upcase();
  475.  
  476.    if (DirEnt->d_attr & A_DIR)
  477.       return( 0 );
  478.    if ( !FName.matches(CpmFName))
  479.       return( 0 );
  480.    _splitpath( FName, NULL,NULL,fname,fext );
  481.    if (strlen(fname) > sizeof(name.f)  ||  strlen(fext) > sizeof(name.t)+1)
  482.       return( 0 );
  483.  
  484.    memset( name.f,' ',sizeof(name.f)+sizeof(name.t) );
  485.    memcpy( name.f,fname,strlen(fname) );
  486.    if (strlen(fext) > 1)
  487.       memcpy( name.t,fext+1,strlen(fext+1) );
  488.  
  489.    for (i = 0;  i < sizeof(name.f)+sizeof(name.t);  i++) {
  490.       if (pat->f[i] != '?'  &&  pat->f[i] != name.f[i])
  491.      return( 0 );
  492.    }
  493.    return( 1 );
  494. }   // SearchPatOk
  495.  
  496.  
  497.  
  498. static void FillSearcher( char *Name )
  499. {
  500.    char fname[_MAX_FNAME], ext[_MAX_EXT];
  501.    int i;
  502.  
  503.    memset( Mem+DmaAdr,0,128 );
  504.    memset( Mem+DmaAdr+1,' ',11 );
  505.    _splitpath( Name, NULL,NULL,fname,ext );
  506.    if ( !stricmp(ext,"."EXT_CPM))
  507.       strcpy( ext,"."EXT_COM );
  508.  
  509.    B(Mem+DmaAdr+0) = User;
  510.    for (i = 0;  i < 8;  i++) {
  511.       if ( !fname[i])
  512.          break;
  513.       B(Mem+DmaAdr+1+i) = toupper( fname[i] );
  514.    }
  515.    if (ext[0]) {
  516.       for (i = 0;  i < 3;  i++) {
  517.          if ( !ext[i+1])
  518.             break;
  519.          B(Mem+DmaAdr+9+i) = toupper( ext[i+1] );
  520.       }
  521.    }
  522. }   // FillSearcher
  523.  
  524.  
  525.  
  526. //////////////////////////////////////////////////////////////////////
  527.  
  528.  
  529.  
  530. void Bdos( REGS *preg )
  531. {
  532.    REGS r = *preg;
  533.  
  534. #ifdef DEBUG
  535.    if (opt_BsCalls)
  536.       RegisterDump( r,"BDOS call entry",1 );
  537. #endif
  538.  
  539.    r.b.A  = 0xff;
  540.    r.w.HL = 0;
  541.    switch(r.b.C)
  542.       {
  543.       case 0:                                                   // system reset
  544.          ExitCode = -10;
  545.          break;
  546.       case 1:                                                   // console input
  547.          r.b.A = ConIn();
  548.          if (r.b.A == 0x03) {
  549. #ifdef DEBUG
  550.             if (opt_Quest)
  551.                RegisterDump( r,"Ctrl-C pressed (BDOS 1)",0 );
  552. #endif
  553.             ExitCode = 3;
  554.             break;
  555.          }
  556.          ConOut( r.b.A );
  557.          break;
  558.       case 2:                                                   // console output
  559.          ConOut( r.b.E );
  560.          break;
  561.       case 6:                                                   // direct console I/O
  562.          r.b.A = 0;
  563.          if (r.b.E == 0xff) {
  564.             if (ConSts())
  565.                r.b.A = ConIn();
  566.          }
  567.          else if (r.b.E == 0xfe) {
  568.             if (ConSts())
  569.                r.b.A = 0xff;
  570.          }
  571.          else if (r.b.E == 0xfd)
  572.             r.b.A = ConIn();
  573.          else {
  574.             ConOut( r.b.E );
  575.             r.b.A = r.b.E;
  576.          }
  577.          break;
  578.       case 9:                                                   // print string
  579.          {
  580.             unsigned char *cp = Mem + r.w.DE;
  581.             while (*cp != '$')
  582.                ConOut( *(cp++) );
  583.          }
  584.          break;
  585.       case 10:                                                  // read console buffer
  586.          {
  587.             unsigned char *ConBuff = Mem + ((r.w.DE == 0) ? DmaAdr : r.w.DE);
  588.             unsigned char Buff[255];
  589.             unsigned char ch;
  590.             int  i;
  591.             int  imax;
  592.  
  593.             i = 0;
  594.             imax = ConBuff[0];
  595.             for (;;) {
  596.                ch = ConIn();
  597.                if (ch == '\b') {
  598.                   ConOutS( "\b \b" );
  599.                   i--;
  600.                }
  601.                else if (ch == 0x1b) {
  602.                   while (i--)
  603.                      ConOutS( "\b \b" );
  604.                   i = 0;
  605.                }
  606.                else if (ch == 0x03) {
  607.                   if (i == 0) {
  608. #ifdef DEBUG
  609.                      if (opt_Quest)
  610.                         RegisterDump( r,"Ctrl-C pressed (BDOS 10)",0 );
  611. #endif
  612.                      ExitCode = -10;
  613.                      break;
  614.                   }
  615.                }
  616.                else if (ch == '\n'  ||  ch == '\r')
  617.                   break;
  618.                else if (isprint(ch)) {
  619.                   if (i >= imax)
  620.                      ConOut( 0x07 );
  621.                   else {
  622.                      ConOut( ch );
  623.                      Buff[i++] = ch;
  624.                   }
  625.                }
  626.             }
  627.             ConBuff[1] = i;
  628.             strncpy( (char *)ConBuff+2,(char *)Buff,i );
  629.          }
  630.          break;
  631.       case 11:                                                  // get console status
  632.          {
  633.             r.b.A = 0;
  634.             if (ConSts())
  635.                r.b.A = 1;
  636.          }
  637.          break;
  638.       case 12:                                                  // return version number
  639.          r.w.HL = 0x0022;
  640.          SET_EXIT_REGS16;
  641.          if (opt_Quest)
  642.             RegisterDump( r,"BDOS Query Version Number",0 );
  643.          break;
  644.       case 13:                                                  // reset disk system
  645.          DefDrive     = 0;
  646.          DmaAdr       = DEF_DMA;
  647.      WriteProtMap = 0x0000;
  648.          break;
  649.       case 14:                                                  // select disk
  650.          {
  651.             DefDrive = r.b.E;
  652.             r.b.A = 0;
  653.          }
  654.          break;
  655.       case 15:                                                  // open file
  656.          {
  657.         FCB *fcb = GET_FCB(r.w.DE);
  658.             String fn = FcbToString( fcb );
  659.             String OsPath;
  660.             int Hnd = 0;
  661.             int fmode;
  662.  
  663.             ImplicitClose( fcb );
  664.  
  665.             fmode = O_RDONLY | O_BINARY;
  666.             OsPath = SearchPath(fn,0);
  667.             if (fexist((char *)OsPath))    // wenn Datei ohne search gefunden wird, dann
  668.                fmode = O_RDWR | O_BINARY;  // darf sie auch beschrieben werden...
  669.             else
  670.                OsPath = SearchPath(fn,1);
  671.  
  672.             if (OsPath != String("")) {
  673.                Hnd = open( (char *)OsPath,fmode );
  674.                if (Hnd < 0) {
  675.                   fprintf( DIAG,"\r\n*** Fatal during open: '%s' not found\r\n",
  676.                            (char *)OsPath );
  677.                   exit( 3 );
  678.                }
  679.            MakeNewFcb( fcb,Hnd,OsPath,fmode );
  680.                r.b.A = 0;
  681.             }
  682.             else
  683.                r.b.A = 0xff;
  684. #ifdef DEBUG
  685.             if (opt_BsCalls)
  686.                fprintf( DIAG,"open '%s' (Hnd=%d)\r\n",(char *)OsPath,Hnd );
  687. #endif
  688.          }
  689.          break;
  690.       case 16:                                                  // close file
  691.          {
  692.         FCB *fcb = GET_FCB(r.w.DE);
  693.  
  694. #ifdef DEBUG
  695.             if (opt_BsCalls) {
  696.                String fn = FcbToString(fcb);
  697.                String OsPath = SearchPath(fn,0);
  698.                fprintf( DIAG,"close '%s' (no-op)\r\n",(char *)OsPath );
  699.             }
  700. #endif
  701.         r.b.A = 0;
  702.          }
  703.          break;
  704.       case 17:                                                  // search for first
  705.      {
  706.         String fn = FcbToString(GET_FCB(r.w.DE));
  707.         String OsPath = SearchPath(fn.before(2),0);
  708.  
  709.         if (fn[0] == '?') {
  710.            RegisterDump( r,"search for first with dr='?' not implemented",1 );
  711.            ExitCode = 3;
  712.            break;
  713.         }
  714.  
  715.         if (SearchDir) {
  716.            closedir( SearchDir );
  717.            SearchDir = NULL;
  718.         }
  719.         SearchDir = opendir( (char *)OsPath );
  720.         rewinddir( SearchDir );
  721.         memcpy( &SearchPat,GET_FCB(r.w.DE),sizeof(FCB) );
  722.      }  // fall into 'search for next'
  723.       case 18:                                                  // search for next
  724.          {
  725.             r.b.A = 0xff;
  726.         if (SearchDir) {
  727.            struct dirent *p;
  728.            while ((p = readdir(SearchDir)) != NULL  &&  !SearchPatOk(&SearchPat,p))
  729.           ;
  730.            if (p != NULL) {
  731.           FillSearcher( p->d_name );
  732.           r.b.A = 0;
  733.            }
  734.         }
  735.          }
  736.          break;
  737.       case 19:                                                  // delete file
  738.          {
  739.         FCB *fcb = GET_FCB(r.w.DE);
  740.             String OsPath;
  741.             char **flist;
  742.             int p;
  743.             String fn = FcbToString( fcb );
  744.  
  745.             ImplicitCloseWild( fcb );
  746.             OsPath = SearchPath(fn,0);
  747.             flist = _fnexplode( (char *)OsPath );
  748.  
  749. #ifdef DEBUG
  750.             if (opt_BsCalls)
  751.                fprintf( DIAG,"delete try '%s'\r\n",(char *)OsPath );
  752. #endif
  753.             r.b.A = 0xff;
  754.             if (flist) {
  755.            r.b.A = 0;
  756.                for (p = 0;  flist[p];  p++) {
  757. #ifdef DEBUG
  758.                   if (opt_BsCalls)
  759.                      fprintf( DIAG,"delete '%s'\r\n",flist[p] );
  760. #endif
  761.                   if (unlink(flist[p]) != 0)
  762.                      r.b.A = 0xff;
  763.                }
  764.                _fnexplodefree( flist );
  765.             }
  766.          }
  767.          break;
  768.       case 20:                                                  // read sequential
  769.          {
  770.             FCB *fcb = GET_FCB(r.w.DE);
  771.             int Hnd = SearchFcb( fcb,1 );
  772.             int radr = fcb->cr + 128*(fcb->ex);
  773.             lseek( FcbHndMap[Hnd].Hnd,128*radr,SEEK_SET );
  774.             int res = read( FcbHndMap[Hnd].Hnd,Mem+DmaAdr,128 );
  775.             if (res <= 0)
  776.                r.b.A = 1;
  777.             else {
  778.                if (res < 128)
  779.                   memset( Mem+DmaAdr+res,0x1a,128-res );
  780.                r.b.A = 0;
  781.                IncCr( fcb );
  782.             }
  783.          }
  784.          break;
  785.       case 21:                                                  // write sequential
  786.          {
  787.             FCB *fcb = GET_FCB(r.w.DE);
  788.             int Hnd = SearchFcb( fcb,1 );
  789.             int wadr = fcb->cr + 128*(fcb->ex);
  790.             lseek( FcbHndMap[Hnd].Hnd,128*wadr,SEEK_SET );
  791.             int res = write( FcbHndMap[Hnd].Hnd,Mem+DmaAdr,128 );
  792.             if (res < 128)
  793.                r.b.A = 2;
  794.             else {
  795.                r.b.A = 0;
  796.                IncCr( fcb );
  797.             }
  798.          }
  799.          break;
  800.       case 22:                                                  // make file
  801.          {
  802.         FCB *fcb = GET_FCB(r.w.DE);
  803.             String fn = FcbToString( fcb );
  804.             String OsPath;
  805.         int Hnd = -1;
  806.  
  807.             ImplicitClose( fcb );
  808.             OsPath = SearchPath(fn,0);
  809.  
  810.             if (fexist((char *)OsPath)) {
  811.                r.b.A = 0xff;
  812.            r.b.H = 0x08;
  813.         }
  814.             else {
  815.                Hnd = open( (char *)OsPath,O_RDWR|O_BINARY|O_CREAT|O_TRUNC,0666 );
  816.                if (Hnd < 0)
  817.                   r.b.A = 0xff;
  818.                else {
  819.           MakeNewFcb( fcb,Hnd,OsPath,O_RDWR|O_BINARY );
  820.                   r.b.A = 0;
  821.                }
  822. #ifdef DEBUG
  823.            if (opt_BsCalls)
  824.           fprintf( DIAG,"create '%s' (Hnd=%d)\r\n",(char *)OsPath,Hnd );
  825. #endif
  826.             }
  827.          }
  828.          break;
  829.       case 23:                                                  // rename file
  830.          {
  831.             String OsPath1, OsPath2;
  832.             String f1 = FcbToString( GET_FCB(r.w.DE) );
  833.             String f2 = FcbToString( GET_FCB(r.w.DE+0x10) );
  834.  
  835.         //
  836.         //  first, look for wildcards
  837.         //
  838.         if (memchr(GET_FCB(r.w.DE+0x00),'?',12) != NULL  ||
  839.         memchr(GET_FCB(r.w.DE+0x00),'*',12) != NULL  ||
  840.         memchr(GET_FCB(r.w.DE+0x80),'?',12) != NULL  ||
  841.         memchr(GET_FCB(r.w.DE+0x80),'*',12) != NULL)
  842.            r.b.A = 0xff;
  843.         else {
  844.            ImplicitClose( GET_FCB(r.w.DE) );
  845.  
  846.            f2[0] = f1[0];    // Laufwerke gleichsetzen
  847.            OsPath1 = SearchPath( f1,0 );
  848.            OsPath2 = SearchPath( f2,0 );
  849. #ifdef DEBUG
  850.            if (opt_BsCalls)
  851.           fprintf( DIAG,"rename '%s' to '%s'\r\n",(char *)OsPath1,(char *)OsPath2 );
  852. #endif
  853.            r.b.A = 0xff;
  854.            if (fexist((char *)OsPath1)  &&  !fexist((char *)OsPath2)) {
  855.           if (rename(OsPath1,OsPath2) > 0)
  856.              r.b.A = 0;                  // ok !
  857.            }
  858.             }
  859.          }
  860.          break;
  861.       case 25:                                                  // return current disk
  862.          r.b.A = DefDrive;
  863.          break;
  864.       case 26:                                                  // set DMA address
  865.          DmaAdr = r.w.DE;
  866.          break;
  867.       case 27:                                                  // get addr(alloc)
  868.          r.w.HL = ALO_ADR;
  869.          SET_EXIT_REGS16;
  870.          if (opt_Quest)
  871.             RegisterDump( r,"BDOS Get Alloc Vector",0 );
  872.          break;
  873.       case 28:                            // write protect disk
  874.      WriteProtMap |= (1 << DefDrive);
  875.      if (opt_Quest)
  876.         RegisterDump( r,"write protect disk",0 );
  877.      break;
  878.       case 29:                            // get read-only vector
  879.      r.w.HL = WriteProtMap;
  880.      if (opt_Quest)
  881.         RegisterDump( r,"get read-only vector",0 );
  882.      break;
  883.       case 31:                                                  // get addr(dpb parms)
  884.          r.w.HL = DBP_ADR;
  885.          SET_EXIT_REGS16;
  886.          if (opt_Quest)
  887.             RegisterDump( r,"BDOS Query DPB",0 );
  888.          break;
  889.       case 32:                                                  // set/get user code
  890.          if (r.b.E != 0xff)
  891.             opt_User = r.b.E % 16;
  892.          r.b.A = opt_User;
  893.          break;
  894.       case 33:                                                  // read random
  895.          {
  896.             FCB *fcb = GET_FCB(r.w.DE);
  897.             int Hnd = SearchFcb( fcb,1 );
  898.             int radr = 65536*(fcb->r[2]) + 256*(fcb->r[1]) + fcb->r[0];
  899.             lseek( FcbHndMap[Hnd].Hnd,128*radr,SEEK_SET );
  900.             int res = read( FcbHndMap[Hnd].Hnd,Mem+DmaAdr,128 );
  901.             if (res <= 0)
  902.                r.b.A = 1;
  903.             else {
  904.                if (res < 128)
  905.                   memset( Mem+DmaAdr+res,0x1a,128-res );
  906.                r.b.A = 0;
  907.                fcb->cr = radr % 128;
  908.                fcb->ex = radr / 128;
  909.             }
  910.          }
  911.          break;
  912.       case 34:                                                  // write random
  913.          {
  914.             FCB *fcb = GET_FCB(r.w.DE);
  915.             int Hnd = SearchFcb( fcb,1 );
  916.             int wadr = 65536*(fcb->r[2]) + 256*(fcb->r[1]) + fcb->r[0];
  917.             lseek( FcbHndMap[Hnd].Hnd,128*wadr,SEEK_SET );
  918.             int res = write( FcbHndMap[Hnd].Hnd,Mem+DmaAdr,128 );
  919.             if (res <= 0)
  920.                r.b.A = 2;
  921.             else {
  922.                r.b.A = 0;
  923.                fcb->cr = wadr % 128;
  924.                fcb->ex = wadr / 128;
  925.             }
  926.          }
  927.          break;
  928.       case 35:                            // compute file size
  929.      {
  930.             FCB *fcb = GET_FCB(r.w.DE);
  931.             int Hnd = SearchFcb( fcb,1 );
  932.         long size;
  933.         size = (filelength(FcbHndMap[Hnd].Hnd) + 127) / 128;
  934.         fcb->r[2] = (size >> 16) & 0xff;
  935.         fcb->r[1] = (size >>  8) & 0xff;
  936.         fcb->r[0] = (size      ) & 0xff;
  937.         r.b.A = 0;
  938. #ifdef DEBUG
  939.         if (opt_BsCalls)
  940.            fprintf( DIAG,"compute file size: %ld\r\n",size );
  941. #endif
  942.      }
  943.       case 36:                            // get random record
  944.      r.b.A = 0;
  945.      if (opt_Quest)
  946.         RegisterDump( r,"get random record (ignored)",1 );
  947.      break;
  948.       case 37:                                                  // reset drive
  949.      WriteProtMap &= ~r.w.DE;
  950.          r.b.A = 0;
  951.          if (opt_Quest)
  952.             RegisterDump( r,"BDOS Reset Drive",0 );
  953.          break;
  954.       case 105:                                                 // get date and time
  955.          {
  956.             //
  957.             //  diese Rechnerei mag nicht ganz korrekt sein, ist aber egal,
  958.             //  da dies sowieso nur ein Hack für den DSD ist
  959.             //
  960.             time_t Time = time(NULL);
  961.             struct tm *Cur;
  962.             unsigned Date;
  963.  
  964.             Cur  = localtime( &Time );
  965.             Date = Cur->tm_yday + 365*(Cur->tm_year-78) +
  966.            (Cur->tm_year-78+2)/4;
  967.             W(Mem+r.w.DE+0) = Date;
  968.             B(Mem+r.w.DE+2) = Cur->tm_hour;
  969.             B(Mem+r.w.DE+3) = Cur->tm_min;
  970.             r.b.A = Cur->tm_sec;
  971.          }
  972.          break;
  973.       default:
  974.          RegisterDump( r,"BDOS function not implemented",1 );
  975.          CoreDump();
  976.          ExitCode = 3;
  977.          break;
  978.       }
  979.  
  980. #ifdef DEBUG
  981.    if (opt_BsCalls)
  982.       RegisterDump( r,"BDOS call exit",1 );
  983. #endif
  984.  
  985.    *preg = r;
  986. }   // Bdos
  987.  
  988.  
  989.  
  990. void BdosInit( void )
  991. {
  992.    int i;
  993.  
  994.    if (opt_Verbose)
  995.       fprintf( stderr,"BdosInit...\r\n" );
  996.  
  997.    if (opt_Verbose)
  998.       fprintf( stderr,"setting up BDOS jumps at 0x%x and 0x0005\r\n",BDOS_BEG);
  999.    B(Mem+5) = 0xc3;
  1000.    W(Mem+6) = (BDOS_BEG + 6);
  1001.    B(Mem+BDOS_BEG+6) = 0xc3;
  1002.    W(Mem+BDOS_BEG+7) = (BDOS_BEG+6);
  1003.  
  1004.    if (opt_Verbose)
  1005.       fprintf( stderr,"clearing FCB->Hnd-Map\r\n" );
  1006.    for (i = 0;  i < MAX_FCBS;  i++) {
  1007.       FcbHndMap[i].Hnd    = FCB_FREE;
  1008.       FcbHndMap[i].LruCnt = 0;
  1009.    }
  1010.  
  1011.    if (opt_Verbose)
  1012.       fprintf( stderr,"setting up misc (DMA,USER,DefDrive)\r\n" );
  1013.    DmaAdr       = DEF_DMA;
  1014.    User         = opt_User;
  1015.    DefDrive     = StartDrive;
  1016.    WriteProtMap = 0x0000;
  1017.  
  1018.    //
  1019.    //
  1020.    //  total 960K, 4K pro Block,  16K pro Track
  1021.    //  256 Dir-Einträge, 512 Bytes physical sector size
  1022.    //
  1023.    if (opt_Verbose)
  1024.       fprintf( stderr,"setting up Disk Parameter Block/Alloc Vector (dummy)\r\n" );
  1025.    W(Mem+DBP_ADR+ 0) = 128;      // SPT = 256 logical records/track (32K)
  1026.    B(Mem+DBP_ADR+ 2) = 5;        // BSH
  1027.    B(Mem+DBP_ADR+ 3) = 31;       // BLM    4K blocks
  1028.    B(Mem+DBP_ADR+ 4) = 3;        // EXM
  1029.    W(Mem+DBP_ADR+ 5) = 239;      // DSM = 240 blocks
  1030.    W(Mem+DBP_ADR+ 7) = 255;      // DRM = 256 dir entries
  1031.    B(Mem+DBP_ADR+ 9) = 0xc0;     // AL0 = 2 blocks reserved for dir entries
  1032.    B(Mem+DBP_ADR+10) = 0x00;     // AL1
  1033.    W(Mem+DBP_ADR+11) = 0x8000;   // CKS = permanently mounted
  1034.    W(Mem+DBP_ADR+13) = 2;        // OFF = 2 reserved tracks
  1035.    B(Mem+DBP_ADR+15) = 2;        // PSH
  1036.    B(Mem+DBP_ADR+16) = 3;        // PHM
  1037.    memset( Mem+ALO_ADR+00,0xea,18 );
  1038.    memset( Mem+ALO_ADR+20,0x50,12 );
  1039.  
  1040. }   // BdosInit
  1041.  
  1042.  
  1043.  
  1044. void BdosExit( void )
  1045. {
  1046. }   // BdosExit
  1047.