/*
    BootLinker.java, Copyright 1997 (c) by G. Fankhauser,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.

    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.

    For further information, please visit http://www.tik.ee.ethz.ch/~topsy

    This software is provided under the terms of the GNU General Public Licence.

    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Boot/mips/BootLinker.java,v $
 	Author(s):             G. Fankhauser
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 2911 $
 	Creation Date:         January 1997
 	Last Date of Change:   $Date: 2007-03-07 14:27:12 +0100 (Mit, 07 Mär 2007) $      by: $Author: rnatau $
	
	
	$Log: BootLinker.java,v $
	Revision 1.4  2000/11/15 10:06:34  ruf
	Repository Reorganisation
	
	Revision 1.3  1999/12/13 21:48:25  ruf
	GNU General Public Licence Update
	
	Revision 1.2  1999/09/16 13:39:34  gfa
	*** empty log message ***
	
	Revision 1.1  1999/06/10 13:16:40  jeker
	cleaning up

	Revision 1.2  1999/06/09 17:04:34  gfa
	added byte cast to hex array for java 1.2

	Revision 1.1  1997/05/09 14:28:28  gfa
	Initial revision

# Revision 1.7  1997/04/28  18:01:27  gfa
# adapted parseInt to parseLong for java 1.1
#
# Revision 1.6  1997/04/13  16:22:57  gfa
# added user-in-kernel-at address
#
# Revision 1.5  1997/03/26  16:35:00  gfa
# binary output
#
# Revision 1.4  1997/03/26  13:58:06  gfa
# we only write the segmap now...
#
# Revision 1.3  1997/03/22  11:55:20  conrad
# added .sbss data segment
#
# Revision 1.2  1997/03/15  14:23:14  gfa
# *** empty log message ***
#
# Revision 1.1  1997/02/04  11:15:26  topsy
# Initial revision
#
*/

import java.lang.*;
import java.io.*;

public class BootLinker extends Object {

    public final static String OUTFILE = "segmap.bin";
    
    public static void main(String argv[]) throws Exception {
	String a, b, s = null;
	File f;
	
	if (argv.length != 5) {
	    System.out.println(
	    "usage: java BootLinker <kSizeFile> <iSizeFile> <user.srec> SEGMAPADDR USERINKERNELAT"
	    );
	    System.exit(0);
	}
	try {
	    f = new File(OUTFILE);
	    f.delete();
	}
	catch (Exception e) { 
	}
	RandomAccessFile segmapFile = new RandomAccessFile(OUTFILE, "rw");
	RandomAccessFile kSizeFile = new RandomAccessFile(argv[0], "r");
	RandomAccessFile iSizeFile = new RandomAccessFile(argv[1], "r");
	RandomAccessFile userSrec = new RandomAccessFile(argv[2], "r");

	// write segmentMap (sizes and start addresses of kernel and 
	// user segments). we assume that the sizeFile was generated 
	// by gnu-size with options -Ax (addresses, hex)
	// ex.: size -Ax
	try {
	    //// write segment map
//	    segmapFile.writeBytes("S3" + // record type
//				  "29" + // const length 41 bytes
//				argv[3]);	// where it goes (segmap addr)	
	    segmapFile.seek(0);						   
	    parseSizeFile(kSizeFile, segmapFile); // size/start info kernel	
	    parseSizeFile(iSizeFile, segmapFile); // size/start info init	
	    try {
		while (true) {
		    s = userSrec.readLine();
		    if ((s.charAt(0) == 'S') && (s.charAt(1) == '7')) {
			//segmapFile.writeBytes(s.substring(4,12));
			segmapFile.writeInt(
			    Integer.parseInt(s.substring(4,12), 16)
			);						
		    }
		}
	    }
	    catch (Exception e) {
	    }
	    // write the user-in-kernel-address at the end
	    segmapFile.writeInt((int)Long.parseLong(argv[4], 16));

//	    if (segmapFile.getFilePointer() != 84) {
//		System.out.print("BootLinker: couldn't write segmap\n");
//		System.exit(-1);
//	    }
	    // data is in, now write the checksum
	    //segmapFile.writeBytes("00");			
	    //segmapFile.writeBytes("\n");
	}
	catch (Exception e) {
		throw e;
	}	
    }
    
    static void parseSizeFile(RandomAccessFile sizeFile, RandomAccessFile out) 
							    throws IOException{
	int addr = 0, size = 0;
	// expect text
	if (expect(sizeFile, ".text")) {
	    size = readHexInt(sizeFile); addr = readHexInt(sizeFile);
	}
//	writeHexInt(out, size); writeHexInt(out, addr); 	
	out.writeInt( size); out.writeInt(addr); 	

	// expect data, add sizes up and find start address by minimizing
	size = 0; addr = 0;
	if (expect(sizeFile, ".data")) {
	    size = readHexInt(sizeFile);
	    addr = readHexInt(sizeFile);
	}
	if (expect(sizeFile, ".rdata")) {
	    size += readHexInt(sizeFile); 
	    addr = Math.min(addr, readHexInt(sizeFile));
	}
	if (expect(sizeFile, ".bss")) {
	    size += readHexInt(sizeFile); 
	    addr = Math.min(addr, readHexInt(sizeFile));
	}
	if (expect(sizeFile, ".sbss")) {
	    size += readHexInt(sizeFile); 
	    addr = Math.min(addr, readHexInt(sizeFile));
	}
//	writeHexInt(out, size); writeHexInt(out, addr);
	out.writeInt( size); out.writeInt(addr); 	
    }
    
    static void writeHexInt(RandomAccessFile segmapFile, int hex) 
							    throws IOException{
	byte hexArray[] =  {(byte)'0',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7',
			    (byte)'8',(byte)'9',(byte)'A',(byte)'B',(byte)'C',(byte)'D',(byte)'E',(byte)'F'};
	for (int i = 0; i < 8; i++) {
		segmapFile.writeByte(hexArray[(hex >> 28) & 0xf]);
		hex = hex << 4;
	}
    }
    
    static boolean expect(RandomAccessFile f, String exp) throws IOException {
	String s;
	long pos;
	
	f.seek(0);
	while (true) {
	    try {
		pos = f.getFilePointer();
		s = f.readLine();
		if (s.length() < exp.length()) continue;
		if (exp.equals(s.substring(0, exp.length()))) {
			f.seek(pos+exp.length());
			return true;
		}
	    }
	    catch (Exception e) {
		    return false;
	    }
	}
    }
	    
    static int readHexInt(RandomAccessFile f) throws IOException {
	    int value = 0, tmp;
	    // skip white space
	    byte c = f.readByte();
	    while ((c == ' ') || (c == '\t')) {
		    c = f.readByte();
	    }
	    // understands 0x hexprefix for newer binutils... (gfa)
	    if (c == '0') {
		    c = f.readByte();
		    if (c == 'x') {
			c = f.readByte();
		    }
		    else  {
		    	f.seek(f.getFilePointer() - 1);
		    }
	    }
	    int i = 28;
	    while (isHex(c)) {
		    tmp = readHexByte(c);
		    value |= ((tmp & 0xf) << i);
		    i-=4; c = f.readByte();
	    }
	    return (value >>> (i+4));
    }
	    
    static byte readHexByte(byte b) {
	    char aChar = 'a';
	    char nullChar = '0';
	    if ((b >= nullChar) && (b < (nullChar+10))) {
		    return (byte)(b - nullChar);
	    }
	    else if ((b >= aChar) && (b < (aChar+6))) {
		    return (byte)(b - aChar + 10);
	    }
	    else {
		    return (byte)0;
	    }	
    }
	    
    static boolean isHex(byte c) {		 
	    return ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f'));
    }		
}


class BootLinkerSizeFormatException extends Exception {
}
	
