[comp.sys.apple2] HyperC filing question

greyelf@wpi.WPI.EDU (Michael J Pender) (11/08/90)

I'm trying to read and write binary files from Hyper C.

Would someone with the documentation please tell me how?

This was my closest attempt:

#include <st.h>

main()
{
    char *screen;

    FILE *fp = open("/ram/output.snd","wb");
    for(screen = 0x400; screen < 0x8; screen++)
       fprintf(*fp, "%x", *screen);
    close(fp);
    exit();
}

Please reply REAL SOON.  I need to have these routines working by 11:00
friday...

---
Michael J Pender Jr  Box 1942 c/o W.P.I.   Part of this D- belongs to 
greyelf@wpi.bitnet   100 Institute Rd.     God...  
greyelf@wpi.wpi.edu  Worcester, Ma 01609           - B. Simpson

-- 
---
Michael J Pender Jr  Box 1942 c/o W.P.I.   Part of this D- belongs to 
greyelf@wpi.bitnet   100 Institute Rd.     God...  

BRL102@psuvm.psu.edu (Ben Liblit) (11/09/90)

In article <1990Nov8.045226.7696@wpi.WPI.EDU>, greyelf@wpi.WPI.EDU (Michael J
Pender) says:
>
>I'm trying to read and write binary files from Hyper C.
>This was my closest attempt:

[ I have omitted below all lines except those requiring modification. ]


>#include <st.h>

    Unless you renamed it, this should be <std.h>.


>    FILE *fp = open("/ram/output.snd","wb");

    ProDOS HyperC takes no second parameter -- if specified, it is ignored.
    (Yes, HyperC does absolutely no parameter checking.)

>    for(screen = 0x400; screen < 0x8; screen++)

    You want to scan as long as "screen < 0x800", not "screen < 0x8".
    Also, although HyperC couldn't care less, it is good programming practice
    to typecast the 0x400 and 0x800 to char pointers.  More strict compilers
    will call you nasty names if you don't.


>       fprintf(*fp, "%x", *screen);

    Ok, here's the core of the problem.  As I stated above, ProDOS HyperC takes
    only one parameter for open().  What this means is that it doesn't know the
    difference between binary and text files.  It's all in how your program
    chooses to read and write the file.  fprintf() is for text files ... for
    binary files, use read() and write().  Both take three parameters:  first,
    the file designator (just as in fprintf()); second, a buffer usually de-
    clared as an array of or pointer to char; lastly, an integer specifying the
    length of the buffer in bytes.

    For those applications where only one byte (char) is being written at a    e
    time, the most efficient technique would be to use putc().  It takes two
    parameters:  first, the file designator as in fprintf(); second, the char-
    acter to be written.  So instead of your fprintf() call, you really want:

        putc( *fp, *screen );


Actually, if all you're doing is a straight screen dump to disk, you can do the
whole thing *really* efficiently using:

        write( *fp, (char *) 0x400, 0x400 );

and eliminate the loop completely.  -- Hope this helps. --

                            Ben Liblit
                            BRL102 @ psuvm.bitnet -- BRL102 @ psuvm.psu.edu
                            "Fais que tes reves soient plus longs que la nuit."

BRL102@psuvm.psu.edu (Ben Liblit) (11/10/90)

A few clarifications to my recent posting concerning binary files in HyperC:

FILE open( TEXT *fname );
-------------------------

   The open() function returns a FILE, not a pointer to a FILE.  Since, in
   HyperC, FILE is simply an int, you can really mess things up if you're
   careless about your pointers.  HyperC couldn't care less if you assign an
   int to a pointer or vice versa.  It's up to the programmer to watch out for
   this.

   Yes, open() does *not* take a second parameter.  ProDOS, and therefore
   HyperC, could not care less wether you are opening to read or write, or text
   versus binary.  It's up to you, the programmer, to deal with files in the
   proper manner.


FILE create( TEXT *fname );
---------------------------

   open() will *not* create a new file.  To start a new file, you should use
   create() instead.  create() actually performs three calls to the operating
   system, through MLI: destroy, create, and open.  Call it as you would call
   open(), and remember that it takes care of opening the file for you (i.e.,
   don't follow up create() with a call to open()).


INT getc( FILE fd );
--------------------

   This returns a single character from the given file.  Again, watch those
   pointers!  The return value is an int, not a char, so that -1 can be
   returned on errors.


VOID putc( FILE fd );
---------------------

   The counterpart to getc(), this function writes a single byte to the given
   file.  It does *not* have a specified return value.


Error Codes
-----------

   Create(), open(), and getc() return -1 to indicate an error condition.
   Additionally, the actual ProDOS error code is placed in _ioresult.  Declare
   "extern int _ioresult;" if you wish to access it.  The linker will resolve
   the reference.

   On the other hand, read(), write(), and putc() will *not* return -1 to
   signal an error.  Read() and write() return simply the actual number of
   bytes read or written.  Putc()'s return value is *unspecified*.  If an error
   occurs, all three of these function *will* set _ioresult to the ProDOS error
   code.

   For most applications the use read() and write(), it should not be necessary
   to check _ioresult after each call.  Instead, simply compare the *requested*
   number of bytes with the *actual* number of bytes.  If they're different,
   something went wrong.  This works especially well for checking for EOF after
   a read() call.  If EOF was reached, read() will return fewer bytes than were
   requested.

One final note -- don't use read() and write() for characters.  Yeah, they'll
work, if you get the parameter types cast just right, but it's not worth it.
Use putc() and getc() instead ... they're quicker, require less code, and much
easier to use.  And whatever you do, don't call printf( fd, "%c", letter ) !!!
Can you say "unnecessary overhead"?  I new you could.

                            Ben Liblit
                            BRL102 @ psuvm.bitnet -- BRL102 @ psuvm.psu.edu
                            "Fais que tes reves soient plus longs que la nuit."

toth@tellabs.com (Joseph G. Toth) (11/15/90)

I have tried mailing this, but my mailers keep rejecting the address
--------------------------


Michael,
  It has been a while since I played with HyperC, and I only 
  have a few of my listings here at work with me, so I can't
  totally guarantee the accuracy of everything I say here.
  In response to some of the questions in your recent posting;

  A) How do I create my own libraries?
     from the command prompt, enter 'mkdir <path>'
     in program code, Sorry, I haven't figured that one out yet,
       using the create didn't work for me.
     Looks like it will have to be written in assembler as a separately
     linked in function.

  B) Can the sym function do anything useful?
     Absolutely,  Try using it once on the supplied archive libraries.
     You will find all kinds of useful info (however, I somehow assumed
     that you did this to be able to find out the entry point names in
     rt65.o to be able to generate your assembly routine graphics
     functions.

  C) How does one make an independent system program using Hyperc?
     Well, this will take a lot of work.  I know the steps but not
     the details, and the details will require a lot of investigation.

     1 - examine the format of the link command, you will notice an very
	 short file (named s.o) that gets linked in before it processes any
	 of your files.  It is the first entry after the -a, followed by
	 the $1 (for which the shell script substitutes your object file).
	 You can even create your own archives and include them here if
	 you have a large program or a bunch of nice short functions (a
	 good place for separately compiled graphics functions, anybody
	 ever create a Unix-like Curses package for an Apple //).
      
	 This entry is the startup code that has external references to
	 exit() and main().  This must be re-created where it provides
	 all of the operations that the HyperC shell provides before
	 executing your program.  These include;
	    a) loading the 'HYPERC' code at its appropriate location
	       in memory ( I will explain shortly ).
	    b) create a stack frame and initialize all required variables.
	    c) parse the command to pass the parameters (argc, argv) to
	       main.
	    d) calling the main program with the argc and argv parameters.
	    e) providing a call to exit() in case the program simply returns
	       (does not perform its own call to 'exit()')
	       I almost forgot, but 'exit()' will hace to be re-written to
	       provide the standard ProDOS exit operation, as the current
	       'exit()' appears to return control to the HyperC shell by
	       some method other than restarting the shell via the ProDOS
	       'quit' code.
	       be a non-standard method.
	 
	 The reason for loading the HYPERC code is that it contains much
	 of the operational code required by the linked operations.
	 You may think you are including 'open', 'close', 'printf', etc.
	 in your linked executable, but you are not.  Those, and many
	 other functions, are actually contained in the HYPERC code.
	 Go back to question B - look at the output.
	    The letters have meaning;
	       A - anchored location
	       T - an entry point in the object file
	       X - an external entry referenced by code in the object.
	 Watch the output on the screen while a link is in progress.
	 Ever wonder why the insertion address of an included object
	 didn't change from one file to the next? The answer is that
	 that object only contained Anchored entries and variable
	 definitions (no actual code).  The numbers following an A in
	 the symbol table listing is the PHYSICAL address in the
	 Apple's memory where other objects containing an external
	 reference will access.
	 It would probably be safest to have the startup code load
	 the HYPERC code since it is possible that code in rt.o, or any
	 other supplied archive entry, could have direct calls to code
	 in the HYPERC code whthout showing up in the Symbol Table dump.

   D) What is the name and function of all assembler directives?
      I have no idea.

   E) How many 65c02 codes are supported?
      I read somewhere that an assembler directive selects c02 code
      and that all op codes were supported. Sorry, a haven't found
      that info yet, will still look.

   F) What is the syntax for 65c02 codes implemented with implied operands?
      But you said you already know.

I hope I was able to provide some useful information, even though I know
I couldn't give any easy solutions.

Joe Toth
Tellabs, Inc.
Lisle, Il.