/* bintohex: convert binary to Srecord
*  Didier Mequignon 2005 May, e-mail: aniplay@wanadoo.fr
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <tos.h>
#include <stdio.h>
#include <string.h>

static int checksum(long addr, unsigned char *buf, long len, int size)
{
	long i,sum;
	
	sum = 0xff - 1 - size - (len & 0xff);
	if(size ==4) sum -= (addr >>24) & 0xff;
	if(size >=3) sum -= (addr >>16) & 0xff;
	sum -= (addr >> 8) & 0xff;
	sum -= addr & 0xff;
	for(i=0;i<len;i++)
		sum -= (unsigned int)*buf++ & 0xff;
	return((int)(sum & 0xff));
}

static int put32(int len, long addr, char *buf, int size, long mask, int handle)
{
	char buf_out[256];
	int	i,ret;

	switch(size)
	{
		case 2: sprintf(buf_out,"S1%02x%04lx",2+len+1,addr & mask); break;
		case 3: sprintf(buf_out,"S2%02x%06lx",3+len+1,addr & mask); break;
		case 4: sprintf(buf_out,"S3%02x%08lx",4+len+1,addr & mask); break;
	}
	strupr(buf_out);
	if((ret=(int)Fwrite(handle,(long)strlen(buf_out),(void *)buf_out))<0)
		return(ret);
	for(i=0;i<len;i++)
	{
		sprintf(buf_out,"%02x",*buf++ & 0xff);
		strupr(buf_out);
		if((ret=(int)Fwrite(handle,(long)strlen(buf_out),(void *)buf_out))<0)
			return(ret);
	}
	sprintf(buf_out,"%02x\n",checksum(addr,buf,len,size));
	strupr(buf_out);
	ret=(int)Fwrite(handle,(long)strlen(buf_out),(void *)buf_out);
	return(ret);
}

int bintohex(unsigned char *source, long size_source, long base, int byte_addr, char *name)
{
	int handle,ret;
	char buf[256];
	char *s;
	long mask,addr,cc;
	
	mask = (1 << (byte_addr *8))-1;
	if(!mask)
		mask = -1;
	switch(byte_addr)
	{
		case 2:	sprintf(buf,"S0%02x%04lx",2+strlen(name)+1,0); break;
		case 3: sprintf(buf,"S0%02x%06lx",3+strlen(name)+1,0); break;
		case 4:	sprintf(buf,"S0%02x%08lx",4+strlen(name)+1,0); break;
		default: return(-1);
	}
	if((handle=(int)Fcreate(name,0))<0)
		return(handle);
	strupr(buf);
	if((ret=(int)Fwrite(handle,(long)strlen(buf),(void *)buf))<0)
	{
		Fclose(handle);
		return(ret);
	}
	s=name;
	while(*s != '\0')
	{
		sprintf(buf,"%02x",*s++ & 0xff);
		strupr(buf);
		if((ret=(int)Fwrite(handle,(long)strlen(buf),(void *)buf))<0)
		{
			Fclose(handle);
			return(ret);
		}
	}
	sprintf(buf,"%02x\n",checksum(0,name,strlen(name),byte_addr));
	strupr(buf);
	if((ret=(int)Fwrite(handle,(long)strlen(buf),(void *)buf))<0)
	{
		Fclose(handle);
		return(ret);
	}
	addr=base;
	cc=21;
	while(cc>0)
	{
		if(cc>size_source)
			cc=size_source;
		if(cc>0)
		{
			if((ret=put32(cc,addr,source,byte_addr,mask,handle))<0)
			{
				Fclose(handle);
				return(ret);
			}			
			addr += cc;
			source += cc;
			size_source -= cc;
		}
	}
	sprintf(buf,"S%d%02x",11-byte_addr,2+1);
	strupr(buf);
	if((ret=(int)Fwrite(handle,(long)strlen(buf),(void *)buf))<0)
	{
		Fclose(handle);
		return(ret);
	}
	switch(byte_addr)
	{
	    case 2: sprintf(buf,"%04lx",base & mask); break;
	    case 3: sprintf(buf,"%06lx",base & mask); break;
	    case 4: sprintf(buf,"%08lx",base & mask); break;
	}
	strupr(buf);
	if((ret=(int)Fwrite(handle,(long)strlen(buf),(void *)buf))<0)
	{
		Fclose(handle);
		return(ret);
	}
	/* kludge -> don't know why you have to add the +1 = works for byte_addr =3 at least */
	sprintf(buf,"%02x\n",checksum(base,(char *)0,0,byte_addr)+1);
	strupr(buf);
	ret=(int)Fwrite(handle,(long)strlen(buf),(void *)buf);
	Fclose(handle);
	return(ret);
}
