www.pudn.com > CÓïÑÔµÄС±àÒëÆ÷.zip > MTERM.C


/* 
 * MICRO-Terminal: 
 * 
 * This is a very simple communications program, which provides 
 * a subset ANSI (VT100) terminal emulation, and basic XMODEM 
 * (with checksum) file transfer. 
 * 
 * If the '-t' option is given, MICRO-Terminal will install 
 * itself as a TSR (Ram-Resident) program, which can be invoked 
 * at any time by pressing both SHIFT keys. 
 * 
 * This demonstrates the use of the MICRO-C video interface 
 * and communications library functions for the IBM/PC, as well 
 * as the "SAVE_VIDEO", "RESTORE_VIDEO" and "TSR" functions. 
 * 
 * Copyright 1990 Dave Dunfield 
 * All rights reserved. 
 */ 
#include \mc\stdio.h		/* Standard I/O definitions */ 
#include \mc\comm.h			/* Comm     I/O definitions */ 
#include \mc\tsr.h			/* Tsr function definitions */ 
#include \mc\video.h		/* Video    I/O definitions */ 
 
/* Screen output positions */ 
#define	SETROW		3		/* Screen row for settings display */ 
#define MSGROW		20		/* Screen row for messages */ 
#define	MENROW		7		/* Screen row for menu items */ 
#define	MAICOL		0		/* Screen column for main menu */ 
#define	SUBCOL		20		/* Screen column for sub menu */ 
#define	FILCOL		5		/* Screen column for file prompt */ 
#define	FILSIZ		50		/* Maximum size of file name */ 
 
/* XMODEM parameters */ 
#define BLOCK_SIZE	128		/* size of transmit blocks */ 
#define RETRYS		10		/* maximum number of retrys */ 
#define	SOH_TIMEOUT	10		/* How long to wait for start of packet */ 
#define	RX_TIMEOUT	2		/* How long in wait for chars in packet */ 
#define	ACK_TIMEOUT	15		/* How long to wait for acknowlege */ 
 
/* Line control codes */ 
#define SOH			0x01	/* start of header */ 
#define ACK			0x06	/* Acknowledge */ 
#define NAK			0x15	/* Negative acknowledge */ 
#define CAN			0x18	/* Cancel */ 
#define EOT			0x04	/* end of text */ 
 
/* Menu text tables (Used by 'vmenu') */ 
	char *main_menu[] = { 
		"Terminal Emulation", 
		"XMODEM Download", 
		"XMODEM Upload", 
		"Serial port config", 
		"Exit to DOS", 
		0 }; 
 
	char *setup_menu[] = { 
		"Comm port", 
		"Baudrate", 
		"Data bits", 
		"Parity", 
		"Stop bits", 
		"Xon/Xoff", 
		0 }; 
 
/* Uart configuration static data tables */ 
	unsigned baudvalue[] = 
		{ _110,  _300,  _1200,  _2400,  _4800,  _9600,  _19200,  _38400 }; 
	char *baudtext[] = 
		{ "110", "300", "1200", "2400", "4800", "9600", "19200", "38400", 0 }; 
	char *databits[] = { "Five", "Six", "Seven", "Eight", 0 }; 
	char *parity[] = { "Odd", "Even", "Mark", "Space", "None", 0 }; 
	char *onetwo[] = { "One", "Two", 0 }; 
	char *flowctrl[] = { "Disabled", "Enabled", 0 }; 
 
/* Communications configuration parameters */ 
	int comm = 0, baud = 5, data = 3, par = 4, stop = 0, flow = 1; 
 
/* Misc global variables */ 
	setup_selection = 0, transfer_selection = 0; 
	char dfile[FILSIZ+1] = "", ufile[FILSIZ+1] = ""; 
 
/* Saved video screens, attributes & cursor position */ 
	char sav_buffer[(25*80)*2], sav_attr; 
	int sav_xy; 
	char video_save_area[SCR_BUF]; 
 
/* 
 * Main terminal program menu 
 */ 
tty_main() 
{ 
	int i; 
 
	i = 0;			/* Default to top of menu */ 
	save_video(video_save_area); 
 
redraw: 
	draw_title(); 
	vdraw_box(0, SETROW, 79, 2); 
	show_settings(); 
 
	for(;;) { 
		message("Select function and press ENTER"); 
		if(vmenu(MAICOL, MENROW, main_menu, 0, &i)) 
			continue; 
		switch(i) { 
			case 0 :		/* Terminal Emulation */ 
				if(!open_comm(flow)) 
					break; 
				vcursor_line(); 
				restore_screen(); 
				ansi_term(); 
				save_screen(); 
				vcursor_off(); 
				goto redraw; 
			case 1 :		/* Download a file */ 
				if(open_comm(0)) 
					download(dfile); 
				break; 
			case 2 :		/* Upload a file */ 
				if(open_comm(0)) 
					upload(dfile); 
				break; 
			case 3 :		/* Setup serial port */ 
				setup(); 
				break; 
			case 4 :		/* Exit to DOS */ 
				Cclose(); 
				restore_video(video_save_area); 
				return; } } 
} 
 
/* 
 * Open a file for read or write (with overwrite prompt) 
 */ 
FILE *openf(fname, rw) 
	char *fname, rw; 
{ 
	char c, omsg[80], *mode; 
	FILE *fp; 
 
	mode = "read"; 
	fp = fopen(fname, "r");		/* First try and read the file */ 
	if(rw) {					/* If writing the file */ 
		mode = "write"; 
		if(fp) { 
			fclose(fp); 
			sprintf(omsg, "Overwrite existing %s (Y/N) ?", fname); 
			message(omsg); 
			do { 
				c = toupper(vgetc()); 
				if((c == 0x1B) || (c == 'N')) 
					return 0; } 
			while(c != 'Y'); } 
		fp = fopen(fname,"w"); } 
	if(!fp) { 
		sprintf(omsg,"Cannot %s %s (Press ENTER)", mode, fname); 
		message(omsg); 
		while(vgetc() != '\n'); } 
	return fp; 
} 
 
/* 
 * Open comm port with correct settings 
 */ 
open_comm(flow) 
	char flow; 
{ 
	int mode; 
 
	/* Calculate the communications parameter value */ 
	mode =	((par << 4) & 0x30) |	/* parity type */ 
			(data & 0x03) |			/* # data bits */ 
			((stop << 2) & 0x04) |	/* # stop bits */ 
			((par < 4) << 3);		/* parity enable */ 
 
	/* Open the communications port */ 
	if(Copen(comm+1, baudvalue[baud], mode, SET_DTR|SET_RTS|OUTPUT_2)) { 
		message("Cannot open COM port (Press ENTER)"); 
		while(vgetc() != '\n'); 
		return 0; } 
 
	/* Remove transparency if XON/XOFF flow control */ 
	disable(); 
	Cflags = (flow) ? Cflags & ~TRANSPARENT : Cflags | TRANSPARENT; 
	enable(); 
 
	return -1; 
} 
 
/* 
 * Draw the title  header 
 */ 
draw_title() 
{ 
	vopen(); 
	V_ATTR = REVERSE; 
	vdraw_box(0, 0, 79, 2); 
	vgotoxy(1, 1); 
	vputf("", 26); 
	vputf("MICRO-Terminal Version 1.2", 52); 
	V_ATTR = NORMAL; 
	vcursor_off(); 
} 
 
/* 
 * Draw the file transfer information box 
 */ 
info_box(mode, filename) 
	char *mode, *filename; 
{ 
	vdraw_box(SUBCOL, MENROW+1, 50, 8); 
	vgotoxy(SUBCOL+2, MENROW+3); 
	vprintf("%-19s: %s", mode, filename); 
	vgotoxy(SUBCOL+2, MENROW+5); 
	vputs("Blocks transferred : 0"); 
	vgotoxy(SUBCOL+2, MENROW+7); 
	vputs("Transfer status    : "); 
	message("File transfer in progress (ESCAPE to abort)"); 
} 
 
/* 
 * Update the transfer status field 
 */ 
transfer_status(text) 
	char *text 
{ 
	vgotoxy(SUBCOL+23, MENROW+7); 
	vputf(text,10); 
} 
 
/* 
 * Show the current COM port settings 
 */ 
show_settings() 
{ 
	vgotoxy(18, SETROW+1); 
	vprintf("COM%u: %5s,%2d,%5s,%2d  Xon/Xoff %-8s", 
		comm+1, baudtext[baud], data+5, parity[par], stop+1, flowctrl[flow]); 
} 
 
/* 
 * Display a message 
 */ 
message(ptr) 
	char *ptr; 
{ 
	vgotoxy(0, MSGROW); 
	vcleos(); 
	vmessage(38 - strlen(ptr)/2, MSGROW, ptr); 
} 
 
/* 
 * Save the MICRO-TERMINAL video screen. 
 */ 
save_screen() 
{ 
	sav_xy = V_XY; 
	sav_attr = V_ATTR; 
	copy_seg(get_ds(), sav_buffer, V_BASE, 0, (25*80)*2); 
} 
 
/* 
 * Restore the MICRO-TERMINAL video screen 
 */ 
restore_screen() 
{ 
	copy_seg(V_BASE, 0, get_ds(), sav_buffer, (25*80)*2); 
	V_ATTR = sav_attr; 
	V_XY = sav_xy; 
	vupdatexy(); 
} 
 
/* 
 * Comm port setup menu handler 
 */ 
setup() 
{ 
	message("Select setting (ESCAPE to cancel)"); 
	for(;;) { 
		show_settings(); 
		if(vmenu(SUBCOL, MENROW+1, setup_menu, 0, &setup_selection)) 
			return; 
		switch(setup_selection) { 
			case 0 :	/* Comm port */ 
				vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&comm); 
				break; 
			case 1 :	/* baudrate */ 
				vmenu(SUBCOL+11,MENROW+2,baudtext,-1,&baud); 
				break; 
			case 2 :	/* Data bits */ 
				vmenu(SUBCOL+11,MENROW+2,databits,-1,&data); 
				break; 
			case 3 :	/* Parity */ 
				vmenu(SUBCOL+11,MENROW+2,parity,-1,&par); 
				break; 
			case 4 :	/* Stop bits */ 
				vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&stop); 
				break; 
			case 5 :	/* Flow control */ 
				vmenu(SUBCOL+11,MENROW+2,flowctrl,-1,&flow); } } 
} 
 
/* 
 * ANSI (VT100) Function key translation table 
 */ 
	char *ansi_keys[] = { 
		"\x1B[A", "\x1B[B", "\x1B[D", "\x1B[C",	/* Arrow keys */ 
		"\x1BOR", "\x1BOS", "\x1BOP", "\x1BOQ",	/* PgUp, Pgdn, Home, End */ 
		"\x1BOM", "\x1BOm", "\x1BOp",			/* Keypad '+','-' Insert */ 
		"\x7F",   "\x08",						/* Delete & Backspace */ 
		"\x1BOq", "\x1BOr", "\x1BOs", "\x1BOt",	/* F1, F2, F3 & F4 */ 
		"\x1BOu", "\x1BOv", "\x1BOw", "\x1BOx",	/* F5, F6, F7 & F8 */ 
		"\x1BOy", "\x1BOp",						/* F9 & F10 */ 
		"\x1BOl", "\X1BOn", 0, 0 };		/* Control: Pgup, Pgdn, Home, End */ 
 
/* 
 * Terminal mode using ANSI (VT100) emulation 
 */ 
ansi_term() 
{ 
	char c, xy_flag, *ptr; 
	unsigned x, y, state, value, parm, parms[5]; 
 
	xy_flag = -1;		/* Force initial cursor update */ 
	state = 0;			/* Not receiving a control sequence */ 
	for(;;) { 
		/* Process any input from the comm port */ 
		if((c = Ctestc()) != -1) { 
			xy_flag = -1; 
			if(c == 0x1B) {				/* Begin escape sequence */ 
				state = 1; 
				parms[0] = parms[1] = value = parm = 0; } 
			else switch(state) { 
				case 1 :				/* Escape already received */ 
					if(c == '[') { 
						state = 2; 
						break; } 
					state = 0; 
				case 0 :				/* No special processing */ 
					vputc(c); 
					break; 
				case 2 :				/* Waiting for numeric parms */ 
					if(isdigit(c)) { 
						value = (value * 10) + (c - '0'); 
						break; } 
					parms[parm++] = value;	/* More to come */ 
					if(c == ';') { 
						value = 0; 
						break; } 
					state = 0; 
					switch(c) { 
						case 'H' :		/* Cursor position (1) */ 
						case 'f' :		/* Cursor position (2) */ 
							if(y = parms[0]) 
								--y; 
							if(x = parms[1]) 
								--x; 
							vgotoxy(x, y); 
							break; 
						case 'J' :		/* Erase in display */ 
							x = V_XY; 
							value ? vclscr() : vcleos(); 
							V_XY = x; 
							break; 
						case 'K' :		/* Erase in line */ 
							x = V_XY; 
							if(value) 
								V_XY &= 0xff00; 
							vcleol(); 
							V_XY = x; 
							break; 
						case 'm' :		/* Select attributes */ 
							x = 0; 
							do { 
								V_ATTR  = (y = parms[x]) ? (y == 4) ? 
									UNDERLINE : REVERSE : NORMAL; } 
							while(++x < parm); } } } 
		else if(xy_flag) {				/* Cursor has moved */ 
			vupdatexy(); 
			xy_flag = 0; } 
 
		/* Process any input from the keyboard */ 
		if(c = vtstc()) { 
			if(c & 0x80) {				/* Special function key */ 
				if(!(ptr = ansi_keys[c & 0x7f])) 
					return; 
				while(*ptr) 
					Cputc(*ptr++); } 
			else 
				Cputc((c == '\n') ? '\r' : c); } } 
} 
 
/* 
 * Receive a file in XMODEM protocol 
 */ 
download() 
{ 
	char rbuffer[BLOCK_SIZE], *error_text, *old_error, ochr; 
	int r, rx_block_num, error_count; 
	FILE *fp; 
 
	if(vgets(FILCOL,MSGROW,"Write to file? ",dfile,FILSIZ) || !*dfile) 
		return; 
 
	if(!(fp = openf(dfile, -1))) 
		return; 
 
	info_box("Download to file", dfile); 
 
	error_text = old_error = rx_block_num = 1; 
	error_count = RETRYS; 
	do { 
		if(vtstc() == 0x1b) {		/* Console escape */ 
			ochr = CAN; 
			error_text = "CANCELED"; 
			error_count = 0; } 
		else if((r = get_record(rbuffer)) == (rx_block_num & 255)) { 
			error_count = RETRYS; 
			fput(rbuffer, BLOCK_SIZE, fp); 
			vgotoxy(SUBCOL+23, MENROW+5); 
			vprintf("%u", rx_block_num++); 
			error_text = "RX PACKET"; 
			ochr = ACK; } 
		else { 
			switch(r) { 
				case -1 :		/* Timeout */ 
					error_text = "TIMEOUT"; 
					ochr = NAK; 
					break; 
				case -2 :		/* Bad block */ 
					error_text = "BAD BLOCK#"; 
					while(Cgett(RX_TIMEOUT) != -1); 
					ochr = NAK; 
					break; 
				case -3 :		/* Bad checksum */ 
					error_text = "BAD CHKSUM"; 
					ochr = NAK; 
					break; 
				case -4 :		/* End of file */ 
					error_text = "DONE"; 
					ochr = ACK; 
					break; 
				case -5 :		/* Cancel */ 
					error_text = "ABORTED"; 
					ochr = ACK; 
					break; 
				default:		/* Block out of sequence */ 
					error_text = "WRONG BLK"; 
					ochr = NAK; } 
				--error_count; } 
			Cputc(ochr); 
			/* Update status message */ 
			if(error_text != old_error) 
				transfer_status(old_error = error_text); } 
		while((r > -3) && error_count); 
 
	message("Download ended (Press ENTER)"); 
	fclose(fp); 
	while(vgetc() != '\n'); 
	vclear_box(SUBCOL, MENROW+1, 50, 8); 
} 
 
/* 
 * Read a record in the XMODEM protocol, return the block number 
 * (0-255) if successful, or one of the following return codes: 
 *	-1 = Timeout 
 *	-2 = Bad block number 
 *	-3 = Bad block checksum 
 *	-4 = End of file 
 *	-5 = Canceled by remote 
 */ 
get_record(rbuffer) 
	char rbuffer[]; 
{ 
	int c, i, block_num, check_sum; 
 
	check_sum = 0; 
	i = -2; 
	switch(Cgett(SOH_TIMEOUT)) { 
		case SOH :		/* Receive packet */ 
			for(;;) { 
				if((c = Cgett(RX_TIMEOUT)) == -1)	/* receive timeout */ 
					break; 
				if(i == -2)							/* first block number */ 
					block_num = c; 
				else if(i == -1) {					/* second block number */ 
					if((255 & ~c) != block_num) 
						return -2; } 
				else if(i == BLOCK_SIZE)			/* checksum at end */ 
					return (check_sum & 0xff) == c ? block_num : -3; 
				else								/* data character */ 
					check_sum += (rbuffer[i] = c); 
				++i; } 
		case -1 :		/* timeout on waiting for packet */ 
			return -1; 
		case EOT :		/* end of file encountered */ 
			return -4; 
		case CAN :		/* cancel protocol */ 
			return -5; } 
} 
 
/* 
 * Transmit a file in XMODEM protocol 
 */ 
upload() 
{ 
	int i, c, tx_block_num, error_count; 
	char buffer[BLOCK_SIZE], *error_text, *old_error; 
	FILE *fp; 
 
	if(vgets(FILCOL,MSGROW,"Read from file? ",ufile,FILSIZ) || !*ufile) 
		return; 
 
	if(!(fp = openf(ufile, 0))) 
		return; 
 
	info_box("Upload from file", ufile); 
 
	tx_block_num = old_error = error_text = 1; 
	error_count = RETRYS; 
 
	/* Transmit the file data */ 
	while(i = fget(buffer, BLOCK_SIZE, fp)) { 
		while(i < 128) 
				buffer[i++] = -1; 
		error_text = "TX PACKET"; 
		while(i = send_record(buffer, tx_block_num)) { 
			switch(i) { 
				case -1 : 
					error_text = "TIMEOUT"; 
					break; 
				case -3 : 
					error_text = "RECV NAK"; 
					break; 
				case -5 : 
					error_text = "ABORTED"; } 
				transfer_status(old_error = error_text); 
				if((i < -3) || !error_count--) 
					break; } 
		if(vtstc() == 0x1b) {		/* Console escape */ 
			i = -5; 
			error_text = "CANCELED"; } 
		if(i) {						/* Error exit */ 
			Cputc(CAN); 
			break; } 
		error_count = RETRYS; 
		vgotoxy(SUBCOL+23, MENROW+5); 
		vprintf("%u", tx_block_num++); 
		if(error_text != old_error) 
			transfer_status(old_error = error_text); } 
 
	/* Send the end of file indicator */ 
	error_count = RETRYS; 
	if(!i) for(;;) { 
		Cputc(EOT); 
		if((c = Cgett(ACK_TIMEOUT)) == ACK) { 
			error_text = "DONE"; 
			break; } 
		if(c == CAN) { 
			error_text = "ABORTED"; 
			break; } 
		if(((c == -1) || (c == NAK)) && !error_count--) { 
			error_text = "TIMEOUT"; 
			break; } } 
 
	transfer_status(error_text); 
	message("Upload ended (Press ENTER)"); 
	fclose(fp); 
	while(vgetc() != '\n'); 
	vclear_box(SUBCOL, MENROW+1, 50, 8); 
} 
 
/* 
 * Send an record in XMODEM protocol, return 0 if successful 
 * Otherwise, return one of the following: 
 *	-1 = Timeout 
 *	-2 = Bad block number	(N/A) 
 *	-3 = Bad block			(NAK RECEIVED) 
 *	-4 = End of file		(N/A) 
 *	-5 = Canceled by remote 
 */ 
send_record(buffer, block_num) 
	char *buffer; 
	int block_num; 
{ 
	int i, check_sum; 
	char *ptr; 
 
	check_sum = 0; 
	ptr = buffer; 
	while(Ctestc() != -1);		/* purge any received data */ 
	Cputc(SOH); 
	Cputc(block_num); 
	Cputc(~block_num); 
	for(i=0; i < BLOCK_SIZE; ++i) { 
		Cputc(*buffer); 
		check_sum += *buffer++; } 
	Cputc(check_sum); 
 
	for(;;) switch(Cgett(ACK_TIMEOUT)) { 
		case ACK :			/* Packet received ok */ 
			return 0; 
		case NAK :			/* Rejected */ 
			return -3; 
		case CAN :			/* Remote cancel */ 
			return -5; 
		case -1 :			/* Timeout */ 
			return -1; } 
} 
 
/* 
 * Wait for a character from the modem (with timeout) 
 */ 
Cgett(timeout) 
	unsigned timeout; 
{ 
	int h, m, s, old_s; 
 
	if((h = Ctestc()) != -1)	/* Very fast if characters buffered */ 
		return h; 
 
	get_time(&h, &m, &old_s); 
	do { 
		do { 
			if((h = Ctestc()) != -1) 
				return h; 
			get_time(&h,&m,&s); } 
		while(s == old_s); 
		old_s = s; } 
	while(--timeout); 
	return -1; 
} 
 
/* 
 * Main program, either TSR or execute main tty program menu 
 */ 
main(argc, argv) 
	int argc; 
	int *argv[]; 
{ 
	vopen(); 
	vputs("MICRO-Terminal: (Press CTRL-HOME or CTRL-END to exit):\n"); 
	save_screen(); 
 
/* If RAM-resident, print startup message & TSR */ 
	if((argc > 1) && (*argv[1] == 't-')) { 
		draw_title(); 
		printf("\n\n\nRam-Resident MICRO-Terminal installed\nPress BOTH SHIFTS to POP UP\n"); 
		vcursor_line(); 
		tsr(&tty_main, L_SHIFT+R_SHIFT, 2000); } 
 
/* Not RAM-resident, execute the program */ 
	vclscr();		/* Return to blank screen */ 
	tty_main(); 
}