/* YAMBOOT.C by Steve Passe (Cnode SYSOP), 1981
	
   Modifications for Australian
   conditions by Bill Bolton (Software Tools SYSOP), 1982

This program uses the "Christensen" MODEM7/YAM protocol for file
transfers.  To receive a  type '<esc>', you will be prompted for
the file name. (<esc> is the ASCII escape character: ^[).

Add whatever code necessary to the initialize_port() function (setup your
port for 8 bits, 1 stop bit, baud rate of 300 if necessary, etc.).
Using a serial modem you will have to make changes of port addresses and
masks in the #ifdef modem area. There is a define CLKMHZ which should
be set to the clock rate for your CPU.

This program uses large chunks of the YAM package for file transfers, 
(thanks Chuck).

*/

#include "apcio.h"		/* Must have modem defines setup */

#define SOH 0x01
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define RETRYMAX 10
#define TIMEOUT (-1)
#define PATHLEN 20
#define WCEOT (-2)
#define CLKMHZ 2		/* CPU speed in Mhz */



#define DIR_IO 0x06		/* You have to have CP/M 2.2 */
#define INPUT 0xff
#define FLAG char

int Baud;
FLAG Rfile;
char Rname[PATHLEN];
unsigned T1pause, Timeout;
char File_buf[BUFSIZ];
char Checksum, Lastrx;
int Wcj, Firstch;

main()
{
	char received, to_send, in_modem(), getch(), escflag;
	Rfile = FALSE;
	T1pause = 311*CLKMHZ;
	escflag = NULL;
	Baud = 300;
	initialize_port();
	printf("\n\tYAMBOOT in the style of MBOOT.\n\n");
	while (TRUE) {
		if (received = in_modem()) putch(received);
		else if (to_send = getch())
			(to_send == ESC) ? download():out_modem(to_send);
	}
}
char
in_modem()
{
	if (peek(MSTAT) & MIMASK) return peek(MDATA);
	else return FALSE;
}
out_modem(out_char)
char out_char;
{
	while (!(peek(MSTAT) & MOMASK))	;
	poke(MDATA, out_char);
}
char
getch()
{
	return bdos(DIR_IO, INPUT);
}

/*

	Most of the following functions taken from YAM package by
Chuck Forsberg, BDS C UG disk Utilities 3, some modifications made for
compatibility with cnode code.

*/

download(filename)
char *filename;
{
	printf("\n\n\tReceive: ");
	scanf("%s", Rname);
	printf("ready to receive '%s'\n", Rname);
	if(wcrx(Rname)==ERROR) {
		abort('r');
	}
	return OK;
}

/* Adapted from CMODEM13.C, by Jack M. Wierda and Roderick W. Hart */
wcrx(name)
char *name;
{
	int sectnum, sectcurr, sectcomp;
	char *cp, rxbuf[128], sendchar;
	if(openrx(name)==ERROR)
		return ERROR;
	sectnum=0;
	sendchar=NAK;
	for(;;) {
		out_modem(sendchar);
		sectcurr=wcgetsec(rxbuf, (sectnum & 0x7f) ? 50 : 130);
		if(sectcurr==(sectnum+1 & 0xff)) {
			sectnum++;
			for(cp=rxbuf,Wcj=128; --Wcj>=0; )
				if(putc(*cp++, File_buf)==ERROR) {
					printf("\nDisk Full\n");
					return ERROR;
				}
			sendchar=ACK;
		}
		else if(sectcurr==sectnum) {
			printf("\nReceived dup Sector %d",sectcurr);
			sendchar=ACK;
		}
		else if(sectcurr==WCEOT) {
			out_modem(ACK);
			closerx(FALSE);
			return OK;
		}
		else {
			printf(" Sync Error\n");
			return ERROR;
		}
	}
}
wcgetsec(rxbuf, time)
char *rxbuf;
int time;
{
	int sectcurr,errors;
	char *cp;

	for(Lastrx=errors=0; errors<RETRYMAX; errors++) {
		do {
			Firstch=readbyt(time);
		}
		while(Firstch != SOH && Firstch != TIMEOUT && Firstch != EOT
			  && Firstch != CAN);	/* wait for one of these */
		if(Firstch==SOH) {
			sectcurr=readbyt(1);
			if((sectcurr+readbyt(1))==255) {
				Checksum=0;
				for(cp=rxbuf,Wcj=128; --Wcj>=0; ) {
					isprint(*cp = readbyt(1)) ? putchar(*cp) : putchar('.');
					Checksum += (*cp++);
				}
				if(((Checksum-readbyt(1))& 0xff)==0)
					return sectcurr;
				else
					printf("Checksum Error #%d", errors);
			}
			else
				printf("Sector number garbled #%d", errors);
		}
		else if(Firstch==EOT)
			return WCEOT;
		else if(Firstch==CAN) {
			if(Lastrx==CAN) {
				printf("\nSender CANcelled");
				return ERROR;
			} else {
				Lastrx=CAN;
				continue;
			}
		}
		else if(Firstch==TIMEOUT)
			printf("\nSOH Timeout #%d", errors);
		Lastrx=0;
		while(readbyt(1)!=TIMEOUT)
			;
		out_modem(NAK);
		time=40;
	}
	out_modem(CAN);out_modem(CAN);out_modem(CAN);
	return ERROR;
}
openrx(name)
char *name;
{
	printf("\nSaving it as '%s'", name);
	if(fopen(name, File_buf) != ERROR) {
		fclose(File_buf);
		printf("\nI already have one, try another name");
		return ERROR;
	}
	if(fcreat(name, File_buf)==ERROR){
		printf("\nCan't create '%s'", name);
		return ERROR;
	}
	Rfile= TRUE;
	return OK;
}
readbyt(decisecs)
int decisecs;
{
	if (peek(MSTAT) & MIMASK)
		return peek(MDATA);
	while (--decisecs >= 0) {
		if (peek(MSTAT) & MIMASK)
			return peek(MDATA);
		if (bdos(DIR_IO, INPUT))
			return TIMEOUT;
		if (peek(MSTAT) & MIMASK)
			return peek(MDATA);
		for (Timeout = T1pause; --Timeout; )
			if (peek(MSTAT) & MIMASK)
				return peek(MDATA);
	}
	return TIMEOUT;
}
abort(flag)
char flag;
{
	flag == 't' ? closetx() : closerx();
	out_modem(CAN);out_modem(CAN);out_modem(CAN);
	return ERROR;
}
closetx()
{
}
closerx()
{
	if(Rfile) {
		fflush(File_buf);
		fclose(File_buf);
		printf("\n\n%s closed", Rname);
		Rfile=FALSE;
	}
}
purgeline()
{
	while (peek(MSTAT) & MIMASK)
		peek(MDATA);
}

/* end of code from yam */

initialize_port()
{
	/* put your modem port intialisation code here, you may NOT need
	   it if you are CERTAIN that the port will be initialised
	   correctly before entering BOOTMODM */
}

isprint(c)
char c;
{
  return (c >= ' ' && c <= '~') || (c == 0x0d) || (c == 0x0a) || (c==0x09);
}

/* END OF YAMBOOT */

