/*
*      ph.c
*
*      Last Modified : November 30, 1981
*
*      By: Larry A. Marek              Aug. 10, 1980
*      Modified by: Doug Porter        Nov. 30, 1981
*
* "PH" is a derivative of the program "Unload" by Larry Marek.
*
* "Unload" Copyright (c) by Larry A. Marek  Not for commercial use.
* Portions of "Ph" Copyright (c) by Doug Porter.
* This program may not be bought or sold.  For hobbiest use only!
*
*
*
* PH (Punch Hex) is intended as a partial solution to the problem
* of how to get a file onto a CP/M system which has no telecommun-
* ications software.  It is particularly useful for getting that
* critical first smart terminal package downline.
*
*
*/





/*


                       Transmitting a File Using PH


       To place the cart firmly before the horse, let's note that
       the receiving system must be ready to accept the file being
       transmitted before you can run PH.

       The command line:

                ph foo.bar

       will read the file "foo.bar" and send an Intel hex equivalent
       to the punch device.  Every 10,000 characters an XOFF is
       transmitted, followed by a delay which is hopefully sufficient
       to allow PIP, running on a CP/M system which is receiving
       the hex file, to flush its buffer properly.  The resulting
       ascii file can be later re-LOADed to working object code.

       It may be necessary to change the IOBYTE using STAT, or
       something similar, to assign the logical PUN: device to a
       serial port.



                       Receiving a File Using PH


       Like the transmitting system, the receiving system may require
       that a serial line be assigned to one of the logical input
       devices to allow PIP to read the incoming file.

       After a serial line has been associated with a logical input
       device, such as RDR:, a command line such as:

               pip foo.hex=rdr:[b]

       will buffer the incoming Intel hex character stream until PIP
       detects an XOFF character or the CP/M end of file character,
       ^Z.  If the character was XOFF, PIP will then flush its buffer
       to disk and return for more characters.  When PIP detects
       the ^Z end of file character, it will flush its buffers and
       close the file.  Then the command line

               load foo

       will create a binary file once more from the hex file.  This
       file will, of course, be named foo.com.  If the file is
       not a CP/M command file, simply rename it:

               ren foo.bar=foo.com

       Remember that a file in Intel hex format is about 3 times as
       large as its equivalent binary file.  This means that if both
       the hex file and the final file are to be on the same disk,
       you need to have about four times as much disk space as you
       would need for the binary file alone.

       PH will not properly transmit a file which has non-contiguous
       random records.  For CP/M command files, this matters not at
       all.
       from a serial port.
*/

#include "bdscio.h"

#define CLOCK_MHZ 4     /* set this for the clock speed of your system */

#define DELAY   20      /* number of seconds to delay after a block */
#define XON     17      /* ASCII Xon character */
#define XOFF    19      /* ASCII Xoff character */

main(argc, argv)
int     argc;
char    **argv;
{
       if (argc != 2) {
               printf("\nUsage: ph file\n");
       } /* if */

       else {
               doit(*++argv);
       } /* else */

}


doit(file_name)
char *file_name;
{
       char    cksum;
       int i, c, fdi, fdo;
       int charcount;
       unsigned addr;
       char    infile[BUFSIZ][BUFSIZ];

/*
* take the filename given and open that
* file for reading
*/

       fdi = fopen(file_name, infile);
       if (fdi == ERROR) {
               printf("can't access '%s'\n", file_name);
               exit();
       }

/*
* the main work loop
*
* Here the start of every line has the address and
* other load information, then sixteen hexadecimal
* ascii bytes.  Last on the line is the checksum and
* a carriage return, linefeed.
*/

       addr = 0x100;           /* cp/m transients start here */

       charcount = 0;          /* no characters sent yet */

       while ((c = getc(infile)) != EOF) {

               if (charcount >= 10000) {       /* if block, */
                       punchc(XOFF);           /* send Xoff and delay */
                       sleep(DELAY * (CLOCK_MHZ / 2) * 10);
                       charcount = 0;
               }

               if ((addr % 16) == 0) {
                       cksum = 0;
                       punchc(':');    /* record mark */
                       charcount++;

                       punchc('1');
                       punchc('0');    /* 16 frames to come */
                       charcount += 2;
                       cksum -= 0x10;

                       /* the load address */
                       punchc(tohex(addr >> 12));
                       punchc(tohex(addr >> 8));
                       charcount += 2;
                       cksum -= (addr >> 8);
                       punchc(tohex(addr >> 4));
                       punchc(tohex(addr));
                       charcount += 2;
                       cksum -= (addr & 0xff);

                       punchc('0');    /* record type */
                       punchc('0');
                       charcount += 2;
               }

               punchc(tohex(c >> 4));
               punchc(tohex(c));
               charcount += 2;
               cksum -= c;

               ++addr;
               if ((addr % 16) == 0) {
                       punchc(tohex(cksum >> 4));
                       punchc(tohex(cksum));
                       punchc('\r');
                       punchc('\n');
                       charcount += 4;
               }

       }

/*
* The unlikely (in cp/m) case the End-o-file was
* reached before writing a full line to the punch
* device.
*/

       if ((addr % 16) != 0) {
               while ((addr % 16) != 0) {
                       punchc('0');
                       charcount++;
                       ++addr;
               }
               punchc(tohex(cksum >> 4));
               punchc(tohex(cksum));
               punchc('\r');
               punchc('\n');
               charcount += 4;
       }

/*
* to show the end of the hex file -  a zero length
* line
*/

       punchc(':');
       charcount++;
       for (i = 0; i <= 1; i++) {
               punchc('0');
               charcount++;
       }
       punchc(CPMEOF); /* cp/m end of file */
       charcount++;
}

/*
* "tohex" converts it's input to an ascii hex digit.
*  Input range is made to fit.
*/

tohex(c)
char    c;
{
       c &= 0xf;               /* range 0 - F */

       if (c <= 9)
               c += '0';
       else
               c += ('A' - 10);
       return(c);
}


punchc(c)       /* send c to the punch device */
char c;
{
       bdos(4, c);
}