/* -*-c-*- -------------- mix_code_file.c :
* Implementation of the functions declared in mix_code_file.h
* ------------------------------------------------------------------
* $Id: mix_code_file.c,v 1.6 2005/09/20 19:43:13 jao Exp $
* ------------------------------------------------------------------
* Copyright (C) 2000, 2002, 2003 Free Software Foundation, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include <string.h>
#include "mix.h"
#include "xmix_io.h"
#include "mix_code_file.h"
/* mix_code_file_t type */
static const gint32 SIGNATURE_ = 0xDEADBEEF; /* release files */
static const gint32 SIGNATURE_D_ = 0xBEEFDEAD; /* debug files */
#define IS_DEBUG_(file) ((file)->header.signature == SIGNATURE_D_)
#define IS_RELEASE_(file) ((file)->header.signature == SIGNATURE_)
typedef struct mix_cfheader_t mix_cfheader_t;
struct mix_cfheader_t
{
gint32 signature;
gint mj_ver;
gint mn_ver;
gint16 start;
size_t path_len;
};
struct mix_code_file_t
{
mix_file_t *file; /* the underlying disk file */
mix_address_t address; /* current address while reading */
mix_cfheader_t header; /* file header */
gchar *source_path; /* variable length part of the header*/
mix_symbol_table_t *symbol_table; /* code files with debug info fill it*/
};
#define to_io_(cf) MIX_IOCHANNEL (cf->file)
/* Code files are lists of tagged words. A MIX_INS_TAG_ denotes
* that the word is an instruction, whose address is the next to
* the previous instruction's one, while a MIX_ADDR_TAG_ indicates
* that the word is a new address origin for the next instructions.
* The tags are stored as bit patterns in bit 31 of the mix word.
*/
#define MIX_ADDR_TAG_ (MIX_WORD_SIGN_BIT<<1)
#define is_ins_(word) (((word)&MIX_ADDR_TAG_) == MIX_WORD_ZERO)
#define is_addr_(word) (((word)&MIX_ADDR_TAG_) == MIX_ADDR_TAG_)
#define tag_ins_(word) (word)
#define tag_addr_(word) ((word)|MIX_ADDR_TAG_)
#define extract_ins_(tagged) (tagged)
#define extract_addr_(tagged) ((tagged)&MIX_SHORT_MAX)
/* mix code files have a default extension (.mix) which is customizable */
static const gchar *DEFEXT_ = NULL;
const gchar *
mix_code_file_get_defext (void)
{
return DEFEXT_;
}
gboolean
mix_code_file_set_defext (const gchar *ext)
{
if (DEFEXT_ != NULL) g_free ((void *)DEFEXT_);
DEFEXT_ = (ext != NULL)? g_strdup (ext):NULL;
return (DEFEXT_ != NULL || ext == NULL);
}
/* create/destroy code files for read or write */
static mix_code_file_t *
mix_code_file_new_ (const gchar *name, mix_fmode_t mode)
{
mix_code_file_t *result = g_new (mix_code_file_t, 1);
if ( result != NULL )
{
result->file = mix_file_new_with_def_ext (name, mode, DEFEXT_);
result->address = MIX_SHORT_ZERO;
result->source_path = NULL;
result->symbol_table = NULL;
}
if ( result != NULL && result->file == NULL )
{
g_free (result);
result = NULL;
}
return result;
}
mix_code_file_t *
mix_code_file_new_read (const gchar *name)
{
mix_code_file_t *result = mix_code_file_new_ (name, mix_io_READ);
mix_cfheader_t *header;
FILE *file;
gboolean check;
if ( result == NULL )
return NULL;
file = mix_file_to_FILE (result->file);
header = &(result->header);
check = fread (header, sizeof (mix_cfheader_t), 1, file)
&& (IS_RELEASE_ (result) || IS_DEBUG_ (result));
if ( check )
{
gint major, minor;
sscanf (VERSION, "%d.%d", &major, &minor);
check = header->mj_ver == major && header->mn_ver <= minor;
}
if ( check )
{/* get source path */
result->source_path = g_strnfill (1 + header->path_len, '\0');
check = result->source_path != NULL
&& fgets (result->source_path, 1 + header->path_len, file) != NULL;
}
if ( check && IS_DEBUG_ (result) )
{/* read symbol table */
result->symbol_table = mix_symbol_table_new_from_file (file);
check = result->symbol_table != NULL;
}
if ( !check )
{
mix_code_file_delete (result);
return NULL;
}
return result;
}
mix_code_file_t *
mix_code_file_new_write(const gchar *name, mix_address_t addr,
const gchar *source_path, gboolean debug,
const mix_symbol_table_t *table)
{
mix_code_file_t *result;
FILE *file;
gboolean check;
result = mix_code_file_new_ (name, mix_io_WRITE);
if ( result == NULL || ( file = mix_file_to_FILE (result->file) ) == NULL )
return NULL;
else if ( source_path != NULL )
{
result->source_path = g_strdup (source_path/*, MAX_PATH_LEN_*/);
if ( result->source_path == NULL )
{
mix_code_file_delete (result);
return NULL;
}
}
else
result->source_path = NULL;
result->header.signature = debug? SIGNATURE_D_:SIGNATURE_;
sscanf (VERSION, "%d.%d", &result->header.mj_ver, &result->header.mn_ver);
result->header.start = (gint16) addr;
result->header.path_len = strlen (result->source_path);
check = write_data_ (to_io_ (result), &result->header, 1);
if ( check && result->source_path != NULL )
check = fputs (result->source_path, file) != EOF;
if ( check && debug )
mix_symbol_table_print (table, MIX_SYM_LINE, file, TRUE);
if ( !check )
{
mix_code_file_delete (result);
return NULL;
}
return result;
}
void
mix_code_file_delete (mix_code_file_t *file)
{
g_return_if_fail (file != NULL);
mix_file_delete (file->file);
if (file->source_path) g_free (file->source_path);
g_free (file);
}
/* get general parameters from a code file */
gboolean
mix_code_file_is_debug (const mix_code_file_t *file)
{
return (file != NULL) && IS_DEBUG_ (file);
}
gint
mix_code_file_major_version (const mix_code_file_t *file)
{
g_return_val_if_fail (file != NULL, 0);
return file->header.mj_ver;
}
gint
mix_code_file_minor_version (const mix_code_file_t *file)
{
g_return_val_if_fail (file != NULL, 0);
return file->header.mn_ver;
}
mix_address_t
mix_code_file_get_start_addr (const mix_code_file_t *file)
{
g_return_val_if_fail (file != NULL, MIX_SHORT_ZERO);
return mix_short_new (file->header.start);
}
mix_symbol_table_t *
mix_code_file_get_symbol_table(mix_code_file_t *file)
{
mix_symbol_table_t *result = NULL;
g_return_val_if_fail (file != NULL, NULL);
result = file->symbol_table;
file->symbol_table = NULL;
return result;
}
/* read instructions from a code file */
gboolean
mix_code_file_is_eof (mix_code_file_t *file)
{
return is_eof_ (to_io_ (file));
}
gboolean
mix_code_file_get_ins (mix_code_file_t *file, mix_ins_desc_t *desc)
{
mix_word_t next;
g_return_val_if_fail (file != NULL, FALSE);
g_return_val_if_fail (desc != NULL, FALSE);
while (TRUE)
{
if ( ! mix_io_read_word_array (to_io_ (file), &next, 1) ) return FALSE;
if ( is_addr_ (next) )
file->address = extract_addr_ (next);
else if ( is_ins_ (next) )
{
desc->ins = extract_ins_ (next);
desc->address = (file->address)++;
if ( IS_DEBUG_ (file) )
{
mix_short_t lineno;
if ( !mix_io_read_short_array (to_io_ (file), &lineno, 1) )
return FALSE;
desc->lineno = mix_short_magnitude (lineno);
}
else
desc->lineno = 0;
return TRUE;
}
else
{
g_assert_not_reached ();
return FALSE;
}
};
}
/* Write instructions to a code file */
gboolean
mix_code_file_write_ins (mix_code_file_t *file, const mix_ins_desc_t *desc)
{
g_return_val_if_fail (desc != NULL, FALSE);
return ( mix_code_file_set_address (file, desc->address) &&
mix_code_file_write_next_ins (file, desc->ins, desc->lineno) );
}
gboolean
mix_code_file_write_next_ins (mix_code_file_t *file, mix_word_t ins,
guint lineno)
{
g_return_val_if_fail (file != NULL, FALSE);
if ( mix_io_write_word (to_io_ (file), tag_ins_ (ins))
&& ( IS_RELEASE_ (file)
|| mix_io_write_short (to_io_ (file), mix_short_new (lineno)) )
)
{
++(file->address);
return TRUE;
}
else
return FALSE;
}
gboolean
mix_code_file_set_address (mix_code_file_t *file, mix_address_t address)
{
g_return_val_if_fail(file != NULL, FALSE);
if ( file->address != address ) {
if ( !mix_io_write_word (to_io_ (file),
tag_addr_ (mix_short_to_word_fast (address))) )
return FALSE;
file->address = address;
}
return TRUE;
}
/* get details about the source file */
const gchar *
mix_code_file_get_source_path (const mix_code_file_t *file)
{
g_return_val_if_fail (file != NULL, NULL);
return file->source_path;
}
syntax highlighted by Code2HTML, v. 0.9.1