//********************************************************************************
//*	 	HP21xx Absolute Binary Load Fpormat utility
//* 
//*	See use message near end of file.
//*
//* 2014 Oct: V1 / bhilpert
//*
//********************************************************************************
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "util.h"

extern int			abs_getWord();
extern int			abs_putWord();
extern int			abs_putTrailer();


#define WEOF			-1					// host int size must be > 16 bits


//* END Refs
//********************************************************************************
//* Abs Analyse

void
AbsAnalyse( absIn, doData )
	FILE			*absIn;
	int			doData;
 {
	int			i, trailerCnt;
	int			len, w, checksum, startAddr, curAddr;

	trailerCnt = 0;
	startAddr = 0;
	curAddr = 0;

	repeat								// for each absolute block
	 {	len = fgetc( absIn );					// first byte is length
		if( len == EOF )
			break;
		if( len == 0 )						// assume zero-length blocks are empty (!!)
		 {	++trailerCnt;
			continue;
		 }
		trailerCnt = 0;

		fgetc( absIn );						// 2nd byte is unused

		w = abs_getWord( absIn );				// get 2-byte address
		if( w == WEOF )
			goto errEOF;

		if( w != curAddr )
		 {	if( curAddr != 0 )
				printf( "  SEGMENT END: addr=%06o len=%d\n\n", curAddr-1, curAddr-startAddr );
			startAddr = w;
			curAddr = w;
			printf( "  SEGMENT START: addr=%06o\n", curAddr );
		 }
		curAddr += len;

		printf( "    BLOCK: addr=%06o len=%3d", w, len );	// output block starting address and len

		checksum = w;						// address contributes to checksum
		for( i=0; i<len; ++i )					// transfer data words
		 {	w = abs_getWord( absIn );
			if( w == WEOF )
				goto errEOF;
			if( doData )
			 {	if( (i % 8) == 0 ) printf( "\n    " );
				printf( "  %6o", w );
			 }
			checksum += w;					// include data in checksum
		 }
		if( doData ) printf( "\n" );

		checksum &= 0xFFFF;

		w = abs_getWord( absIn );				// get and check checksum
		if( w == WEOF )
		 {errEOF:
			printf( "\n  -- unexpected EOF\n" );
			break;
		 }

		printf( "    checksum=%06o", checksum );
		if( w != checksum )
		 {	printf( " checksum err, data=%6o\n", w );
			break;
		 }
		printf( "\n" );
		if( doData ) printf( "\n" );
	 }

	if( curAddr != 0 )
		printf( "  SEGMENT END: addr=%06o len=%d\n\n", curAddr-1, curAddr-startAddr );

	if( trailerCnt < 10 )
		printf( "  TRAILER: %d NULLs -- Too short!\n\n", trailerCnt );
	else
		printf( "  TRAILER: %d NULLs\n\n", trailerCnt );
 }


//* END Abs Analyse
//********************************************************************************
//* Abs Concatenate

void
_AbsTransfer( absIn, absOut )
	FILE			*absIn, *absOut;
 {
	int			i;
	int			len, w, checksum;

	repeat								// for each absolute block
	 {	len = fgetc( absIn );					// first byte is length
		if( len == EOF )
			break;
		if( len == 0 )						// assume zero-length blocks are empty (!!)
			continue;
		fputc( len, absOut );

		fgetc( absIn );						// 2nd byte is unused
		fputc( 0, absOut );

		w = abs_getWord( absIn );					// get 2-byte address
		if( w == WEOF )
			goto errEOF;
		abs_putWord( absOut, w );					// output address

		checksum = w;						// address contributes to checksum
		for( i=0; i<len; ++i )					// transfer data words
		 {	w = abs_getWord( absIn );
			if( w == WEOF )
				goto errEOF;
			abs_putWord( absOut, w );
			checksum += w;					// include data in checksum
		 }

		w = abs_getWord( absIn );					// get and check checksum
		if( w == WEOF )
		 {errEOF:
			fprintf( stderr, "  -- unexpected EOF\n" );
			break;
		 }
		if( w != (checksum&0xFFFF) )
		 {	fprintf( stderr, "  -- bad checksum\n" );
			break;
		 }
		abs_putWord( absOut, checksum );				// output checksum
	 }
 }


void
AbsCat( fnames, fcnt, absOut )
	char			*fnames[];
	int			fcnt;
	FILE			*absOut;
 {
	FILE			*absIn;
	int			i;

	for( i=0; i<fcnt; ++i )						// for each file
	 {	absIn = fopen( fnames[i], "r" );
		if( !absIn )
		 {	fprintf( stderr, "  -- Error opening file %s\n", fnames[i] );
			continue;
		 }

		_AbsTransfer( absIn, absOut );				// transfer all the blocks, but not the trailer
	 }

	abs_putTrailer( absOut );					// final trailer
 }


//* END Abs Cat
//********************************************************************************
//* Main

char				useMsg[] =
	"  -- Use:  hp21abs [-d] {absFile}                  : analyse ABS file\n"
	"           hp21abs [-d]               < {absFile}  : analyse ABS file\n"
	"           hp21abs -cat {absFile}...  > {absFile}  : concatentate ABS files\n"
	"           hp21abs -u                              : display this message\n"
	"     Options:\n"
	"                    -d                             : display data too\n";


int
main( argc, argv )
	int			argc;
	char**			argv;
 {
	FILE			*absIn;
	char			*fname;
	int			mode, doData, i;

	//------------------------------
	// Command line configuration

	mode = 0;
	doData = FALSE;
	fname = NULL;

	for( i=1; i<argc; ++i )
		if(      streql(argv[i],"-cat") )
		 {	mode = 'c';
			++i;
			break;
		 }
		else if( streql(argv[i],"-u") )
			goto useFail;
		else if( streql(argv[i],"-d") )
			doData = TRUE;
		else
		 {	if( fname )
			 {useFail:
				fprintf( stderr, useMsg, argv[0] );
				exit( -1 );
			 }
			fname = argv[i];
		 }

	//------------------------------
	// Init

	if( fname )
	 {	absIn = fopen( fname, "r" );
		if( !absIn )
		 {	fprintf( stderr, "  -- Error opening file: %s\n", fname );
			exit( -1 );
		 }
	 }
	else
		absIn = stdin;

	//------------------------------
	// Activity

	switch( mode )
	 {	case 'c':	AbsCat( &argv[i], argc-i, stdout );	break;
		default:	AbsAnalyse( absIn, doData );		break;
	 }

	//------------------------------
	// Termination

	if( fname ) fclose( absIn );

	return( 0 );
 }


//* EOF
//********************************************************************************
