/*  Tilem, TI-Linux Emulator
 *  Copyright (C) 2001 Solignac Julien <x1cygnus@xcalc.org>
 *  Portions copyright (C) 2004 Benjamin Moody <benjamin@ecg.mit.edu>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* >>> Exported */

int load_hex(char *file, byte *mem, int min, int max, int osmask, int grav)
{
	extern struct hardware *hw;
	char xline[256], tmpx[3] = {0x00, 0x00, 0x00};
	byte j, rompage, type, n;
	int addr, relpage;
	FILE *app;
	byte osmode = 0;
  
	if (!(app = fopen(file, "rb"))) {
		fprintf(stderr, "** unable to open file **\n");
		return grav?max:min;
	}


	if (fread(xline, 1, 8, app) && 0 == strncmp(xline, "**TIFL**", 8)) {
		fread(xline, 1, 2+7+8+23, app); /* stuff we don't care about */

		if (fgetc(app) != hw->magic) {
			fprintf(stderr, "** incorrect hardware model **\n");
			fclose(app);
			return grav?max:min;
		}

		j = fgetc(app);
		if (j == 0x23) {
			osmode = 1;
		}
		else if (j != 0x24) {
			fprintf(stderr, "** unsupported Flash file type **\n");
			fclose(app);
			return grav?max:min;
		}

		fread(xline, 1, 24+4, app); /* stuff we don't care about */
	}
	else {
		fseek(app, 0L, SEEK_SET);
	}

	if (osmode) {
		/* Skip the header block */
		do {
			fgets(xline, 256, app);
		} while (strncmp(xline, ":00000001FF", 11)
			 && !feof(app) && !ferror(app));
		if (feof(app) || ferror(app)) {
			fprintf(stderr, "** error reading OS file **\n");
			fclose(app);
			return grav?max:min;
		}

		rompage = 0;
	}
	else {
		if (grav)
			for (j = min, rompage = 0x00; j <= max; j++) {
				if (*(mem + 0x4000 * j) == 0xFF)
					rompage = j;
			}
		else
			for (j = max, rompage = 0x00; j >= min; j--) {
				if (*(mem + 0x4000 * j) == 0xFF)
					rompage = j;
			}
		
		if (!rompage) {
			fprintf(stderr, "** not enough ROM **\n");
			fclose(app);
			return grav?max:min;
		}
	}

	relpage = 0;
	type = 0;

	while (!feof(app) && !ferror(app) && type != 1) {
		if (fgets(xline, 256, app)) {
			if (xline[0] != ':') {
				puts("Bad application format!");
				fclose(app);
				return grav?max:min;
			}

      
			/* Record type */
			tmpx[0] = xline[7];
			tmpx[1] = xline[8];
			type = strtol(tmpx, NULL, 16);

			switch(type) {

			case 0:

				/* Record type */
				tmpx[0] = xline[3];
				tmpx[1] = xline[4];
				addr = strtol(tmpx, NULL, 16)*256;

				/* Record type */
				tmpx[0] = xline[5];
				tmpx[1] = xline[6];
				addr |= strtol(tmpx, NULL, 16);

				addr &= 0x3fff;

				/* Number of data bytes */
				tmpx[0] = xline[1];
				tmpx[1] = xline[2];
				n = strtol(tmpx, NULL, 16);

				for (j=0; j<n; j++) {
					tmpx[0] = xline[2 * j + 9];
					tmpx[1] = xline[2 * j + 9 + 1];
					*(mem + 0x4000 * (rompage + relpage)
					  + j + addr) =
						(byte) strtol(tmpx, NULL, 16);
				}

				break;

			case 1:
				break;

			case 2:
	
				/* Page */
				tmpx[0] = xline[11];
				tmpx[1] = xline[12];
				n = strtol(tmpx, NULL, 16);

				if (osmode)
					relpage = n > 0x07 ? n | osmask : n;
				else if (grav)
					relpage = -n;
				else
					relpage = n;

				if (rompage + relpage < (osmode?0:min) ||
				    rompage + relpage >
				    (osmode ? 0x1f | osmask : max)) {
					printf("Page %02x out of range\n",n);
					fclose(app);
					return grav?max:min;
				}

			}

		}

	}

	fprintf(stderr, "Loaded %s '%s' to page %02x\n", osmode ? "OS" : "app",
		file, rompage);

	fclose(app);

	if (osmode) {
		mem[0x0056] = 0x5A;
		mem[0x0057] = 0xA5;
		Z80_Reset();
		Rezet(0);
		return grav?max:min;
	}
	else
		return grav?rompage+relpage-1:rompage+relpage+1;
}

void xp_loadapp(char *file)
{
	int p = load_hex(file,mpages,0x0c,0x15,0x00,1) + 1;

	for(; p < 0x15; p++)
		noexec[p/8] &= ~(1<<(p%8));
}

/* Exported <<< */
