/*--------------------------------------------------------------------------*\

	FILE....: COFF.CPP
	TYPE....: Microsoft C Functions
	AUTHOR..: David Rowe
	DATE....: 19/11/97
	AUTHOR..: Ron Lee
	DATE....: 11/11/06

	This file contains functions that extract information from COFF Files.
	See the "TMS320 Floating-Point DSP Assembly Language Tools User's
	Guide" Appendix A for more information.

	The final stage of the C5x' C compiler (or assembler) is a file that
	contains the executable code.  This file also contains other
	information, such as location details and a symbol table.  This file
	is known as a COFF File.

	A COFF File has the following format (see COFF.H) for more info

	    File Header
	    Section 1 header
	    ...
	    Section n header
	    Section 1 data
	    Section n data
	    Symbol table
	    String Table


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2007 Voicetronix www.voicetronix.com.au

         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., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include <assert.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "coff.h"
#include "vpbapi.h"
#include "mess.h"
#include "wobbly.h"


// General defines 

#define MAX_SECT 	20	// maximum number of sections 		
#define WORD            2l	// number of bytes/word 		

// COFF file section header flags 

#define STYP_COPY	0x10	// initialisation data 			
#define STYP_TEXT	0x20	// executable code 			
#define STYP_DATA	0x40	// initialised data 			
#define STYP_BSS	0x80	// uninitialised data 			

// COFF file magic numbers 

#define	F_MAGIC		0x92	// COFF file magic number		
#define F_OPTHDR	0x1c	// optional header magic number		

// DSP memory test defines 

#define	ADDR		0x0	// base address of memory test		
#define	LENGTH		0x1000	// length of memory test	

#define	B_LEN		0xffff	// temp buffer

/*----------------------------------------------------------------------*\

		STRUCTURE DEFINITIONS FOR COFF FILE

\*----------------------------------------------------------------------*/

// pragma tells Microsoft C not to mess about with the alignment
// of the members of the following structures.  This may be 
// compiler and operating system specific.  Without this pragma
// compiler changes alignment to suit itself, which results in
// structures of different size to the actual COFF file structures.

#pragma pack(1)

// File Header ---------------------------------------------------------

typedef struct {
    uint16_t    f_magic;	// magic number			
    uint16_t    f_nscns;	// number of sections		
    int32_t     f_timdat;	// date and time stamp		
    int32_t     f_symptr;	// file ptr to symtab		
    int32_t     f_nsyms;	// # symtab entries		
    uint16_t    f_opthdr;	// sizeof(opt hdr)		
    uint16_t    f_flags;	// flags			
} FILHEADER;

// Optional File Header ------------------------------------------------

typedef struct {
    int16_t	magic;			// magic number 	   	
    int16_t 	version;		// version stamp 	       	
    int32_t 	exec_size;		// size (in words) of executable code 
    int32_t 	init_size;	        // size (in words) of initialised words	
    int32_t 	uninit_size;		// size (in words) of uninit. data
    int32_t 	entry;			// entry point address 	       	
    int32_t 	start_code;		// beginning address of executable code
    int32_t 	start_data;		// beginning address of initialise data
} OPT_FILHEADER;

// Section Header ------------------------------------------------------

typedef struct {
    int8_t	s_name[8];	// section name			
    int32_t	s_paddr;	// physical address		
    int32_t	s_vaddr;	// virtual address		
    int32_t	s_size;		// section size			
    int32_t	s_scnptr;	// file ptr to raw data		
    int32_t	s_relptr;	// file ptr to reloc		
    int32_t	s_lnnoptr;	// file ptr to line no.		
    uint16_t 	s_nreloc;	// # reloc entries		
    uint16_t 	s_nlnno;	// # line number entries	
    uint16_t 	flags;		// flags			
    int8_t 	reserved;
    int8_t 	mem_page;	// memory page number 		
} SECTION_HEADER;

// Symbol Table entry ----------------------------------------------------

typedef struct {
    char	n_name[8];	// name of symbol		
    int32_t	n_value;	// value of symbol		
    uint16_t	n_scnum;	// section number		
    uint16_t	n_type;		// type and derived type	
    int8_t	n_class;	// storage class		
    int8_t	n_numaux;	// number of aux entries	
} SYMBOL_ENTRY;

// The structure below is used for storing COFF file header information in
// PC memory, to speed up COFF file access 

typedef struct {
	char       	file_name[MAX_STR];	// DOS filename of COFF file  
	int 		*ptr;			// addr of mirrored buffer 
	FILHEADER 	file_hdr;		// file header for COFF file 
	OPT_FILHEADER	opt_file_hdr;		// optional file header     
	SECTION_HEADER	sect_hdr[MAX_SECT];	// header for each section  
	FILE		*file;			// file ptr for COFF file   
} COFF_FILE;


/*---------------------------------------------------------------------------*\

	FUNCTION....: swap_byte_order()

	AUTHOR......: David Rowe
	DATE CREATED: 7/2/96

	Swaps the byte order of an array of chars, ie bytes in:
	0,1,2,3,4,5,...
	becomes
	1,0,3,2,5,4,...

        char   s[]	array of shorts		
        int    length	length of array		

\*---------------------------------------------------------------------------*/

static inline void swap_byte_order(char s[], int length)
{ //{{{
    for(int i=0; i < length; i += 2)
    {
	char x = s[i+1];
	s[i+1] = s[i];
	s[i] = x;
    }
} //}}}


/*---------------------------------------------------------------------------*\

	FUNCTION.: open_coff_file
	AUTHOR...: John Kostogiannis & D. Rowe
	DATE.....: 6/2/96

	Opens a COFF file, and loads the header information into the header
	structure.

\*---------------------------------------------------------------------------*/

static void open_coff_file(const char *file_name, COFF_FILE *c)
//  char file_name[]	filename (including path) of the coff file	
//  COFF_FILE *c	ptr to the COFF_FILE structure			
{ //{{{
	int   i;
	FILE  *f;

	assert(file_name != NULL);
	assert(c != NULL);

	f = fopen(file_name,"rb");
	if(f == NULL)
		throw VpbException("open_coff_file: can't open '%s'", file_name);

	// Now set up COFF file structure 
	strncpy(c->file_name, file_name, MAX_STR);
	c->file_name[MAX_STR-1] = '\0';
	c->file                 = f;

	// Load the file header and optional file header if present 	
	fread(&c->file_hdr,sizeof(FILHEADER),1,c->file);
	if(c->file_hdr.f_magic != F_MAGIC)
		throw VpbException("open_coff_file: bad magic number %d != %d in '%s'",
				   c->file_hdr.f_magic, F_MAGIC, file_name);

	if (c->file_hdr.f_opthdr == F_OPTHDR)
		fread(&c->opt_file_hdr,sizeof(OPT_FILHEADER),1,c->file);

	// Load the section headers 					
	if (c->file_hdr.f_nscns >= MAX_SECT)
		throw VpbException("open_coff_file: too many sections %d > %d in '%s'",
				   c->file_hdr.f_nscns, MAX_SECT - 1, file_name);

	for(i=0; i<c->file_hdr.f_nscns; i++)
		fread(&c->sect_hdr[i],sizeof(SECTION_HEADER),1,c->file);
} //}}}

/*---------------------------------------------------------------------------*\

	FUNCTION....: load_coff()

	AUTHOR......: David Rowe
	DATE CREATED: 30/9/97

	Loads the sections of the COFF file required for the DSP program
	into the DSP.  This function supports both ROM and RAM initialisation
	models.

\*---------------------------------------------------------------------------*/

static void load_coff(Hip *hip, unsigned short board, COFF_FILE *c)
//  Hip			*hip	host interface port
//  unsigned short	board;	VPB board number	
//  COFF_FILE           *c 	ptr to COFF_FILE structure		
{
	int32_t     s_paddr;	// address of current section (words)	
	int32_t     s_size;	// current section size (words)		
	uint16_t    flags; 	// flags for this section 		
	int32_t     num_read;   // number of bytes read from disk	
	uint16_t    rec_size;	// init record length in words		
	int32_t     bss_addr;	// init record dest address		
	uint16_t   *pw;		// buffer of words to load into DSP	
	int         i;

	// validate arguments 

	assert(c != NULL);

	// check every section 

	for(i=0; i<c->file_hdr.f_nscns; i++)
	{
		flags = c->sect_hdr[i].flags;
		s_paddr = c->sect_hdr[i].s_paddr;
		s_size = c->sect_hdr[i].s_size;

		// check validity of s_paddr and s_size 

		if ((s_paddr < 0x0) || (s_paddr >= MAX_LENGTH))
			throw Wobbly(COFF_INVALID_COFF_FILE);
		if ((s_size < 0x0) || (s_size > MAX_LENGTH))
			throw Wobbly(COFF_INVALID_COFF_FILE);
		if ((s_paddr + s_size) > MAX_LENGTH)
			throw Wobbly(COFF_INVALID_COFF_FILE);

		// display some useful messages if messaging enabled 

		mprintf("[%d]: %8s  flags: 0x%04x  ",i,
			c->sect_hdr[i].s_name,flags);
		mprintf("addr: 0x%04x  length: 0x%04x ",s_paddr,s_size);

		// Look for executable code or initialised data 

 		if( (flags == STYP_TEXT || flags == STYP_DATA)
		 && s_size != 0 )
		{
			mprintf(".");

			// malloc buffer 

			pw = (uint16_t*)calloc(1,WORD*s_size);
			if (pw == NULL)
				throw Wobbly(COFF_CANT_ALLOCATE_MEMORY);

			// read current section into buffer 

			fseek(c->file,c->sect_hdr[i].s_scnptr,SEEK_SET);
			num_read = fread(pw,WORD,s_size,c->file);
			if (num_read != s_size)
				throw Wobbly(COFF_FILE_READ_ERROR);

			// download buffer to DSP 

			hip->WriteDspSram(board, s_paddr, s_size, pw);

			free(pw);
		}

		/*
		  Look for init data that requires "smart loading" into .bss.
		  This is used for RAM model (-cr linker option) as loader 
		  inits .bss at load time (now!), instead of the DSP 
		  initialising bss at boot time.

		  This requires that we read the .cinit init table format,
		  and use this data to init the .bss section.  See page
		  4.21 of C compiler manual for .cinit table format.	
		*/

		if ((flags & STYP_COPY) && (flags & STYP_DATA))
		{
			fseek(c->file,c->sect_hdr[i].s_scnptr,SEEK_SET);

			do {
				fread(&rec_size,WORD,1,c->file);

				// end of init_table reached when when 
				// rec_size == 0 

				if (rec_size != 0) {

					mprintf(".");

					// read init record into buffer 

					bss_addr = 0;
					fread(&bss_addr,WORD,1,c->file);

					// malloc buffer 

					pw = (uint16_t*)malloc(WORD*rec_size);
					if (pw == NULL)
						throw Wobbly(COFF_CANT_ALLOCATE_MEMORY);

					// read current section into buffer 

					num_read = fread(pw, WORD, rec_size, c->file);
					if (num_read != rec_size)
						throw Wobbly(COFF_FILE_READ_ERROR);

					// download buffer to DSP 

					hip->WriteDspSram(board, bss_addr,
							  rec_size, pw);

					free(pw);

				} // if (rec_size ...
			} while(rec_size != 0);

		} // if ((flags ... 

		mprintf("\n");

	} // for(i=0 ... 
}

/*---------------------------------------------------------------------------*\

	FUNCTION....: readback_coff()

	AUTHOR......: David Rowe
	DATE CREATED: 13/6/98

	Called after load_coff() to read back the program and compare it
	to the COFF file.

\*---------------------------------------------------------------------------*/

static void readback_coff(Hip *hip, unsigned short board, COFF_FILE *c)
//  Hip		   *hip	        host interface port
//  unsigned short  board       VPB board number	
//  COFF_FILE      *c 		ptr to COFF_FILE structure		
{
    long	    s_paddr;	// address of current section (words)	
    long 	    s_size;	// current section size (words)		
    unsigned short  flags; 	// flags for this section 		
    long            num_read;	// number of bytes read from disk	
    int 	    i,j;
    uint16_t	   *pw;		// buffer of words from COFF file	
    uint16_t	   *pr;		// buffer of words read back from DSP	

    mprintf("checking program....\n");

    // validate arguments 

    assert(c != NULL);

    // check every section 

    for(i=0; i<c->file_hdr.f_nscns; i++)
    {

		flags = c->sect_hdr[i].flags;
		s_paddr = c->sect_hdr[i].s_paddr;
		s_size = c->sect_hdr[i].s_size;

		// check validity of s_paddr and s_size 

		if ((s_paddr < 0x0) || (s_paddr >= MAX_LENGTH))
			throw Wobbly(COFF_INVALID_COFF_FILE);
		if ((s_size < 0x0) || (s_size > MAX_LENGTH))
			throw Wobbly(COFF_INVALID_COFF_FILE);
		if ((s_paddr + s_size) > MAX_LENGTH)
			throw Wobbly(COFF_INVALID_COFF_FILE);

		// display some useful messages if messaging enabled 

		mprintf("[%d]: %8s  flags: 0x%04x  ",i,c->sect_hdr[i].s_name,
			flags);
		mprintf("addr: 0x%04lx  length: 0x%04lx ",s_paddr,s_size);

		// Look for executable code or initialised data 

 		if (((flags == STYP_TEXT)) && (s_size != 0))
		{

			mprintf(".");

			// malloc buffers 

			pw = (uint16_t*)malloc(WORD*s_size);
			if (pw == NULL)
				throw Wobbly(COFF_CANT_ALLOCATE_MEMORY);
			pr = (uint16_t*)malloc(WORD*s_size);
			if (pr == NULL)
				throw Wobbly(COFF_CANT_ALLOCATE_MEMORY);

			// read current section into buffer 

			fseek(c->file,c->sect_hdr[i].s_scnptr,SEEK_SET);
			num_read = fread(pw,WORD,s_size,c->file);
			if (num_read != s_size)
				throw Wobbly(COFF_FILE_READ_ERROR);

			// read buffer from DSP 

			hip->ReadDspSram(board, s_paddr, s_size, pr);

			// compare to coff data and flag error

			for(j=0; j<s_size; j++)
				if (pw[j] != pr[j]) {
				  //assert(0);
					mprintf("\nError [0x%4lx]: rd: [0x%4x]"
						"coff [0x%4x]\n",j+s_paddr,
						pr[j], pw[j]);
				}

			free(pw);
			free(pr);
		}

		mprintf("\n");

    } // for(i=0 ... 
}

/*-------------------------------------------------------------------------*\

    FUNCTION.: dsp_memory_test
    AUTHOR...: David Rowe
    DATE.....: 6/10/97

    Performs a memory test on a block of the DSPs memory.  This tests the
    HIP and the DSPs SRAM.  Only problem with this function is that we
    need large buffers to test the entire memory (larger than DOS will
    allow us to malloc), therefore I have limited the test to a part of
    the DSPs SRAM.

\*-------------------------------------------------------------------------*/

static void dsp_memory_test(Hip *hip, unsigned short board)
//  Hip		*hip;	HIP object
//  unsigned short  board;	VPB board number
{
	uint16_t   *bufwr;	// write buffer		
	uint16_t   *bufrd;	// read buffer		
	int	    i;

	// set up buffers

	bufwr = (uint16_t*)malloc(sizeof(uint16_t)*LENGTH);
	bufrd = (uint16_t*)malloc(sizeof(uint16_t)*LENGTH);
	if (bufrd == NULL || bufwr == NULL)
		throw Wobbly(COFF_CANT_ALLOCATE_MEMORY);
	// Initilize with different values
	for(i=0; i<LENGTH; i++){
		bufwr[i] = i;
		bufrd[i] = 0;
	}

	// write block, then read back and check 

	hip->WriteDspSram(board, ADDR, LENGTH, bufwr);
	hip->ReadDspSram(board, ADDR, LENGTH, bufrd);

	for(i=0; i<LENGTH; i++){
		if (bufwr[i] != bufrd[i]) {
			fprintf(stderr, "DSP memory test failed!\n");
			fprintf(stderr, "This usually means:\n");
			fprintf(stderr, "1) The I/O base address is set incorrectly (ISA bus cards)\n");
			fprintf(stderr, "2) The card is not installed\n");
			fprintf(stderr, "3) Plug and Play BIOS error (see FAQ)\n");

			throw VpbException("dsp_memory_test: failed");
		}
	}

	// clean up and finish 

	free(bufwr);
	free(bufrd);

	mprintf("DSP [%02d] Memory test passed OK...\n",board);
}

/*-------------------------------------------------------------------------*\

    FUNCTION.: coff_load_dsp_firmware
    AUTHOR...: David Rowe
    DATE.....: 30/9/97

    Loads a 'C5x program stored in a COFF file into a DSP.  Leaves the DSP
    in RESET.

    Hip	   *hip		    host interface port object
    int     board;	    VPB board number	
    char    coff_file[]	    filename of COFF file   	

\*-------------------------------------------------------------------------*/

void coff_load_dsp_firmware(Hip *hip, int board, const char *coff_file)
{ //{{{
    COFF_FILE 	c;

    assert(coff_file != NULL);

    open_coff_file(coff_file, &c);

    // perform memory test to verify DSP SRAM 
    hip->DspReset(board);
    dsp_memory_test(hip, board);

    // load DSP firmware 
    load_coff(hip, board, &c);
    mprintf("Entry point: 0x%04x\n", c.opt_file_hdr.entry);
    mprintf("DSP [%02d] code downloaded OK....\n", board);
    fclose(c.file);
} //}}}

/*-------------------------------------------------------------------------*\

    FUNCTION.: coff_check_dsp_firmware
    AUTHOR...: David Rowe
    DATE.....: 13/6/98

    Checks the code in the DSP against the COFF file.

    Hip	   *hip		    host interface port object
    int     board;	    VPB board number	
    char    coff_file[]	    filename of COFF file  	

\*-------------------------------------------------------------------------*/

void coff_check_dsp_firmware(Hip *hip, int board, const char *coff_file)
{ //{{{
    COFF_FILE 	c;

    assert(coff_file != NULL);

    open_coff_file(coff_file,&c);

    // check DSP firmware 
    readback_coff(hip, board, &c);
    mprintf("DSP [%02d] code checked OK....\n",board);
    fclose(c.file);
} //}}}

/*-------------------------------------------------------------------------*\

    FUNCTION.: coff_get_address
    AUTHOR...: David Rowe
    DATE.....: 1/10/97

    Determines the address of a global variable in a 'C5x program.  Only
    works for symbols with 7 characters of less.

    char 	 coff_file[]	filename of COFF file		
    char	 symbol[]	name of symbol (7 chars of less)    
    uint32_t	*addr	        address of symbol

\*-------------------------------------------------------------------------*/

void coff_get_address(const char *coff_file, const char *symbol, uint32_t *addr)
{ //{{{
	COFF_FILE 		  c;
	SYMBOL_ENTRY	  sym;		// current symbol table entry

	assert(coff_file != NULL);
	assert(symbol != NULL);
	assert(addr != NULL);

	open_coff_file(coff_file,&c);

	// now scan symbol table for symbol 
	fseek(c.file, c.file_hdr.f_symptr, SEEK_SET);
	bool found = false;
	int  i = 0;
	do {
		fread(&sym, sizeof(SYMBOL_ENTRY), 1, c.file);
		if (strcmp(sym.n_name, symbol) == 0) {
			found = true;
			*addr = sym.n_value;
		}
	} while( (i++ < c.file_hdr.f_nsyms) && !found );

	fclose(c.file);

	if( ! found )
		throw VpbException("coff_get_address: can't find symbol '%s'",
				   symbol);
} //}}}

/*---------------------------------------------------------------------------*\

	FUNCTION....: coff_construct_image()

	AUTHOR......: David Rowe
	DATE CREATED: 26/6/98

	Reads the sections of the COFF file, and constructs a binary
	image of the C5x program that is compatible with the 'C52 boot 
	loader.

        The length of the image in words is returned.

        char	 coff_file[]	ptr to COFF_FILE structure		
        uint16_t buf[]		buffer containing binary 
        long	 buf_len 	length of buffer

\*---------------------------------------------------------------------------*/

uint32_t coff_construct_image(const char *coff_file, uint16_t buf[], uint32_t buf_len)
{
    uint32_t	s_paddr;	// address of current section
    uint32_t 	s_size;		// current section size
    uint16_t	flags; 		// flags for this section
    uint32_t	dest_address;   // destination address
    uint32_t 	high_address;	// highest address of section
//  uint32_t 	length;		// length of codewords
    uint16_t	rec_size;	// init record length in words
    uint32_t	bss_addr;	// init record dest address
    uint16_t	b[B_LEN];
    COFF_FILE   coff,*c;

    // initialise 

    open_coff_file(coff_file,&coff);
    c = &coff;
    dest_address = 0x7fff;
    high_address = 0;

    // load image of executable and init data into buffer 

    for(int i=0; i<c->file_hdr.f_nscns; i++)
    {

		flags = c->sect_hdr[i].flags;
		s_paddr = c->sect_hdr[i].s_paddr;
		s_size = c->sect_hdr[i].s_size;

		mprintf("\n[%d]: %10s  flags: 0x%04x  ",i,
			c->sect_hdr[i].s_name,flags);
		mprintf("addr: 0x%08x  length: 0x%08x",s_paddr,s_size);

		// Look for executable code or initialised data 

		/*
			Dismiss any code in internal program memory. The code
			is uploaded from data memory (in form of a C callable 
			array) during run-time.
		*/

		flags &= 0xfff;
		if( flags == STYP_TEXT || (flags == STYP_DATA && s_paddr != 0xfe00) )
		{
			mprintf("*");

			// check for buffer overflow 

			assert(s_size*WORD <= B_LEN);

			// read current section and store in binary file 

			fseek(c->file,c->sect_hdr[i].s_scnptr,SEEK_SET);
			fread(b,WORD,s_size,c->file);
			swap_byte_order((char*)b,s_size*WORD);
			memcpy(&buf[s_paddr], b, s_size*WORD);

			// determine lowest & highest address (in words) 

			if(s_paddr<dest_address)
				dest_address = s_paddr;
			if((s_paddr+s_size)>high_address)
				high_address = s_paddr + s_size;
		}

		/*
			Look for init data that requires "smart loading" into
			.bss. This is used for RAM model (-cr linker option) 
			as loader inits .bss at load time (now!), instead of 
			the DSP initialising bss at boot time.

			This requires that we read the .cinit init table 
			format, and use this data to init the .bss section.  
			See page 4.21 of C compiler manual for .cinit table 
			format.
		*/

		/* 
			Dismiss any code in internal program memory. The code
			is uploaded from data memory (in form of a C callable 
			array) during run-time
		*/

		if( (flags & STYP_COPY) && (flags & STYP_DATA) && s_paddr != 0xfe00 )
		{
			mprintf("*");
			fseek(c->file,c->sect_hdr[i].s_scnptr,SEEK_SET);

			do {
				fread(&rec_size,WORD,1,c->file);

				// end of init_table reached when when 
				// rec_size == 0 

				if (rec_size != 0) {

					// read init record into buffer 

					bss_addr = 0;
					fread(&bss_addr,WORD,1,c->file);

					// check for buffer overflow 

					assert(rec_size*WORD <= B_LEN);

					fread(b,WORD,rec_size,c->file);
					swap_byte_order((char*)b,
							rec_size*WORD);
					memcpy(&buf[bss_addr], b,
					       rec_size*sizeof(uint16_t));

					// determine lowest & highest address
					// (in words) 

					if(bss_addr<dest_address)
						dest_address = bss_addr;
					if((bss_addr+rec_size)>high_address)
						high_address = bss_addr + rec_size;
				} // if 
		    } while(rec_size != 0);

		} // if ((flags ... 

    } // for(i=0 ... 

    assert(high_address < buf_len);

    // determine length of the block to be boot loaded  
    // length = high_address-dest_address;

    // move block up to make space for dest and length words

    for(int i = high_address + 2; i >= 2; --i)
	    buf[i] = buf[i-2];

    // place destination address in buffer 

    buf[0] = 0;
    buf[1] = (uint16_t)high_address;

    fclose(coff.file);

    return high_address + 2;
}

