/* * 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); }  .