home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / books / progem / gemdos.15 < prev    next >
Text File  |  1986-11-01  |  19KB  |  435 lines

  1.       Permission to reprint or excerpt is granted only if the following
  2.       line appears at the top of the article:
  3.  
  4.        ANTIC PUBLISHING INC., COPYRIGHT 1986.  REPRINTED BY PERMISSION.
  5.  
  6.  
  7.  
  8.       PROFESSIONAL GEM  by Tim Oren
  9.       Column #15 - Coping with GEMDOS
  10.  
  11.  
  12.            While it's fun playing with windows and object trees,  one of
  13.       the  day-to-day realities of working with the ST is its  operating
  14.       system, GEMDOS.  A successful application should insulate the user
  15.       from  the foibles and occasional calamities of the machine's  file
  16.       system.  The GEM environment provides some minimal tools for doing
  17.       this,  but a good deal of responsibility still rests with you, the
  18.       programmer.
  19.  
  20.             This column (#15 in the ST PRO GEM series) tries to  address
  21.       the  GEM/DOS integration problem by providing you some stock  code
  22.       for common functions, along with a discussion of some of the worst
  23.       "gotchas" lurking for the unwary.  The download for this column is
  24.       GMCL15.C, and it can be found in DL3 of PCS-58.  You should obtain
  25.       and list this file before proceeding.
  26.  
  27.            A  BIT OF HISTORY.   There has been a good deal of  confusion
  28.       in  the Atari press and among developers over what GEMDOS is,  and
  29.       how it relates to TOS and CP/M-68K.   It's important to clear this
  30.       up,  so  you can get a true picture of what GEMDOS is intended  to
  31.       do.  The best way is to tell the story of GEMDOS' origins, which I
  32.       can do, because I was there.
  33.  
  34.            As  most developers are aware,  GEM was first implemented  on
  35.       the  IBM  PC.   PC GEM performed two functions.   The first was  a
  36.       windowed graphics extension to the PC environment.  The second was
  37.       a  visual  shell,  the Desktop,  which ran on top of the  existing
  38.       operating system, PC-DOS.
  39.  
  40.            When work started on moving GEM to the ST, there were two big
  41.       problems.   First,  no STs actually existed.  Second, there was no
  42.       operating system on the 68000 with which GEM and the Desktop could
  43.       run.   Unix  was  too  large,  and  CP/M-68K lacked  a  number  of
  44.       capabilities,  such  as  hierarchical files,  which were needed to
  45.       support GEM.
  46.  
  47.            Work on porting the graphics parts of GEM to the 68000 had to
  48.       start immediately to meet schedules.   Therefore, CP/M-68K running
  49.       on  Apple Lisa's was used to get this part of the project off  the
  50.       ground.   Naturally,  the  Alcyon C compiler and other tools which
  51.       were native to this environment were used.
  52.  
  53.            In  parallel,  an  effort was begun to write a new  operating
  54.       system for the 68000,  which would ultimately become the ST's file
  55.       system.   It was designed to be a close clone of PC-DOS,  since it
  56.       would   perform   the   same  functions  for  GEM   in   the   new
  57.       environment.  At  this  point,  the term TOS was introduced.   TOS
  58.       really meant "the operating system,  whatever it may be, that will
  59.       run on the ST",  since not even the specifications,  let alone the
  60.       code, were complete at that time.
  61.  
  62.            The  first engineer to work on "TOS" at Digital Research  was
  63.       Jason  Loveman.   This  name  leaked  to the press,  and  in  some
  64.       distorted  fashion generated a rumor about "Jason DOS",  which was
  65.       still  just  the same unfinished project.   As "TOS"  became  more
  66.       solid,  the  developer's tools were ported to the new  environment
  67.       one by one, and the GEM programming moved with them.  CP/M-68K was
  68.       completely  abandoned,  though the old manuals for C and the tools
  69.       lived on and are still found in the Atari developer's kit.
  70.  
  71.            All of this work had been done on Lisas or  Compupro  systems
  72.       fitted with 68000 boards.   At this point,  workable ST prototypes
  73.       became  available.   An  implementation  of "TOS" for  the  target
  74.       machine  was  begun,  even before the basic operating  system  was
  75.       fully completed.
  76.  
  77.            The  other  intent for the new operating system was to  be  a
  78.       base for GEM on other 68000 systems as well as the ST.  Because of
  79.       this,  Digital  Research  named  it  GEMDOS when  it  was  finally
  80.       complete,  thus providing the final bit of nomenclature.  "TOS" as
  81.       now  found  in the ST is in fact a  particular  implementation  of
  82.       generic GEMDOS, including the ST specific BIOS.
  83.  
  84.            So,  GEMDOS is a PC-DOS clone,  but,  not  quite.   There are
  85.       enough  differences  to  cause  problems  if  they  are   ignored.
  86.       (Remember,  it looks like a duck, and quacks like a duck, but it's
  87.       not a duck.)
  88.  
  89.            GOING  FOR  IT.   As a first example,  consider the  routines
  90.       open_file()  and create_file() at the beginning of  the  download.
  91.       They make use of the GEMDOS calls Fopen() and Fcreate().  You will
  92.       notice that these names are not the ones specified in the  Digital
  93.       Research  GEMDOS  manual.   Developers who have used PC  GEM  will
  94.       also  observe that they are radically different from the  function
  95.       names in the PC-DOS bindings.
  96.  
  97.            In  fact,  all  of  the GEMDOS function calls on the  ST  are
  98.       defined  as  macros  in the file osbind.h,  distributed  with  the
  99.       developer's  kit.   At compile time they are turned into calls  to
  100.       the  assembly  language  routine gemdos(),  part of  the  osbind.o
  101.       binary.  So, if you find the naming conventions to be particularly
  102.       offensive  for  some reason,  just edit the appropriate macros  in
  103.       osbind.h.
  104.  
  105.            In  DRI's PC-DOS bindings,  any error codes were returned  in
  106.       the  global  variable  DOS_ERR.    In  the  GEMDOS  bindings,  the
  107.       operation result or an error code is returned as the value of  the
  108.       calling  function.   In  the  case of Fopen() and  Fcreate(),  the
  109.       result  is  a  valid file handle if it is  positive.   A  negative
  110.       result  is  always an error code,  indicating that  the  operation
  111.       failed.
  112.  
  113.            An application which encounters a GEMDOS error should display
  114.       an  alert,  and  query  for  retry or abort.   The  type  of  loop
  115.       structure  exemplified  by open_file()  and  create_file()  should
  116.       be  usable with most GEMDOS functions which might fail.   The  AES
  117.       provides  a  function,  form_error,  which  implements  a  set  of
  118.       "canned" error alerts appropriate to the various possible errors.
  119.  
  120.            However,  this is where the fun starts.  For unknown reasons,
  121.       the form_error on the ST expects to see PC-DOS,  not GEMDOS, error
  122.       codes as it's input! Therefore you need a routine to translate one
  123.       into the other.   The routine dos_error() in the download provides
  124.       this  function.   The  GEMDOS errors are in the same  sequence  as
  125.       those  for  PC-DOS,  but  their numerical order  is  reversed  and
  126.       shifted.   Notice  also  that  dos_error() does  NOT  perform  the
  127.       translation if the error code is less than -50.   These codes have
  128.       no  PC-DOS  equivalent;  computing a bogus translation will  cause
  129.       form_error to crash.   Instead,  they are passed through verbatim,
  130.       resulting in a "generic" alert which gives only the error number.
  131.  
  132.            The  other major task in integrating a GEM  application  with
  133.       the  file  system is selecting file names for  input  and  output.
  134.       Again,  the AES provides some assistance with the fsel_input call,
  135.       which invokes the standard file selector dialog.
  136.  
  137.            There  are several drawbacks to the standard  file  selector.
  138.       One  is that the "ITEM SELECTOR" title is constant and  cannot  be
  139.       changed  by the application.    This  could  cause  confusion  for
  140.       the  user,  since it may not be clear which of several  functions,
  141.       closely spaced in the FILE menu,  was actually invoked.   While it
  142.       might  be  possible  to find and "rewire" the  AES  resource  that
  143.       defines  the file selector,  it is unlikely that such an  approach
  144.       would be portable to a later version of ST GEM.
  145.  
  146.            A  viable approach to eliminating confusion is to  display  a
  147.       small "marquee" box, with a message defining the operation, on the
  148.       screen  just  above  the  file selector.  To  do  this,  you  must
  149.       initialize  the location of the box so that it is outside  of  the
  150.       file selector's bounds,  and then draw it just before invoking the
  151.       file  selector.   This  way  they will  appear  together.   Before
  152.       returning  to its main event loop,  the application should post  a
  153.       redraw  message for the "marquee" area.   The AES will merge  this
  154.       redraw  with the one generated by fsel_input,  and the result will
  155.       be received by the application's evnt_multi.
  156.  
  157.            Another problem with the file selector is that it resets your
  158.       application's virtual workstation clip rectangle without  warning.
  159.       There  are other AES functions,  such as objc_draw,  which also do
  160.       this,  but  the file selector can be troublesome because it may be
  161.       the only AES call used by some VDI-based ST applications.
  162.  
  163.            The veteran developer will also notice that the file selector
  164.       takes  and returns the path and filename as two separate  strings,
  165.       while the GEMDOS file functions require a fully pathed file  name.
  166.       Also, the file selector doesn't remember its "home" directory; you
  167.       are responsible for determining the default directory, and keeping
  168.       track of any changes.  The remainder of the download and column is
  169.       devoted  to  set of utilities which should alleviate some  of  the
  170.       "grunt work" of these chores.
  171.  
  172.            The  top level routine in this collection is get_file().   It
  173.       is  called with two string arguments.   The first must point to  a
  174.       four  byte string area containing the desired file name  extension
  175.       (three  characters plus a null).   The second is the default  file
  176.       name.
  177.  
  178.            If the default file name is non-null, then get_file() invokes
  179.       parse_fname() to break it into path and name.   Parse_fname() also
  180.       adds  the  necessary "wild card" file specification to  the  path,
  181.       using the extent name given as input.
  182.  
  183.            If  no  default  file was supplied,  or the default  did  not
  184.       contain  a  path,  the routine get_path() is invoked to  find  the
  185.       current  default directory and construct a legal path  string  for
  186.       it.
  187.  
  188.            The   results   of  these  manipulations  are   supplied   to
  189.       fsel_input.   Notice  that  the  result of the  file  selector  is
  190.       returned via its third argument,  rather than as a function value.
  191.       If  the result is TRUE,  get_file() merges the temporary path  and
  192.       file  string,  storing the result via the second input  parameter.
  193.       This  result  string is suitable for use with Fopen,  and  may  be
  194.       resubmitted  to get_file() when the next operation is  invoked  by
  195.       the user.
  196.  
  197.            Parse_fname() is straight-forward C.  It looks backward along
  198.       the  file to find the first character which is part of  the  path.
  199.       The tail of the filename is copied off, and its former location is
  200.       overlaid with the wild card specification.
  201.  
  202.            Get_path()  is a bit more interesting.   It makes use of  two
  203.       GEMDOS  functions,  Dgetdrv() and Dgetpath() to obtain the default
  204.       disk drive and directory, respectively.  Note that Dgetpath() will
  205.       return  a null string if the current default is the root,  but  it
  206.       puts  a back-slash at the beginning of the path  otherwise.   This
  207.       forces  a  check for insertion in the root case,  since  the  file
  208.       selector  wants  to  see something like  "A:\*.RSC",  rather  than
  209.       "A:*.RSC".   After  making  this fix,  get_path() concatenates the
  210.       wild card specification derived from the input extent.
  211.  
  212.            The last routine in the download is new_ext().   This utility
  213.       is  useful if your application uses more than one associated  file
  214.       at a time.   For instance, the Resource Construction Set uses both
  215.       an RSC and a DEF file, with the same base name.  New_ext() takes a
  216.       fully  formed file name,  and replaces its old extent with the new
  217.       one  which you supply.   This lets you quickly generate both  file
  218.       names after one call to the file selector.   Notice that new_ext()
  219.       looks BACKWARD along the name to find the delimiting period, since
  220.       this  character  can also be part of a subdirectory  name  in  the
  221.       path.
  222.  
  223.            So  we reach the end of the code and this column.   Hopefully
  224.       both will keep you profitably occupied for a while.  July's column
  225.       will return to graphics topics,  with a look at writing customized
  226.       rubber  box and drag box routines,  and ways to implement your own
  227.       "pop-up"  menus.   August  will  bring techniques  for  displaying
  228.       progress  indicators,  associating  dialog and menu  entries  with
  229.       keystrokes, and customizing objc_edit.
  230.  
  231.            I  CAN'T HEAR YOU!   The Feedback mailbag has been  noticably
  232.       flat  of  late.   There have been a number of compliments  on  the
  233.       column,  which  are  much  appreciated,  and some suggestions  for
  234.       topics  which fall outside the bounds of this series.   The latter
  235.       have  been passed on to Antic for possible inclusion in their  new
  236.       ST quarterly, START.
  237.  
  238.            One recurring problem is finding the downloads.   A number of
  239.       the  earlier columns say they are in PCS-132 (the old  SIG*ATARI),
  240.       and  one says PCS-57 (mea culpa).   In fact,  ALL of the downloads
  241.       are  now  in DL3 of PCS-58 (ATARI16).   Filenames for  first  nine
  242.       columns  are  all in the form GEMCLx.C,  where x is  the  column's
  243.       digit.   For reasons unknown to me,  the next two files were named
  244.       GEMC10.C  and  GEMC11.C;  the  latest  two  downloads  are  called
  245.       GMCL13.C and GMCL15.C.   The latter naming pattern should continue
  246.       into the future.
  247.  
  248.            Undoubtedly,  one reason for the shortage of questions is the
  249.       amazing ability to get a quick answer on the Developer's SIG, PCS-
  250.       57.   This  is  a  good  sign  of  a  strong  Atari  community  on
  251.       Compuserve.   However,  the  SIG message style doesn't really lend
  252.       itself  to lengthy explanation,  so suggestions for longer  topics
  253.       are always welcome here.
  254.  
  255.            Finally,  I  am now beginning the process of collecting these
  256.       columns and some additional material into a book.  In doing so, it
  257.       would  be  helpful  to know if you feel that any part of  GEM  has
  258.       been  slighted  in my discussions.   If so,  let  me  know.   Your
  259.       suggestions  will appear in future columns and finally make  their
  260.       way into the book.
  261.  
  262.  
  263.  
  264.  
  265. /*------------------------------*/
  266. /*         includes             */
  267. /*------------------------------*/
  268.  
  269. #include "portab.h"            /* portable coding conv     */
  270. #include "machine.h"            /* machine depndnt conv     */
  271. #include "osbind.h"            /* BDOS defintions     */
  272. #include "gemdefs.h"
  273.  
  274.  
  275. /*------------------------------*/
  276. /*          open_file           */
  277. /*------------------------------*/
  278.     WORD
  279. open_file(file_name)
  280.     BYTE    *file_name;
  281.     {
  282.     LONG    dos_hndl;
  283.  
  284.     FOREVER
  285.         {
  286.         dos_hndl = Fopen(file_name, 0);
  287.         if (dos_hndl >= 0)
  288.             return ((WORD) dos_hndl);
  289.             if ( !dos_error((WORD) dos_hndl) )
  290.                 return (-1);
  291.         }
  292.  
  293.     return (-1);        /* Appease lint */
  294.     }
  295.  
  296.  
  297. /*------------------------------*/
  298. /*        create_file           */
  299. /*------------------------------*/
  300.     WORD
  301. create_file(file_name)
  302.     BYTE    *file_name;
  303.     {
  304.     LONG    dos_hndl;
  305.  
  306.     FOREVER
  307.         {
  308.         dos_hndl = Fcreate(file_name, 0);
  309.         if (dos_hndl >= 0)
  310.             return ((WORD) dos_hndl);
  311.         if ( !dos_error((WORD) dos_hndl) )
  312.             return (-1);
  313.         }
  314.  
  315.     return (-1);          /* Appease lint */
  316.     }
  317.  
  318.  
  319. /*------------------------------*/
  320. /*          dos_error           */
  321. /*------------------------------*/
  322.     WORD
  323. dos_error(tos_err)
  324.     WORD    tos_err;
  325.     {
  326.     WORD    f_ret;
  327.  
  328.     graf_mouse(ARROW, 0x0L);
  329.     if (tos_err > -50)
  330.         {
  331.         tos_err += 31;
  332.         tos_err = -tos_err;
  333.         }
  334.     f_ret = form_error(tos_err);
  335.     return (f_ret);
  336.     }
  337.  
  338.  
  339. /*------------------------------*/
  340. /*          get_file           */
  341. /*------------------------------*/
  342.     WORD
  343. get_file(extnt, got_file)
  344.     BYTE    *extnt, *got_file;
  345.     {
  346.     WORD    butn, ii;
  347.     BYTE    tmp_path[64], tmp_name[13];
  348.  
  349.     tmp_name[0] = '\0';
  350.     tmp_path[0] = '\0';
  351.  
  352.     if (*got_file)
  353.         parse_fname(got_file, tmp_path, tmp_name, extnt);
  354.     if (!tmp_path[0])
  355.         get_path(&tmp_path[0], extnt);
  356.  
  357.     fsel_input(tmp_path, tmp_name, &butn);
  358.     if (butn)
  359.         {
  360.         strcpy(got_file, tmp_path);
  361.         for (ii = 0; got_file[ii] && got_file[ii] != '*'; ii++);
  362.         got_file[ii - 1] = '\0';
  363.         strcat (got_file, "\\");
  364.         strcat(got_file, tmp_name);
  365.         return (TRUE);
  366.         }
  367.     else
  368.         return (FALSE);
  369.     }
  370.  
  371.  
  372. /*------------------------------*/
  373. /*        parse_fname           */
  374. /*------------------------------*/
  375.     VOID
  376. parse_fname(full, path, name, extnt)
  377.     BYTE    *full, *path, *name, *extnt;
  378.     {
  379.     WORD    i, j;
  380.     BYTE    *s, *d;
  381.  
  382.     for (i = strlen(full); i--; )          /* scan for end of path */
  383.         if (full[i] == '\\' || full[i] == ':')
  384.             break;
  385.     if (i == -1)
  386.         strcpy(name, full);          /* "Naked" file name */
  387.     else
  388.         {
  389.         strcpy(name, &full[i+1]);
  390.         for (s = full, d = path, j = 0; j++ < i + 1;
  391.         *d++ = *s++);
  392.         strcpy(&path[i+1], "*.");
  393.         strcat(path, extnt);
  394.         }
  395.     }
  396.  
  397.  
  398. /*------------------------------*/
  399. /*          get_path           */
  400. /*------------------------------*/
  401.     VOID
  402. get_path(tmp_path, spec)
  403.     BYTE    *tmp_path, *spec;
  404.     {
  405.     WORD    cur_drv;
  406.  
  407.     cur_drv = Dgetdrv();
  408.     tmp_path[0] = cur_drv + 'A';
  409.     tmp_path[1] = ':';
  410.     Dgetpath(&tmp_path[2], 0);
  411.     if (strlen(tmp_path) > 3)
  412.         strcat(tmp_path, "\\");
  413.     else
  414.         tmp_path[2] = '\0';
  415.     strcat(tmp_path, "*.");
  416.     strcat(tmp_path, spec);
  417.     }
  418.  
  419.  
  420. /*------------------------------*/
  421. /*           new_ext            */
  422. /*------------------------------*/
  423.     VOID
  424. new_ext(o_fname, n_fname, ext)
  425.     BYTE    *o_fname, *n_fname, *ext;
  426.     {
  427.     WORD    ii, jj;
  428.  
  429.     strcpy(n_fname, o_fname);
  430.     for (ii = (jj = strlen(n_fname)) - 1; ii && n_fname[ii] != '.'; ii--);
  431.     if (!ii)
  432.         n_fname[ii = jj] = '.';
  433.     strcpy(&n_fname[++ii], ext);
  434.     }
  435.