// -*- c++ -*-
//
// loader.cc:
//
// (for Motorola S-Record format file)
//
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <sstream>	// strstream is obsolete
#include <string>
#include "loader.h"

using namespace std;

//
// loader class
//

loader::loader(const char *fname)
{
    in.open(fname);
    if (!in) {
	cerr << "can't open file " << fname << endl;
	exit(1);
    }

    line_num = 0;	// reset line number
    entry_addr = 0;
}

loader::~loader()
{
    if (in.is_open())
	in.close();
}

void
loader::process_lines()
{
    string linebuf;
    while (!in.eof()) {
	line_num++;		// increment before processing the line

	getline(in, linebuf);
//	cerr << dec << line_num << ": " << linebuf << endl;
	process_line(linebuf);
    }
}


void
loader::process_line(string line)
{
    if (line.length() == 0)             // empty line
	return;

    int idx = 0;
    if (line[idx++] != 'S')		// not valid line (skip current line)
	return;

    char record_type = line[idx++];
//    cerr << "record type = " << record_type << endl;

    istringstream is;
    int count = 0;
    is.str(line.substr(idx, 2)); idx += 2;
    is >> hex >> count;

//    cerr << "count=" << dec << count << endl;

    if (record_type == '0') {	// string information (maybe filename ?)
	idx += 4;	// skip 4 bytes (address field not used)
	count -= 2;

//	cerr << "name='";
	for (int i = 0; i < count-1; i++) {
	    int c;
	    is.clear();		// don't forget to clear
	    is.str(line.substr(idx, 2)); idx += 2;
	    is >> hex >> c;
//	    cerr << (char)c;
	}
//	cerr << "'" << endl;

	return;
    }
    if (record_type == '4') {
	// symbol record ? (LSI Corp. specific)
	return;
    }
    
    sim_addr addr;
    switch (record_type) {
    case '1':		// 16bit address
    case '9':
	is.clear();		// don't forget to clear
	is.str(line.substr(idx, 4)); idx += 4;
	is >> hex >> addr;
	count -= 2;
	break;

    case '2':		// 24bit address
    case '8':
	is.clear();		// don't forget to clear
	is.str(line.substr(idx, 6)); idx += 6;
	is >> hex >> addr;
	count -= 3;
	break;

    case '3':		// 32bit address
    case '7':
	is.clear();		// don't forget to clear
	is.str(line.substr(idx, 8)); idx += 8;
	is >> hex >> addr;
	count -= 4;
	break;

    default:
	break;
    }

    if (record_type == '7' || record_type == '8' || record_type == '9') {
	// these are end markers
	// address field might contain entry address of program
	entry_addr = addr;
	if (addr != 0)
	    cerr << "program entry = " << hex << addr << endl;
	return;
    }

//    cerr << "addr=" << hex << addr << endl;

    if (!mem->is_allocated(addr, count-1))
	mem->alloc(addr, count-1);

    for (int i = 0; i < count-1; i++) {
	int val;
	is.clear();		// don't forget to clear
	is.str(line.substr(idx, 2)); idx += 2;
	is >> hex >> val;
//	cerr << "mem[" << hex << addr << "]=" << val << endl;
	mem->write_byte(addr, val);
	addr++;
    }
}


void
loader::load(memory &mem)
{
    this->mem = &mem;
    process_lines();
}


loader &
loader::operator>>(memory &mem)
{
    load(mem);
    return *this;
}


sim_addr
loader::entry_address()
{
    return entry_addr;
}

// end of loader.cc