diff options
author | Jose Antonio Ortega Ruiz <jao@gnu.org> | 2006-03-20 22:46:46 +0000 |
---|---|---|
committer | Jose Antonio Ortega Ruiz <jao@gnu.org> | 2006-03-20 22:46:46 +0000 |
commit | 300160e73da486946ae513f1d039dcd7b85ff17c (patch) | |
tree | f26691ff724b57dac450ab47e4eea91e63cdadc1 /mixlib | |
parent | 50375f34b611281a3b05a37221e2baa143f5f5ca (diff) | |
download | mdk-300160e73da486946ae513f1d039dcd7b85ff17c.tar.gz mdk-300160e73da486946ae513f1d039dcd7b85ff17c.tar.bz2 |
Version 1.2.1 imported1.2.1
git-archimport-id: mdk@sv.gnu.org/mdk--devel--1--patch-1
Diffstat (limited to 'mixlib')
60 files changed, 12683 insertions, 0 deletions
diff --git a/mixlib/Makefile.am b/mixlib/Makefile.am new file mode 100644 index 0000000..b925ff5 --- /dev/null +++ b/mixlib/Makefile.am @@ -0,0 +1,42 @@ +## Process this file with automake to produce Makefile.in + +# Copyright (C) 2000, 2001 Free Software Foundation, Inc. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# $Id: Makefile.am,v 1.6 2002/12/08 01:36:49 jao Exp $ + +SUBDIRS = testsuite +INCLUDES = -I$(includedir) -I$(top_srcdir) -DG_LOG_DOMAIN=\"libmix\" + +noinst_LIBRARIES = libmix.a +libmix_a_SOURCES = gettext.h mix.h mix.c \ + mix_types.h mix_types.c \ + mix_ins.h mix_ins.c \ + mix_vm.h mix_vm.c xmix_vm.h xmix_vm.c \ + mix_vm_dump.h mix_vm_dump.c \ + mix_io.h mix_io.c xmix_io.h xmix_io.c \ + mix_symbol_table.h mix_symbol_table.c \ + mix_file.h mix_file.c \ + mix_code_file.h mix_code_file.c \ + mix_parser.h xmix_parser.h mix_parser.c mix_scanner.l \ + mix_device.h mix_device.c xmix_device.h xmix_device.c \ + mix_eval.h mix_eval.c xmix_eval.h mix_eval_scanner.l \ + mix_src_file.c mix_src_file.h \ + mix_vm_clock.c mix_vm_clock.h \ + mix_vm_command.c mix_vm_command.h \ + xmix_vm_command.c xmix_vm_command.h \ + xmix_vm_handlers.h xmix_vm_handlers.c \ + mix_config.c mix_config.h \ + mix_predicate.c mix_predicate.h \ + mix_predicate_list.c mix_predicate_list.h + + + + diff --git a/mixlib/gettext.h b/mixlib/gettext.h new file mode 100644 index 0000000..fa3622f --- /dev/null +++ b/mixlib/gettext.h @@ -0,0 +1,69 @@ +/* Convenience header for conditional use of GNU <libintl.h>. + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library 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. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include <libintl.h> + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of <locale.h> a NOP. We don't include <libintl.h> + as well because people using "gettext.h" will not include <libintl.h>, + and also including <libintl.h> would fail on SunOS 4, whereas <locale.h> + is OK. */ +#if defined(__sun) +# include <locale.h> +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#endif /* _LIBGETTEXT_H */ diff --git a/mixlib/mix.c b/mixlib/mix.c new file mode 100644 index 0000000..3578a57 --- /dev/null +++ b/mixlib/mix.c @@ -0,0 +1,99 @@ +/* -*-c-*- -------------- mix.c : + * Implementation of the functions declared in mix.h + * ------------------------------------------------------------------ + * $Id: mix.c,v 1.6 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2002 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 <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> + + +#include "mix_types.h" +#include "mix_ins.h" +#include "mix_code_file.h" +#include "mix.h" + + +/* This function must be called before using the library */ +void +mix_init_lib(void) +{ + mix_init_types (); + mix_init_ins (); + mix_code_file_set_defext (MIX_CODE_DEFEXT); +} + + +/* This function must be called for deallocating the lib resources + when it is no longer in use +*/ +void +mix_release_lib (void) +{ + /* clean the user defined code file extension (if any) */ + mix_code_file_set_defext (NULL); + mix_release_ins (); +} + +const char *MIX_GPL_LICENSE = +"Copyright (C) 2001 Free Software Foundation, Inc.\n" +"There is NO warranty. You may redistribute this software\n" +"under the terms of the GNU General Public License.\n" +"For more information about these matters, see the files named COPYING.\n"; + +void +mix_print_license (const gchar *program) +{ + fprintf (stderr, _("%s (GNU MDK %s)\n\n"), + program, VERSION); + fprintf (stderr, MIX_GPL_LICENSE); +} + +/* check dir, and create it if it doesn't exist */ +gboolean +mix_stat_dir (const gchar *dirname, const gchar *alias) +{ + struct stat statbuf; + + g_return_val_if_fail (dirname != NULL, FALSE); + if (alias == NULL) alias = ""; + + if (stat (dirname, &statbuf) == -1) + { + if (errno != ENOENT || mkdir (dirname, S_IRWXU | S_IRWXG | S_IRWXO)) + { + g_warning (_("Error creating %s dir %s: %s"), + alias, dirname, strerror (errno)); + return FALSE; + } + stat (dirname, &statbuf); + } + + if (!(statbuf.st_mode & S_IFDIR)) + { + g_warning (_("Error setting %s dir: %s is not a directory"), + alias, dirname); + return FALSE; + } + + return TRUE; +} diff --git a/mixlib/mix.h b/mixlib/mix.h new file mode 100644 index 0000000..6a3fd1d --- /dev/null +++ b/mixlib/mix.h @@ -0,0 +1,78 @@ +/* -*-c-*- ---------------- mix.h : + * Initialisation of the mix library + * ------------------------------------------------------------------ + * $Id: mix.h,v 1.7 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001 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. + * + */ + + +#ifndef MIX_H +#define MIX_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#else +# warning "config.h not found, package misconfigured." +# define PACKAGE "mdk" +# define VERSION "0.0" +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +/* +#ifdef HAVE_GETTEXT +# include <libintl.h> +# define gettext_noop(String) (String) +#else +# include <intl/libgettext.h> +#endif +*/ +#include "gettext.h" + +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include <glib.h> + +/* This function must be called before using the library */ +extern void +mix_init_lib (void); + +/* This function must be called for deallocating the lib resources + when it is no longer in use +*/ +extern void +mix_release_lib (void); + + +extern const char *MIX_GPL_LICENSE; + +extern void +mix_print_license (const gchar *program); + + +/* check dir, and create it if it doesn't exist */ +extern gboolean +mix_stat_dir (const gchar *dirname, const gchar *alias); + + +#endif /* MIX_H */ + diff --git a/mixlib/mix_code_file.c b/mixlib/mix_code_file.c new file mode 100644 index 0000000..ab67812 --- /dev/null +++ b/mixlib/mix_code_file.c @@ -0,0 +1,330 @@ +/* -*-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; +} diff --git a/mixlib/mix_code_file.h b/mixlib/mix_code_file.h new file mode 100644 index 0000000..84b89fb --- /dev/null +++ b/mixlib/mix_code_file.h @@ -0,0 +1,106 @@ +/* -*-c-*- ---------------- mix_code_file.h : + * Declaration of mix_code_file_t, a file containing compiled mix + * instructions. + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef MIX_CODE_FILE_H +#define MIX_CODE_FILE_H + +#include "mix_file.h" +#include "mix_ins.h" +#include "mix_symbol_table.h" + +/* mix_code_file_t type */ +typedef struct mix_code_file_t mix_code_file_t; + +/* instructions are loaded at specific memory addresses and related to + source line numbers +*/ +typedef struct mix_ins_desc_t mix_ins_desc_t; +struct mix_ins_desc_t +{ + mix_word_t ins; /* a mix instruction coded into a word */ + mix_address_t address; /* the address of this instruction */ + guint lineno; /* source file line no. */ +}; + +/* mix code files have a default extension (.mix) which is customizable */ +extern const gchar * +mix_code_file_get_defext(void); + +extern gboolean +mix_code_file_set_defext(const gchar *ext); + +/* create/destroy code files for read or write */ +/* if -name- does not end with defext, it is automatically appended */ +extern mix_code_file_t * +mix_code_file_new_read(const gchar *name); + +/* open a code file for write with/out debug information */ +extern mix_code_file_t * +mix_code_file_new_write(const gchar *name, mix_address_t start, + const gchar *source_path, gboolean debug, + const mix_symbol_table_t *table); + +extern void +mix_code_file_delete(mix_code_file_t *file); + +/* get general parameters from a code file */ +extern gboolean +mix_code_file_is_debug(const mix_code_file_t *file); + +extern gint +mix_code_file_major_version(const mix_code_file_t *file); + +extern gint +mix_code_file_minor_version(const mix_code_file_t *file); + +extern mix_address_t +mix_code_file_get_start_addr(const mix_code_file_t *file); + +extern mix_symbol_table_t * +mix_code_file_get_symbol_table(mix_code_file_t *file); + +/* read instructions from a code file */ +extern gboolean +mix_code_file_is_eof(mix_code_file_t *file); + +extern gboolean +mix_code_file_get_ins(mix_code_file_t *file, mix_ins_desc_t *desc); + +/* write instructions to a code file */ +extern gboolean +mix_code_file_write_ins(mix_code_file_t *file, const mix_ins_desc_t *desc); + +extern gboolean +mix_code_file_write_next_ins(mix_code_file_t *file, mix_word_t ins, + guint lineno); + +extern gboolean +mix_code_file_set_address(mix_code_file_t *file, mix_address_t address); + +/* get details about the source file */ +extern const gchar * +mix_code_file_get_source_path (const mix_code_file_t *file); + + +#endif /* MIX_CODE_FILE_H */ + diff --git a/mixlib/mix_config.c b/mixlib/mix_config.c new file mode 100644 index 0000000..694b18b --- /dev/null +++ b/mixlib/mix_config.c @@ -0,0 +1,298 @@ +/* -*-c-*- -------------- mix_config.c : + * Implementation of the functions declared in mix_config.h + * ------------------------------------------------------------------ + * $Id: mix_config.c,v 1.12 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2002, 2004 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include "mix_config.h" + +const gchar *MIX_CONFIG_DIR = ".mdk"; + +static const gchar COMMENT_PREFIX_ = '#'; +static const gchar *AUTOSAVE_KEY_ = "Autosave"; +static const gchar *AUTOSAVE_YES_ = "True"; +static const gchar *AUTOSAVE_NO_ = "False"; +static const gchar *DEVICES_KEY_ = "Devices.dir"; +static const gchar *HISTORY_KEY_ = "History.file"; +static const gchar *HISTORY_SIZE_KEY_ = "History.size"; + +/* the config type */ +struct mix_config_t +{ + gchar *filename; /* full path to configuration file */ + gboolean autosave; /* whether save on destroy */ + GHashTable *items; /* configuration items */ +}; + +/* create a new config handler, giving the dir and name of the config file */ +mix_config_t * +mix_config_new (const gchar *dirname, const gchar *filename) +{ + static const gchar *DEF_DIRNAME_ = NULL; + static const gchar *DEF_FILENAME_ = "config"; + + const gchar *autosave; + FILE *f; + mix_config_t *result = NULL; + + if (DEF_DIRNAME_ == NULL) + DEF_DIRNAME_ = g_strconcat (g_get_home_dir (), G_DIR_SEPARATOR_S, + MIX_CONFIG_DIR, NULL); + + if (dirname == NULL) dirname = DEF_DIRNAME_; + if (filename == NULL) filename = DEF_FILENAME_; + + if (!mix_stat_dir (dirname, "configuration")) return NULL; + + result = g_new (mix_config_t, 1); + result->filename = g_strdup_printf ("%s/%s", dirname, filename); + result->items = g_hash_table_new (g_str_hash, g_str_equal); + + f = fopen (result->filename, "r"); + if (f != NULL) + { + enum {LEN = 256}; + gchar buffer[LEN]; + gchar *line = buffer; + while (!feof (f)) + { + line = fgets (line, LEN, f); + if (line) line = g_strstrip (line); + if (line && line[0] != COMMENT_PREFIX_) + { + gchar **vals = g_strsplit (line, "=", 2); + g_hash_table_insert (result->items, + (gpointer) g_strstrip (vals[0]), + (gpointer) g_strstrip (vals[1])); + } + } + fclose (f); + } + + autosave = mix_config_get (result, AUTOSAVE_KEY_); + result->autosave = autosave && !g_ascii_strcasecmp (autosave, AUTOSAVE_YES_); + + return result; +} + +/* delete a config handler, saving the configuration if needed */ +void +mix_config_delete (mix_config_t *config) +{ + g_return_if_fail (config != NULL); + if (mix_config_is_autosave (config)) mix_config_save (config); + g_free (config->filename); + g_hash_table_destroy (config->items); + g_free (config); +} + +/* get the config filename */ +const gchar * +mix_config_get_filename (const mix_config_t *config) +{ + g_return_val_if_fail (config != NULL, NULL); + return config->filename; +} + +/* get a config item's value from its key */ +const gchar * +mix_config_get (const mix_config_t *config, const gchar *key) +{ + g_return_val_if_fail (config != NULL, NULL); + g_return_val_if_fail (key != NULL, NULL); + + return (const gchar*)g_hash_table_lookup (config->items, key); +} + +gint +mix_config_get_integer (const mix_config_t *config, const gchar *key) +{ + const gchar *val; + g_return_val_if_fail (config != NULL, 0); + g_return_val_if_fail (key != NULL, 0); + val = mix_config_get (config, key); + if (!val) return 0; + return atoi (val); +} + +/* update (or create if it does not exist) a new config item */ +void +mix_config_update (mix_config_t *config, const gchar *key, const gchar *value) +{ + gpointer okey = NULL; + gpointer oval = NULL; + + g_return_if_fail (config != NULL); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + + if (g_hash_table_lookup_extended (config->items, key, &okey, &oval)) + { + if (oval != value) + { + g_free (oval); + oval = (gpointer)g_strdup (value); + } + } + else + { + okey = (gpointer)g_strdup (key); + oval = (gpointer)g_strdup (value); + } + + g_hash_table_insert (config->items, okey, oval); +} + +void +mix_config_update_integer (mix_config_t *config, const gchar *key, gint value) +{ + gchar *val; + + g_return_if_fail (config != NULL); + g_return_if_fail (key != NULL); + + val = g_strdup_printf ("%d", value); + mix_config_update (config, key, val); + g_free (val); +} + +void +mix_config_remove (mix_config_t *config, const gchar *key) +{ + gchar *val; + + g_return_if_fail (config != NULL); + g_return_if_fail (key != NULL); + + val = g_hash_table_lookup (config->items, key); + if (val != NULL) + { + g_hash_table_remove (config->items, key); + g_free (val); + } +} + +/* save the current configuration */ +static void +save_ (gpointer key, gpointer value, gpointer file) +{ + fprintf ((FILE *)file, "%s=%s\n", (char *)key, (char *)value); +} + +void +mix_config_save (const mix_config_t *config) +{ + FILE *f; + + g_return_if_fail (config != NULL); + + f = fopen (config->filename, "w"); + if (!f) + { + g_warning (_("Unable to open config file %s (%s)"), + config->filename, g_strerror (errno)); + return; + } + g_hash_table_foreach (config->items, save_, (gpointer)f); + fclose (f); +} + +/* set autosave on delete flag */ +void +mix_config_set_autosave (mix_config_t *config, gboolean autosave) +{ + mix_config_update (config, AUTOSAVE_KEY_, + autosave? AUTOSAVE_YES_ : AUTOSAVE_NO_); + config->autosave = autosave; +} + + +gboolean +mix_config_is_autosave (const mix_config_t *config) +{ + g_return_val_if_fail (config != NULL, FALSE); + return config->autosave; +} + +/* devices dir*/ +void +mix_config_set_devices_dir (mix_config_t *config, const gchar *dirname) +{ + g_return_if_fail (config != NULL); + g_return_if_fail (dirname != NULL); + if (mix_stat_dir (dirname, "devices")) + mix_config_update (config, DEVICES_KEY_, dirname); +} + +extern const gchar * +mix_config_get_devices_dir (const mix_config_t *config) +{ + g_return_val_if_fail (config != NULL, NULL); + return mix_config_get (config, DEVICES_KEY_); +} + +/* history file. if relative path, config dir taken as root */ +void +mix_config_set_history_file (mix_config_t *config, const gchar *path) +{ + g_return_if_fail (config != NULL); + g_return_if_fail (path != NULL); + if (g_path_is_absolute (path)) + { + mix_config_update (config, HISTORY_KEY_, path); + } + else + { + gchar *base = g_path_get_dirname (config->filename); + gchar *hf = g_strconcat (base, G_DIR_SEPARATOR_S, path, NULL); + mix_config_update (config, HISTORY_KEY_, hf); + g_free (hf); + g_free (base); + } +} + +const gchar * +mix_config_get_history_file (const mix_config_t *config) +{ + g_return_val_if_fail (config != NULL, NULL); + return mix_config_get (config, HISTORY_KEY_); +} + +void +mix_config_set_history_size (mix_config_t *config, gint s) +{ + g_return_if_fail (config != NULL); + g_return_if_fail (s >= 0); + mix_config_update_integer (config, HISTORY_SIZE_KEY_, s); +} + +gint +mix_config_get_history_size (const mix_config_t *config) +{ + g_return_val_if_fail (config != NULL, 0); + return mix_config_get_integer (config, HISTORY_SIZE_KEY_); +} diff --git a/mixlib/mix_config.h b/mixlib/mix_config.h new file mode 100644 index 0000000..9d2af32 --- /dev/null +++ b/mixlib/mix_config.h @@ -0,0 +1,100 @@ +/* -*-c-*- ---------------- mix_config.h : + * Basic config storage utility. + * ------------------------------------------------------------------ + * $Id: mix_config.h,v 1.6 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001 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. + * + */ + + +#ifndef MIX_CONFIG_H +#define MIX_CONFIG_H + +#include "mix.h" + +/* default config dir */ +extern const gchar *MIX_CONFIG_DIR; + +/* the config type */ +typedef struct mix_config_t mix_config_t; + +/* create a new config handler, giving the dir and name of the config file */ +extern mix_config_t * +mix_config_new (const gchar *dirname, const gchar *filename); + +/* delete a config handler, saving the configuration if needed */ +extern void +mix_config_delete (mix_config_t *config); + +/* get the config filename (fully qualified) */ +extern const gchar * +mix_config_get_filename (const mix_config_t *config); + +/* get a config item's value from its key */ +extern const gchar * +mix_config_get (const mix_config_t *config, const gchar *key); + +extern gint +mix_config_get_integer (const mix_config_t *config, const gchar *key); + +/* update (or create if it does not exist) a new config item */ +extern void +mix_config_update (mix_config_t *config, const gchar *key, const gchar *value); + +extern void +mix_config_update_integer (mix_config_t *config, const gchar *key, gint value); + +extern void +mix_config_remove (mix_config_t *config, const gchar *key); + +/* save the current configuration */ +extern void +mix_config_save (const mix_config_t *config); + +/** shared config params **/ + +/* set autosave on delete flag */ +extern void +mix_config_set_autosave (mix_config_t *config, gboolean autosave); + +extern gboolean +mix_config_is_autosave (const mix_config_t *config); + +/* devices dir*/ +extern void +mix_config_set_devices_dir (mix_config_t *config, const gchar *dirname); + +extern const gchar * +mix_config_get_devices_dir (const mix_config_t *config); + +/* history file. if relative path, config dir taken as root */ +extern void +mix_config_set_history_file (mix_config_t *config, const gchar *path); + +extern const gchar * +mix_config_get_history_file (const mix_config_t *config); + +extern void +mix_config_set_history_size (mix_config_t *config, gint s); + +extern gint +mix_config_get_history_size (const mix_config_t *config); + + +#endif /* MIX_CONFIG_H */ + diff --git a/mixlib/mix_device.c b/mixlib/mix_device.c new file mode 100644 index 0000000..6a99e8a --- /dev/null +++ b/mixlib/mix_device.c @@ -0,0 +1,164 @@ +/* -*-c-*- -------------- mix_device.c : + * Implementation of the functions declared in mix_device.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001 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 "mix_file.h" +#include "xmix_device.h" + +/* + Set the directory for mix device files (by default, it's ".") + If the dir does not exist, it is created. +*/ +gboolean +mix_device_set_dir (const gchar *dirname) +{ + if (mix_stat_dir (dirname, "devices")) + { + if (DEV_DIR_) g_free (DEV_DIR_); + DEV_DIR_ = g_strdup (dirname); + return TRUE; + } + else + return FALSE; +} + +const gchar * +mix_device_get_dir (void) +{ + return DEV_DIR_; +} + +mix_device_t * +mix_device_new (mix_device_type_t type) +{ + mix_device_t *result = NULL; + g_return_val_if_fail (type < mix_dev_INVALID, NULL); + result = g_new (mix_device_t, 1); + construct_device_ (result, type); + return result; +} + + + +mix_device_t * +mix_device_new_with_name (mix_device_type_t type, const gchar *name) +{ + mix_device_t *result = NULL; + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (type < mix_dev_INVALID, NULL); + result = g_new (mix_device_t, 1); + construct_device_with_name_ (result, type, name); + return result; +} + +/* + Create a new device with a given type and stream +*/ +mix_device_t * +mix_device_new_with_file (mix_device_type_t type, FILE *file) +{ + mix_device_t *result = NULL; + g_return_val_if_fail (file != NULL, NULL); + g_return_val_if_fail (type < mix_dev_INVALID, NULL); + result = g_new (mix_device_t, 1); + construct_device_with_file_ (result, type, file); + return result; +} + +void +mix_device_delete (mix_device_t *dev) +{ + if (dev != NULL) + { + (dev->vtable->destroy) (dev); + g_free (dev); + } +} + +mix_device_type_t +mix_device_type (const mix_device_t *dev) +{ + g_return_val_if_fail (dev != NULL, mix_dev_INVALID); + return dev->type; +} + +const char * +mix_device_get_name (const mix_device_t *dev) +{ + g_return_val_if_fail (dev != NULL, NULL); + return mix_file_base_name(GET_FILE_(dev)); +} + +/* + Get the device block size +*/ +size_t +mix_device_block_size (const mix_device_t *dev) +{ + g_return_val_if_fail (dev != NULL, 0); + return SIZES_[dev->type]; +} + +/* + Get the device io mode +*/ +mix_device_mode_t +mix_device_mode (const mix_device_t *dev) +{ + g_return_val_if_fail (dev != NULL, 0); + return MODES_[dev->type]; +} + +/* + Write a block to the device. +*/ +gboolean +mix_device_write (mix_device_t *dev, const mix_word_t *block) +{ + g_return_val_if_fail (dev != NULL, FALSE); + g_return_val_if_fail (block != NULL, FALSE); + g_assert (dev->vtable != NULL); + return (dev->vtable->write) (dev, block); +} + +gboolean +mix_device_read (mix_device_t *dev, mix_word_t *block) +{ + g_return_val_if_fail (dev != NULL, FALSE); + g_return_val_if_fail (block != NULL, FALSE); + g_assert (dev->vtable != NULL); + return (dev->vtable->read) (dev, block); +} + +gboolean +mix_device_ioc (mix_device_t *dev, mix_short_t arg) +{ + g_return_val_if_fail (dev != NULL, FALSE); + g_assert (dev->vtable != NULL); + return (dev->vtable->ioc) (dev, arg); +} + +gboolean +mix_device_busy (const mix_device_t *dev) +{ + g_return_val_if_fail (dev != NULL, FALSE); + return (dev->vtable->busy) (dev); + +} diff --git a/mixlib/mix_device.h b/mixlib/mix_device.h new file mode 100644 index 0000000..7830dfe --- /dev/null +++ b/mixlib/mix_device.h @@ -0,0 +1,160 @@ +/* -*-c-*- ---------------- mix_device.h : + * Declaration of mix_device_t and associated methods. + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2004 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. + * + */ + + +#ifndef MIX_DEVICE_H +#define MIX_DEVICE_H + +#include <stddef.h> +#include <stdio.h> +#include "mix.h" +#include "mix_types.h" + +/* + A mix device, which derives from mix_io_channel_t +*/ +typedef struct mix_device_t mix_device_t; + +/* + The device type +*/ +typedef enum { + mix_dev_TAPE_0, + mix_dev_TAPE_1, + mix_dev_TAPE_2, + mix_dev_TAPE_3, + mix_dev_TAPE_4, + mix_dev_TAPE_5, + mix_dev_TAPE_6, + mix_dev_TAPE_7, + mix_dev_DISK_0, + mix_dev_DISK_1, + mix_dev_DISK_2, + mix_dev_DISK_3, + mix_dev_DISK_4, + mix_dev_DISK_5, + mix_dev_DISK_6, + mix_dev_DISK_7, + mix_dev_CARD_RD, + mix_dev_CARD_WR, + mix_dev_PRINTER, + mix_dev_CONSOLE, + mix_dev_PAPER_TAPE, + mix_dev_INVALID +} mix_device_type_t; + +/* + The device io mode +*/ +typedef enum { + mix_dev_BIN, + mix_dev_CHAR +} mix_device_mode_t; + +/* + Set the directory for mix device files (by default, it's ".") + If the dir does not exist, it is created. +*/ +extern gboolean +mix_device_set_dir (const gchar *dirname); + +extern const gchar * +mix_device_get_dir (void); + +/* + Create a new device with default name and given type. +*/ +extern mix_device_t * +mix_device_new (mix_device_type_t type); + +/* + Create a new device with a given type and name. +*/ +extern mix_device_t * +mix_device_new_with_name (mix_device_type_t type, const gchar *name); + +/* + Create a new device with a given type and stream +*/ +extern mix_device_t * +mix_device_new_with_file (mix_device_type_t type, FILE *file); + +/* + Delete a device. +*/ +extern void +mix_device_delete (mix_device_t *dev); + +/* + Get a device type +*/ +extern mix_device_type_t +mix_device_type (const mix_device_t *dev); + + +/* + Get a device name +*/ +extern const char * +mix_device_get_name (const mix_device_t *dev); + +/* + Get the device block size +*/ +extern size_t +mix_device_block_size (const mix_device_t *dev); + +/* + Get the device io mode +*/ +extern mix_device_mode_t +mix_device_mode (const mix_device_t *dev); + +/* + Write a block to the device. +*/ +extern gboolean +mix_device_write (mix_device_t *dev, const mix_word_t *block); + +/* + Read a block from the device. +*/ +extern gboolean +mix_device_read (mix_device_t *dev, mix_word_t *block); + +/* + Perform an io control operation on the device. + The parameter _arg_ is the operation's argument: + 0- rewind to beginning + <0 - rewind the given number of blocks + >0 - skip forward the given number of blocks +*/ +extern gboolean +mix_device_ioc (mix_device_t *dev, mix_short_t arg); + +/* + Check if a device is busy +*/ +extern gboolean +mix_device_busy (const mix_device_t *dev); + +#endif /* MIX_DEVICE_H */ + diff --git a/mixlib/mix_eval.c b/mixlib/mix_eval.c new file mode 100644 index 0000000..f6f816a --- /dev/null +++ b/mixlib/mix_eval.c @@ -0,0 +1,191 @@ +/* -*-c-*- -------------- mix_eval.c : + * Implementation of the functions declared in mix_eval.h + * ------------------------------------------------------------------ + * Last change: Time-stamp: "01/02/20 00:23:58 jose" + * ------------------------------------------------------------------ + * Copyright (C) 2000 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 "xmix_eval.h" + + +static const gchar *errors_[] = { + N_("Successful evaluation"), + N_("Syntax error in expression"), + N_("Out of range F-specification"), + N_("Mismatched parenthesis"), + N_("Undefined symbol"), + N_("Internal error") +}; + + +/* create a new evaluator */ +mix_eval_t * +mix_eval_new (void) +{ + mix_eval_t *result = g_new (mix_eval_t, 1); + result->table = mix_symbol_table_new (); + if (result->table == NULL) { + g_free (result); + return NULL; + } + result->towner = TRUE; + result->value = MIX_WORD_ZERO; + result->errpos = 0; + return result; +} + +/* create a new evaluator with an external symbol table */ +mix_eval_t * +mix_eval_new_with_table (mix_symbol_table_t *table) +{ + mix_eval_t *result = g_new (mix_eval_t, 1); + result->table = table; + result->towner = FALSE; + result->value = MIX_WORD_ZERO; + result->errpos = 0; + return result; +} + +/* delete */ +void +mix_eval_delete (mix_eval_t *eval) +{ + g_return_if_fail (eval); + if (eval->table && eval->towner) { + mix_symbol_table_delete (eval->table); + } + g_free (eval); +} + +/* eval an expression */ +mix_eval_result_t +mix_eval_expression_with_loc (mix_eval_t *eval, const gchar *expr, + mix_short_t loc) +{ + mix_eval_data_ data; + + if (expr == NULL || eval == NULL) + return MIX_EVAL_INTERN; + data.expr = g_strdup_printf ("%s\n", expr); + data.table = eval->table; + data.errpos = eval->errpos; + data.value = eval->value; + data.loc = loc; + eval->result = mix_eval_expr (&data); + if (eval->result == MIX_EVAL_OK) { + eval->value = data.value; + eval->errpos = -1; + } else { + eval->errpos = data.errpos; + } + g_free (data.expr); + + return eval->result; +} + +/* get the result of the last evaluation */ +mix_word_t +mix_eval_value (const mix_eval_t *eval) +{ + g_return_val_if_fail (eval != NULL, MIX_WORD_ZERO); + return eval->value; +} + +/* get the last eval result code */ +mix_eval_result_t +mix_eval_last_error (const mix_eval_t *eval) +{ + g_return_val_if_fail (eval != NULL, MIX_EVAL_INTERN); + return eval->result; +} + +/* get the last error string */ +const gchar* +mix_eval_last_error_string (const mix_eval_t *eval) +{ + g_return_val_if_fail (eval != NULL, errors_[MIX_EVAL_INTERN]); + return errors_[eval->result]; +} + +/* get the position of last error */ +guint +mix_eval_last_error_pos (const mix_eval_t *eval) +{ + g_return_val_if_fail (eval != NULL, 0); + return eval->errpos; +} + +/* add, or redefine, a symbol. see mix_symbol_table.h for + possible outcomes. */ +gint +mix_eval_set_symbol (mix_eval_t *eval, const gchar *symbol, + mix_word_t value) +{ + g_return_val_if_fail (eval != NULL && eval->table != NULL, + MIX_SYM_FAIL); + return mix_symbol_table_insert (eval->table, symbol, value); +} + +void +mix_eval_remove_symbol (mix_eval_t *eval, const gchar *symbol) +{ + g_return_if_fail (eval != NULL && eval->table != NULL); + mix_symbol_table_remove (eval->table, symbol); +} + +void +mix_eval_use_symbol_table (mix_eval_t *eval, + mix_symbol_table_t *table) +{ + g_return_if_fail (eval != NULL); + if (eval->table != NULL && eval->towner) + mix_symbol_table_delete (eval->table); + eval->table = table; + eval->towner = FALSE; +} + +const mix_symbol_table_t * +mix_eval_symbol_table (const mix_eval_t *eval) +{ + g_return_val_if_fail (eval != NULL, NULL); + return eval->table; +} + +gboolean +mix_eval_set_symbols_from_table (mix_eval_t *eval, + const mix_symbol_table_t *table) +{ + g_return_val_if_fail (eval != NULL, FALSE); + if (eval->table != NULL) + return mix_symbol_table_merge_table (eval->table, table); + else + return FALSE; +} + +gboolean +mix_eval_remove_symbols_from_table (mix_eval_t *eval, + const mix_symbol_table_t *table) +{ + g_return_val_if_fail (eval != NULL, FALSE); + if (eval->table != NULL) + return mix_symbol_table_substract_table (eval->table, table); + else + return FALSE; +} diff --git a/mixlib/mix_eval.h b/mixlib/mix_eval.h new file mode 100644 index 0000000..c1c1243 --- /dev/null +++ b/mixlib/mix_eval.h @@ -0,0 +1,110 @@ +/* -*-c-*- ---------------- mix_eval.h : + * mix_eval_t is an evaluator of MIX W-expressions + * ------------------------------------------------------------------ + * Last change: Time-stamp: <01/02/20 00:23:58 jose> + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef MIX_EVAL_H +#define MIX_EVAL_H + +#include "mix.h" +#include "mix_types.h" +#include "mix_symbol_table.h" + +/* the evaluator type */ +typedef struct mix_eval_t mix_eval_t; + +/* possible evaluation outcomes*/ +typedef enum { + MIX_EVAL_OK, /* sucess*/ + MIX_EVAL_SYNTAX, /* syntax error */ + MIX_EVAL_INV_FSPEC, /* out of range fspec */ + MIX_EVAL_MIS_PAREN, /* mismatched parenthesis */ + MIX_EVAL_UNDEF_SYM, /* undefined symbol */ + MIX_EVAL_INTERN /* internal error */ +} mix_eval_result_t; + +/* create a new evaluator */ +extern mix_eval_t * +mix_eval_new (void); + +/* create a new evaluator with an external symbol table */ +extern mix_eval_t * +mix_eval_new_with_table (mix_symbol_table_t *table); + +/* delete */ +extern void +mix_eval_delete (mix_eval_t *eval); + +/* provide a new symbol table to be used */ +extern void +mix_eval_use_symbol_table (mix_eval_t *eval, + mix_symbol_table_t *table); + +extern const mix_symbol_table_t * +mix_eval_symbol_table (const mix_eval_t *eval); + +/* eval an expression providing a value for loc counter */ +extern mix_eval_result_t +mix_eval_expression_with_loc (mix_eval_t *eval, const gchar *expr, + mix_short_t loc); +/* eval an expression with null loc*/ +#define mix_eval_expression (eval,expr) \ + mix_eval_expression_with_loc (eval, expr, MIX_SHORT_ZERO) + + +/* get the result of the last evaluation */ +extern mix_word_t +mix_eval_value (const mix_eval_t *eval); + +/* get the last eval result code */ +extern mix_eval_result_t +mix_eval_last_error (const mix_eval_t *eval); + +/* get the last error string */ +extern const gchar* +mix_eval_last_error_string (const mix_eval_t *eval); + +/* get the position of last error */ +extern guint +mix_eval_last_error_pos (const mix_eval_t *eval); + +/* add, or redefine, a symbol. see mix_symbol_table.h for + possible outcomes. */ +extern gint +mix_eval_set_symbol (mix_eval_t *eval, const gchar *symbol, + mix_word_t value); + +extern gboolean +mix_eval_set_symbols_from_table (mix_eval_t *eval, + const mix_symbol_table_t *table); + +extern void +mix_eval_remove_symbol (mix_eval_t *eval, const gchar *symbol); + +extern gboolean +mix_eval_remove_symbols_from_table (mix_eval_t *eval, + const mix_symbol_table_t *table); + + + +#endif /* MIX_EVAL_H */ + diff --git a/mixlib/mix_eval_scanner.l b/mixlib/mix_eval_scanner.l new file mode 100644 index 0000000..8d0d3bd --- /dev/null +++ b/mixlib/mix_eval_scanner.l @@ -0,0 +1,276 @@ +/* -*-c-*- ------------------ mix_eval_scanner.l : + * scanner used by mix_eval_t + * ------------------------------------------------------------------ + * $Id: mix_eval_scanner.l,v 1.4 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2004 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_eval.h" + +#define YY_DECL \ + mix_eval_result_t mix_eval_expr (mix_eval_data_ *data) + +/* keep track of current position in buffer */ +#define YY_USER_ACTION yypos += yyleng; + +#define RETURN_STATE(err) \ + do { \ + done = TRUE; \ + state = err; \ + BEGIN (INITIAL); \ + } while (FALSE) + +#define CLEAN_UP() \ + do { \ + yy_delete_buffer (buffer); \ + g_free (expr_cp); \ + } while (FALSE) + +static mix_word_t eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y); + +static int unput_word_ (mix_word_t word); + +/* { */ +/* gchar *value; */ +/* gint k, result; */ +/* value = g_strdup_printf ("%s%ld", */ +/* mix_word_is_negative (word)? "-":"+", */ +/* mix_word_magnitude (word)); */ +/* result = strlen (value); */ +/* for (k = result - 1; k >= 0; --k) */ +/* unput (value[k]); */ +/* g_free (value); */ +/* return result; */ +/* } */ + +%} + +%option nomain +%option caseless +%option array +%option stack +%option noyywrap +%option noyy_top_state +%option noreject +%option outfile="lex.yy.c" + +%s EVAL +%s WEVAL + +ws [ \t]+ +digit [0-9] +letter [A-Z] +number [+-]?{digit}+ +mixchar [0-9A-Z .,'')(+*/=$<>@;:\-] +locsymbol {digit}H +flocsymbol {digit}F +blocsymbol {digit}B +symbol {digit}*{letter}+[A-Z0-9]* +binops "+"|"-"|"/"|"//"|":" +binop {binops}|"*" +atexpr {digit}+|{symbol}|\* +expr [+-]?{atexpr}({binop}{1}{atexpr})* +fpart \({expr}\) +wexpr {expr}({fpart})?(,{expr}({fpart})?)* + + +%% + +%{ + YY_BUFFER_STATE buffer; + mix_word_t expr_val = MIX_WORD_ZERO, wexpr_val = MIX_WORD_ZERO; + mix_word_t wexpr_val_tmp = MIX_WORD_ZERO; + mix_eval_result_t state = MIX_EVAL_OK; + gchar *expr_cp; + gint yypos = -22; /* to account for padding */ + gboolean is_fp = FALSE, done = FALSE; + g_assert (data != NULL && data->expr != NULL); + /* make room enough to unput computed values */ + expr_cp = g_strdup_printf ("%-20s%s"," ", data->expr); + buffer = yy_scan_string (expr_cp); + data->errpos = -1; +%} + + +<*><<EOF>> { + CLEAN_UP (); + return MIX_EVAL_INTERN; +} + +<INITIAL>{ + {ws} /* eat whitespace */ + . { + if (!done && state == MIX_EVAL_OK) { + yypos -= yyleng; yyless (0); + yy_push_state (WEVAL); + } else { + CLEAN_UP (); + if (state == MIX_EVAL_OK) return (MIX_EVAL_SYNTAX); + data->errpos = yypos; + return state; + } + } + "\n" { + CLEAN_UP(); + if (state == MIX_EVAL_OK) + data->value = wexpr_val; + else + data->errpos = yypos; + return state; + } +} + +<WEVAL>{ + {number}"(" { + is_fp = TRUE; + wexpr_val_tmp = mix_word_new (atol (yytext)); + } + {number}")" { + glong val = atol (yytext); + if ( !is_fp ) { + RETURN_STATE (MIX_EVAL_MIS_PAREN); + } else if ( val < 0 || val > MIX_BYTE_MAX + || !mix_fspec_is_valid (mix_byte_new (val)) ) { + RETURN_STATE (MIX_EVAL_INV_FSPEC); + } else { + is_fp = FALSE; + wexpr_val = mix_word_store_field (mix_byte_new (val), + wexpr_val_tmp, + wexpr_val); + } + } + {number}/({ws}*\n)|[,()] { + wexpr_val = mix_word_new (atol (yytext)); + } + {expr}/({ws}*\n)|[,()] { + if (yytext[0] != '*') + { + yypos -= yyleng; + yyless (0); + } + else + { + yypos -= yyleng - 1; + expr_val = mix_short_to_word_fast (data->loc); + yyless (1); + } + yy_push_state (EVAL); + } + ,/{expr} /* eat comma if followed by expression */ + [\t\n ] { /* ok if not inside an f-part */ + if ( is_fp ) { + RETURN_STATE (MIX_EVAL_MIS_PAREN); + } + unput (yytext[yyleng-1]); --yypos; + done = TRUE; + yy_pop_state (); + } + . RETURN_STATE (MIX_EVAL_SYNTAX); +} + +<EVAL>{ + {binop}{digit}+ { + const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1; + mix_word_t value = mix_word_new (atol (s)); + expr_val = eval_binop_ (yytext, expr_val, value); + } + {binop}{symbol} { + const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1; + if ( !mix_symbol_table_is_defined (data->table, s) ) { + RETURN_STATE (MIX_EVAL_UNDEF_SYM); + } + expr_val = eval_binop_ (yytext, expr_val, + mix_symbol_table_value (data->table, s)); + } + {binop}"*" { + expr_val = eval_binop_ (yytext, expr_val, + mix_short_to_word_fast (data->loc)); + } + "*" yypos -= unput_word_ (mix_short_to_word_fast (data->loc)); + {number} expr_val = mix_word_new (atol (yytext)); + {symbol} { + if ( !mix_symbol_table_is_defined (data->table, yytext) ) { + RETURN_STATE (MIX_EVAL_UNDEF_SYM); + } + expr_val = mix_symbol_table_value (data->table, yytext); + } + [,)(\n\t ] { + unput (yytext[0]); --yypos; + yypos -= unput_word_ (expr_val); + yy_pop_state (); + } + + . RETURN_STATE (MIX_EVAL_SYNTAX); +} + + +%% + +static mix_word_t +eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y) +{ + mix_word_t result = MIX_WORD_ZERO; + switch (op[0]) + { + case '+': + result = mix_word_add (x,y); + break; + case '-': + result = mix_word_sub (x,y); + break; + case '*': + mix_word_mul (x, y, NULL, &result); + break; + case ':': + { + mix_word_t a; + mix_word_mul (x, 8, NULL, &a); + result = mix_word_add (a, y); + break; + } + case '/': + if ( strlen (op) > 1 && op[1] == '/' ) { + mix_word_div (x,MIX_WORD_ZERO,y, &result, NULL); + } else { + mix_word_div (MIX_WORD_ZERO, x, y, &result, NULL); + } + break; + default: + g_assert_not_reached (); + } + return result; +} + +static int +unput_word_ (mix_word_t word) +{ + gchar *value; + gint k, result; + value = g_strdup_printf ("%s%ld", + mix_word_is_negative (word)? "-":"+", + mix_word_magnitude (word)); + result = strlen (value); + for (k = result - 1; k >= 0; --k) + unput (value[k]); + g_free (value); + return result; +} diff --git a/mixlib/mix_file.c b/mixlib/mix_file.c new file mode 100644 index 0000000..383d3b4 --- /dev/null +++ b/mixlib/mix_file.c @@ -0,0 +1,159 @@ +/* -*-c-*- -------------- mix_file.c : + * Implementation of the functions declared in mix_file.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001 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 "xmix_io.h" +#include "mix_file.h" + +const gchar *MIX_SRC_DEFEXT = ".mixal", + *MIX_LIST_DEFEXT = ".mls", *MIX_CODE_DEFEXT = ".mix"; + +/* file names completions */ +#define needs_completion_(name,defext) \ + ( strcmp(name + strlen(name) - strlen(defext), defext) != 0 ) + +#define add_completion_(name, defext) \ + g_strconcat(name, defext, NULL) + +/* The actual definition of mix_file_t */ +struct mix_file_t +{ + mix_iochannel_t parent; + gchar *base_name; + gchar *ext; +}; + +/* Creation/destruction of files */ +static mix_file_t * +open_file_(const gchar *name, mix_fmode_t mode) +{ + mix_file_t *result; + FILE *file; + const gchar *fmode = fmode_to_type_ (mode); + + /* if the read/write file already exists, open in r+ mode */ + if (mode == mix_io_RDWRT && (file = fopen (name, "r"))) + { + fmode = "r+"; + fclose (file); + } + + result = g_new(mix_file_t, 1); + file = fopen(name, fmode); + if ( file == NULL ) { + g_free (result); + return NULL; + } + io_init_from_file_(MIX_IOCHANNEL(result), file); + return result; +} + +mix_file_t * +mix_file_new(const gchar *name, mix_fmode_t mode) +{ + mix_file_t *result; + gchar *bname; + + if ( name == NULL ) return NULL; + bname = g_strdup(name); + if ( bname == NULL ) return NULL; + result = open_file_(name, mode); + if ( result == NULL ) + { + g_free(bname); + return NULL; + } + result->base_name = bname; + result->ext = NULL; + return result; +} + +/* creates a file adding to its name the defext if missing */ +mix_file_t * +mix_file_new_with_def_ext(const gchar *name, mix_fmode_t mode, + const gchar *defext) +{ + const gchar *real_name; + mix_file_t *result; + + if ( name == NULL ) return NULL; + if ( defext == NULL ) return mix_file_new(name, mode); + real_name = needs_completion_(name, defext) ? + add_completion_(name, defext) : name; + result = open_file_(real_name, mode); + if ( real_name != name ) g_free((void *)real_name); + if ( result == NULL ) return NULL; + result->ext = g_strdup(defext); + if ( needs_completion_(name, defext) ) + result->base_name = g_strdup(name); + else + result->base_name = g_strndup(name, strlen(name) - strlen(defext)); + if ( result->ext == NULL || result->base_name == NULL ) + { + mix_file_delete(result); + return NULL; + } + return result; +} + +void +mix_file_delete(mix_file_t *file) +{ + g_return_if_fail(file != NULL); + io_close_(MIX_IOCHANNEL(file)); + if (file->base_name) g_free(file->base_name); + if (file->ext) g_free(file->ext); + g_free(file); +} + +/* convert to a standard FILE */ +extern FILE * +mix_file_to_FILE(const mix_file_t *file) +{ + if ( file == NULL ) return NULL; + return io_get_FILE_(file); +} + +/* complete a name with an extension, if needed */ +gchar * +mix_file_complete_name (const gchar *name, const gchar *extension) +{ + if (!name) return NULL; + if (!extension || !needs_completion_ (name, extension)) + return g_strdup (name); + return add_completion_ (name, extension); +} + +/* Get the base name and extension of file */ +const gchar * +mix_file_base_name(const mix_file_t *file) +{ + g_return_val_if_fail(file != NULL, NULL); + return file->base_name; +} + +const gchar * +mix_file_extension(const mix_file_t *file) +{ + g_return_val_if_fail(file != NULL, NULL); + return file->ext; +} diff --git a/mixlib/mix_file.h b/mixlib/mix_file.h new file mode 100644 index 0000000..059eebb --- /dev/null +++ b/mixlib/mix_file.h @@ -0,0 +1,65 @@ +/* -*-c-*- ---------------- mix_file.h : + * Declarations for the mix_file_t type. + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001 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. + * + */ + + +#ifndef MIX_FILE_H +#define MIX_FILE_H + +#include "mix_io.h" + +/* The mix_file_t type, deriving from mix_iochannel_t and + representing a disk file +*/ +typedef struct mix_file_t mix_file_t; + +/* Creation/destruction of files */ +extern mix_file_t * +mix_file_new(const gchar *name, mix_fmode_t mode); + +/* creates a file adding to its name the defext if missing */ +extern mix_file_t * +mix_file_new_with_def_ext(const gchar *name, mix_fmode_t mode, + const gchar *defext); + +extern void +mix_file_delete(mix_file_t *file); + +/* convert to a standard FILE */ +extern FILE * +mix_file_to_FILE(const mix_file_t *file); + +/* standard default extensions */ +extern const gchar *MIX_SRC_DEFEXT, *MIX_LIST_DEFEXT, *MIX_CODE_DEFEXT; + +/* complete a name with an extension, if needed */ +extern gchar * +mix_file_complete_name (const gchar *name, const gchar *extension); + +/* Get the base name and extension of file */ +extern const gchar * +mix_file_base_name(const mix_file_t *file); + +extern const gchar * +mix_file_extension(const mix_file_t *file); + + +#endif /* MIX_FILE_H */ + diff --git a/mixlib/mix_ins.c b/mixlib/mix_ins.c new file mode 100644 index 0000000..1aa3f70 --- /dev/null +++ b/mixlib/mix_ins.c @@ -0,0 +1,269 @@ +/* -*-c-*- ------------------ mix_ins.c : + * Implementation of the functions declared in mix_ins.h + * ------------------------------------------------------------------ + * Copyright (C) 1999, 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 "mix_ins.h" + + +struct mix_ins_desc_ +{ + mix_opcode_t opcode; + mix_fspec_t def_fspec; + const gchar *string_rep; + gboolean is_ext; +}; + +#define IDES_(c,f,s) {c,f,#s,FALSE} +#define IDESX_(c,f,s) {c,f,#s,TRUE} + +static const struct mix_ins_desc_ id_to_desc_[] = { + IDES_(0,0,NOP), IDES_(1,5,ADD), IDES_(2,5,SUB), IDES_(3,5,MUL), + IDES_(4,5,DIV), + IDESX_(5,0,NUM), IDESX_(5,1,CHAR), IDESX_(5,2,HLT), + IDESX_(6,0,SLA), IDESX_(6,1,SRA), IDESX_(6,2,SLAX), IDESX_(6,3,SRAX), + IDESX_(6,4,SLC), IDESX_(6,5,SRC), + IDES_(7,1,MOVE), IDES_(8,5,LDA), IDES_(9,5,LD1), IDES_(10,5,LD2), + IDES_(11,5,LD3), IDES_(12,5,LD4), IDES_(13,5,LD5), + IDES_(14,5,LD6), IDES_(15,5,LDX), IDES_(16,5,LDAN), IDES_(17,5,LD1N), + IDES_(18,5,LD2N), IDES_(19,5,LD3N), IDES_(20,5,LD4N), + IDES_(21,5,LD5N), IDES_(22,5,LD6N), IDES_(23,5,LDXN), + IDES_(24,5,STA), IDES_(25,5,ST1), IDES_(26,5,ST2), IDES_(27,5,ST3), + IDES_(28,5,ST4), IDES_(29,5,ST5), IDES_(30,5,ST6), IDES_(31,5,STX), + IDES_(32,2,STJ), IDES_(33,5,STZ), IDES_(34,0,JBUS), IDES_(35,0,IOC), + IDES_(36,0,IN), IDES_(37,0,OUT), IDES_(38,0,JRED), + IDESX_(39,0,JMP), IDESX_(39,1,JSJ), IDESX_(39,2,JOV), IDESX_(39,3,JNOV), + IDESX_(39,4,JL), IDESX_(39,5,JE), IDESX_(39,6,JG), IDESX_(39,7,JGE), + IDESX_(39,8,JNE), IDESX_(39,9,JLE), + IDESX_(40,0,JAN), IDESX_(40,1,JAZ), IDESX_(40,2,JAP), IDESX_(40,3,JANN), + IDESX_(40,4,JANZ), IDESX_(40,5,JANP), + IDESX_(41,0,J1N), IDESX_(41,1,J1Z), IDESX_(41,2,J1P), IDESX_(41,3,J1NN), + IDESX_(41,4,J1NZ), IDESX_(41,5,J1NP), + IDESX_(42,0,J2N), IDESX_(42,1,J2Z), IDESX_(42,2,J2P), IDESX_(42,3,J2NN), + IDESX_(42,4,J2NZ), IDESX_(42,5,J2NP), + IDESX_(43,0,J3N), IDESX_(43,1,J3Z), IDESX_(43,2,J3P), IDESX_(43,3,J3NN), + IDESX_(43,4,J3NZ), IDESX_(43,5,J3NP), + IDESX_(44,0,J4N), IDESX_(44,1,J4Z), IDESX_(44,2,J4P), IDESX_(44,3,J4NN), + IDESX_(44,4,J4NZ), IDESX_(44,5,J4NP), + IDESX_(45,0,J5N), IDESX_(45,1,J5Z), IDESX_(45,2,J5P), IDESX_(45,3,J5NN), + IDESX_(45,4,J5NZ), IDESX_(45,5,J5NP), + IDESX_(46,0,J6N), IDESX_(46,1,J6Z), IDESX_(46,2,J6P), IDESX_(46,3,J6NN), + IDESX_(46,4,J6NZ), IDESX_(46,5,J6NP), + IDESX_(47,0,JXN), IDESX_(47,1,JXZ), IDESX_(47,2,JXP), IDESX_(47,3,JXNN), + IDESX_(47,4,JXNZ), IDESX_(47,5,JXNP), + IDESX_(48,0,INCA), IDESX_(48,1,DECA), IDESX_(48,2,ENTA), IDESX_(48,3,ENNA), + IDESX_(49,0,INC1), IDESX_(49,1,DEC1), IDESX_(49,2,ENT1), IDESX_(49,3,ENN1), + IDESX_(50,0,INC2), IDESX_(50,1,DEC2), IDESX_(50,2,ENT2), IDESX_(50,3,ENN2), + IDESX_(51,0,INC3), IDESX_(51,1,DEC3), IDESX_(51,2,ENT3), IDESX_(51,3,ENN3), + IDESX_(52,0,INC4), IDESX_(52,1,DEC4), IDESX_(52,2,ENT4), IDESX_(52,3,ENN4), + IDESX_(53,0,INC5), IDESX_(53,1,DEC5), IDESX_(53,2,ENT5), IDESX_(53,3,ENN5), + IDESX_(54,0,INC6), IDESX_(54,1,DEC6), IDESX_(54,2,ENT6), IDESX_(54,3,ENN6), + IDESX_(55,0,INCX), IDESX_(55,1,DECX), IDESX_(55,2,ENTX), IDESX_(55,3,ENNX), + IDES_(56,5,CMPA), IDES_(57,5,CMP1), IDES_(58,5,CMP2), IDES_(59,5,CMP3), + IDES_(60,5,CMP4), IDES_(61,5,CMP5), IDES_(62,5,CMP6), IDES_(63,5,CMPX) +}; + +static const gsize ID_TO_DESC_SIZE_= +sizeof(id_to_desc_)/sizeof(id_to_desc_[0]); + +/* To look for the mix_ins_id corresponding to a pair (fspec,opcode) + we use an array indexed by opcode with values + (initial_ins, final_ins - initial_ins) +*/ +struct mix_opcode_desc_ +{ + mix_ins_id_t init_id; + guchar inc; /* when inc == 0, the id does not depend on fspec */ +}; + +static struct mix_opcode_desc_ opcode_to_id_[MIX_BYTE_MAX + 1]; + +/* a hash table mapping strings to mix_ins_id's */ +static GHashTable *string_to_id_ = NULL; + +void +mix_init_ins (void) +{ + guint k; + g_assert (ID_TO_DESC_SIZE_ == mix_INVALID_INS); + + for ( k = 0; k < MIX_BYTE_MAX + 1; ++k ) + { + opcode_to_id_[k].init_id = mix_INVALID_INS; + opcode_to_id_[k].inc = 0; + } + + for ( k = 0; k < ID_TO_DESC_SIZE_; ++k ) + { + if ( opcode_to_id_[id_to_desc_[k].opcode].init_id == mix_INVALID_INS ) + opcode_to_id_[id_to_desc_[k].opcode].init_id = k; + else + ++opcode_to_id_[id_to_desc_[k].opcode].inc; + } + + if ( string_to_id_ == NULL ) + { + string_to_id_ = g_hash_table_new (g_str_hash, g_str_equal); + for ( k = 0; k < ID_TO_DESC_SIZE_; ++k) + g_hash_table_insert (string_to_id_, (gpointer)id_to_desc_[k].string_rep, + GUINT_TO_POINTER (k)); + } + +} + +void +mix_release_ins (void) +{ + g_hash_table_destroy (string_to_id_); +} + +/* Conversions between words and ins */ +mix_word_t +mix_ins_to_word (const mix_ins_t *ins) +{ + g_return_val_if_fail (ins != NULL, MIX_WORD_ZERO); + return (mix_word_t)((ins->address<<18)| + ((ins->index&7)<<12)|(ins->fspec<<6)|(ins->opcode)); +} + +mix_ins_id_t +mix_word_to_ins (mix_word_t word, mix_ins_t *ins) +{ + mix_ins_id_t result = mix_get_ins_id (mix_get_ins_opcode (word), + mix_get_ins_fspec (word)); + + g_return_val_if_fail (ins != NULL, result); + mix_word_to_ins_uncheck (word,*ins); + return result; +} + +/* Getting ins parameters */ +mix_opcode_t +mix_get_opcode_from_id (mix_ins_id_t id) +{ + g_return_val_if_fail (id < ID_TO_DESC_SIZE_, MIX_BYTE_ZERO); + return id_to_desc_[id].opcode; +} + +gboolean +mix_ins_id_is_extended (mix_ins_id_t id) +{ + g_return_val_if_fail (id < ID_TO_DESC_SIZE_, FALSE); + return id_to_desc_[id].is_ext; +} + + +mix_fspec_t +mix_get_fspec_from_id (mix_ins_id_t id) +{ + g_return_val_if_fail (id < ID_TO_DESC_SIZE_, MIX_BYTE_ZERO); + return id_to_desc_[id].def_fspec; +} + + +const gchar * +mix_get_string_from_id (mix_ins_id_t id) +{ + g_return_val_if_fail (id < ID_TO_DESC_SIZE_, NULL); + return id_to_desc_[id].string_rep; +} + +mix_ins_id_t +mix_get_id_from_string (const gchar *name) +{ + gpointer key, value; + if ( !g_hash_table_lookup_extended (string_to_id_, (gpointer)name, + &key, &value) ) + return mix_INVALID_INS; + return (mix_ins_id_t)GPOINTER_TO_UINT (value); +} + + +mix_ins_id_t +mix_get_ins_id (mix_opcode_t code, mix_fspec_t fspec) +{ + if ( opcode_to_id_[code].inc == 0 ) + return opcode_to_id_[code].init_id; + else if ( opcode_to_id_[code].inc < fspec ) + return mix_INVALID_INS; + else + return (opcode_to_id_[code].init_id + fspec); +} + +/* Printable representation */ +extern gchar * /* this pointer must be freed by caller */ +mix_ins_to_string (const mix_ins_t *ins) +{ + gboolean needs_f; + gchar *result; + mix_ins_id_t id; + + g_return_val_if_fail (ins != NULL, NULL); + id = mix_ins_id_from_ins (*ins); + needs_f = ins->fspec != id_to_desc_[id].def_fspec; + if ( needs_f ) + result = g_strdup_printf ("%s\t%s%d,%d(%d:%d)", + mix_get_string_from_id (id), + mix_short_is_negative (ins->address) ? "-" : "", + mix_short_magnitude (ins->address), ins->index, + mix_fspec_left (ins->fspec), + mix_fspec_right (ins->fspec)); + else + result = g_strdup_printf ("%s\t%s%d,%d", mix_get_string_from_id (id), + mix_short_is_negative (ins->address) ? "-" : "", + mix_short_magnitude (ins->address), ins->index); + return result; +} + +extern void +mix_ins_to_string_in_buffer (const mix_ins_t *ins, gchar *buf, guint len) +{ + gboolean needs_f; + mix_ins_id_t id; + + g_return_if_fail (ins != NULL); + g_return_if_fail (buf != NULL); + id = mix_ins_id_from_ins (*ins); + needs_f = ins->fspec != id_to_desc_[id].def_fspec; + if ( needs_f ) + g_snprintf (buf, len, "%s\t%s%d,%d(%d:%d)", + mix_get_string_from_id (id), + mix_short_is_negative (ins->address) ? "-" : "", + mix_short_magnitude (ins->address), ins->index, + mix_fspec_left (ins->fspec), + mix_fspec_right (ins->fspec)); + else + g_snprintf (buf, len, "%s\t%s%d,%d", + mix_get_string_from_id (id), + mix_short_is_negative (ins->address) ? "-" : "", + mix_short_magnitude (ins->address), ins->index); +} + + + +void +mix_ins_print (const mix_ins_t *ins) +{ + g_return_if_fail (ins != NULL); + g_print (mix_get_string_from_id (mix_ins_id_from_ins (*ins))); + g_print (" %s%d,%d(%d:%d)", mix_short_is_negative (ins->address) ? "-" : "+", + mix_short_magnitude (ins->address), ins->index, + mix_fspec_left (ins->fspec), mix_fspec_right (ins->fspec)); +} + diff --git a/mixlib/mix_ins.h b/mixlib/mix_ins.h new file mode 100644 index 0000000..07d8e38 --- /dev/null +++ b/mixlib/mix_ins.h @@ -0,0 +1,195 @@ +/* -*-c-*- -------------------- mix_ins.h: + * This file declares types and functions for manipulating MIX + * instructions + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef MIX_INS_H +#define MIX_INS_H + +#include "mix_types.h" + + +/* Initialise and free mix_ins data */ +extern void +mix_init_ins(void); + +extern void +mix_release_ins(void); + +/* A MIX instruction is made up of address, index, fspec and op_code */ + +/*-- Address field: contains two bytes */ +typedef mix_short_t mix_address_t; + +/*-- Index field: a value between 0 and 6 */ +typedef enum { + mix_I0, mix_I1, mix_I2, mix_I3, mix_I4, mix_I5, mix_I6 } mix_index_t; + +/*-- Instruction id: enumeration of MIX instruction set */ +typedef enum { + mix_NOP, mix_ADD, mix_SUB, mix_MUL, mix_DIV, + mix_NUM, mix_CHAR, mix_HLT, + mix_SLA, mix_SRA, mix_SLAX, mix_SRAX, mix_SLC, mix_SRC, + mix_MOVE, mix_LDA, mix_LD1, mix_LD2, mix_LD3, mix_LD4, mix_LD5, + mix_LD6, mix_LDX, mix_LDAN, mix_LD1N, mix_LD2N, mix_LD3N, mix_LD4N, + mix_LD5N, mix_LD6N, mix_LDXN, mix_STA, mix_ST1, mix_ST2, mix_ST3, mix_ST4, + mix_ST5, mix_ST6, mix_STX, mix_STJ, mix_STZ, mix_JBUS, mix_IOC, mix_IN, + mix_OUT, mix_JRED, + mix_JMP, mix_JSJ, mix_JOV, mix_JNOV, mix_JL, mix_JE, mix_JG, mix_JGE, + mix_JNE, mix_JLE, + mix_JAN, mix_JAZ, mix_JAP, mix_JANN, mix_JANZ, mix_JANP, + mix_J1N, mix_J1Z, mix_J1P, mix_J1NN, mix_J1NZ, mix_J1NP, + mix_J2N, mix_J2Z, mix_J2P, mix_J2NN, mix_J2NZ, mix_J2NP, + mix_J3N, mix_J3Z, mix_J3P, mix_J3NN, mix_J3NZ, mix_J3NP, + mix_J4N, mix_J4Z, mix_J4P, mix_J4NN, mix_J4NZ, mix_J4NP, + mix_J5N, mix_J5Z, mix_J5P, mix_J5NN, mix_J5NZ, mix_J5NP, + mix_J6N, mix_J6Z, mix_J6P, mix_J6NN, mix_J6NZ, mix_J6NP, + mix_JXN, mix_JXZ, mix_JXP, mix_JXNN, mix_JXNZ, mix_JXNP, + mix_INCA, mix_DECA, mix_ENTA, mix_ENNA, + mix_INC1, mix_DEC1, mix_ENT1, mix_ENN1, + mix_INC2, mix_DEC2, mix_ENT2, mix_ENN2, + mix_INC3, mix_DEC3, mix_ENT3, mix_ENN3, + mix_INC4, mix_DEC4, mix_ENT4, mix_ENN4, + mix_INC5, mix_DEC5, mix_ENT5, mix_ENN5, + mix_INC6, mix_DEC6, mix_ENT6, mix_ENN6, + mix_INCX, mix_DECX, mix_ENTX, mix_ENNX, + mix_CMPA, mix_CMP1, mix_CMP2, mix_CMP3, mix_CMP4, + mix_CMP5, mix_CMP6, mix_CMPX, mix_INVALID_INS +} mix_ins_id_t; + +/* each one of the above id's has associated an opcode, a default + fspec and a string representation */ +/* the opcode fits in a byte */ +typedef mix_byte_t mix_opcode_t; + +/* labels for each opcode */ +enum { + mix_opNOP = 0, mix_opADD, mix_opSUB, mix_opMUL, mix_opDIV, + mix_opSPC, mix_opSLx, mix_opMOVE, + mix_opLDA, mix_opLD1, mix_opLD2, mix_opLD3, mix_opLD4, mix_opLD5, + mix_opLD6, mix_opLDX, mix_opLDAN, mix_opLD1N, mix_opLD2N, mix_opLD3N, + mix_opLD4N, mix_opLD5N, mix_opLD6N, mix_opLDXN, + mix_opSTA, mix_opST1, mix_opST2, mix_opST3, mix_opST4, + mix_opST5, mix_opST6, mix_opSTX, mix_opSTJ, mix_opSTZ, + mix_opJBUS, mix_opIOC, mix_opIN, mix_opOUT, mix_opJRED, + mix_opJMP, mix_opJAx, mix_opJ1x, mix_opJ2x, mix_opJ3x, + mix_opJ4x, mix_opJ5x, mix_opJ6x, mix_opJXx, + mix_opINCA, mix_opINC1, mix_opINC2, mix_opINC3, + mix_opINC4, mix_opINC5, mix_opINC6, mix_opINCX, + mix_opCMPA, mix_opCMP1, mix_opCMP2, mix_opCMP3, mix_opCMP4, + mix_opCMP5, mix_opCMP6, mix_opCMPX +}; + +extern mix_opcode_t +mix_get_opcode_from_id(mix_ins_id_t id); + +extern mix_fspec_t +mix_get_fspec_from_id(mix_ins_id_t id); + +/* For extended instructions, both the opcode and fspec determine + the id (i.e., an explicit fspec cannot be used) +*/ +extern gboolean +mix_ins_id_is_extended(mix_ins_id_t id); + +extern const gchar * +mix_get_string_from_id(mix_ins_id_t id); + +extern mix_ins_id_t +mix_get_id_from_string(const gchar *name); + +extern mix_ins_id_t +mix_get_ins_id(mix_opcode_t code, mix_fspec_t fspec); + + +/*-- MIX instruction type */ +typedef struct mix_ins_t mix_ins_t; +struct mix_ins_t +{ + mix_address_t address; + mix_index_t index; + mix_fspec_t fspec; + mix_opcode_t opcode; +}; + +#define mix_ins_fill_from_id(ins,id) \ + do { \ + (ins).opcode = mix_get_opcode_from_id(id); \ + (ins).fspec = mix_get_fspec_from_id(id); \ + } while(FALSE) + + +/* A mix ins can be codified into a word */ +extern mix_word_t +mix_ins_to_word(const mix_ins_t *ins); + +extern mix_ins_id_t +mix_word_to_ins(mix_word_t w, mix_ins_t *ins); + +#define mix_word_add_address(word,addr) (word) |= ((addr)<<18) + +/* decompose an instruction codified in a word into its parts */ +#define mix_get_ins_address(word) ((mix_address_t)((word)>>18)) +#define mix_get_ins_index(word) ((mix_index_t)(((word)>>12)&7)) +#define mix_get_ins_fspec(word) ((mix_fspec_t)(mix_byte_new((word)>>6))) +#define mix_get_ins_opcode(word) ((mix_opcode_t)(mix_byte_new(word))) + +/* unchecked versions for speed */ +#define mix_ins_to_word_uncheck(ins) \ + (mix_word_t)(((ins).address<<18)| \ + (((ins).index&7)<<12)|((ins).fspec<<6)|((ins).opcode)) + +#define mix_word_to_ins_uncheck(word,ins) \ + do { \ + (ins).address = mix_get_ins_address(word); \ + (ins).index = mix_get_ins_index(word); \ + (ins).fspec = mix_get_ins_fspec(word); \ + (ins).opcode = mix_get_ins_opcode(word); \ + } while(FALSE) + +#define mix_ins_id_from_ins(ins) mix_get_ins_id((ins).opcode,(ins).fspec) + + +/* Printable representation */ +extern gchar * /* this pointer must be freed by caller */ +mix_ins_to_string(const mix_ins_t *ins); + +extern void +mix_ins_to_string_in_buffer (const mix_ins_t *ins, gchar *buf, guint len); + +extern void +mix_ins_print(const mix_ins_t *ins); + + + +/* A MIX ins has an associated execution time */ +/* Initialise mix_ins data providing an interlock time for I/O devices */ +/* --------------------------------------> MOVE to VM +typedef guint mix_time_t; + +extern void +mix_init_ins_with_interlock_time(mix_time_t t); + +extern mix_time_t +mix_ins_exec_time(const mix_ins_t *ins); +*/ + +#endif /* MIX_INS_H */ diff --git a/mixlib/mix_io.c b/mixlib/mix_io.c new file mode 100644 index 0000000..31e43df --- /dev/null +++ b/mixlib/mix_io.c @@ -0,0 +1,208 @@ +/* -*-c-*- --------------- mix_io.c : + * Implementation of the functions declared in mix_io.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2004 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 "xmix_io.h" + +mix_iochannel_t * +mix_io_new (FILE *file) +{ + mix_iochannel_t *result; + g_return_val_if_fail (file != NULL, NULL); + result = g_new (mix_iochannel_t, 1); + result->file = file; + return result; +} + +void +mix_io_delete (mix_iochannel_t *ch) +{ + if (ch != NULL) + { + fclose (ch->file); + g_free (ch); + } +} + +FILE * +mix_io_to_FILE (mix_iochannel_t *ioc) +{ + if (ioc == NULL) return NULL; + return ioc->file; +} + +gboolean +mix_io_eof (mix_iochannel_t *ioc) +{ + if (ioc == NULL) return TRUE; + return is_eof_ (ioc); +} + +gboolean +mix_io_is_ready (mix_iochannel_t *ioc) +{ + if (ioc == NULL) return FALSE; + return is_ready_ (ioc); +} + +gboolean +mix_io_write_byte (mix_iochannel_t *ioc, mix_byte_t b) +{ + if (ioc == NULL) return FALSE; + return write_data_ (ioc, &b, 1); +} + +gboolean +mix_io_write_byte_array (mix_iochannel_t *ioc, const mix_byte_t *b, + size_t s) +{ + if (ioc == NULL || b == NULL) return FALSE; + return write_data_ (ioc, b, s); +} + +mix_byte_t +mix_io_read_byte (mix_iochannel_t *ioc) +{ + mix_byte_t result = MIX_BYTE_ZERO; + if (ioc != NULL) read_data_ (ioc, &result, 1); + return result; +} + +gboolean +mix_io_read_byte_array (mix_iochannel_t *ioc, mix_byte_t *b, size_t s) +{ + return (ioc != NULL) && (b != NULL) && read_data_ (ioc, b, s); +} + +gboolean +mix_io_write_word (mix_iochannel_t *ioc, mix_word_t w) +{ + return (ioc != NULL) && write_data_ (ioc, &w, 1); +} + +gboolean +mix_io_write_word_array (mix_iochannel_t *ioc, const mix_word_t *w, + size_t s) +{ + return (ioc != NULL) && (w != NULL) && write_data_ (ioc, w, s); +} + +mix_word_t +mix_io_read_word (mix_iochannel_t *ioc) +{ + mix_word_t result = MIX_WORD_ZERO; + if (ioc != NULL) read_data_ (ioc, &result, 1); + return result; +} + +gboolean +mix_io_read_word_array (mix_iochannel_t *ioc, mix_word_t *w, size_t s) +{ + return (ioc != NULL) && (w != NULL) && read_data_ (ioc, w, s); +} + +gboolean +mix_io_write_short (mix_iochannel_t *ioc, mix_short_t w) +{ + return (ioc != NULL) && write_data_ (ioc, &w, 1); +} + +gboolean +mix_io_write_short_array (mix_iochannel_t *ioc, const mix_short_t *w, + size_t s) +{ + return (ioc != NULL) && (w != NULL) && write_data_ (ioc, w, s); +} + +mix_short_t +mix_io_read_short (mix_iochannel_t *ioc) +{ + mix_short_t result = MIX_SHORT_ZERO; + if (ioc != NULL) read_data_ (ioc, &result, 1); + return result; +} + +gboolean +mix_io_read_short_array (mix_iochannel_t *ioc, mix_short_t *w, size_t s) +{ + return (ioc != NULL) && (w != NULL) && read_data_ (ioc, w, s); +} + +gboolean +mix_io_write_char (mix_iochannel_t *ioc, mix_char_t c) +{ + guchar value = mix_char_to_ascii (c); + return (ioc != NULL) && write_data_ (ioc, &value, 1); +} + +mix_char_t +mix_io_read_char (mix_iochannel_t *ioc) +{ + guchar value = MIX_CHAR_MAX; + if (ioc != NULL) read_data_ (ioc, &value, 1); + return mix_ascii_to_char (value); +} + +gboolean +mix_io_write_word_array_as_char (mix_iochannel_t *ioc, + const mix_word_t *w, size_t s) +{ + guint k, j; + guchar value; + + if ((ioc == NULL) || (w == NULL)) return FALSE; + + for (k = 0; k < s; k++) + for (j = 1; j < 6; j++) + { + mix_char_t ch = mix_byte_to_char (mix_word_get_byte (w[k], j)); + value = mix_char_to_ascii (ch); + if (!write_data_ (ioc, &value, 1)) return FALSE; + } + value = '\n'; + return write_data_ (ioc, &value, 1); +} + +gboolean +mix_io_read_word_array_as_char (mix_iochannel_t *ioc, + mix_word_t *w, size_t s) +{ + guint k, j; + guchar value; + gboolean eol = FALSE; + mix_char_t spc = mix_ascii_to_char (' '); + + if ((ioc == NULL) || (w == NULL)) return FALSE; + + for (k = 0; k < s && !eol; k++) + for (j = 1; j < 6; j++) + { + if (!eol && !read_data_ (ioc, &value, 1)) return FALSE; + eol = eol || (value == '\n') || is_eof_ (ioc); + mix_word_set_byte (&w[k], j, eol? spc : mix_ascii_to_char (value)); + } + for (; k < s; ++k) w[k] = MIX_WORD_ZERO; + + while (!eol && !is_eof_ (ioc) && value != '\n') + if (!read_data_ (ioc, &value, 1)) return FALSE; + + return TRUE; +} diff --git a/mixlib/mix_io.h b/mixlib/mix_io.h new file mode 100644 index 0000000..0e79d67 --- /dev/null +++ b/mixlib/mix_io.h @@ -0,0 +1,119 @@ +/* -*-c-*- ------------------ mix_io.h : + * Declarations for mix_iochannel_t and mix_file_t + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2004 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. + * + */ + + +#ifndef MIX_IOCHANNEL_H +#define MIX_IOCHANNEL_H + +#include <stdio.h> +#include "mix_types.h" + +/* mix_iochannel_t: an object for input/output of mix types */ +typedef struct mix_iochannel_t mix_iochannel_t; + +/* Cast to mix_iochannel_t */ +#define MIX_IOCHANNEL(file) (mix_iochannel_t *)(file) + +/* I/O channels can be created in different modes: */ +typedef enum { + mix_io_READ, /* read existing file */ + mix_io_WRITE, /* write new file */ + mix_io_RDWRT, /* read/write existing from beginning */ + mix_io_APPEND, /* append to existing or new file */ + mix_io_RAPPEND /* read from beginning, append to end */ +} mix_fmode_t; + +/* Create from a file handle */ +extern mix_iochannel_t * +mix_io_new (FILE *file); + +/* Delete */ +extern void +mix_io_delete (mix_iochannel_t *ch); + +/* Convert to a FILE * */ +extern FILE * +mix_io_to_FILE (mix_iochannel_t *ioc); + +/* Read/write from/to an iochannel */ +extern gboolean +mix_io_eof (mix_iochannel_t *ioc); + +extern gboolean +mix_io_is_ready (mix_iochannel_t *ioc); + +extern gboolean +mix_io_write_byte (mix_iochannel_t *ioc, mix_byte_t b); + +extern gboolean +mix_io_write_byte_array (mix_iochannel_t *ioc, const mix_byte_t *b, size_t s); + + +extern mix_byte_t +mix_io_read_byte (mix_iochannel_t *ioc); + +extern gboolean +mix_io_read_byte_array (mix_iochannel_t *ioc, mix_byte_t *b, size_t s); + + +extern gboolean +mix_io_write_word (mix_iochannel_t *ioc, mix_word_t w); + + +extern gboolean +mix_io_write_word_array (mix_iochannel_t *ioc, const mix_word_t *w, size_t s); + +extern mix_word_t +mix_io_read_word (mix_iochannel_t *ioc); + +extern gboolean +mix_io_read_word_array (mix_iochannel_t *ioc, mix_word_t *w, size_t s); + +extern gboolean +mix_io_write_short (mix_iochannel_t *ioc, mix_short_t w); + + +extern gboolean +mix_io_write_short_array (mix_iochannel_t *ioc, const mix_short_t *w, size_t s); + +extern mix_short_t +mix_io_read_short (mix_iochannel_t *ioc); + +extern gboolean +mix_io_read_short_array (mix_iochannel_t *ioc, mix_short_t *w, size_t s); + +extern gboolean +mix_io_write_char (mix_iochannel_t *ioc, mix_char_t c); + +extern mix_char_t +mix_io_read_char (mix_iochannel_t *ioc); + +extern gboolean +mix_io_write_word_array_as_char (mix_iochannel_t *ioc, + const mix_word_t *w, size_t s); + +extern gboolean +mix_io_read_word_array_as_char (mix_iochannel_t *ioc, + mix_word_t *w, size_t s); + + +#endif /* MIX_IOCHANNEL_H */ + diff --git a/mixlib/mix_parser.c b/mixlib/mix_parser.c new file mode 100644 index 0000000..0dfcf81 --- /dev/null +++ b/mixlib/mix_parser.c @@ -0,0 +1,605 @@ +/* -*-c-*- -------------- mix_parser.c : + * Implementation of the functions declared in mix_parser.h and + * xmix_parser.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2003, 2004 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 "mix_code_file.h" +#include "xmix_parser.h" + +/* The flex-generated scanner, according to file mix_scanner.l */ +extern mix_parser_err_t +mix_flex_scan (mix_parser_t *parser); + +/*------------ mixparser.h functions -------------------------------------*/ + +/* error messages */ +static const gchar * const ERR_MESSAGE_[] = { + N_("successful compilation"), + N_("file not yet compiled"), + N_("internal error"), + N_("unable to open MIX source file"), + N_("unable to open MIX output file"), + N_("unexpected end of file"), + N_("invalid location field"), + N_("duplicated symbol"), + N_("symbol too long"), + N_("missing operator field"), + N_("unexpected location symbol"), + N_("invalid address field"), + N_("invalid index field"), + N_("invalid f-specification"), + N_("invalid operation field"), + N_("invalid expression"), + N_("undefined symbol"), + N_("mismatched parenthesis"), + N_("unexpected f-specfication"), + N_("missing symbol name"), + N_("symbol is an instruction name"), + N_("failed write access to code file"), + N_("operand of ALF pseudo instruction has less than 5 chars"), + N_("operand of ALF pseudo instruction has more than 5 chars"), + N_("operand of ALF pseudo instruction must be quoted") +}; + +static const guint NO_OF_MESSAGES_ = sizeof(ERR_MESSAGE_)/sizeof (gchar*); + +const gchar * +mix_parser_err_string (mix_parser_err_t error) +{ + return (error < NO_OF_MESSAGES_) ? _(ERR_MESSAGE_[error]) : NULL; +} + +guint +mix_parser_err_count (const mix_parser_t *parser) +{ + return (parser) ? parser->err_count : 0; +} + +guint +mix_parser_warning_count (const mix_parser_t *parser) +{ + return (parser) ? parser->warn_count : 0; +} + +const gchar * +mix_parser_src_file_base_name (const mix_parser_t *parser) +{ + return (parser) ? mix_file_base_name (parser->in_file) : NULL; +} + +const gchar * +mix_parser_src_file_extension (const mix_parser_t *parser) +{ + return (parser) ? mix_file_extension (parser->in_file) : NULL; +} + + +/* Create/destroy a mix_parser */ +/* compare function for the table of ins */ +static gint +compare_shorts_ (gconstpointer s1, gconstpointer s2) +{ + mix_short_t a = (mix_short_t)GPOINTER_TO_UINT (s1); + mix_short_t b = (mix_short_t)GPOINTER_TO_UINT (s2); + if ( mix_short_sign (a) == mix_short_sign (b) ) + return mix_short_magnitude (a) - mix_short_magnitude (b); + else if ( mix_short_magnitude (a) == 0 && mix_short_magnitude (b) == 0 ) + return 0; + else if ( mix_short_is_positive (a) ) + return 1; + return -1; +} + +mix_parser_t * +mix_parser_new (const gchar *in_file) +{ + mix_parser_t *result; + mix_file_t *f = mix_file_new_with_def_ext (in_file, mix_io_READ, + MIX_SRC_DEFEXT); + + if ( f == NULL ) return NULL; + result = g_new (mix_parser_t, 1); + result->symbol_table = mix_symbol_table_new (); + result->ls_table = mix_symbol_table_new (); + result->cur_ls = 0; + result->future_refs = g_hash_table_new (g_str_hash, g_str_equal); + result->ins_table = g_tree_new (compare_shorts_); + if ( result->symbol_table == NULL || result->future_refs == NULL + || result->ins_table == NULL || result->ls_table == NULL ) + { + mix_symbol_table_delete (result->symbol_table); + mix_symbol_table_delete (result->ls_table); + g_hash_table_destroy (result->future_refs); + g_tree_destroy (result->ins_table); + mix_file_delete (f); + g_free (result); + g_warning (_("No system resources")); + return NULL; + } + result->con_list = NULL; + result->alf_list = NULL; + result->in_file = f; + result->loc_count = MIX_SHORT_ZERO; + result->start = MIX_SHORT_ZERO; + result->end = MIX_SHORT_ZERO; + result->status = MIX_PERR_NOCOMP; + result->err_line = 0; + result->err_count = 0; + result->warn_count = 0; + return result; +} + +static void +delete_list_vals_ (gpointer key, gpointer value, gpointer data) +{ + g_free (key); + g_slist_free ((GSList*)value); +} + +static int +delete_tree_vals_ (gpointer key, gpointer value, gpointer data) +{ + g_free (value); + return FALSE; +} + +void +mix_parser_delete (mix_parser_t *parser) +{ + g_return_if_fail (parser != NULL); + /* clear the GSList values of future_refs and its keys */ + g_hash_table_foreach (parser->future_refs, delete_list_vals_, NULL); + /* clear the ins_node_'s of the ins tree */ + g_tree_foreach (parser->ins_table, delete_tree_vals_, NULL); + /* destroy the tree and hash tables */ + g_tree_destroy (parser->ins_table); + mix_symbol_table_delete (parser->symbol_table); + mix_symbol_table_delete (parser->ls_table); + g_hash_table_destroy (parser->future_refs); + g_slist_free (parser->con_list); + g_slist_free (parser->alf_list); + mix_file_delete (parser->in_file); + g_free (parser); +} + +/* Compile a mix source file */ +static void +update_future_refs_value_ (mix_parser_t *parser, const gchar *name, + mix_short_t value, gboolean remove) +{ + GSList *list = NULL; + gpointer *plist = (gpointer *)(&list); + gpointer key; + + g_assert (parser != NULL && name != NULL); + if ( g_hash_table_lookup_extended (parser->future_refs, name, &key, plist) ) + { + GSList *tmp = list; + ins_node_ *node; + while ( tmp != NULL ) + { + node = + (ins_node_ *)g_tree_lookup (parser->ins_table, tmp->data); + g_assert (node); + if (mix_get_ins_address (node->ins) == 1) { + value = mix_short_negative (value); + node->ins = mix_word_set_field (node->ins, + MIX_WORD_ZERO, + mix_fspec_new (1,2)); + } + mix_word_add_address (node->ins, value); + g_tree_insert (parser->ins_table, tmp->data, (gpointer)node); + tmp = g_slist_next (tmp); + } + if (remove) { + g_hash_table_remove (parser->future_refs, name); + g_free (key); + } + g_slist_free (list); + } +} + +#define update_future_refs_(parser,name,rem) \ + update_future_refs_value_(parser, name, (parser)->loc_count, rem) + +static void +update_ls_ (gpointer symbol, gpointer value, gpointer parser) +{ /* add an instruction on current location and update refs to it */ + mix_ins_t ins; + mix_word_t w = (mix_word_t) GPOINTER_TO_UINT (value); + mix_parser_t *par = (mix_parser_t *) parser; + + mix_word_to_ins_uncheck (w, ins); + update_future_refs_ (par, (const gchar *)symbol, TRUE); + mix_parser_add_ins (par, &ins, 0); + par->loc_count++; +} + +static gboolean +undef_warning_ (gpointer symbol, gpointer value, gpointer data) +{ + mix_parser_t *parser = (mix_parser_t *)data; + const gchar *name = (const gchar *)symbol; + mix_ins_t ins; + + mix_parser_log_error (parser, MIX_PERR_UNDEF_SYM, 0, name, TRUE); + + mix_word_to_ins_uncheck (MIX_WORD_ZERO, ins); + update_future_refs_ (parser, name, FALSE); + mix_parser_add_ins (parser, &ins, 0); + mix_symbol_table_insert (parser->symbol_table, name, + mix_short_to_word_fast (parser->loc_count)); + + parser->loc_count++; + return TRUE; +} + +mix_parser_err_t +mix_parser_compile (mix_parser_t *parser) +{ + g_return_val_if_fail (parser != NULL, MIX_PERR_INTERNAL); + g_return_val_if_fail (parser->in_file != NULL, MIX_PERR_NOIN); + g_return_val_if_fail (parser->symbol_table != NULL, MIX_PERR_INTERNAL); + g_return_val_if_fail (parser->future_refs != NULL, MIX_PERR_INTERNAL); + g_return_val_if_fail (parser->ins_table != NULL, MIX_PERR_INTERNAL); + + parser->status = mix_flex_scan (parser); + + if ( parser->status == MIX_PERR_OK ) + { + parser->loc_count = parser->end; + mix_symbol_table_foreach (parser->ls_table, update_ls_, (gpointer)parser); + if ( g_hash_table_size (parser->future_refs) > 0) + { + g_hash_table_foreach_remove (parser->future_refs, + undef_warning_, (gpointer)parser); + } + } + + return parser->status; +} + +/* Write a compiled source to a code file */ +struct write_code_context_ +{ + mix_code_file_t *file; + mix_parser_t *parser; +}; + +static gint +write_code_ (gpointer address, gpointer ins_node, gpointer context) +{ + mix_ins_desc_t desc; + struct write_code_context_ *cntx = (struct write_code_context_ *)context; + desc.ins = ((ins_node_ *)ins_node)->ins; + desc.lineno = ((ins_node_ *)ins_node)->lineno; + desc.address = (mix_address_t)GPOINTER_TO_UINT (address); + if ( mix_code_file_write_ins (cntx->file, &desc) ) + return FALSE; + else + { + cntx->parser->status = MIX_PERR_NOWRITE; + return TRUE; + } +} + +mix_parser_err_t +mix_parser_write_code (mix_parser_t *parser, const gchar *code_file, + gboolean debug) +{ + struct write_code_context_ context; + const gchar *cfname = (code_file) ? + code_file : mix_file_base_name (parser->in_file); + gchar *source_path; + + g_return_val_if_fail (parser != NULL, MIX_PERR_INTERNAL); + if (parser->status != MIX_PERR_OK ) return parser->status; + context.parser = parser; + if (!g_path_is_absolute (mix_file_base_name (parser->in_file))) + { + gchar *dir = g_get_current_dir (); + source_path = g_strconcat (dir, G_DIR_SEPARATOR_S, + mix_file_base_name (parser->in_file), NULL); + g_free (dir); + } + else + source_path = g_strdup (mix_file_base_name (parser->in_file)); + + context.file = mix_code_file_new_write (cfname, parser->start, source_path, + debug, parser->symbol_table); + g_free (source_path); + + if (context.file == NULL) return MIX_PERR_NOOUT; + g_tree_foreach (parser->ins_table, write_code_, (gpointer)&context); + mix_code_file_delete (context.file); + return parser->status; +} + +/* Produce a listing file summarising the compilation */ +typedef struct +{ + FILE *file; + mix_parser_t *parser; +} listing_context_t; + +static gint +write_listing_ (gpointer address, gpointer ins, gpointer context) +{ + guint k; + FILE *file = ((listing_context_t *)context)->file; + mix_parser_t *parser = ((listing_context_t *)context)->parser; + guint end = parser->end; + ins_node_ *ins_node = (ins_node_ *)ins; + mix_ins_t instruct; + + fprintf (file, "%03d %05d %s ", + ins_node->lineno, + GPOINTER_TO_INT (address), + mix_word_is_negative (ins_node->ins)? "-":"+"); + for ( k = 1; k < 6; ++k ) + fprintf (file, "%02d ", mix_word_get_byte (ins_node->ins, k)); + + if (g_slist_find (parser->con_list, GUINT_TO_POINTER (ins_node->lineno)) + || GPOINTER_TO_UINT (address) >= end) + fprintf (file, "\tCON\t%04d\n", (int)(ins_node->ins)); + else if (g_slist_find (parser->alf_list, GUINT_TO_POINTER (ins_node->lineno))) + { + size_t i; + fprintf (file, "\tALF\t\""); + for (i = 1; i < 6; ++i) + fprintf (file, "%c", + mix_char_to_ascii (mix_byte_to_char + (mix_word_get_byte (ins_node->ins, i)))); + fprintf (file, "\"\n"); + } + else if (GPOINTER_TO_UINT (address) < end) + { + gchar *instext = NULL; + mix_ins_id_t id = mix_word_to_ins (ins_node->ins, &instruct); + if (id != mix_INVALID_INS) + instext = mix_ins_to_string (&instruct); + fprintf (file, _("\t%s\n"), instext? instext : _("UNKNOWN")); + if (instext) g_free (instext); + } + else + g_assert_not_reached (); + + return FALSE; +} + +mix_parser_err_t +mix_parser_write_listing (mix_parser_t *parser, const gchar *list_file) +{ + mix_file_t *mfile; + const gchar *name; + listing_context_t context; + static const char *sep = + "-----------------------------------------------------------------\n"; + + g_return_val_if_fail (parser != NULL, MIX_PERR_INTERNAL); + if (parser->status != MIX_PERR_OK ) return parser->status; + name = (list_file) ? list_file : mix_file_base_name (parser->in_file); + mfile = mix_file_new_with_def_ext (name, mix_io_WRITE, MIX_LIST_DEFEXT); + if ( mfile == NULL ) return MIX_PERR_NOOUT; + context.file = mix_file_to_FILE (mfile); + context.parser = parser; + fprintf (context.file, _("*** %s%s: compilation summary ***\n\n"), + mix_file_base_name (parser->in_file), + mix_file_extension (parser->in_file)); + fputs (sep, context.file); + fputs ( _("Src Address Compiled word Symbolic rep\n"), + context.file); + fputs (sep, context.file); + g_tree_foreach (parser->ins_table, write_listing_, (gpointer)(&context)); + fputs (sep, context.file); + fprintf (context.file, _("\n*** Start address:\t%d\n*** End address:\t%d\n"), + mix_short_magnitude (parser->start), + mix_short_magnitude (parser->end)); + fprintf (context.file, _("\n*** Symbol table\n")); + mix_symbol_table_print (parser->symbol_table, MIX_SYM_ROWS, + context.file, TRUE); + fprintf (context.file, _("\n*** End of summary ***\n")); + mix_file_delete (mfile); + return parser->status; +} + +/* load a virtual machine's memory with the contents of a compiled file */ +static gint +load_vm_ (gpointer address, gpointer ins, gpointer vm) +{ + mix_vm_set_addr_contents ((mix_vm_t*)vm, + (mix_address_t)GPOINTER_TO_UINT (address), + ((ins_node_ *)ins)->ins); + return FALSE; +} + +mix_parser_err_t +mix_parser_load_vm (const mix_parser_t *parser, mix_vm_t *vm) +{ + g_return_val_if_fail (parser != NULL, MIX_PERR_INTERNAL); + g_return_val_if_fail (vm != NULL, MIX_PERR_INTERNAL); + g_return_val_if_fail (parser->status == MIX_PERR_OK, parser->status); + mix_vm_reset (vm); + g_tree_foreach (parser->ins_table, load_vm_, (gpointer)vm); + mix_vm_set_start_addr (vm, parser->start); + return parser->status; +} + + + +/*------------ xmiparser.h functions -------------------------------------*/ +/* functions to manipulate mix_parser_t during compilation */ + + +/* symbol table */ +/* Define a new symbol with value equal to the current loc_count + * and update future refs to this symbol + */ +mix_parser_err_t +mix_parser_define_symbol_here (mix_parser_t *parser, const gchar *name) +{ + mix_word_t value = mix_short_to_word_fast (parser->loc_count); + return mix_parser_define_symbol_value (parser, name, value); +} + +mix_parser_err_t +mix_parser_define_symbol_value (mix_parser_t *parser, const gchar *name, + mix_word_t value) +{ + g_assert (parser != NULL && name != NULL); + + switch (mix_symbol_table_add (parser->symbol_table, name, value)) + { + case MIX_SYM_OK: + if ( parser->status == MIX_PERR_NOCOMP ) + update_future_refs_value_ (parser, name, value, TRUE); + return MIX_PERR_OK; + case MIX_SYM_LONG: return MIX_PERR_LONG_SYMBOL; + case MIX_SYM_DUP: return MIX_PERR_DUP_SYMBOL; + default: return MIX_PERR_INTERNAL; + } +} + +/* Obtain the value of a symbol */ +void +mix_parser_set_future_ref (mix_parser_t *parser, const gchar *name) +{ + const gchar *nname = name; + GSList *list; + + g_assert (parser != NULL && name != NULL); + + if ( parser->status == MIX_PERR_NOCOMP ) + { + list = g_hash_table_lookup (parser->future_refs, name); + if ( list == NULL ) nname = g_strdup (name); + list = g_slist_prepend (list, (gpointer)((guint)parser->loc_count)); + g_hash_table_insert (parser->future_refs, (gpointer)nname, list); + } +} + +/* Redefine the value of a local symbol as the current loc_count */ +void +mix_parser_manage_local_symbol (mix_parser_t *parser, const gchar *name, + mix_short_t value) +{ + gchar ref[3]; + ref[2] = 0; + + g_assert (parser != NULL && name != NULL); + g_assert (strlen(name) == 2); + + switch (name[1]) + { + case 'f': case 'F': + mix_parser_set_future_ref (parser, name); + break; + case 'h': case 'H': + ref[0] = name[0]; + ref[1] = 'F'; + if ( parser->status == MIX_PERR_NOCOMP ) + update_future_refs_value_ (parser, ref, value, TRUE); + ref[1] = 'B'; + mix_symbol_table_insert (parser->symbol_table, ref, + mix_short_to_word_fast (value)); + break; + default: + return; + } +} + +/* Literal strings symbols */ +void +mix_parser_define_ls (mix_parser_t *parser, mix_word_t value) +{ + gchar *name = g_strdup_printf ("%05d", parser->cur_ls++); + mix_symbol_table_add (parser->ls_table, name, value); + mix_parser_set_future_ref (parser, name); + g_free (name); +} + +/* Compilation */ +static void +add_raw_ (mix_parser_t *parser, mix_word_t word, guint lineno) +{ + if ( parser->status == MIX_PERR_NOCOMP || parser->status == MIX_PERR_OK ) + { + ins_node_ *node = g_new (ins_node_, 1); + node->ins = word; + node->lineno = lineno; + g_tree_insert (parser->ins_table, (gpointer)((guint)parser->loc_count), + (gpointer)node); + } +} + +void +mix_parser_add_ins (mix_parser_t *parser, const mix_ins_t *new_ins, + guint lineno) +{ + g_assert (parser != NULL && new_ins != NULL); + add_raw_ (parser, mix_ins_to_word_uncheck (*new_ins), lineno); +} + +void +mix_parser_add_raw (mix_parser_t *parser, mix_word_t word, guint lineno, + gboolean is_con) +{ + g_assert (parser != NULL); + add_raw_ (parser, word, lineno); + if ( parser->status == MIX_PERR_NOCOMP || parser->status == MIX_PERR_OK ) + { + if (is_con) + parser->con_list = g_slist_append (parser->con_list, + GUINT_TO_POINTER (lineno)); + else + parser->alf_list = g_slist_append (parser->alf_list, + GUINT_TO_POINTER (lineno)); + } +} + +/* Error handling */ +void +mix_parser_log_error (mix_parser_t *parser, mix_parser_err_t error, + gint lineno, const gchar *comment, gboolean warn) +{ + g_assert (parser != NULL); + if ( warn ) + parser->warn_count += 1; + else + { + parser->err_count += 1; + parser->err_line = lineno; + parser->status = error; + } + + fprintf (stderr, "%s%s:%d: %s: %s", + mix_file_base_name (parser->in_file), + mix_file_extension (parser->in_file), + lineno, warn ? _("warning"):_("error"), _(ERR_MESSAGE_[error])); + + if (comment != NULL) + fprintf (stderr, ": %s\n", comment); + else + fputs ("\n", stderr); +} + diff --git a/mixlib/mix_parser.h b/mixlib/mix_parser.h new file mode 100644 index 0000000..2335fb1 --- /dev/null +++ b/mixlib/mix_parser.h @@ -0,0 +1,111 @@ +/* -*-c-*- ---------------- mix_parser.h : + * Declarations for mix_parser_t, which compiles a source file into + * a mix code file. + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2003, 2004 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. + * + */ + + +#ifndef MIX_PARSER_H +#define MIX_PARSER_H + +#include <glib.h> +#include "mix_vm.h" + +/* The parser type */ +typedef struct mix_parser_t mix_parser_t; + +/* Create/destroy a mix_parser */ +extern mix_parser_t * +mix_parser_new (const gchar *in_file); + +extern void +mix_parser_delete (mix_parser_t *parser); + +/* Access source file name */ +extern const gchar * +mix_parser_src_file_base_name (const mix_parser_t *parser); + +extern const gchar * +mix_parser_src_file_extension (const mix_parser_t *parser); + +/* Compile a mix source file */ +/* compilation errors */ +typedef enum { + MIX_PERR_OK, /* no error */ + MIX_PERR_NOCOMP, /* file not yet compiled */ + MIX_PERR_INTERNAL, /* internal error */ + MIX_PERR_NOIN, /* unable to open input file */ + MIX_PERR_NOOUT, /* unable to open output file */ + MIX_PERR_UNEX_EOF, /* unexpected end of file */ + MIX_PERR_INV_LOC, /* invalid loc field */ + MIX_PERR_DUP_SYMBOL, /* duplicated symbol */ + MIX_PERR_LONG_SYMBOL, /* symbol name too long */ + MIX_PERR_NOOP, /* missing op field */ + MIX_PERR_UNEX_LOC, /* unexpected location symbol */ + MIX_PERR_INV_ADDRESS, /* invalid address field */ + MIX_PERR_INV_IDX, /* invalid index field */ + MIX_PERR_INV_FSPEC, /* invalid fspec */ + MIX_PERR_INV_OP, /* invalid operation */ + MIX_PERR_INV_EXPR, /* invalid expression */ + MIX_PERR_UNDEF_SYM, /* undefined symbol */ + MIX_PERR_MIS_PAREN, /* mismatched parenthesis */ + MIX_PERR_UNEX_FSPEC, /* unexpected f-spec */ + MIX_PERR_MIS_SYM, /* missing symbol name */ + MIX_PERR_SYM_INS, /* symbol has the same name as instruction */ + MIX_PERR_NOWRITE, /* failed code write */ + MIX_PERR_SHORT_ALF, /* short ALF operand */ + MIX_PERR_LONG_ALF, /* too long ALF operand */ + MIX_PERR_UNQUOTED_ALF /* unquoted ALF operand */ +} mix_parser_err_t; + +extern const gchar * +mix_parser_err_string (mix_parser_err_t error); + +extern mix_parser_err_t +mix_parser_compile (mix_parser_t *parser); + +extern guint +mix_parser_warning_count (const mix_parser_t *parser); + +extern guint +mix_parser_err_count (const mix_parser_t *parser); + +/* Write the compilation result to a code file with the given name. + code_file is completed, if required, with the requisite extension; + if code_file == NULL [source_file_name].[extension] is used. + If debug == TRUE, debug information is written. +*/ +extern mix_parser_err_t +mix_parser_write_code (mix_parser_t *parser, const gchar *code_file, + gboolean debug); + +/* Write a "canonical" listing of a compiled source, i.e. a source + file with all symbols substituted by their actual values after + compilation. +*/ +extern mix_parser_err_t +mix_parser_write_listing (mix_parser_t *parser, const gchar *list_file); + +/* load a virtual machine's memory with the contents of a compiled file */ +extern mix_parser_err_t +mix_parser_load_vm (const mix_parser_t *parser, mix_vm_t *vm); + + +#endif /* MIX_PARSER_H */ + diff --git a/mixlib/mix_predicate.c b/mixlib/mix_predicate.c new file mode 100644 index 0000000..2143757 --- /dev/null +++ b/mixlib/mix_predicate.c @@ -0,0 +1,215 @@ +/* -*-c-*- -------------- mix_predicate.c : + * Implementation of the functions declared in mix_predicate.h + * ------------------------------------------------------------------ + * $Id: mix_predicate.c,v 1.6 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2002 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 "mix_vm.h" +#include "mix_predicate.h" + +/* predicate data */ +typedef union pred_data_t +{ + mix_word_t regA; + mix_word_t regX; + mix_short_t regI; + mix_cmpflag_t cmp; + gboolean over; + mix_word_t mem; +} pred_data_t; + +/* the predicate function type */ +typedef gboolean (*mix_predicate_fun_t) (mix_predicate_t *pred, + const mix_vm_t *vm); + +/* the predicate type */ +struct mix_predicate_t +{ + mix_predicate_type_t type; + pred_data_t data; + guint control; +}; + +/* predicate funcs */ +static gboolean +pred_func_rA (mix_predicate_t *pred, const mix_vm_t *vm) +{ + mix_word_t val = mix_vm_get_rA (vm); + if (pred->data.regA == val) return FALSE; + pred->data.regA = val; + return TRUE; +} + +static gboolean +pred_func_rX (mix_predicate_t *pred, const mix_vm_t *vm) +{ + mix_word_t val = mix_vm_get_rX (vm); + if (pred->data.regX == val) return FALSE; + pred->data.regX = val; + return TRUE; +} + +static gboolean +pred_func_rI (mix_predicate_t *pred, const mix_vm_t *vm) +{ + mix_short_t val = (pred->control == 0) ? mix_vm_get_rJ (vm) + : mix_vm_get_rI (vm, pred->control); + if (pred->data.regI == val) return FALSE; + pred->data.regI = val; + return TRUE; +} + +static gboolean +pred_func_mem (mix_predicate_t *pred, const mix_vm_t *vm) +{ + mix_word_t val = + mix_vm_get_addr_contents (vm, (mix_address_t)pred->control); + if (pred->data.mem == val) return FALSE; + pred->data.mem = val; + return TRUE; +} + +static gboolean +pred_func_cmp (mix_predicate_t *pred, const mix_vm_t *vm) +{ + mix_cmpflag_t val = mix_vm_get_cmpflag (vm); + if (pred->data.cmp == val) return FALSE; + pred->data.cmp = val; + return TRUE; +} + +static gboolean +pred_func_over (mix_predicate_t *pred, const mix_vm_t *vm) +{ + gboolean val = mix_vm_get_overflow (vm); + if (pred->data.over == val) return FALSE; + pred->data.over = val; + return TRUE; +} + +static mix_predicate_fun_t PRED_FUNCS_[] = { + pred_func_rA, pred_func_rX, pred_func_rI, pred_func_rI, pred_func_rI, + pred_func_rI, pred_func_rI, pred_func_rI, pred_func_rI, + pred_func_over, pred_func_cmp, pred_func_mem +}; + +/* create predicates based on vm status */ +mix_predicate_t * +mix_predicate_new (mix_predicate_type_t type) +{ + mix_predicate_t *result; + g_return_val_if_fail (type <= MIX_PRED_MEM, NULL); + result = g_new (mix_predicate_t, 1); + result->type = type; + result->data.regA = MIX_WORD_ZERO; + if (type >= MIX_PRED_REG_I1 && type <= MIX_PRED_REG_I6) + result->control = 1 + type - MIX_PRED_REG_I1; + else + result->control = 0; + return result; +} + +/* delete a predicate */ +void +mix_predicate_delete (mix_predicate_t *predicate) +{ + g_return_if_fail (predicate != NULL); + g_free (predicate); +} + +/* return the predicate's type */ +mix_predicate_type_t +mix_predicate_get_type (const mix_predicate_t *pred) +{ + g_return_val_if_fail (pred != NULL, MIX_PRED_INVALID); + return pred->type; +} + +/* test a predicate */ +gboolean +mix_predicate_eval(mix_predicate_t *pred, const mix_vm_t *vm) +{ + g_return_val_if_fail (pred != NULL, FALSE); + g_return_val_if_fail (vm != NULL, FALSE); + return PRED_FUNCS_[pred->type] (pred, vm); +} + +/* change mem address of a MIX_PRED_MEM predicate */ +void +mix_predicate_set_mem_address (mix_predicate_t *predicate, + mix_address_t address) +{ + g_return_if_fail (predicate != NULL); + predicate->control = address; +} + +/* get message about predicate evaluation */ +const gchar * +mix_predicate_get_message (const mix_predicate_t *predicate) +{ + enum {SIZE = 256}; + static gchar BUFFER[SIZE]; + static const gchar *CMP_STRINGS[] = { "L", "E", "G"}; + + g_return_val_if_fail (predicate != NULL, NULL); + + switch (predicate->type) + { + case MIX_PRED_REG_A: + g_snprintf (BUFFER, SIZE, _("Register A changed to %s%ld"), + mix_word_is_negative (predicate->data.regA)? "-" : "+", + mix_word_magnitude (predicate->data.regA)); + break; + case MIX_PRED_REG_X: + g_snprintf (BUFFER, SIZE, _("Register X changed to %s%ld"), + mix_word_is_negative (predicate->data.regX)? "-" : "+", + mix_word_magnitude (predicate->data.regX)); + break; + case MIX_PRED_REG_J: + g_snprintf (BUFFER, SIZE, _("Register J changed to %d"), + mix_short_magnitude (predicate->data.regI)); + break; + case MIX_PRED_REG_I1: case MIX_PRED_REG_I2: case MIX_PRED_REG_I3: + case MIX_PRED_REG_I4: case MIX_PRED_REG_I5: case MIX_PRED_REG_I6: + g_snprintf (BUFFER, SIZE, _("Register I%d changed to %s%d"), + predicate->control, + mix_short_is_negative (predicate->data.regI)? "-" : "+", + mix_short_magnitude (predicate->data.regI)); + break; + case MIX_PRED_CMP: + g_snprintf (BUFFER, SIZE, _("Comparison flag changed to %s"), + CMP_STRINGS[predicate->data.cmp]); + break; + case MIX_PRED_OVER: + g_snprintf (BUFFER, SIZE, _("Overflow toggled %s"), + predicate->data.over ? "ON" : "OFF"); + break; + case MIX_PRED_MEM: + g_snprintf (BUFFER, SIZE, _("Memory address %d changed to %s%ld"), + predicate->control, + mix_word_is_negative (predicate->data.mem)? "-" : "+", + mix_word_magnitude (predicate->data.mem)); + break; + default: + g_assert_not_reached (); + } + return BUFFER; +} + diff --git a/mixlib/mix_predicate.h b/mixlib/mix_predicate.h new file mode 100644 index 0000000..903de57 --- /dev/null +++ b/mixlib/mix_predicate.h @@ -0,0 +1,80 @@ +/* -*-c-*- ---------------- mix_predicate.h : + * Predicates and lists of predicates testing vm status. + * ------------------------------------------------------------------ + * Last change: Time-stamp: <2001-07-13 22:32:42 jao> + * ------------------------------------------------------------------ + * Copyright (C) 2001 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. + * + */ + + +#ifndef MIX_PREDICATE_H +#define MIX_PREDICATE_H + +#include "mix.h" +#include "mix_types.h" +#include "mix_ins.h" + +/* the predicate type */ +typedef struct mix_predicate_t mix_predicate_t; + +/* predicate types */ +typedef enum { + MIX_PRED_REG_A, + MIX_PRED_REG_X, + MIX_PRED_REG_J, + MIX_PRED_REG_I1, + MIX_PRED_REG_I2, + MIX_PRED_REG_I3, + MIX_PRED_REG_I4, + MIX_PRED_REG_I5, + MIX_PRED_REG_I6, + MIX_PRED_OVER, + MIX_PRED_CMP, + MIX_PRED_MEM, + MIX_PRED_INVALID +} mix_predicate_type_t; + +/* create predicates based on vm status */ +extern mix_predicate_t * +mix_predicate_new (mix_predicate_type_t type); + +/* delete a predicate */ +extern void +mix_predicate_delete (mix_predicate_t *predicate); + +/* return the predicate's type */ +extern mix_predicate_type_t +mix_predicate_get_type (const mix_predicate_t *pred); + +/* change mem address of a MIX_PRED_MEM predicate */ +extern void +mix_predicate_set_mem_address (mix_predicate_t *predicate, + mix_address_t address); + +/* get message about predicate evaluation */ +extern const gchar * +mix_predicate_get_message (const mix_predicate_t *predicate); + +/* test a predicate */ +#include "mix_vm.h" + +extern gboolean +mix_predicate_eval(mix_predicate_t *pred, const mix_vm_t *vm); + +#endif /* MIX_PREDICATE_H */ + diff --git a/mixlib/mix_predicate_list.c b/mixlib/mix_predicate_list.c new file mode 100644 index 0000000..1b68d35 --- /dev/null +++ b/mixlib/mix_predicate_list.c @@ -0,0 +1,117 @@ +/* -*-c-*- -------------- mix_predicate_list.c : + * Implementation of the functions declared in mix_predicate_list.h + * ------------------------------------------------------------------ + * Last change: Time-stamp: "01/07/18 23:02:26 jao" + * ------------------------------------------------------------------ + * Copyright (C) 2001 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 "mix_predicate_list.h" + +/* the predicate list type */ +struct mix_predicate_list_t +{ + GSList *predicates; + const mix_vm_t *vm; + const mix_predicate_t *last; +}; + +/* a list of predicates */ +mix_predicate_list_t * +mix_predicate_list_new (const mix_vm_t *vm) +{ + mix_predicate_list_t *result; + g_return_val_if_fail (vm != NULL, NULL); + result = g_new (mix_predicate_list_t, 1); + result->predicates = NULL; + result->vm = vm; + result->last = NULL; + return result; +} + +void +mix_predicate_list_delete (mix_predicate_list_t *list) +{ + g_return_if_fail (list != NULL); + g_slist_free (list->predicates); + g_free (list); +} + +/* evaluate the predicate list */ +gboolean +mix_predicate_list_eval (mix_predicate_list_t *list) +{ + GSList *node; + + g_return_val_if_fail (list != NULL, FALSE); + node = list->predicates; + while (node) { + mix_predicate_t *pred = (mix_predicate_t *)(node->data); + if (mix_predicate_eval (pred, list->vm)) + { + list->last = pred; + return TRUE; + } + node = node->next; + } + list->last = NULL; + return FALSE; +} + +/* add/remove predicates to the list */ +void +mix_predicate_list_add (mix_predicate_list_t *list, mix_predicate_t *predicate) +{ + g_return_if_fail (list != NULL); + g_return_if_fail (predicate != NULL); + if (!g_slist_find (list->predicates, predicate)) + list->predicates = g_slist_append (list->predicates, (gpointer)predicate); + (void)mix_predicate_eval (predicate, list->vm); +} + +gboolean +mix_predicate_list_remove (mix_predicate_list_t *list, + mix_predicate_t *predicate) +{ + g_return_val_if_fail (list != NULL, FALSE); + g_return_val_if_fail (predicate != NULL, FALSE); + if (g_slist_find (list->predicates, predicate)) + { + list->predicates = g_slist_remove (list->predicates, predicate); + return TRUE; + } + else + return FALSE; +} + +void +mix_predicate_list_clear (mix_predicate_list_t *list) +{ + g_return_if_fail (list != NULL); + g_slist_free (list->predicates); + list->predicates = NULL; + list->last = NULL; +} + +const mix_predicate_t * +mix_predicate_list_last_true_eval (const mix_predicate_list_t *list) +{ + g_return_val_if_fail (list != NULL, NULL); + return list->last; +} diff --git a/mixlib/mix_predicate_list.h b/mixlib/mix_predicate_list.h new file mode 100644 index 0000000..d651df7 --- /dev/null +++ b/mixlib/mix_predicate_list.h @@ -0,0 +1,64 @@ +/* -*-c-*- ---------------- mix_predicate_list.h : + * A list of predicates. + * ------------------------------------------------------------------ + * Last change: Time-stamp: <01/07/18 23:02:40 jao> + * ------------------------------------------------------------------ + * Copyright (C) 2001 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. + * + */ + + +#ifndef MIX_PREDICATE_LIST_H +#define MIX_PREDICATE_LIST_H + +#include "mix.h" +#include "mix_vm.h" +#include "mix_predicate.h" + +/* the predicate list type */ +typedef struct mix_predicate_list_t mix_predicate_list_t; + +/* create/destroy a list of predicates */ +extern mix_predicate_list_t * +mix_predicate_list_new (const mix_vm_t *vm); + +extern void +mix_predicate_list_delete (mix_predicate_list_t *list); + +/* evaluate the predicate list */ +extern gboolean +mix_predicate_list_eval (mix_predicate_list_t *list); + +extern const mix_predicate_t * +mix_predicate_list_last_true_eval (const mix_predicate_list_t *list); + +#define mix_predicate_list_last_true_eval_type(list) \ + mix_predicate_get_type (mix_predicate_list_last_true_eval (list)) + +/* add/remove predicates to the list */ +extern void +mix_predicate_list_add (mix_predicate_list_t *list, mix_predicate_t *predicate); + +extern gboolean +mix_predicate_list_remove (mix_predicate_list_t *list, + mix_predicate_t *predicate); + +extern void +mix_predicate_list_clear (mix_predicate_list_t *list); + +#endif /* MIX_PREDICATE_LIST_H */ + diff --git a/mixlib/mix_scanner.l b/mixlib/mix_scanner.l new file mode 100644 index 0000000..6150007 --- /dev/null +++ b/mixlib/mix_scanner.l @@ -0,0 +1,581 @@ +/* -*-c-*- -------------- mix_scanner.l : + * Lexical scanner used by mix_parser_t + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2003, 2004 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 <ctype.h> +#include <string.h> + +#include "mix.h" +#include "xmix_parser.h" + +#define YY_DECL mix_parser_err_t mix_flex_scan (mix_parser_t *parser) + +#define RESET() \ + do { \ + mix_ins_fill_from_id (ins, mix_NOP); \ + ins.address = 0; \ + ins.index = 0; \ + nof = FALSE; \ + lsf = FALSE; \ + if (symbol != NULL ) \ + { \ + g_free (symbol); \ + symbol = NULL; \ + } \ + if (lsymbol != NULL) \ + { \ + g_free (lsymbol); \ + lsymbol = NULL; \ + } \ + } while (FALSE) + +#define NEXT() \ + do { \ + if (lsymbol != NULL) \ + mix_parser_manage_local_symbol (parser,lsymbol, \ + loc); \ + parser->loc_count++; \ + RESET (); \ + ++lineno; \ + BEGIN (INITIAL); \ + } while (FALSE) + +#define ADD_INS() \ + do { \ + mix_parser_add_ins (parser, &ins, lineno); \ + NEXT (); \ + } while (FALSE) + +#define ADD_RAW(value,is_con) \ + do { \ + mix_parser_add_raw (parser, value, lineno, is_con); \ + NEXT (); \ + } while (FALSE) + + +#define ENTER_EVAL() \ + do { \ + if (yytext[0] != '*') \ + { \ + expr_val = MIX_WORD_ZERO; \ + yyless (0); \ + } \ + else \ + { \ + expr_val = mix_short_to_word_fast (parser->loc_count); \ + yyless (1); \ + } \ + yy_push_state (EVAL); \ + } while (FALSE) + +#define ENTER_WEVAL(s) \ + do { \ + wexpr_val = MIX_WORD_ZERO; \ + wexpr_val_tmp = MIX_WORD_ZERO; \ + is_fp = FALSE; \ + yyless (s); \ + yy_push_state (WEVAL); \ + } while (FALSE) + +#define RETURN_ERROR(error, comment) \ + do { \ + char c; \ + mix_parser_log_error (parser,error,lineno,comment,FALSE); \ + while ( (c = input ()) != '\n' && c != EOF ) ; \ + if ( c == EOF ) return error; else ++lineno; \ + RESET (); \ + BEGIN (INITIAL); \ + } while (FALSE) + + +static mix_word_t eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y); + +static void unput_word_ (mix_word_t word); + +%} + +%option nomain +%option caseless +%option array +%option stack +%option noyywrap +%option noyy_top_state +%option noreject +%option outfile="lex.yy.c" + +%s LOC +%s OP +%s ADDRESS +%s INDEX +%s FSPEC +%s EVAL +%s WEVAL +%s ORIG +%s CON +%s EQU +%s END + +ws [ \t] +digit [0-9] +letter [A-Z] +number [+-]?{digit}+ +mixchar [0-9A-Z .,'')(+*/=$<>@;:\-] +locsymbol {digit}H +flocsymbol {digit}F +blocsymbol {digit}B +symbol {digit}*{letter}+[A-Z0-9]* +binop "+"|"-"|"*"|"/"|"//"|":" +atexpr {digit}+|{symbol}|\* +expr [+-]?{atexpr}({binop}{1}{atexpr})* +fpart \({expr}\) +wexpr {expr}({fpart})?(,{expr}({fpart})?)* + + +%% + +%{ + + mix_ins_t ins; + gboolean nof = FALSE, is_fp = FALSE, end = FALSE, lsf = FALSE; + mix_word_t expr_val = MIX_WORD_ZERO, wexpr_val = MIX_WORD_ZERO, + wexpr_val_tmp = MIX_WORD_ZERO; + gchar *symbol = NULL, *lsymbol = NULL; + mix_address_t loc = MIX_SHORT_ZERO; + guint lineno = 1; + mix_ins_fill_from_id (ins, mix_NOP); + ins.address = 0; + ins.index = 0; + parser->err_line = 0; +#ifdef FLEX_DEBUG + yy_flex_debug = getenv("FLEX_DEBUG"); +#endif + yyin = mix_file_to_FILE (parser->in_file); + +%} + + +<*><<EOF>> { + mix_parser_log_error (parser, MIX_PERR_UNEX_EOF, lineno, NULL, FALSE); + return MIX_PERR_UNEX_EOF; +} + +<INITIAL>{ + ^\*.* /* eat comments */ + . { + if ( end ) return parser->status; + yyless (0); + BEGIN (LOC); + } + \n { + ++lineno; + if ( end ) return parser->status; + } +} + +<LOC>{ + {ws}+ BEGIN (OP); /* LOC field is empty */ + {locsymbol} { /* manage local symbol */ + loc = get_ploc_ (parser); + lsymbol = g_strdup (yytext); + if ( lsymbol == NULL ) { + mix_parser_log_error (parser, MIX_PERR_INTERNAL, lineno, NULL, FALSE); + return MIX_PERR_INTERNAL; + } + BEGIN (OP); + } + {locsymbol}/({ws}+EQU) {/* local symbol with value */ + loc = get_ploc_ (parser); + symbol = g_strdup (yytext); + lsymbol = g_strdup (yytext); + if ( symbol == NULL || lsymbol == NULL) { + mix_parser_log_error (parser, MIX_PERR_INTERNAL, lineno, NULL, FALSE); + return MIX_PERR_INTERNAL; + } + symbol[1] = 'B'; /* this will be referred as nB afterwards */ + BEGIN (OP); + } + {flocsymbol}|{blocsymbol} RETURN_ERROR (MIX_PERR_UNEX_LOC, yytext); + {symbol}/({ws}+EQU) { /* store symbol name for future definition */ + symbol = g_strdup (yytext); + if ( symbol == NULL ) { + mix_parser_log_error (parser, MIX_PERR_INTERNAL, lineno, NULL, FALSE); + return MIX_PERR_INTERNAL; + } + BEGIN (OP); + } + {symbol} { /* define a new symbol */ + mix_parser_err_t err; + if ( mix_get_id_from_string (yytext) != mix_INVALID_INS ) + mix_parser_log_error (parser, MIX_PERR_SYM_INS, lineno, yytext, TRUE); + if ( (err = mix_parser_define_symbol_here (parser,yytext)) != MIX_PERR_OK ) + mix_parser_log_error (parser, err, lineno, yytext, FALSE); + BEGIN (OP); + } + . RETURN_ERROR (MIX_PERR_INV_LOC, yytext); + \n ++lineno; /* empty line */ +} + +<OP>{ + {ws}+ /* eat leading whitespace */ + \n RETURN_ERROR (MIX_PERR_NOOP, NULL); + ORIG{ws}+ BEGIN (ORIG); + CON{ws}+ BEGIN (CON); + EQU{ws}+ BEGIN (EQU); + END{ws}+ BEGIN (END); + ALF{ws}+\"{mixchar}{0,5}\"{ws}+.*\n | + ALF{ws}+\"{mixchar}{0,5}\"{ws}*\n { + mix_byte_t bytes[5]; + mix_word_t value; + guint k, j = 4; + + while ( yytext[j++] != '\"' ) ; + for ( k = j; k < 5+j && yytext[k] != '\"'; ++k ) + bytes[k-j] = mix_ascii_to_char (yytext[k]); + if ( k-j < 5 ) + { + mix_parser_log_error (parser, MIX_PERR_SHORT_ALF, lineno, NULL, TRUE); + /* Fill with spaces */ + for (; k < 5+j; k++) + bytes[k-j] = mix_ascii_to_char (' '); + } + else if ( yytext[k] != '\"' ) + mix_parser_log_error (parser, MIX_PERR_LONG_ALF, lineno, NULL, TRUE); + + value = mix_bytes_to_word (bytes, 5); + ADD_RAW (value, FALSE); + } + ALF{ws}*\n { + mix_byte_t bytes[5]; + memset (bytes, mix_ascii_to_char (' '), 5); + mix_word_t value = mix_bytes_to_word (bytes, 5); + ADD_RAW (value, FALSE); + } + ALF{ws}+({mixchar}{1,5}) { + mix_byte_t bytes[5]; + mix_word_t value; + int i, n; + for (n = 3; n < yyleng; n++) + if (!isspace (yytext[n])) + break; + + for (i = 0; i < 5 && n < yyleng; i++, n++) + bytes[i] = mix_ascii_to_char (yytext[n]); + + for (; i < 5; i++) + bytes[i] = mix_ascii_to_char (' '); + + value = mix_bytes_to_word (bytes, 5); + ADD_RAW (value, FALSE); + } + /* ALF " " */ + [A-Z0-9]+{ws}*/\n | + [A-Z0-9]+{ws}+ { + mix_ins_id_t id = mix_get_id_from_string (g_strchomp (yytext)); + if ( id == mix_INVALID_INS ) + mix_parser_log_error (parser, MIX_PERR_INV_OP, lineno, yytext, FALSE); + else { + mix_ins_fill_from_id (ins, id); + nof = mix_ins_id_is_extended (id); + } + BEGIN (ADDRESS); + } + {expr} RETURN_ERROR (MIX_PERR_INV_OP, yytext); + . RETURN_ERROR (MIX_PERR_INV_OP, yytext); +} + + +<ORIG>{ + {number}{ws}*\n | + {number}{ws}+.*\n { + mix_word_t value = mix_word_new (atol (yytext)); + parser->loc_count = mix_word_to_short_fast (value); + ++lineno; + BEGIN (INITIAL); + } +} + +<CON>{ + {number}{ws}*\n | + {number}{ws}+.*\n { + mix_word_t value = mix_word_new (atol (yytext)); + ADD_RAW (value, TRUE); + } +} + +<EQU>{ + {number}{ws}*\n | + {number}{ws}+.*\n { + mix_word_t value; + gint def; + if ( symbol == NULL ) RETURN_ERROR (MIX_PERR_MIS_SYM, NULL); + value = mix_word_new (atol (yytext)); + def = mix_parser_define_symbol_value (parser, symbol, value); + if ( def == MIX_SYM_DUP ) RETURN_ERROR (MIX_PERR_DUP_SYMBOL, symbol); + if ( def == MIX_SYM_LONG ) RETURN_ERROR (MIX_PERR_LONG_SYMBOL, symbol); + ++lineno; + BEGIN (INITIAL); + } +} + +<END>{ + {number}{ws}*\n | + {number}{ws}+.*\n { + parser->start = mix_short_new (atol (yytext)); + parser->end = parser->loc_count; + end = TRUE; + if ( parser->status == MIX_PERR_NOCOMP ) parser->status = MIX_PERR_OK; + return parser->status; + } +} + +<ORIG,CON,EQU,END>{ + {wexpr} ENTER_WEVAL (0); + . RETURN_ERROR (MIX_PERR_INV_OP, yytext); +} + + +<ADDRESS,INDEX,FSPEC>{locsymbol} RETURN_ERROR (MIX_PERR_UNEX_LOC, yytext); + +<ADDRESS>{ + =/[+-]?{number}= lsf = TRUE; + =/{expr}= lsf = TRUE; + ={wexpr}= { lsf = TRUE; ENTER_WEVAL (1); } + [+-]?{number}={ws}*\n | + [+-]?{number}={ws}+.*\n { + if (!lsf) RETURN_ERROR (MIX_PERR_INV_ADDRESS, yytext); + mix_parser_define_ls (parser, mix_word_new (atol (yytext))); + lsf = FALSE; + ADD_INS (); + } + [+-]?{number}=[(,] { + int pos = yyleng - 3; + if (!lsf) RETURN_ERROR (MIX_PERR_INV_ADDRESS, yytext); + unput (yytext[yyleng - 1]); + while (pos >= 0) { + unput (yytext[pos]); + --pos; + } + } + [+-]?{number}{ws}+.*\n | + [+-]?{number}[(,\n] { + ins.address = mix_short_new (atol (yytext)); + switch ( yytext[yyleng-1] ) { + case '(' : BEGIN (FSPEC); break; + case ',' : BEGIN (INDEX); break; + case '\n' : ADD_INS (); break; + default: g_assert_not_reached (); + } + } + ([+-]?{symbol})/[(,\n\t ] { + gboolean neg = (yytext[0] == '-'); + const gchar *s = (neg || yytext[0] == '+')? yytext+1 : yytext; + if ( !mix_symbol_table_is_defined (parser->symbol_table, s) ) + { + mix_parser_set_future_ref (parser, s); + if (neg) + mix_parser_log_error (parser, MIX_PERR_UNDEF_SYM, lineno, s, TRUE); + unput (neg? '1':'0'); + } + else + { + mix_word_t v = mix_symbol_table_value (parser->symbol_table, s); + if ( neg ) mix_word_reverse_sign (v); + unput_word_ (v); + } + } + {expr}/[(,=\n\t ] ENTER_EVAL (); + \n ADD_INS (); + . RETURN_ERROR (MIX_PERR_INV_ADDRESS, yytext); +} + + +<INDEX>{ + {number}[\n(\t ] { + int end = yytext[yyleng-1]; + ins.index = mix_byte_new (atol (yytext)); + if ( end == '\n' ) + ADD_INS (); + else if ( end == '(' ) + BEGIN (FSPEC); + else + { /* eat rest of line (comment) */ + while ( (end = input()) != '\n' && end != EOF ) ; + if ( end == '\n' ) ADD_INS (); + else RETURN_ERROR (MIX_PERR_UNEX_EOF, NULL); + } + } + {expr}/[\n(\t ] ENTER_EVAL (); + \n { + mix_parser_log_error (parser, MIX_PERR_INV_IDX, lineno++, NULL, FALSE); + RESET (); + BEGIN (INITIAL); + } + . RETURN_ERROR (MIX_PERR_INV_IDX, yytext); +} + +<FSPEC>{ + {number}")"(({ws}+.*\n)|\n) { + glong val = atol (yytext); + if ( val < 0 || val > MIX_BYTE_MAX ) + RETURN_ERROR (MIX_PERR_INV_FSPEC, NULL); + if ( ins.opcode != mix_opMOVE + && ( ins.opcode < mix_opJBUS || ins.opcode > mix_opJXx ) + && !mix_fspec_is_valid (mix_byte_new (val)) ) + RETURN_ERROR (MIX_PERR_INV_FSPEC, NULL); + if ( nof ) + mix_parser_log_error (parser, MIX_PERR_INV_FSPEC, + lineno, _("ignored"), TRUE); + else + { + ins.fspec = mix_byte_new (val); + if (lsf) + { + mix_parser_define_ls (parser, + mix_short_to_word_fast (ins.address)); + ins.address = MIX_WORD_ZERO; + lsf = FALSE; + } + ADD_INS (); + } + } + {expr}/")" { + ENTER_EVAL (); + } + . RETURN_ERROR (MIX_PERR_INV_FSPEC, yytext); +} + + +<EVAL>{ + {binop}{digit}+ { + const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1; + mix_word_t value = mix_word_new (atol (s)); + expr_val = eval_binop_ (yytext, expr_val, value); + } + {binop}{symbol} { + const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1; + if ( !mix_symbol_table_is_defined (parser->symbol_table, s) ) { + mix_parser_log_error (parser, MIX_PERR_UNDEF_SYM, lineno, s, FALSE); + yy_pop_state (); + } + expr_val = eval_binop_ (yytext, expr_val, + mix_symbol_table_value (parser->symbol_table, s)); + } + {binop}"*" { + expr_val = eval_binop_ (yytext, expr_val, + mix_short_to_word_fast (parser->loc_count)); + } + "*" unput_word_ (mix_short_to_word_fast (parser->loc_count)); + {number} expr_val = mix_word_new (atol (yytext)); + {symbol} { + if ( !mix_symbol_table_is_defined (parser->symbol_table, yytext) ) { + mix_parser_log_error (parser, MIX_PERR_UNDEF_SYM, lineno, yytext, FALSE); + yy_pop_state (); + } + expr_val = mix_symbol_table_value (parser->symbol_table, yytext); + } + [,)(=\n\t ] unput (yytext[0]); unput_word_ (expr_val); yy_pop_state (); + . RETURN_ERROR (MIX_PERR_INV_EXPR, yytext); +} + +<WEVAL>{ + {number}"(" { + is_fp = TRUE; + wexpr_val_tmp = mix_word_new (atol (yytext)); + } + {number}")" { + glong val = atol (yytext); + if ( !is_fp ) { + mix_parser_log_error (parser, MIX_PERR_MIS_PAREN, lineno, NULL, FALSE); + yy_pop_state (); + } + if ( val < 0 || val > MIX_BYTE_MAX + || !mix_fspec_is_valid (mix_byte_new (val)) ) { + mix_parser_log_error (parser, MIX_PERR_INV_FSPEC, lineno, NULL, FALSE); + yy_pop_state (); + } + is_fp = FALSE; + wexpr_val = mix_word_store_field (mix_byte_new (val), wexpr_val_tmp, + wexpr_val); + } + {number}/[,()\n\t ] wexpr_val = mix_word_new (atol (yytext)); + {expr}/[,()\n\t ] ENTER_EVAL (); + ,/{expr} /* eat comma if followed by expression */ + [=\n\t ] { /* ok if not inside an f-part */ + if ( is_fp ) { + mix_parser_log_error (parser, MIX_PERR_MIS_PAREN, lineno, NULL, FALSE); + yy_pop_state (); + } + unput (yytext[yyleng-1]); + unput_word_ (wexpr_val); + yy_pop_state (); + } + . RETURN_ERROR (MIX_PERR_INV_EXPR, NULL); +} + +%% + +static mix_word_t +eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y) +{ + mix_word_t result = MIX_WORD_ZERO; + switch (op[0]) + { + case '+': + result = mix_word_add (x,y); + break; + case '-': + result = mix_word_sub (x,y); + break; + case '*': + mix_word_mul (x, y, NULL, &result); + break; + case ':': + { + mix_word_t a; + mix_word_mul (x, 8, NULL, &a); + result = mix_word_add (a, y); + break; + } + case '/': + if ( strlen (op) > 1 && op[1] == '/' ) { + mix_word_div (x,MIX_WORD_ZERO,y, &result, NULL); + } else { + mix_word_div (MIX_WORD_ZERO, x, y, &result, NULL); + } + break; + default: + g_assert_not_reached (); + } + return result; +} + +static void +unput_word_ (mix_word_t word) +{ + gchar *value; + gint k; + value = g_strdup_printf ("%s%ld", + mix_word_is_negative (word)? "-":"+", + mix_word_magnitude (word)); + for (k = strlen (value) - 1; k >= 0; --k) + unput (value[k]); + g_free (value); +} diff --git a/mixlib/mix_src_file.c b/mixlib/mix_src_file.c new file mode 100644 index 0000000..b376643 --- /dev/null +++ b/mixlib/mix_src_file.c @@ -0,0 +1,157 @@ +/* -*-c-*- -------------- mix_src_file.c : + * Implementation of the functions declared in mix_src_file.h + * ------------------------------------------------------------------ + * Last change: Time-stamp: "01/03/10 15:23:46 jose" + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001 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 <ctype.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include "mix_src_file.h" + +/* the MIXAL source file type */ +struct mix_src_file_t +{ + gchar *path; /* the path to the disk file */ + GPtrArray *lines; /* an array of the file lines */ + guint lineno; /* the number of lines */ +}; + +/* format a source line */ +static gchar * +format_line_ (gchar *line) +{ + const gchar *label, *op, *rest; + gint k = 0; + + if (!line) return line; + if (line[0] == '*' || strlen(line) == 0) + return g_strdup (line); + + if (isspace (line[0])) + { + label = " "; + while (line[k] && isspace (line[k])) ++k; + } + else + { + label = line; + while (line[k] && !isspace (line[k])) ++k; + while (line[k] && isspace (line[k])) ++k; + } + + if (line[k]) + { + line[k - 1] = 0; + op = line + k; + while (line[k] && !isspace (line[k])) ++k; + while (line[k] && isspace (line[k])) ++k; + line[k - 1] = 0; + rest = (line[k]) ? line + k: ""; + } + else + { + op = rest = ""; + } + + + return g_strdup_printf ("%-11s %-5s %s", label, op, rest); +} + +/* load the source file lines into memory */ +static gboolean +load_file_ (mix_src_file_t *file) +{ + mix_file_t *mf = mix_file_new_with_def_ext (file->path, + mix_io_READ, + MIX_SRC_DEFEXT); + if (mf != NULL) + { + enum {BUFFER_SIZE = 256}; + static gchar BUFFER[BUFFER_SIZE]; + + FILE *f = mix_file_to_FILE (mf); + file->lines = g_ptr_array_new (); + file->lineno = 0; + + while (fgets (BUFFER, BUFFER_SIZE, f) == BUFFER) + { + g_ptr_array_add (file->lines, (gpointer) format_line_ (BUFFER)); + file->lineno++; + } + + mix_file_delete (mf); + return TRUE; + } + return FALSE; +} + +/* create a new src file from an existing disk file */ +mix_src_file_t * +mix_src_file_new_for_read (const gchar *path) +{ + mix_src_file_t *result = g_new (mix_src_file_t, 1); + result->lines = NULL; + result->path = g_strdup (path); + result->lineno = 0; + return result; +} + +/* destroy a src file object */ +void +mix_src_file_delete (mix_src_file_t *src) +{ + g_return_if_fail (src != NULL); + if (src->lines) g_ptr_array_free (src->lines, TRUE); + g_free (src->path); + g_free (src); +} + +/* get the source file path */ +const gchar * +mix_src_file_get_path (const mix_src_file_t *src) +{ + g_return_val_if_fail (src != NULL, NULL); + return src->path; +} + +/* get a given line of the source file */ +const gchar * +mix_src_file_get_line (const mix_src_file_t *src, guint lineno) +{ + g_return_val_if_fail (src != NULL, NULL); + if (src->lines == NULL && !load_file_ ((mix_src_file_t*)src)) + return NULL; + if (lineno > src->lineno || lineno == 0) + return NULL; + return (gchar *)g_ptr_array_index (src->lines, lineno - 1); +} + +/* get the total no. of lines in the file */ +guint +mix_src_file_get_line_no (const mix_src_file_t *src) +{ + g_return_val_if_fail (src != NULL, 0); + if (src->lines == NULL && !load_file_ ((mix_src_file_t*)src)) + return 0; + return src->lineno; +} + diff --git a/mixlib/mix_src_file.h b/mixlib/mix_src_file.h new file mode 100644 index 0000000..d3a7461 --- /dev/null +++ b/mixlib/mix_src_file.h @@ -0,0 +1,57 @@ +/* -*-c-*- ---------------- mix_src_file.h : + * Declaration of mix_src_file_t, a type representing a MIXAL source + * file. + * ------------------------------------------------------------------ + * Last change: Time-stamp: <01/02/20 00:23:58 jose> + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef MIX_SRC_FILE_H +#define MIX_SRC_FILE_H + +#include "mix_file.h" + +/* the MIXAL source file type */ +typedef struct mix_src_file_t mix_src_file_t; + +/* create a new src file from an existing disk file */ +extern mix_src_file_t * +mix_src_file_new_for_read (const gchar *path); + +/* destroy a src file object */ +extern void +mix_src_file_delete (mix_src_file_t *src); + +/* get the source file path */ +extern const gchar * +mix_src_file_get_path (const mix_src_file_t *src); + +/* get a given line of the source file */ +extern const gchar * +mix_src_file_get_line (const mix_src_file_t *src, guint lineno); + +/* get the total no. of lines in the file */ +extern guint +mix_src_file_get_line_no (const mix_src_file_t *src); + + + +#endif /* MIX_SRC_FILE_H */ + diff --git a/mixlib/mix_symbol_table.c b/mixlib/mix_symbol_table.c new file mode 100644 index 0000000..dc56b7a --- /dev/null +++ b/mixlib/mix_symbol_table.c @@ -0,0 +1,198 @@ +/* -*-c-*- -------------- mix_symbol_table.c : + * Implementation of the functions declared in mix_symbol_table.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2004 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 <ctype.h> /* isdigit */ +#include <string.h> + +#include "mix_symbol_table.h" + +/* Create an empty table */ +mix_symbol_table_t * +mix_symbol_table_new (void) +{ + return g_hash_table_new (g_str_hash, g_str_equal); +} + +/* Create a table and populate it with the contents of a table stored + in -file- using mix_symbol_table_print (table, MIX_SYM_LINE, file) +*/ +mix_symbol_table_t * +mix_symbol_table_new_from_file (FILE *file) +{ + mix_symbol_table_t *result = mix_symbol_table_new (); + if ( result != NULL ) + { + gchar sym[MIX_SYM_MAX_LEN + 1]; + glong val; + + while ( getc (file) == ',' ) + { + if ( fscanf (file, "%s =%ld", sym, &val) != EOF ) + mix_symbol_table_add (result, sym, mix_word_new (val)); + } + } + return result; +} + +/* Delete a table */ +static void +delete_hash_keys_ (gpointer key, gpointer value, gpointer data) +{ + g_free (key); +} + +void +mix_symbol_table_delete (mix_symbol_table_t *table) +{ + g_hash_table_foreach (table, delete_hash_keys_, NULL); + g_hash_table_destroy (table); +} + +/* add/remove symbols from other table */ +static void +add_symbol_ (gpointer symbol, gpointer value, gpointer target) +{ + mix_symbol_table_t *t = (mix_symbol_table_t *)target; + gchar *s = (gchar *)symbol; + mix_word_t v = (mix_word_t)GPOINTER_TO_UINT (value); + mix_symbol_table_insert (t, s, v); +} + +static void +remove_symbol_ (gpointer symbol, gpointer value, gpointer target) +{ + mix_symbol_table_t *t = (mix_symbol_table_t *)target; + gchar *s = (gchar *)symbol; + mix_symbol_table_remove (t, s); +} + +gboolean +mix_symbol_table_merge_table (mix_symbol_table_t *table, + const mix_symbol_table_t *from) +{ + g_return_val_if_fail (table != NULL, FALSE); + if (from != NULL) + { + mix_symbol_table_foreach ((gpointer)from, add_symbol_, table); + } + return TRUE; +} + +gboolean +mix_symbol_table_substract_table (mix_symbol_table_t *table, + const mix_symbol_table_t *other) +{ + g_return_val_if_fail (table != NULL, FALSE); + if (other != NULL) + { + mix_symbol_table_foreach ((gpointer)other, remove_symbol_, table); + } + return TRUE; +} + + +/* Add/remove symbols one by one */ +gint +mix_symbol_table_add (mix_symbol_table_t *table, + const gchar *sym, mix_word_t value) +{ + gpointer key, val; + + if ( table == NULL || sym == NULL ) return MIX_SYM_FAIL; + if ( strlen (sym) > MIX_SYM_MAX_LEN ) return MIX_SYM_LONG; + + if ( !g_hash_table_lookup_extended (table, sym, &key, &val) ) + { + key = g_strdup (sym); + g_hash_table_insert (table, key, (gpointer)value); + return MIX_SYM_OK; + } + else + return MIX_SYM_DUP; +} + +/* Add or modify symbol if it exists */ +gint +mix_symbol_table_insert (mix_symbol_table_t *table, + const gchar *sym, mix_word_t new_value) +{ + gpointer key, val; + + if ( table == NULL || sym == NULL ) return MIX_SYM_FAIL; + if ( strlen (sym) > MIX_SYM_MAX_LEN ) return MIX_SYM_LONG; + + if ( !g_hash_table_lookup_extended (table, sym, &key, &val) ) + key = g_strdup (sym); + g_hash_table_insert (table, key, (gpointer)new_value); + return MIX_SYM_OK; +} + + +/* Symbols lookup */ +gboolean +mix_symbol_table_is_defined (const mix_symbol_table_t *table, const gchar *sym) +{ + gpointer key, val; + return g_hash_table_lookup_extended((GHashTable *)table, sym, &key, &val); +} + +/* Print table */ +#define is_local_sym_(sym) \ + ((sym) && (strlen (sym)==2) && (sym[1] == 'B') && isdigit (sym[0])) + +static gboolean skip_ = FALSE; + +static void +print_sym_rows_ (gpointer symbol, gpointer value, gpointer file) +{ + char *s = (char *)symbol; + if (skip_ && !is_local_sym_ (s)) { + mix_word_t word = (mix_word_t)GPOINTER_TO_UINT (value); + fprintf((FILE *)file, "%-20s: %s%ld\n", s, + mix_word_is_negative (word)? "-":"", + mix_word_magnitude (word)); + } +} + +static void +print_sym_line_ (gpointer symbol, gpointer value, gpointer file) +{ + char *s = (char *)symbol; + if (skip_ && !is_local_sym_ (s)) { + mix_word_t word = (mix_word_t)GPOINTER_TO_UINT (value); + fprintf((FILE *)file, ",%s =%s%ld", s, + mix_word_is_negative (word)? "-":"", + mix_word_magnitude (word)); + } +} + +void +mix_symbol_table_print (const mix_symbol_table_t *table, gint mode, + FILE *file, gboolean skiplocal) +{ + GHFunc func = (mode == MIX_SYM_LINE)? print_sym_line_ : print_sym_rows_; + skip_ = skiplocal; + if ( table != NULL ) + g_hash_table_foreach ((GHashTable *)table, func, (gpointer)file); + if ( mode == MIX_SYM_LINE ) putc (';', file); /* to mark end-of-table */ +} + + diff --git a/mixlib/mix_symbol_table.h b/mixlib/mix_symbol_table.h new file mode 100644 index 0000000..c54c3f8 --- /dev/null +++ b/mixlib/mix_symbol_table.h @@ -0,0 +1,109 @@ +/* -*-c-*- ---------------- mix_symbol_table.h : + * Type mix_symbol_table_t and functions to manipulate it. + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2004 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. + * + */ + + +#ifndef MIX_SYMBOL_TABLE_H +#define MIX_SYMBOL_TABLE_H + +#include <stdio.h> +#include "mix_types.h" + +/* A symbol table shall be implemented as a hash table */ +typedef GHashTable mix_symbol_table_t ; + +/* Maximum length of stored symbols */ +#define MIX_SYM_MAX_LEN 10 + +/* Create an empty table */ +extern mix_symbol_table_t * +mix_symbol_table_new (void); + +/* Create a table and populate it with the contents of a table stored + in -file- using mix_symbol_table_print (table, MIX_SYM_LINE, file) +*/ +extern mix_symbol_table_t * +mix_symbol_table_new_from_file (FILE *file); + + +/* Delete a table */ +extern void +mix_symbol_table_delete (mix_symbol_table_t *table); + +/* add/remove symbols from other table */ +extern gboolean +mix_symbol_table_merge_table (mix_symbol_table_t *table, + const mix_symbol_table_t *from); + +extern gboolean +mix_symbol_table_substract_table (mix_symbol_table_t *table, + const mix_symbol_table_t *other); + +/* Add/remove symbols one by one */ +/* possible outcomes: */ +enum { + MIX_SYM_FAIL, /* attempt failed */ + MIX_SYM_OK, + MIX_SYM_DUP, /* duplicated symbol */ + MIX_SYM_LONG /* symbol too long: only MIX_SYM_MAX_LEN chars used */ +}; + +extern gint +mix_symbol_table_add (mix_symbol_table_t *table, + const gchar *sym, mix_word_t value); + +#define mix_symbol_table_remove(table, sym) \ + g_hash_table_remove (table, sym) + + +/* Add or modify symbol if it exists */ +extern gint +mix_symbol_table_insert (mix_symbol_table_t *table, + const gchar *sym, mix_word_t new_value); + +/* Add or modify symbol if it exists, without copying sym */ +#define mix_symbol_table_insert_static(table,sym,value)\ + g_hash_table_insert (table,(gpointer)sym,GUINT_TO_POINTER (value)) + +/* Symbols lookup */ +extern gboolean +mix_symbol_table_is_defined (const mix_symbol_table_t *table, const gchar *sym); + +#define mix_symbol_table_value(table,sym) \ + (mix_word_t)GPOINTER_TO_UINT (g_hash_table_lookup ((GHashTable *)table, \ + (gpointer)sym)) + +/* Traverse the table */ +#define mix_symbol_table_foreach(table,func,data) \ + g_hash_table_foreach (table,func,data) + +/* Print the table */ +enum { + MIX_SYM_ROWS, /* each symbol/value in a row */ + MIX_SYM_LINE /* {,symbol =value}*; in a single row */ +}; + +extern void +mix_symbol_table_print (const mix_symbol_table_t *table, gint mode, + FILE *file, gboolean skiplocal); + + +#endif /* MIX_SYMBOL_TABLE_H */ + diff --git a/mixlib/mix_types.c b/mixlib/mix_types.c new file mode 100644 index 0000000..f87247a --- /dev/null +++ b/mixlib/mix_types.c @@ -0,0 +1,569 @@ +/* -*-c-*- ------------------ mix_types.c : + * Implementation file for mix_types.h declarations. + * ------------------------------------------------------------------ + * $Id: mix_types.c,v 1.5 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2002, 2004 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 "mix.h" +#include <limits.h> +#include <ctype.h> +#include "mix_types.h" + + +/*------------------------ initialisation stuff ------------------------*/ +/* flag telling whether a field spec is valid */ +static gboolean is_bad_field_[MIX_BYTE_MAX + 1]; +/* shift associated with a fspec */ +static guint shift_[MIX_BYTE_MAX + 1]; +/* mask associated with a fspec */ +static glong mask_[MIX_BYTE_MAX + 1]; + +/* maps from gchar's to mix_char_t */ +#define NONP_ (guchar)('?') +static mix_char_t ascii_to_mix_char_[UCHAR_MAX + 1]; +static guchar mix_char_to_ascii_[MIX_CHAR_MAX + 1] = { + ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '~', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '[', '#', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '.', ',', '(', ')', '+', '-', '*', '/', '=', '$', + '<', '>', '@', ';', ':', '\'' +}; + + +/* initialise the above arrays */ +void +mix_init_types (void) +{ + guint lt, rt; + for (lt = 0; lt < 8; ++lt) + for (rt = 0; rt < 8; ++rt) + { + guint F = 8 * lt + rt; + is_bad_field_[F] = rt < lt || 5 < rt; + if ( is_bad_field_[F] ) + shift_[F] = 0, mask_[F] = 0; + else + { + guint width = rt - (lt == 0 ? 1 : lt) + 1; + shift_[F] = 6 * (5 - rt); + mask_[F] = ((1L << (6 * width)) - 1) << shift_[F]; + } + } + + for ( lt = 0; lt < UCHAR_MAX + 1; ++lt ) + ascii_to_mix_char_[lt] = NONP_; + for ( lt = 0; lt < MIX_CHAR_MAX + 1; ++lt ) + ascii_to_mix_char_[mix_char_to_ascii_[lt]] = (guchar)lt; +} + +mix_char_t +mix_ascii_to_char (guchar c) +{ + return ascii_to_mix_char_[toupper (c)]; +} + + +guchar +mix_char_to_ascii (mix_char_t c) +{ + return c > MIX_CHAR_MAX ? NONP_ : mix_char_to_ascii_[c]; +} + + +/*---------------------------- m_word_t --------------------------------- */ + +/* Create mix_word_t from an array of mix_byte_t */ +mix_word_t +mix_bytes_to_word (mix_byte_t *bytes, guint byteno) +{ + mix_word_t result = 0; + guint k; + + g_return_val_if_fail (bytes != NULL, MIX_WORD_ZERO); + g_return_val_if_fail (byteno != 0, MIX_WORD_ZERO); + + if ( byteno > 5 ) byteno = 5; + for ( k = 0; k < byteno; k++ ) + result |= ((gulong)bytes[k]) << (6*(byteno-k-1)); + + return result; +} + + +/* Field operations */ +mix_word_t /* the specified field or 0 if f is not valid */ +mix_word_get_field (mix_fspec_t f, mix_word_t word) +{ + mix_word_t result; + + g_return_val_if_fail (!is_bad_field_[f],MIX_WORD_ZERO); + + result = ( (word & mask_[f]) >> shift_[f] ); + if (f < 8) /* if f is of the form (0:R), retain the sign of word */ + result |= mix_word_sign (word); + + return result; +} + + +mix_word_t +mix_word_set_field (mix_fspec_t f, mix_word_t from, mix_word_t to) +{ + mix_word_t result; + glong m = mask_[f]; + + g_return_val_if_fail (!is_bad_field_[f],to); + + if (f < 8) /* if F is of the form (0:R), use the sign of -from- */ + result = (to & ~m & ~MIX_WORD_SIGN_BIT) + | ((from /*<< shift_[f]*/) & m) | mix_word_sign (from); + else + result = (to & ~m) | ((from /*<< shift_[f]*/) & m); + + return result; +} + +mix_word_t +mix_word_store_field (mix_fspec_t f, mix_word_t from, mix_word_t to) +{ + mix_word_t result; + glong m = mask_[f]; + + g_return_val_if_fail (!is_bad_field_[f],to); + + if (f < 8) /* if F is of the form (0:R), use the sign of -from- */ + result = (to & ~m & ~MIX_WORD_SIGN_BIT) + | ((from << shift_[f]) & m) | mix_word_sign (from); + else + result = (to & ~m) | ((from << shift_[f]) & m); + + return result; +} + + + +gboolean +mix_fspec_is_valid (mix_fspec_t f) +{ + return !(is_bad_field_[f]); +} + +mix_byte_t +mix_word_get_byte (mix_word_t word, /* word parsed */ + guint idx /* byte: 1 to 5 */ ) +{ + mix_byte_t fspec = mix_fspec_new (idx,idx); + + g_return_val_if_fail ((idx > 0 && idx < 6), MIX_BYTE_ZERO); + + return mix_byte_new (mix_word_get_field (fspec,word)); +} + + +extern void +mix_word_set_byte (mix_word_t *into, guint idx, mix_byte_t value) +{ + mix_word_t from = value; + mix_byte_t fspec = mix_fspec_new (idx,idx); + + g_return_if_fail (into != NULL); + g_return_if_fail (idx > 0 && idx < 6); + + from <<= shift_[fspec]; + *into = mix_word_set_field (fspec,from,*into); +} + +/* Arithmetic operations */ +mix_word_t +mix_word_add (mix_word_t x, mix_word_t y) +{ + static const gulong MIX_WORD_MAX_SIM = 2*MIX_WORD_MAX + 1; + + gint32 result; + if ( mix_word_sign (x) == mix_word_sign (y) ) + { + result = (mix_word_magnitude (x) + mix_word_magnitude (y)); + if ( result > MIX_WORD_MAX ) { + /* for instance MIX_WORD_MAX + 1 = - MIX_WORD_MAX */ + result = MIX_WORD_MAX_SIM - result; + result |= mix_word_sign (mix_word_negative (x)); + } else if ( result != 0 ) + result |= mix_word_sign (x); + /* keep positive sign for 0 so that w - w == -w + w */ + } + else + { + result = mix_word_magnitude (x) - mix_word_magnitude (y); + if (result < 0) + result = -result|mix_word_sign (y); + else if (result > 0) + result = result|mix_word_sign (x); + /* keep positive sign for 0 so that w - w == -w + w */ + } + + g_assert ( result >= 0 ); + + return (mix_word_t)result; +} + +gboolean /* TRUE if overflow */ +mix_word_add_and_carry (mix_word_t x, mix_word_t y, + mix_word_t *h, mix_word_t *l) +{ + gboolean result; + + if ( mix_word_sign (x) == mix_word_sign (y) ) + { + guint32 sum = (mix_word_magnitude (x) + mix_word_magnitude (y)); + if ( sum > MIX_WORD_MAX ) + { + result = TRUE; + if ( l != NULL ) + { + *l = sum - MIX_WORD_MAX -1; + *l |= mix_word_sign (x); + } + if ( h != NULL ) + { + *h = sum >> 30; + *h |= mix_word_sign (x); + } + } + else /* sum <= MIX_WORD_MAX */ + { + result = FALSE; + if ( h != NULL ) *h = 0; + if ( l != NULL ) + { + *l = sum; + if ( sum != 0 ) + *l |= mix_word_sign (x); + /* keep positive sign for 0 so that w - w == -w + w */ + } + } + } + else /* mix_word_sign (x) != mix_word_sign (y) */ + { + result = FALSE; + if ( h != NULL ) *h = 0; + if ( l != NULL ) + { + gint32 dif = mix_word_magnitude (x) - mix_word_magnitude (y); + if ( dif < 0) + *l = (-dif)|mix_word_sign (y); + else if ( dif > 0) + *l = dif|mix_word_sign (x); + else /* keep positive sign for 0 so that w - w == -w + w */ + *l = MIX_WORD_ZERO; + } + } + + return result; +} + + +void +mix_word_mul (mix_word_t x, mix_word_t y, + mix_word_t *high_word, mix_word_t *low_word) +{ + guint32 sign = mix_word_sign (x) ^ mix_word_sign (y); + + /* + x = x0 + x1 * 2 ^ 10 + x2 * 2 ^ 20 + y = y0 + y1 * 2 ^ 10 + y2 * 2 ^ 20 + x0, x1, x2, y0, y1, y2 are < 2 ^ 10 + */ + guint32 x0 = (x & 0x000003FF); + guint32 x1 = (x & 0x000FFC00) >> 10; + guint32 x2 = (x & 0x3FF00000) >> 20; + guint32 y0 = (y & 0x000003FF); + guint32 y1 = (y & 0x000FFC00) >> 10; + guint32 y2 = (y & 0x3FF00000) >> 20; + + /* + x * y = partial0 + + partial1 * 2 ^ 10 + + partial2 * 2 ^ 20 + + partial3 * 2 ^ 30 + + partial4 * 2 ^ 40 + partial0 and partial4 are < 2 ^ 20 + partial1 and partial3 are < 2 ^ 21 + partial2 is < 3 * 2 ^ 20 + */ + guint32 partial0 = x0 * y0; + guint32 partial1 = x0 * y1 + x1 * y0; + guint32 partial2 = x0 * y2 + x1 * y1 + x2 * y0; + guint32 partial3 = x1 * y2 + x2 * y1; + guint32 partial4 = x2 * y2; + + /* sum1 has a place value of 1 and is < 2 ^ 32 */ + guint32 sum1 = partial0 + (partial1 << 10); + guint32 carry1 = (sum1 & 0xFFF00000) >> 20; + + /* sum2 has a place value of 2 ^ 20 and is < 2 ^ 32 */ + guint32 sum2 = partial2 + (partial3 << 10) + carry1; + guint32 carry2 = (sum2 & 0xFFF00000) >> 20; + + /* sum3 has a place value of 2 ^ 40 and is < 2 ^ 20 */ + guint32 sum3 = partial4 + carry2; + + sum1 &= ~0xFFF00000; + sum2 &= ~0xFFF00000; + + /* + Now paste the three values back into two. + */ + if ( low_word != NULL ) { + *low_word = sum1 | ((sum2 & 0x000003FF) << 20); + *low_word |= sign; + } + if ( high_word != NULL ) { + *high_word = ((sum2 & 0x000FFC00) >> 10) | (sum3 << 10); + *high_word |= sign; + } +} + + +gboolean +mix_word_div (mix_word_t n1, mix_word_t n0, mix_word_t d, + mix_word_t *quotient, mix_word_t *remainder) +{ + gboolean overflow = FALSE; + long magn1 = mix_word_magnitude (n1); + long magd = mix_word_magnitude (d); + + if (magd == 0) + { + overflow = TRUE; + /* just so they have -some- valid value */ + if ( quotient != NULL ) *quotient = 0; + if ( remainder != NULL ) *remainder = 0; + } + else if (magn1 == 0) + { /* special-cased for speed */ + if ( quotient != NULL ) + *quotient = (mix_word_sign (n1) ^ mix_word_sign (d)) + | (mix_word_magnitude (n0) / magd); + if ( remainder != NULL ) + *remainder = mix_word_sign (n1) | (mix_word_magnitude (n0) % magd); + } + else if (magd <= magn1) + { + overflow = TRUE; + if ( quotient != NULL ) *quotient = 0; + if ( remainder != NULL ) *remainder = 0; + } + else + { + long q = mix_word_magnitude (n0); + long r = magn1; + unsigned i; + for (i = 30; i != 0; --i) { + r <<= 1; + if ( (q & (1L << 29)) != 0 ) + ++r; + q = (q << 1) & ((1L << 30) - 1); + if (magd <= r) + ++q, r -= magd; + } + if ( quotient != NULL ) + *quotient = (mix_word_sign (n1) ^ mix_word_sign (d)) | q; + if ( remainder != NULL ) + *remainder = mix_word_sign(n1) | r; + } + return overflow; +} + +void +mix_word_shift_right (mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX) +{ + if ( pX != NULL ) *pX = mix_word_sign (X); + if ( pA != NULL ) *pA = mix_word_sign (A); + if (count < 5) { + if ( pA != NULL ) + *pA |= mix_word_magnitude (A) >> (6 * count); + if ( pX != NULL ) + *pX |= MIX_WORD_MAX & ( (mix_word_magnitude (X) >> (6 * count)) + | (A << (30 - 6 * count)) ); + } else if (count < 10 && pX != NULL) + *pX |= mix_word_magnitude (A) >> (6 * count - 30); + else + ; +} + + +void +mix_word_shift_left (mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX) +{ + if ( pX != NULL ) *pX = mix_word_sign (X); + if ( pA != NULL ) *pA = mix_word_sign (A); + if (count < 5) { + if ( pX != NULL ) *pX |= MIX_WORD_MAX & (X << (6 * count)); + if ( pA != NULL ) + *pA |= MIX_WORD_MAX & ( (A << (6 * count)) | + (mix_word_magnitude (X) >> (30 - 6 * count)) ); + } else if (count < 10 && pA != NULL) + *pA |= MIX_WORD_MAX & (X << (6 * count - 30)); + else + ; +} + +void +mix_word_shift_left_circular (mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX) +{ + mix_word_t A1, X1; + guint c; + + count %= 10; + A1 = count < 5 ? A : X; + X1 = count < 5 ? X : A; + c = 6 * (count < 5 ? count : count - 5); + if ( pX != NULL ) + *pX = mix_word_sign (X) + | ( MIX_WORD_MAX & (X1 << c) ) | ( mix_word_magnitude (A1) >> (30 - c) ); + if ( pA != NULL ) + *pA = mix_word_sign (A) + | ( MIX_WORD_MAX & (A1 << c) ) | ( mix_word_magnitude (X1) >> (30 - c) ); +} + +void +mix_word_shift_right_circular (mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX) +{ + mix_word_t A1, X1; + guint c; + + count %= 10; + A1 = count < 5 ? A : X; + X1 = count < 5 ? X : A; + c = 6 * (count < 5 ? count : count - 5); + if ( pX != NULL ) + *pX = mix_word_sign (X) + | ( mix_word_magnitude (X1) >> c ) | ( MIX_WORD_MAX & (A1 << (30 - c)) ); + if ( pA != NULL ) + *pA = mix_word_sign (A) + | ( mix_word_magnitude (A1) >> c ) | ( MIX_WORD_MAX & (X1 << (30 - c)) ); +} + + +/* Printable representation */ +void +mix_word_print_to_file (mix_word_t word, const char *message, FILE *f) +{ + guint k; + if ( message ) fprintf (f, "%s ", message); + fprintf (f, "%s ", mix_word_sign (word) == 0 ? "+" : "-"); + for ( k = 1; k < 6; ++k ) { + fprintf (f, "%02d ", mix_word_get_byte (word,k)); + } + fprintf (f, "(%010ld)", mix_word_magnitude (word)); +} + +void +mix_word_print_to_buffer (mix_word_t word, gchar *buf) +{ + g_return_if_fail (buf != NULL); + sprintf (buf, "%s %02d %02d %02d %02d %02d", + mix_word_sign (word) == 0 ? "+" : "-", + mix_word_get_byte (word, 1), + mix_word_get_byte (word, 2), + mix_word_get_byte (word, 3), + mix_word_get_byte (word, 4), + mix_word_get_byte (word, 5)); +} + +/* Conversions between words and shorts */ +mix_short_t +mix_word_to_short (mix_word_t word) +{ + if ( mix_word_is_negative (word) ) + return ( (word & MIX_SHORT_MAX) | MIX_SHORT_SIGN_BIT ); + else + return (word & MIX_SHORT_MAX); +} + +mix_word_t +mix_short_to_word (mix_short_t s) +{ + if ( mix_short_is_negative (s) ) + return ((mix_word_t) (mix_short_magnitude (s) | MIX_WORD_SIGN_BIT)); + else + return ((mix_word_t)s); +} + +mix_short_t +mix_short_add (mix_short_t x, mix_short_t y) +{ + static const guint16 MIX_SHORT_MAX_SIM = 2*MIX_SHORT_MAX + 1; + + gint16 result; + if ( mix_short_sign (x) == mix_short_sign (y) ) + { + result = (mix_short_magnitude (x) + mix_short_magnitude (y)); + if ( result > MIX_SHORT_MAX ) { + /* for instance MIX_SHORT_MAX + 1 = - MIX_SHORT_MAX */ + result = MIX_SHORT_MAX_SIM - result; + result |= mix_short_sign (mix_short_negative (x)); + } else if ( result != 0 ) + result |= mix_short_sign (x); + /* keep positive sign for 0 so that w - w == -w + w */ + } + else + { + result = mix_short_magnitude (x) - mix_short_magnitude (y); + if (result < 0) + result = -result|mix_short_sign (y); + else if (result > 0) + result = result|mix_short_sign (x); + /* keep positive sign for 0 so that w - w == -w + w */ + } + + g_assert ( result >= 0 ); + + return (mix_short_t)result; +} + + +/* Printable representation */ +void +mix_short_print (mix_short_t s, const gchar *message) +{ + if ( message ) g_print ("%s ", message); + g_print ("%s ", mix_short_sign (s) == 0 ? "+" : "-"); + g_print ("%02d %02d ", mix_byte_new (s>>6), mix_byte_new (s)); + g_print ("(%04d)", mix_short_magnitude (s)); +} + +void +mix_short_print_to_buffer (mix_short_t s, gchar *buf) +{ + g_return_if_fail (buf != NULL); + sprintf (buf, "%s %02d %02d", + mix_short_sign (s) == 0 ? "+" : "-", + mix_byte_new (s>>6), mix_byte_new (s)); + /* g_print ("(%04d)", mix_short_magnitude (s));*/ +} + + + + diff --git a/mixlib/mix_types.h b/mixlib/mix_types.h new file mode 100644 index 0000000..c628e05 --- /dev/null +++ b/mixlib/mix_types.h @@ -0,0 +1,293 @@ +/* -*-c-*- --------------- mix_types.h: + * This file contains declarations for the basic types used in MIX: + * mix_byte_t, mix_char_t, mix_short_t and mix_word_t. + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2004 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. + * + */ + + +#ifndef MIX_TYPES_H +#define MIX_TYPES_H + +#include <stdio.h> +#include "mix.h" + +/* Initialisation function to be called before using the other + * functions in this file + */ +extern void +mix_init_types(void); + + +/*----------------- m_byte_t --------------------------------------------*/ +/* MIX byte type */ +typedef guint8 mix_byte_t; + +/* Maximum value stored in an mix_byte_t */ +#define MIX_BYTE_MAX ((1L << 6) - 1) + +/* Zero mix byte */ +#define MIX_BYTE_ZERO ((mix_byte_t)0) + +/* Create a mix_byte_t from any native type */ +#define mix_byte_new(x) ((mix_byte_t)((x) & MIX_BYTE_MAX )) + +/* Operations */ +/* Addition */ +#define mix_byte_add(x,y) mix_byte_new((x) + (y)) +/* Substraction */ +#define mix_byte_sub(x,y) mix_byte_new((x) - (y)) +/* Product */ +#define mix_byte_mul(x,y) mix_byte_new((x) * (y)) +/* Quotient */ +#define mix_byte_div(x,y) mix_byte_new((x) / (y)) + + +/*----------------- mix_char_t --------------------------------------------*/ +/* MIX char type: chars are coded in MIX from 0 to MIX_CHAR_MAX */ +typedef guint8 mix_char_t; + +#define MIX_CHAR_MAX 55 + +/* Conversions for mix_char_t's */ +#define mix_char_new(l) (((l) < MIX_CHAR_MAX )? (l) : MIX_CHAR_MAX) +#define mix_char_to_byte(mchar) mix_byte_new (mchar) +#define mix_byte_to_char(byte) mix_char_new (byte) + +extern mix_char_t +mix_ascii_to_char(guchar c); + +extern guchar +mix_char_to_ascii(mix_char_t c); + + +/*----------------- mix_word_t --------------------------------------------*/ +/* + * Represented as a gint32 (glib ensures that this type has 32 + * bits). Bit 30 is the sign, higher bits are 0, + * and bits 0-29 are the magnitude. + * Each MIX 'byte' is a 6-bit substring of the magnitude. + */ +typedef guint32 mix_word_t; + +/* Maximum value stored in an mix_word_t */ +#define MIX_WORD_MAX ((1L << 30) - 1) +/* Sign bit in a word */ +#define MIX_WORD_SIGN_BIT (1L << 30) +/* Zero mix word */ +#define MIX_WORD_ZERO ((mix_word_t)0) +/* Negative zero mix word */ +#define MIX_WORD_MINUS_ZERO (MIX_WORD_ZERO | MIX_WORD_SIGN_BIT) + + +/* Create a mix_word_t from any native type */ +#define mix_word_new(x) \ +( (x) < 0 \ + ? ( MIX_WORD_SIGN_BIT | ((mix_word_t)(-(x)) & MIX_WORD_MAX) ) \ + : ( (mix_word_t)(x) & MIX_WORD_MAX ) \ +) + +/* Create a mix_word_t from individual bytes */ +#define mix_word_new_b(b1,b2,b3,b4,b5) \ +((mix_word_t)(mix_byte_new(b5) + (mix_byte_new(b4)<<6) + \ + (mix_byte_new(b3)<<12) + (mix_byte_new(b2)<<18) + \ + (mix_byte_new(b1)<<24))) + +/* Create a negative mix_word_t from individual bytes */ +#define mix_word_new_bn(b1,b2,b3,b4,b5) \ + mix_word_negative(mix_word_new_b(b1,b2,b3,b4,b5)) + +/* Create mix_word_t from an array of mix_byte_t */ +extern mix_word_t +mix_bytes_to_word(mix_byte_t *bytes, guint byteno); + +/* Access byte within a word */ +extern mix_byte_t /* byte -idx- or MIX_BYTE_ZERO if -idx- out of range */ +mix_word_get_byte(mix_word_t word, /* word parsed */ + guint idx /* byte: 1 to 5 */); + +/* Set a byte within a mix_word_t */ +extern void +mix_word_set_byte(mix_word_t *into, /* word to be modified */ + guint idx, /* byte: 1 to 5 */ + mix_byte_t value /* byte's value */); + + +/* Operations */ +/* Sign-related definitions */ +#define mix_word_negative(word) ( (word) ^ MIX_WORD_SIGN_BIT ) +#define mix_word_reverse_sign(word) ( word ^= MIX_WORD_SIGN_BIT ) +#define mix_word_sign(word) ( (word) & MIX_WORD_SIGN_BIT ) +#define mix_word_magnitude(word) ( (word) & (MIX_WORD_SIGN_BIT - 1) ) +#define mix_word_is_positive(word) ( mix_word_sign(word) == 0 ) +#define mix_word_is_negative(word) ( mix_word_sign(word) != 0 ) + + +/* Arithmetic operations */ +extern mix_word_t +mix_word_add(mix_word_t x, mix_word_t y); + +#define mix_word_sub(x,y) ( mix_word_add((x),mix_word_negative(y)) ) + +/* Add two words filling a high word if needed. + -high_word- and/or -low_word- can be NULL. +*/ +extern gboolean /* TRUE if overflow */ +mix_word_add_and_carry(mix_word_t x, mix_word_t y, + mix_word_t *high_word, mix_word_t *low_word); + +/* Product, stored in -high_word- and -low_word-, which + can be NULL. +*/ +extern void +mix_word_mul(mix_word_t x, mix_word_t y, + mix_word_t *high_word, mix_word_t *low_word); + +/* Division. -quotient- and/or -remainder- can be NULL. */ +extern gboolean /* TRUE if overflow */ +mix_word_div(mix_word_t n1, mix_word_t n0, mix_word_t d, + mix_word_t *quotient, mix_word_t *remainder); + +/* Shift operations */ +extern void +mix_word_shift_left(mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX); +extern void +mix_word_shift_right(mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX); +extern void +mix_word_shift_left_circular(mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX); +extern void +mix_word_shift_right_circular(mix_word_t A, mix_word_t X, gulong count, + mix_word_t *pA, mix_word_t *pX); + + + + +/* + * Fields within a word: a word containing the (L:R) + * bytes of the original one. L and R (with 0 <= L <= R < 6) + * are specified by a mix_fspec_t F = 8*L + R. + */ +typedef guint8 mix_fspec_t; + +#define mix_fspec_left(f) ( ((f)>>3) & 7 ) +#define mix_fspec_right(f) ( (f) & 7 ) +#define mix_fspec_new(L,R) ( mix_byte_new(8*(L) + (R)) ) + +extern gboolean +mix_fspec_is_valid(mix_fspec_t f); + +extern mix_word_t /* the specified field or 0 if f is not valid */ +mix_word_get_field(mix_fspec_t f, mix_word_t word); + +extern mix_word_t /* -to- with the field -f- from -from- or -to- + if -f- is not a valid fspec */ +mix_word_set_field(mix_fspec_t f, mix_word_t from, mix_word_t to); + +/* set field into a zero word */ +#define mix_word_extract_field(fspec,from_word) \ + mix_word_set_field(fspec,from_word,MIX_WORD_ZERO) + +/* Store operation: the no. of bytes determined by -f- is taken + * from the right of -from- and stored into -to- in the location + * specified by -f- + */ +extern mix_word_t +mix_word_store_field(mix_fspec_t f, mix_word_t from, mix_word_t to); + + +/* Printable representation */ +#define mix_word_print(word,message) \ + mix_word_print_to_file (word, message, stdout) + +extern void +mix_word_print_to_file (mix_word_t word, const char *message, FILE *f); + +extern void +mix_word_print_to_buffer (mix_word_t word, gchar *buf); + + +/*----------------- mix_short_t ------------------------------------------*/ +typedef guint16 mix_short_t; + +#define MIX_SHORT_MAX ((1L << 12) - 1) +#define MIX_SHORT_SIGN_BIT ((mix_short_t)(1L << 12)) +#define MIX_SHORT_ZERO ((mix_short_t)0) +#define MIX_SHORT_MINUS_ZERO (MIX_SHORT_ZERO | MIX_SHORT_SIGN_BIT) + +/* Sign-related definitions */ +#define mix_short_negative(s) ( (s) ^ MIX_SHORT_SIGN_BIT ) +#define mix_short_sign(s) ( (s) & MIX_SHORT_SIGN_BIT ) +#define mix_short_magnitude(s) \ + ( (s) & (MIX_SHORT_SIGN_BIT - 1) ) +#define mix_short_is_positive(s) ( mix_short_sign(s) == 0 ) +#define mix_short_is_negative(s) ( mix_short_sign(s) != 0 ) +#define mix_short_reverse_sign(s) ( (s) ^= MIX_SHORT_SIGN_BIT ) + +/* create short from a long */ +#define mix_short_new(val) \ + ((val)>= 0 ? (val)&MIX_SHORT_MAX : mix_short_negative(-(val))) + +/* Create shorts from individual bytes */ +#define mix_short_new_b(b1,b2) \ +((mix_short_t)((mix_byte_new(b1)<<6) + mix_byte_new(b2))) + +#define mix_short_new_bn(b1,b2) mix_short_negative(mix_short_new_b(b1,b2)) + +/* Conversions between words and shorts. Arithmetic operations + on shorts are not provided but for addition: use words instead. +*/ +/* Make a short taking word's sign and its two least significant + bytes (bytes no. 4 and 5) +*/ +extern mix_short_t +mix_word_to_short(mix_word_t word); + +extern mix_word_t +mix_short_to_word(mix_short_t s); + +/* fast conversion (these macros' argument are evaluated twice */ +#define mix_word_to_short_fast(w) \ +( mix_word_is_negative(w) ? \ + ((w) & MIX_SHORT_MAX)|MIX_SHORT_SIGN_BIT : (w)&MIX_SHORT_MAX ) + +#define mix_short_to_word_fast(s) \ +( mix_short_is_negative(s) ? \ +(mix_word_t) (mix_short_magnitude(s)|MIX_WORD_SIGN_BIT): (mix_word_t)(s) ) + + +extern mix_short_t +mix_short_add(mix_short_t x, mix_short_t y); + + +/* printable representation */ +extern void +mix_short_print(mix_short_t s, const gchar *message); + +extern void +mix_short_print_to_buffer (mix_short_t s, gchar *buf); + + + +#endif /* MIX_TYPES_H */ + + + + diff --git a/mixlib/mix_vm.c b/mixlib/mix_vm.c new file mode 100644 index 0000000..71c8bae --- /dev/null +++ b/mixlib/mix_vm.c @@ -0,0 +1,706 @@ +/* -*-c-*- ------------------ mix_vm.c : + * Implementation of the functions declared in mix_vm.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2004 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 "mix.h" +#include "xmix_vm.h" + +#define vm_reset_(vm) vm_reset_reload_ (vm, FALSE); + +static void +vm_reset_reload_ (mix_vm_t *vm, gboolean is_reload) +{ + guint k; + set_status_ (vm, MIX_VM_EMPTY); + set_rA_ (vm, MIX_WORD_ZERO); + set_rX_ (vm, MIX_WORD_ZERO); + set_rJ_ (vm, MIX_WORD_ZERO); + for ( k = 1; k < IREG_NO_+1; ++k ) set_rI_ (vm, k, MIX_WORD_ZERO); + set_over_ (vm, FALSE); + set_cmp_ (vm, mix_EQ); + set_loc_ (vm, MIX_WORD_ZERO); + set_last_error_ (vm, MIX_VM_ERROR_NONE); + for ( k = 0; k < MEM_CELLS_NO_; ++k) set_cell_ (vm, k, MIX_WORD_ZERO); + + if (vm->symbol_table != NULL ) + { + mix_symbol_table_delete (vm->symbol_table); + vm->symbol_table = NULL; + } + + if (vm->line_table != NULL) + { + g_tree_destroy (vm->line_table); + vm->line_table = NULL; + } + + if (vm->address_table != NULL) + { + g_tree_destroy (vm->address_table); + vm->address_table = NULL; + } + + if (vm->src_file != NULL) + { + mix_src_file_delete (vm->src_file); + vm->src_file = NULL; + } + + for (k = 0; k < BD_NO_; ++k) + if (vm->devices[k] != NULL) + { + mix_device_type_t type = mix_device_type (vm->devices[k]); + if (!is_reload || (type != mix_dev_CONSOLE + && type != mix_dev_PRINTER + && type != mix_dev_CARD_WR)) + { + mix_device_delete (vm->devices[k]); + vm->devices[k] = NULL; + } + } + + if (!is_reload) + mix_vm_clear_all_breakpoints (vm); + + if (vm->address_list) + { + g_slist_free (vm->address_list); + vm->address_list = NULL; + } +} + + +/* Create/destroy a mix vm */ +mix_vm_t * +mix_vm_new (void) +{ + int i; + + mix_vm_t *vm = g_new (struct mix_vm_t,1); + vm->line_table = NULL; + vm->address_table = NULL; + vm->symbol_table = NULL; + vm->src_file = NULL; + vm->pred_list = mix_predicate_list_new (vm); + vm->address_list = NULL; + vm->last_error = MIX_VM_ERROR_NONE; + + for (i = 0; i < BD_NO_; ++i) + vm->devices[i] = NULL; + + vm->clock = mix_vm_clock_new (); + vm->factory = mix_device_new; + + vm_reset_ (vm); + + return vm; +} + + +void +mix_vm_delete (mix_vm_t * vm) +{ + int i; + + g_return_if_fail (vm != NULL); + + if (vm->line_table != NULL) g_tree_destroy (vm->line_table); + if (vm->address_table != NULL) g_tree_destroy (vm->address_table); + if (vm->symbol_table != NULL) mix_symbol_table_delete (vm->symbol_table); + if (vm->src_file != NULL) mix_src_file_delete (vm->src_file); + if (vm->pred_list != NULL) mix_predicate_list_delete (vm->pred_list); + if (vm->address_list != NULL) g_slist_free (vm->address_list); + for (i = 0; i < BD_NO_; ++i) + mix_device_delete (vm->devices[i]); + mix_vm_clock_delete (vm->clock); + g_free (vm); +} + +/* connect devices to a virtual machine */ +mix_device_t * +mix_vm_connect_device (mix_vm_t *vm, mix_device_t *device) +{ + mix_device_t *old; + mix_device_type_t type; + + g_return_val_if_fail (vm != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); + + type = mix_device_type (device); + old = vm->devices[type]; + vm->devices[type] = device; + + return old; +} + +/* get device */ +mix_device_t * +mix_vm_get_device (const mix_vm_t *vm, mix_device_type_t dev) +{ + g_return_val_if_fail (vm != NULL, NULL); + g_return_val_if_fail (dev < mix_dev_INVALID, NULL); + return vm->devices[dev]; +} + +/* install a device factory for automatic connection */ +void +mix_vm_set_device_factory (mix_vm_t *vm, mix_device_factory_t factory) +{ + g_return_if_fail (vm != NULL); + g_return_if_fail (factory != NULL); + vm->factory = factory; +} + +/* Reset a vm (set state as of a newly created one) */ +void +mix_vm_reset (mix_vm_t * vm) +{ + g_return_if_fail (vm != NULL); + vm_reset_ (vm); +} + +/* Set start address for execution */ +void +mix_vm_set_start_addr (mix_vm_t *vm, mix_address_t addr) +{ + g_return_if_fail (vm != NULL); + set_loc_ (vm, addr); +} + +/* Access to the vm's registers */ +mix_word_t +mix_vm_get_rA (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, MIX_WORD_ZERO); + return get_rA_ (vm); +} + +mix_word_t +mix_vm_get_rX (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, MIX_WORD_ZERO); + return get_rX_ (vm); +} + +mix_short_t +mix_vm_get_rJ (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, MIX_SHORT_ZERO); + return mix_word_to_short_fast (get_rJ_ (vm)); +} + +mix_short_t +mix_vm_get_rI (const mix_vm_t *vm, guint idx) +{ + g_return_val_if_fail (vm != NULL, MIX_SHORT_ZERO); + g_return_val_if_fail (IOK_ (idx), MIX_SHORT_ZERO); + return mix_word_to_short_fast (get_rI_ (vm, idx)); +} + +void +mix_vm_set_rA (mix_vm_t *vm, mix_word_t value) +{ + g_return_if_fail (vm != NULL); + set_rA_ (vm, value); +} + +void +mix_vm_set_rX (mix_vm_t *vm, mix_word_t value) +{ + g_return_if_fail (vm != NULL); + set_rX_ (vm, value); +} + +void +mix_vm_set_rJ (mix_vm_t *vm, mix_short_t value) +{ + g_return_if_fail (vm != NULL); + g_return_if_fail (mix_short_is_positive (value)); + set_rJ_ (vm, mix_short_to_word_fast (value)); +} + +void +mix_vm_set_rI (mix_vm_t *vm, guint idx, mix_short_t value) +{ + g_return_if_fail (vm != NULL); + g_return_if_fail (IOK_ (idx)); + set_rI_ (vm, idx, mix_short_to_word_fast (value)); +} + +/* Access to the comparison flag and overflow toggle */ +mix_cmpflag_t +mix_vm_get_cmpflag (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, mix_EQ); + return get_cmp_ (vm); +} + +void +mix_vm_set_cmpflag (mix_vm_t *vm, mix_cmpflag_t value) +{ + g_return_if_fail (vm != NULL); + set_cmp_ (vm, value); +} + +gboolean +mix_vm_get_overflow (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, TRUE); + return get_over_ (vm); +} + +void +mix_vm_set_overflow (mix_vm_t *vm, gboolean value) +{ + g_return_if_fail (vm != NULL); + set_over_ (vm, value); +} + +void +mix_vm_toggle_overflow (mix_vm_t *vm) +{ + g_return_if_fail (vm != NULL); + set_over_ (vm, !get_over_ (vm)); +} + +/* Access to memory cells */ +mix_word_t +mix_vm_get_addr_contents (const mix_vm_t *vm, mix_address_t addr) +{ + g_return_val_if_fail (vm != NULL, MIX_WORD_ZERO); + return (MEMOK_ (addr))? get_cell_ (vm, addr) : MIX_WORD_ZERO; +} + +void +mix_vm_set_addr_contents (mix_vm_t *vm, mix_address_t addr, mix_word_t value) +{ + g_return_if_fail (vm != NULL); + if (MEMOK_ (addr)) set_cell_ (vm, addr, value); +} + +gboolean +mix_vm_is_halted (const mix_vm_t *vm) +{ + return is_halted_ (vm); +} + +/* Execution of instructions */ +gboolean /* TRUE if success */ +mix_vm_exec_ins (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_return_val_if_fail (vm != NULL, FALSE); + g_return_val_if_fail (ins != NULL, FALSE); + return (*ins_handlers_[ins->opcode]) (vm,ins); +} + +/* comparison function for the line and address tables tree */ +static gint +cmp_uint_ (gconstpointer a, gconstpointer b) +{ + return GPOINTER_TO_UINT (a) - GPOINTER_TO_UINT (b); +} + +gboolean +mix_vm_load_file (mix_vm_t *vm, const gchar *name) +{ + mix_code_file_t *file; + mix_src_file_t *sfile = NULL; + mix_ins_desc_t ins; + const gchar *sp; + gboolean reload = FALSE; + + g_return_val_if_fail (vm != NULL, FALSE); + file = mix_code_file_new_read (name); + if (file == NULL) + { + set_status_ (vm, MIX_VM_ERROR); + return FALSE; + } + sp = mix_code_file_get_source_path (file); + + if (sp != NULL) + { + sfile = mix_src_file_new_for_read (sp); + reload = (vm->src_file + && !strcmp (mix_src_file_get_path (vm->src_file), + mix_src_file_get_path (sfile))); + } + + vm_reset_reload_ (vm, reload); + + if ( mix_code_file_is_debug (file) ) + { + vm->symbol_table = mix_code_file_get_symbol_table (file); + vm->line_table = g_tree_new (cmp_uint_); + vm->address_table = g_tree_new (cmp_uint_); + } + + vm->src_file = sfile; + + while ( mix_code_file_get_ins (file, &ins) ) + { + set_cell_ (vm, ins.address, ins.ins); + if ( vm->line_table != NULL ) + { + g_tree_insert (vm->line_table, + GUINT_TO_POINTER (ins.lineno), + GUINT_TO_POINTER ((guint)ins.address)); + g_tree_insert (vm->address_table, + GUINT_TO_POINTER ((guint)ins.address), + GUINT_TO_POINTER (ins.lineno)); + } + } + set_loc_ (vm, mix_code_file_get_start_addr (file)); + set_start_ (vm, get_loc_ (vm)); + mix_code_file_delete (file); + + set_status_ (vm, MIX_VM_LOADED); + return TRUE; +} + +const mix_src_file_t * +mix_vm_get_src_file (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, NULL); + return vm->src_file; +} + +const mix_symbol_table_t * +mix_vm_get_symbol_table (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, NULL); + return vm->symbol_table; +} + +mix_address_t +mix_vm_get_prog_count (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, MIX_SHORT_ZERO); + return get_loc_ (vm); +} + +/* Get the source line number for a given address */ +guint +mix_vm_get_address_lineno (const mix_vm_t *vm, mix_address_t addr) +{ + gpointer gp_addr = GUINT_TO_POINTER ((guint)addr); + guint lineno; + + g_return_val_if_fail (vm != NULL, 0); + if (!(MEMOK_ (addr))) return 0; + lineno = GPOINTER_TO_UINT (g_tree_lookup (vm->address_table, gp_addr)); + return lineno; +} + +/* Get the address for a given source line number */ +typedef struct +{ + guint lineno; + mix_address_t result; +} addr_traverse_t; + +static gint +get_address_ (gpointer key, gpointer value, gpointer data) +{ + addr_traverse_t *tr = (addr_traverse_t *)data; + if (GPOINTER_TO_UINT (key) == tr->lineno) + { + tr->result = mix_short_new (GPOINTER_TO_UINT (value)); + return TRUE; + } + return (GPOINTER_TO_UINT (key) < tr->lineno)? FALSE:TRUE; +} + +mix_address_t +mix_vm_get_lineno_address (const mix_vm_t *vm, guint lineno) +{ + addr_traverse_t tr; + + g_return_val_if_fail (vm != NULL, MIX_VM_CELL_NO); + if (!vm->line_table) return MIX_VM_CELL_NO; + tr.lineno = lineno; + tr.result = MIX_VM_CELL_NO; + g_tree_foreach (vm->line_table, get_address_, (gpointer)&tr); + return tr.result; +} + + +/* continue execution of instructions in memory */ +mix_vm_status_t +mix_vm_run (mix_vm_t *vm) +{ + mix_ins_t ins; + g_return_val_if_fail (vm != NULL, MIX_VM_ERROR); + + while ( !is_halted_ (vm) ) + { + mix_word_to_ins_uncheck (get_cell_ (vm, get_loc_ (vm)), ins); + vm->address_list = + g_slist_prepend (vm->address_list, + GINT_TO_POINTER ((gint)get_loc_ (vm))); + if ( !(*ins_handlers_[ins.opcode]) (vm,&ins) ) + return set_status_ (vm, MIX_VM_ERROR); + else + update_time_ (vm, &ins); + if (bp_is_set_ (vm, get_loc_ (vm))) + return set_status_ (vm, MIX_VM_BREAK); + if (mix_predicate_list_eval (get_pred_list_ (vm))) + return set_status_ (vm, MIX_VM_COND_BREAK); + if (get_loc_ (vm) >= MIX_VM_CELL_NO) halt_ (vm, TRUE); + } + return set_status_ (vm, MIX_VM_HALT); +} + +/* execute next memory instruction */ +mix_vm_status_t +mix_vm_exec_next (mix_vm_t *vm) +{ + mix_ins_t ins; + g_return_val_if_fail (vm != NULL, MIX_VM_ERROR); + if (get_loc_ (vm) >= MIX_VM_CELL_NO) halt_ (vm, TRUE); + if (is_halted_ (vm)) return set_status_ (vm, MIX_VM_HALT); + vm->address_list = + g_slist_prepend (vm->address_list, + GINT_TO_POINTER ((gint)get_loc_ (vm))); + mix_word_to_ins_uncheck (get_cell_ (vm, get_loc_ (vm)), ins); + if (!(*ins_handlers_[ins.opcode]) (vm, &ins)) + return set_status_ (vm, MIX_VM_ERROR); + else + update_time_ (vm, &ins); + if (is_halted_ (vm)) return set_status_ (vm, MIX_VM_HALT); + if (bp_is_set_ (vm, get_loc_ (vm))) return set_status_ (vm, MIX_VM_BREAK); + if (mix_predicate_list_eval (get_pred_list_ (vm))) + return set_status_ (vm, MIX_VM_COND_BREAK); + return set_status_ (vm, MIX_VM_RUNNING); +} + +/* get the current execution status */ +mix_vm_status_t +mix_vm_get_run_status (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, MIX_VM_ERROR); + return get_status_ (vm); +} + +mix_vm_error_t +mix_vm_get_last_error (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, MIX_VM_ERROR_UNEXPECTED); + return get_last_error_ (vm); +} + +const gchar * +mix_vm_get_last_error_string (const mix_vm_t *vm) +{ + return mix_vm_get_error_string (mix_vm_get_last_error (vm)); +} + +const gchar * +mix_vm_get_error_string (mix_vm_error_t code) +{ + static const gchar *errors[] = { + N_("No error"), + N_("Invalid memory address"), + N_("Invalid device number"), + N_("Invalid fspec"), + N_("Invalid M-value"), + N_("Cannot access device"), + N_("Cannot access device for reading"), + N_("Cannot access device for writing"), + N_("Unexpected error"), + N_("Unknow error code") + }; + + return errors[(code < 0 || code > MIX_VM_ERROR_UNEXPECTED) + ? MIX_VM_ERROR_UNEXPECTED + 1 : code]; +} + +/* Breakpoints */ +gulong +mix_vm_get_break_lineno (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, 0); + if (vm->address_table == NULL) + return 0; + else + { + gpointer loc = GUINT_TO_POINTER ((guint)get_loc_ (vm)); + return GPOINTER_TO_UINT (g_tree_lookup (vm->address_table,loc)); + } +} + +typedef struct +{ + mix_vm_t *vm; + guint lineno; + gint result; +} bp_traverse_t; + +static gint +set_break_ (gpointer key, gpointer value, gpointer data) +{ + bp_traverse_t *tr = (bp_traverse_t *)data; + if (GPOINTER_TO_UINT (key) >= tr->lineno) + { + bp_set_ (tr->vm, mix_short_new (GPOINTER_TO_UINT (value))); + tr->lineno = GPOINTER_TO_UINT (key); + tr->result = MIX_VM_BP_OK; + return TRUE; + } + return FALSE; +} + +gint /* if >0 the line no. of the break point */ +mix_vm_set_breakpoint (mix_vm_t *vm, guint lineno) +{ + bp_traverse_t tr; + + g_return_val_if_fail (vm != NULL, MIX_VM_BP_ERROR); + if (!vm->line_table) return MIX_VM_BP_NDEBUG; + tr.lineno = lineno; + tr.vm = vm; + tr.result = MIX_VM_BP_INV_LINE; + g_tree_foreach (vm->line_table, set_break_, (gpointer)&tr); + if (tr.result == MIX_VM_BP_OK) + return tr.lineno; + else + return tr.result; +} + +gint +mix_vm_set_breakpoint_address (mix_vm_t *vm, guint address) +{ + g_return_val_if_fail (vm != NULL, MIX_VM_BP_ERROR); + if (address >= MIX_VM_CELL_NO) + return MIX_VM_BP_INV_ADDRESS; + else + bp_set_ (vm, mix_short_new (address)); + return MIX_VM_BP_OK; +} + +gboolean +mix_vm_has_breakpoint_at_address (const mix_vm_t *vm, guint address) +{ + g_return_val_if_fail (vm != NULL, FALSE); + if (address >= MIX_VM_CELL_NO) return FALSE; + return (bp_is_set_ (vm, address)); +} + +static gint +clear_break_ (gpointer key, gpointer value, gpointer data) +{ + bp_traverse_t *tr = (bp_traverse_t *)data; + if (GPOINTER_TO_UINT (key) == tr->lineno) + { + bp_clear_ (tr->vm, mix_short_new (GPOINTER_TO_UINT (value))); + tr->result = MIX_VM_BP_OK; + return TRUE; + } + else if (GPOINTER_TO_UINT (key) > tr->lineno) + return TRUE; + + return FALSE; +} + +gint /* one of MIX_VM_BP_ */ +mix_vm_clear_breakpoint (mix_vm_t *vm, guint lineno) +{ + bp_traverse_t tr; + + g_return_val_if_fail (vm != NULL, MIX_VM_BP_ERROR); + if (!vm->line_table) return MIX_VM_BP_NDEBUG; + tr.lineno = lineno; + tr.vm = vm; + tr.result = MIX_VM_BP_INV_LINE; + g_tree_foreach (vm->line_table, clear_break_, (gpointer)&tr); + return tr.result; +} + +gint +mix_vm_clear_breakpoint_address (mix_vm_t *vm, guint address) +{ + g_return_val_if_fail (vm != NULL, MIX_VM_BP_ERROR); + if (address >= MIX_VM_CELL_NO) + return MIX_VM_BP_INV_ADDRESS; + else + bp_clear_ (vm, mix_short_new (address)); + return MIX_VM_BP_OK; +} + +void +mix_vm_clear_all_breakpoints (mix_vm_t *vm) +{ + g_return_if_fail (vm != NULL); + bp_clear_all_ (vm); + mix_predicate_list_clear (get_pred_list_ (vm)); +} + + +gboolean +mix_vm_set_conditional_breakpoint (mix_vm_t *vm, mix_predicate_t *pred) +{ + g_return_val_if_fail (vm != NULL, FALSE); + g_return_val_if_fail (pred != NULL, FALSE); + mix_predicate_list_add (get_pred_list_ (vm), pred); + return TRUE; +} + +gboolean +mix_vm_clear_conditional_breakpoint (mix_vm_t *vm, mix_predicate_t *pred) +{ + g_return_val_if_fail (vm != NULL, FALSE); + g_return_val_if_fail (pred != NULL, FALSE); + return mix_predicate_list_remove (get_pred_list_ (vm), pred); +} + +const gchar * +mix_vm_get_last_breakpoint_message (const mix_vm_t *vm) +{ + const mix_predicate_t *last = NULL; + g_return_val_if_fail (vm != NULL, NULL); + if ((last = mix_predicate_list_last_true_eval (get_pred_list_ (vm))) != NULL) + return mix_predicate_get_message (last); + return NULL; +} + +mix_predicate_type_t +mix_vm_get_last_conditional_breakpoint_type (const mix_vm_t *vm) +{ + const mix_predicate_t *last = NULL; + g_return_val_if_fail (vm != NULL, MIX_PRED_INVALID); + last = mix_predicate_list_last_true_eval (get_pred_list_ (vm)); + return last? mix_predicate_get_type (last) : MIX_PRED_INVALID; +} + +/* Get the vm uptime, defined as the time spent executing instructions */ +mix_time_t +mix_vm_get_uptime (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, 0); + return mix_vm_clock_get_time (get_clock_ (vm)); +} + +/* Get the list of addresses for executed instructions */ +const GSList * +mix_vm_get_backtrace (const mix_vm_t *vm) +{ + g_return_val_if_fail (vm != NULL, NULL); + return get_address_list_ (vm); +} diff --git a/mixlib/mix_vm.h b/mixlib/mix_vm.h new file mode 100644 index 0000000..12b082a --- /dev/null +++ b/mixlib/mix_vm.h @@ -0,0 +1,255 @@ +/* ---------------------- mix_vm.h : + * Types and functions implementing the MIX virtual machine + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001, 2004 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. + * + */ + + +#ifndef MIX_VM_H +#define MIX_VM_H + +#include "mix_types.h" +#include "mix_ins.h" +#include "mix_device.h" +#include "mix_code_file.h" +#include "mix_src_file.h" +#include "mix_symbol_table.h" +#include "mix_vm_clock.h" + +/* Comparison flag */ +typedef enum { mix_LESS, mix_EQ, mix_GREAT } mix_cmpflag_t; + +/* Number of memory cells in the virtual machine */ +enum { MIX_VM_CELL_NO = 4000 }; + +/* Opaque type for the mix virtual machine */ +typedef struct mix_vm_t mix_vm_t; + +/* Create/destroy a mix vm */ +extern mix_vm_t * +mix_vm_new(void); + +extern void +mix_vm_delete(mix_vm_t * vm); + +/* connect devices to a virtual machine */ +extern mix_device_t * /* previously connected device */ +mix_vm_connect_device (mix_vm_t *vm, mix_device_t *device); + +/* get device */ +extern mix_device_t * +mix_vm_get_device (const mix_vm_t *vm, mix_device_type_t dev); + +/* install a device factory for automatic connection */ +typedef mix_device_t * (* mix_device_factory_t) (mix_device_type_t device); +extern void +mix_vm_set_device_factory (mix_vm_t *vm, mix_device_factory_t factory); + +/* Reset a vm (set state as of a newly created one) */ +extern void +mix_vm_reset(mix_vm_t * vm); + +/* Set start address for execution */ +extern void +mix_vm_set_start_addr(mix_vm_t *vm, mix_address_t addr); + +/* Access to the vm's registers */ +extern mix_word_t +mix_vm_get_rA(const mix_vm_t *vm); + +extern mix_word_t +mix_vm_get_rX(const mix_vm_t *vm); + +extern mix_short_t +mix_vm_get_rJ(const mix_vm_t *vm); + +extern mix_short_t +mix_vm_get_rI(const mix_vm_t *vm, guint idx); + +extern void +mix_vm_set_rA(mix_vm_t *vm, mix_word_t value); + +extern void +mix_vm_set_rX(mix_vm_t *vm, mix_word_t value); + +extern void +mix_vm_set_rJ(mix_vm_t *vm, mix_short_t value); + +extern void +mix_vm_set_rI(mix_vm_t *vm, guint idx, mix_short_t value); + +/* Access to the comparison flag and overflow toggle */ +extern mix_cmpflag_t +mix_vm_get_cmpflag(const mix_vm_t *vm); + +extern void +mix_vm_set_cmpflag(mix_vm_t *vm, mix_cmpflag_t value); + +extern gboolean +mix_vm_get_overflow(const mix_vm_t *vm); + +extern void +mix_vm_set_overflow(mix_vm_t *vm, gboolean value); + +extern void +mix_vm_toggle_overflow(mix_vm_t *vm); + +extern gboolean +mix_vm_is_halted(const mix_vm_t *vm); + +/* Access to memory cells */ +extern mix_word_t +mix_vm_get_addr_contents(const mix_vm_t *vm, mix_address_t addr); + +extern void +mix_vm_set_addr_contents(mix_vm_t *vm, mix_address_t addr, mix_word_t value); + +/* Execution of instructions and programs */ +extern gboolean /* TRUE if success */ +mix_vm_exec_ins(mix_vm_t *vm, const mix_ins_t *ins); + +/* Load a code file into memory (-name- does not need the default extension) + * resetting the vm's state + */ +extern gboolean +mix_vm_load_file(mix_vm_t *vm, const gchar *name); + +/* Get the source file object corresponding to the last loaded code file */ +extern const mix_src_file_t * +mix_vm_get_src_file (const mix_vm_t *vm); + +/* Get symbol table of loaded file */ +extern const mix_symbol_table_t * +mix_vm_get_symbol_table (const mix_vm_t *vm); + +/* Get current program counter */ +extern mix_address_t +mix_vm_get_prog_count (const mix_vm_t *vm); + +/* Get the source line number for a given address */ +extern guint +mix_vm_get_address_lineno (const mix_vm_t *vm, mix_address_t addr); + +/* Get the address for a given source line number */ +extern mix_address_t +mix_vm_get_lineno_address (const mix_vm_t *vm, guint lineno); + +/* continue execution of instructions in memory */ +/* Possible outcomes */ +typedef enum { + MIX_VM_ERROR, /* error executing instructions */ + MIX_VM_BREAK, /* breakpoint found */ + MIX_VM_COND_BREAK, /* conditional breakpoint found */ + MIX_VM_HALT, /* end of execution */ + MIX_VM_RUNNING, /* successful instruction execution */ + MIX_VM_LOADED, /* program loaded */ + MIX_VM_EMPTY /* no program loaded */ +} mix_vm_status_t; + +/* execution errors */ +typedef enum { + MIX_VM_ERROR_NONE, /* no error */ + MIX_VM_ERROR_BAD_ACCESS, /* bad memory address */ + MIX_VM_ERROR_BAD_DEVICE_NO, /* bad device number */ + MIX_VM_ERROR_BAD_FSPEC, /* invalid fspec */ + MIX_VM_ERROR_BAD_M, /* invalid M-value */ + MIX_VM_ERROR_DEV_CTL, /* error accessing device for ioctl */ + MIX_VM_ERROR_DEV_READ, /* error accessing device for reading */ + MIX_VM_ERROR_DEV_WRITE, /* error accessing device for writing */ + MIX_VM_ERROR_UNEXPECTED /* unexpected error */ +} mix_vm_error_t; + +extern mix_vm_error_t +mix_vm_get_last_error (const mix_vm_t *vm); + +extern const gchar * +mix_vm_get_last_error_string (const mix_vm_t *vm); + +extern const gchar * +mix_vm_get_error_string (mix_vm_error_t code); + +/* run until next breakpoint or end of execution */ +extern mix_vm_status_t +mix_vm_run (mix_vm_t *vm); + +/* execute next memory instruction */ +extern mix_vm_status_t +mix_vm_exec_next (mix_vm_t *vm); + +/* get the current execution status */ +extern mix_vm_status_t +mix_vm_get_run_status (const mix_vm_t *vm); + +/* get the line no. of the last break or 0 if not found */ +extern gulong +mix_vm_get_break_lineno (const mix_vm_t *vm); + + +/* Breakpoints */ +/* possible error outcomes */ +enum { + MIX_VM_BP_ERROR = -4, /* internal error */ + MIX_VM_BP_NDEBUG = -3, /* no debug info */ + MIX_VM_BP_INV_ADDRESS = -2, /* address out of range */ + MIX_VM_BP_INV_LINE = -1, /* invalid line no. */ + MIX_VM_BP_OK = 0 /* success */ +}; + + +extern gint /* if >0 the line no. of the break point */ +mix_vm_set_breakpoint (mix_vm_t *vm, guint lineno); + +extern gint /* one of MIX_VM_BP_ */ +mix_vm_set_breakpoint_address (mix_vm_t *vm, guint address); + +extern gint /* one of MIX_VM_BP_ */ +mix_vm_clear_breakpoint (mix_vm_t *vm, guint lineno); + +extern gint /* one of MIX_VM_BP_ */ +mix_vm_clear_breakpoint_address (mix_vm_t *vm, guint address) ; + +extern gboolean +mix_vm_has_breakpoint_at_address (const mix_vm_t *vm, guint address); + +extern void +mix_vm_clear_all_breakpoints (mix_vm_t *vm); + +#include "mix_predicate.h" +extern gboolean +mix_vm_set_conditional_breakpoint (mix_vm_t *vm, mix_predicate_t *pred); + +extern gboolean +mix_vm_clear_conditional_breakpoint (mix_vm_t *vm, mix_predicate_t *pred); + +extern const gchar * +mix_vm_get_last_breakpoint_message (const mix_vm_t *vm); + +extern mix_predicate_type_t +mix_vm_get_last_conditional_breakpoint_type (const mix_vm_t *vm); + +/* Get the vm uptime, defined as the time spent executing instructions */ +extern mix_time_t +mix_vm_get_uptime (const mix_vm_t *vm); + +/* Get the list of addresses for executed instructions */ +extern const GSList * +mix_vm_get_backtrace (const mix_vm_t *vm); + + +#endif /* MIX_VM_H */ + diff --git a/mixlib/mix_vm_clock.c b/mixlib/mix_vm_clock.c new file mode 100644 index 0000000..cc39992 --- /dev/null +++ b/mixlib/mix_vm_clock.c @@ -0,0 +1,70 @@ +/* -*-c-*- -------------- mix_vm_clock.c : + * Implementation of the functions declared in mix_vm_clock.h + * ------------------------------------------------------------------ + * $Id: mix_vm_clock.c,v 1.3 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2001 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 "mix_vm_clock.h" + +static const mix_time_t exec_times_[] = { + 1, 2, 2, 10, 12, 10, 2, 1, + 2, 2, 2, 2 , 2, 2, 2, 2, + 2, 2, 2, 2 , 2, 2, 2, 2, + 2, 2, 2, 2 , 2, 2, 2, 2, + 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2 , 2, 2, 2, 2 +}; + + +/* Create/delete a clock */ +mix_vm_clock_t * +mix_vm_clock_new () +{ + mix_vm_clock_t *result = g_new (mix_vm_clock_t, 1); + result->time = 0; + return result; +} + +void +mix_vm_clock_delete (mix_vm_clock_t *clock) +{ + g_return_if_fail (clock != NULL); +} + +/* Increase time with the units needed to execute ins */ +mix_time_t +mix_vm_clock_add_lapse (mix_vm_clock_t *clock, const mix_ins_t *ins) +{ + mix_time_t t = 0; + g_return_val_if_fail (clock != NULL, 0); + if (ins != NULL) + { + t = exec_times_[ins->opcode]; + if (ins->opcode == mix_opMOVE) { + t += 2 * (ins->fspec); + } + } + clock->time += t; + return t; +} + diff --git a/mixlib/mix_vm_clock.h b/mixlib/mix_vm_clock.h new file mode 100644 index 0000000..3c19973 --- /dev/null +++ b/mixlib/mix_vm_clock.h @@ -0,0 +1,55 @@ +/* -*-c-*- ---------------- mix_vm_clock.h : + * Declaration of mix_vm_clock_t, a clock for the MIX virtual machine. + * ------------------------------------------------------------------ + * Last change: Time-stamp: <01/02/20 00:23:58 jose> + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef MIX_VM_CLOCK_H +#define MIX_VM_CLOCK_H + +#include "mix.h" +#include "mix_ins.h" + +/* the type of time unit */ +typedef gulong mix_time_t; + +/* the clock type */ +typedef struct mix_vm_clock_t +{ + mix_time_t time; +} mix_vm_clock_t; + +/* Create/delete a clock */ +extern mix_vm_clock_t * +mix_vm_clock_new (); + +extern void +mix_vm_clock_delete (mix_vm_clock_t *clock); + +/* Increase time with the units needed to execute ins */ +extern mix_time_t /* the added lapse */ +mix_vm_clock_add_lapse (mix_vm_clock_t *clock, const mix_ins_t *ins); + +/* Get the time since creation */ +#define mix_vm_clock_get_time(clock) (clock? clock->time:0) + +#endif /* MIX_VM_CLOCK_H */ + diff --git a/mixlib/mix_vm_command.c b/mixlib/mix_vm_command.c new file mode 100644 index 0000000..350dae5 --- /dev/null +++ b/mixlib/mix_vm_command.c @@ -0,0 +1,641 @@ +/* -*-c-*- -------------- mix_vm_command.c : + * Implementation of the functions declared in mix_vm_command.h + * ------------------------------------------------------------------ + * $Id: mix_vm_command.c,v 1.30 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2002, 2004 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 "xmix_vm_command.h" +#include "xmix_vm_handlers.h" + +#ifdef HAVE_LIBHISTORY +# include <readline/history.h> +#endif + +#ifdef HAVE_LIBREADLINE +# include <readline/readline.h> +#endif + +/* hook execution */ +static void +exec_hook_list_ (GSList *list, mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + while (list) + { + hook_ *hook = (hook_ *)list->data; + if (hook) + (hook->func)(dis, arg, hook->data); + list = list->next; + } +} + +static void +exec_global_hook_list_ (GSList *list, + mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, const gchar *arg) +{ + while (list) + { + global_hook_ *hook = (global_hook_ *)list->data; + if (hook) + (hook->func)(dis, cmd, arg, hook->data); + list = list->next; + } +} + +/* conversion from/to commands to strings */ +const gchar * +mix_vm_command_to_string (mix_vm_command_t cmd) +{ + if (cmd < MIX_CMD_INVALID) return commands_[cmd].name; + else return NULL; +} + +mix_vm_command_t +mix_vm_command_from_string (const gchar *name) +{ + gint cmd = 0; + while (cmd < MIX_CMD_INVALID && strcmp (name, commands_[cmd].name)) + ++cmd; + return cmd; +} + +/* get help string about a command */ +const gchar * +mix_vm_command_help (mix_vm_command_t cmd) +{ + if (cmd < MIX_CMD_INVALID) return commands_[cmd].doc; + else return NULL; +} + +const gchar * +mix_vm_command_usage (mix_vm_command_t cmd) +{ + if (cmd < MIX_CMD_INVALID) return commands_[cmd].usage; + else return NULL; +} + +/* create a new command dispatcher */ +static GCompletion * +make_completions_ (void) +{ + GList *cmds = NULL; + gint k; + + GCompletion *completions = g_completion_new (NULL); + for (k = 0; k < MIX_CMD_INVALID; ++k) + cmds = g_list_append (cmds, (gpointer) mix_vm_command_to_string (k)); + g_completion_add_items (completions, cmds); + return completions; +} + +mix_vm_cmd_dispatcher_t * +mix_vm_cmd_dispatcher_new (FILE *out_fd, /* output messages file */ + FILE *err_fd /* error messages file */) +{ + mix_vm_cmd_dispatcher_t *result = NULL; + int k; + + /* g_return_val_if_fail (out_fd && err_fd, NULL); */ + + result = g_new (mix_vm_cmd_dispatcher_t, 1); + result->result = TRUE; + result->out = out_fd; + result->err = err_fd; + result->log_msg = TRUE; + result->uptime = result->laptime = result->progtime = 0; + result->printtime = TRUE; + result->trace = FALSE; + result->program = NULL; + result->editor = NULL; + result->assembler = NULL; + result->eval = mix_eval_new (); + result->dump = mix_dump_context_new (out_fd, + MIX_SHORT_ZERO, MIX_SHORT_ZERO, + MIX_DUMP_ALL); + result->vm = mix_vm_new (); + result->global_pre = result->global_post = NULL; + + for (k =0; k < MIX_CMD_INVALID; ++k) + result->pre_hooks[k] = result->post_hooks[k] = NULL; + + result->config = NULL; + + for (k = 0; k < PRNO_; ++k) + result->preds[k] = mix_predicate_new (k); + + result->mem_preds = g_hash_table_new (NULL, NULL); + result->commands = g_hash_table_new (g_str_hash, g_str_equal); + result->completions = make_completions_ (); + + return result; +} + +mix_vm_cmd_dispatcher_t * +mix_vm_cmd_dispatcher_new_with_config (FILE *out, FILE *err, + mix_config_t *config) +{ + mix_vm_cmd_dispatcher_t *result = mix_vm_cmd_dispatcher_new (out, err); + if (result != NULL && (result->config = config) != NULL) + { + gint hsize = 0; + const gchar *val = mix_config_get (result->config, TRACING_KEY_); + if (val) cmd_strace_ (result, val); + val = mix_config_get (result->config, EDITOR_KEY_); + if (val) mix_vm_cmd_dispatcher_set_editor (result, val); + val = mix_config_get (result->config, ASM_KEY_); + if (val) mix_vm_cmd_dispatcher_set_assembler (result, val); + val = mix_config_get (result->config, TIMING_KEY_); + if (val) cmd_stime_ (result, val); + val = mix_config_get_devices_dir (result->config); + if (!val || !mix_stat_dir (val, "devices")) + { + gchar *dirname = + g_path_get_dirname (mix_config_get_filename (config)); + cmd_sddir_ (result, dirname); + g_free (dirname); + } + else + mix_device_set_dir (val); + val = mix_config_get (result->config, LOGGING_KEY_); + if (val) cmd_slog_ (result, val); +#ifdef HAVE_LIBHISTORY + val = mix_config_get_history_file (result->config); + hsize = mix_config_get_history_size (result->config); + using_history (); + stifle_history (hsize); + if (val) + { + read_history ((char *)val); + history_set_pos (history_base + history_length - 1); + } +#endif + } + return result; +} + + +/* delete (does not close the fds in the constructor) */ +static gboolean +del_pred_ (gpointer key, gpointer val, gpointer data) +{ + if (val) mix_predicate_delete ((mix_predicate_t *)val); + return TRUE; +} + +static void +del_hook_ (gpointer data, gpointer ignored) +{ + if (data) g_free (data); +} + +static void +del_hook_list_ (GSList *s) +{ + if (s) + { + g_slist_foreach (s, del_hook_, NULL); + g_slist_free (s); + } +} + +void +mix_vm_cmd_dispatcher_delete (mix_vm_cmd_dispatcher_t *dis) +{ + const gchar *hfile = NULL; + gint k; + + g_return_if_fail (dis != NULL); + mix_eval_delete (dis->eval); + mix_dump_context_delete (dis->dump); + mix_vm_delete (dis->vm); + if (dis->editor) g_free (dis->editor); + if (dis->editor) g_free (dis->assembler); +#ifdef HAVE_LIBHISTORY + if (dis->config && (hfile = mix_config_get_history_file + (dis->config))) + write_history ((char *)hfile); +#endif + for (k = 0; k < PRNO_; ++k) mix_predicate_delete (dis->preds[k]); + g_hash_table_foreach_remove (dis->mem_preds, del_pred_, NULL); + g_hash_table_destroy (dis->mem_preds); + g_hash_table_destroy (dis->commands); + g_completion_free (dis->completions); + for (k = 0; k < MIX_CMD_INVALID; ++k) + { + del_hook_list_ (dis->pre_hooks[k]); + del_hook_list_ (dis->post_hooks[k]); + } + del_hook_list_ (dis->global_pre); + del_hook_list_ (dis->global_post); + g_free (dis); +} + +/* register new commands for a dispatcher */ +void +mix_vm_cmd_dispatcher_register_new (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_info_t *cmd) +{ + GList *list = NULL; + g_return_if_fail (dis != NULL); + g_return_if_fail (cmd != NULL); + g_hash_table_insert (dis->commands, (gpointer)cmd->name, (gpointer)cmd); + list = g_list_append (list, (gpointer)cmd->name); + g_completion_add_items (dis->completions, list); +} + +const GList * +mix_vm_cmd_dispatcher_complete (const mix_vm_cmd_dispatcher_t *dis, + const gchar *cmd, gchar **prefix) +{ + char *cp; + GList *result; + + g_return_val_if_fail (dis != NULL, NULL); + g_return_val_if_fail (cmd != NULL, NULL); + + cp = g_strdup (cmd); + result = g_completion_complete (dis->completions, cp, prefix); + g_free (cp); + return result; +} + +/* set/get out/error streams */ +FILE * +mix_vm_cmd_dispatcher_get_out_stream (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis, NULL); + return dis->out; +} + +FILE * +mix_vm_cmd_dispatcher_get_err_stream (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis, NULL); + return dis->err; +} + +FILE * /* old output stream */ +mix_vm_cmd_dispatcher_set_out_stream (mix_vm_cmd_dispatcher_t *dis, FILE *out) +{ + FILE *old = NULL; + g_return_val_if_fail (dis != NULL, old); + old = dis->out; + dis->out = out; + dis->dump->channel = out; + return old; +} + +FILE * /* old error stream */ +mix_vm_cmd_dispatcher_set_error_stream (mix_vm_cmd_dispatcher_t *dis, + FILE *err) +{ + FILE *old = NULL; + g_return_val_if_fail (dis != NULL, old); + old = dis->err; + dis->err = err; + return old; +} + + +/* set editor and compiler templates */ +void +mix_vm_cmd_dispatcher_set_editor (mix_vm_cmd_dispatcher_t *dis, + const gchar *edit_tplt) +{ + g_return_if_fail (dis != NULL); + if (dis->editor) g_free (dis->editor); + dis->editor = (edit_tplt) ? g_strdup (edit_tplt) : NULL; + if (dis->config && dis->editor) + mix_config_update (dis->config, EDITOR_KEY_, dis->editor); +} + +void +mix_vm_cmd_dispatcher_set_assembler (mix_vm_cmd_dispatcher_t *dis, + const gchar *asm_tplt) +{ + g_return_if_fail (dis != NULL); + if (dis->assembler) g_free (dis->assembler); + dis->assembler = (asm_tplt) ? g_strdup (asm_tplt) : NULL; + if (dis->config && dis->assembler) + mix_config_update (dis->config, ASM_KEY_, dis->assembler); +} + +const gchar * +mix_vm_cmd_dispatcher_get_editor (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, NULL); + return dis->editor; +} + +const gchar * +mix_vm_cmd_dispatcher_get_assembler (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, NULL); + return dis->assembler; +} + +const gchar * +mix_vm_cmd_dispatcher_get_src_file_path (const mix_vm_cmd_dispatcher_t *dis) +{ + static gchar *PATH = NULL; + + const mix_vm_t *vm = mix_vm_cmd_dispatcher_get_vm (dis); + const mix_src_file_t *f = mix_vm_get_src_file (vm); + + if (PATH) + { + g_free (PATH); + PATH = NULL; + } + + if (f) + PATH = mix_file_complete_name (mix_src_file_get_path (f), MIX_SRC_DEFEXT); + + return PATH; +} + +const gchar * +mix_vm_cmd_dispatcher_get_program_path (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, NULL); + return dis->program; +} + +/* install hooks */ +void +mix_vm_cmd_dispatcher_pre_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, + mix_vm_cmd_hook_t hook, gpointer data) +{ + hook_ *phook; + + g_return_if_fail (dis != NULL); + g_return_if_fail (cmd < MIX_CMD_INVALID); + + phook = g_new (hook_, 1); + phook->func = hook; + phook->data = data; + + dis->pre_hooks[cmd] = g_slist_append (dis->pre_hooks[cmd], phook); +} + +void +mix_vm_cmd_dispatcher_post_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, + mix_vm_cmd_hook_t hook, gpointer data) +{ + hook_ *phook; + + g_return_if_fail (dis != NULL); + g_return_if_fail (cmd < MIX_CMD_INVALID); + + phook = g_new (hook_, 1); + phook->func = hook; + phook->data = data; + + dis->post_hooks[cmd] = g_slist_append (dis->post_hooks[cmd], phook); +} + +void +mix_vm_cmd_dispatcher_global_pre_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_cmd_global_hook_t hook, + gpointer data) +{ + global_hook_ *phook; + + g_return_if_fail (dis != NULL); + + phook = g_new (global_hook_, 1); + phook->func = hook; + phook->data = data; + + dis->global_pre = g_slist_append (dis->global_pre, phook); +} + +void +mix_vm_cmd_dispatcher_global_post_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_cmd_global_hook_t hook, + gpointer data) +{ + global_hook_ *phook; + + g_return_if_fail (dis != NULL); + + phook = g_new (global_hook_, 1); + phook->func = hook; + phook->data = data; + + dis->global_post = g_slist_append (dis->global_post, phook); +} + +/* dispatch a command */ +gboolean /* TRUE if success, FALSE otherwise */ +mix_vm_cmd_dispatcher_dispatch (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, const gchar *arg) +{ + g_return_val_if_fail (dis != NULL, FALSE); + + if (dis->global_pre) + exec_global_hook_list_ (dis->global_pre, dis, cmd, arg); + + if (cmd < MIX_CMD_INVALID) + { + if (dis->pre_hooks[cmd]) + exec_hook_list_ (dis->pre_hooks[cmd], dis, arg); + + fflush (dis->out); + fflush (dis->err); + + dis->result = (commands_[cmd].func)(dis, arg); + + fflush (dis->out); + fflush (dis->err); + + if (dis->post_hooks[cmd]) + exec_hook_list_ (dis->post_hooks[cmd], dis, arg); + + fflush (dis->out); + fflush (dis->err); + } + else + { + fprintf (dis->err, _("Unknown command. Try: help\n")); + } + + if (dis->global_post) + exec_global_hook_list_ (dis->global_post, dis, cmd, arg); + + fflush (dis->out); + fflush (dis->err); + return dis->result; +} + +/* dispatch a command in text format */ +gboolean +mix_vm_cmd_dispatcher_dispatch_text (mix_vm_cmd_dispatcher_t *dis, + const gchar *text) +{ + gchar *cp, *arg = ""; + int k = 0; + + g_return_val_if_fail (dis != NULL, FALSE); + g_return_val_if_fail (text != NULL, FALSE); + + cp = g_strdup (text); + while (cp[k] && !isspace (cp[k])) ++k; + if (cp[k]) + { + cp[k] = '\0'; ++k; + while (cp[k] && isspace (cp[k])) ++k; + arg = cp + k; + } + + (void) mix_vm_cmd_dispatcher_dispatch_split_text (dis, cp, arg); + + g_free (cp); + + return dis->result; +} + +/* dispatch a command in text format, with command and arg split */ +gboolean +mix_vm_cmd_dispatcher_dispatch_split_text (mix_vm_cmd_dispatcher_t *dis, + const gchar *command, + const gchar *arg) +{ + mix_vm_command_info_t *info; + + g_return_val_if_fail (dis, FALSE); + + if (!command) return FALSE; + if (!arg) arg = ""; + + info = (mix_vm_command_info_t *)g_hash_table_lookup (dis->commands, command); + + if (info) + { + if (dis->global_pre) + exec_global_hook_list_ (dis->global_pre, dis, MIX_CMD_LOCAL, arg); + + fflush (dis->out); + fflush (dis->err); + + dis->result = info->func (dis, arg); + + fflush (dis->out); + fflush (dis->err); + + if (dis->global_post) + exec_global_hook_list_ (dis->global_post, dis, MIX_CMD_LOCAL, arg); + + fflush (dis->out); + fflush (dis->err); + } + else + dis->result = + mix_vm_cmd_dispatcher_dispatch (dis, + mix_vm_command_from_string (command), + arg); + return dis->result; +} + + +/* get the last dispatch's result */ +gboolean +mix_vm_cmd_dispatcher_get_last_result (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, FALSE); + return dis->result; +} + +/* get total uptime */ +mix_time_t +mix_vm_cmd_dispatcher_get_uptime (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, 0); + return dis->uptime; +} + +/* get program total time */ +mix_time_t +mix_vm_cmd_dispatcher_get_progtime (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, 0); + return dis->progtime; +} + +/* get time lapse */ +mix_time_t +mix_vm_cmd_dispatcher_get_laptime (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, 0); + return dis->laptime; +} + +/* toggle time printing */ +void +mix_vm_cmd_dispatcher_print_time (mix_vm_cmd_dispatcher_t * dis, gboolean print) +{ + g_return_if_fail (dis != NULL); + dis->printtime = print; +} + +/* get the mix vm */ +const mix_vm_t * +mix_vm_cmd_dispatcher_get_vm (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, NULL); + return dis->vm; +} + +/* src file info */ +gulong +mix_vm_cmd_dispatcher_get_src_file_lineno (const mix_vm_cmd_dispatcher_t *dis) +{ + g_return_val_if_fail (dis != NULL, 0); + return mix_vm_get_break_lineno (dis->vm); +} + +const gchar * +mix_vm_cmd_dispatcher_get_src_file_line (const mix_vm_cmd_dispatcher_t *dis, + gulong line, gboolean cr) +{ + const mix_src_file_t *file; + g_return_val_if_fail (dis != NULL, NULL); + + file = mix_vm_get_src_file (dis->vm); + + if (line == 0 || file == NULL) return cr? "" : "\n"; + + if (cr) + return mix_src_file_get_line (file, line); + else + { + enum {BUFF_SIZE = 256}; + static gchar BUFFER[BUFF_SIZE]; + int len = + g_snprintf (BUFFER, BUFF_SIZE, "%s", mix_src_file_get_line (file, line)); + if (len > 0 && BUFFER[len - 1] == '\n') BUFFER[len - 1] = '\0'; + return BUFFER; + } +} + + diff --git a/mixlib/mix_vm_command.h b/mixlib/mix_vm_command.h new file mode 100644 index 0000000..1ff561f --- /dev/null +++ b/mixlib/mix_vm_command.h @@ -0,0 +1,256 @@ +/* -*-c-*- ---------------- mix_vm_command.h : + * declarations for mix_vm_command_t, describing commands issued to a vm + * ------------------------------------------------------------------ + * $Id: mix_vm_command.h,v 1.15 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001 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. + * + */ + + +#ifndef MIX_VM_COMMAND_H +#define MIX_VM_COMMAND_H + +#include <stdio.h> +#include "mix.h" +#include "mix_vm.h" +#include "mix_config.h" + +/* mix_vm_cmd_dispatcher encapsulates a virtual machine and helper + objects, providing a command driven interface with output to + provided files (in the posix sense). +*/ +typedef struct mix_vm_cmd_dispatcher_t mix_vm_cmd_dispatcher_t; + +/* mix_vm_cmd_dispatcher understands the commands of this type */ +typedef enum { + MIX_CMD_HELP = 0, /* echo help message */ + MIX_CMD_LOAD, /* load a mix program */ + MIX_CMD_EDIT, /* edit mixal source */ + MIX_CMD_PEDIT, /* print editor command */ + MIX_CMD_SEDIT, /* set editor command*/ + MIX_CMD_COMPILE, /* compile mixal source */ + MIX_CMD_PASM, /* print compiler command */ + MIX_CMD_SASM, /* set assembler command */ + MIX_CMD_RUN, /* run a loaded program */ + MIX_CMD_NEXT, /* run next instruction */ + MIX_CMD_PSTAT, /* print current vm status */ + MIX_CMD_LOC, /* print location pointer */ + MIX_CMD_PSYM, /* print symbol */ + MIX_CMD_PREG, /* print registry */ + MIX_CMD_PFLAGS, /* print comp and overf flags */ + MIX_CMD_PALL, /* print all registers and flags */ + MIX_CMD_PMEM, /* print memory cells */ + MIX_CMD_SREG, /* set register value */ + MIX_CMD_SCMP, /* set comparison flag value */ + MIX_CMD_SOVER, /* set overflow toggle value */ + MIX_CMD_SMEM, /* set memory cell value */ + MIX_CMD_SSYM, /* set symbol value */ + MIX_CMD_SBP, /* set breakpoint at lineno */ + MIX_CMD_CBP, /* clear breakpoint at lineno */ + MIX_CMD_SBPA, /* set breakpoint at address */ + MIX_CMD_CBPA, /* clear breakpoint at address */ + MIX_CMD_SBPR, /* set breakpoint on register changed */ + MIX_CMD_CBPR, /* clear breakpoint on register changed */ + MIX_CMD_SBPM, /* set breakpoint on mem cell changed */ + MIX_CMD_CBPM, /* clear breakpoint on mem cell changed */ + MIX_CMD_SBPC, /* set breakpoint on comp flag changed */ + MIX_CMD_CBPC, /* clear breakpoint on comp flag changed */ + MIX_CMD_SBPO, /* set breakpoint on overflow toggled */ + MIX_CMD_CBPO, /* clear breakpoint on overflow toggled */ + MIX_CMD_CABP, /* clear all breakpoints */ + MIX_CMD_WEVAL, /* evaluate a w-expression */ + MIX_CMD_W2D, /* print word in decimal notation */ + MIX_CMD_STRACE, /* enable/disable instruction traces */ + MIX_CMD_PBT, /* print backtrace */ + MIX_CMD_STIME, /* enable/disable timing statistics */ + MIX_CMD_PTIME, /* print current time statistics */ + MIX_CMD_SDDIR, /* set device directory */ + MIX_CMD_PDDIR, /* print current device directory */ + MIX_CMD_SLOG, /* set on/off message logging */ + MIX_CMD_PPROG, /* print the current program path */ + MIX_CMD_PSRC, /* print the current program source path */ + MIx_CMD_PLINE, /* print the current line no */ + MIX_CMD_INVALID, /* invalid command identifier */ + MIX_CMD_LOCAL /* locally defined command */ +} mix_vm_command_t; + +/* new commands definition */ +typedef gboolean (*mix_vm_cmd_function_t) (mix_vm_cmd_dispatcher_t *, + const gchar *); +typedef struct { + const gchar *name; /* User printable name of the function. */ + mix_vm_cmd_function_t func; /* Function to call to do the job. */ + const char *doc; /* Documentation for this function. */ + const char *usage; /* Usage */ +} mix_vm_command_info_t; + +/* hook functions, to be invoked before and/or after command execution */ +typedef void (*mix_vm_cmd_hook_t)(mix_vm_cmd_dispatcher_t *dis, + const gchar *arg, gpointer data); + +/* global hook functions */ +typedef void (*mix_vm_cmd_global_hook_t)(mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, + const gchar *arg, gpointer data); + +/* conversion from/to commands to strings */ +extern const gchar * +mix_vm_command_to_string (mix_vm_command_t cmd); + +extern mix_vm_command_t +mix_vm_command_from_string (const gchar *name); + +/* get help string about a command */ +extern const gchar * +mix_vm_command_help (mix_vm_command_t cmd); + +extern const gchar * +mix_vm_command_usage (mix_vm_command_t cmd); + +/* create a new command dispatcher */ +extern mix_vm_cmd_dispatcher_t * +mix_vm_cmd_dispatcher_new (FILE *out, /* output messages file */ + FILE *err /* error messages file */); + +extern mix_vm_cmd_dispatcher_t * +mix_vm_cmd_dispatcher_new_with_config (FILE *out, FILE *err, + mix_config_t *config); + +/* delete (does not close the fds in the constructor) */ +extern void +mix_vm_cmd_dispatcher_delete (mix_vm_cmd_dispatcher_t *dis); + +/* register new commands for a dispatcher */ +extern void +mix_vm_cmd_dispatcher_register_new (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_info_t *cmd); + +/* get command completion list */ +const GList * +mix_vm_cmd_dispatcher_complete (const mix_vm_cmd_dispatcher_t *dis, + const gchar *cmd, gchar **prefix); + +/* set/get out/error streams */ +extern FILE * +mix_vm_cmd_dispatcher_get_out_stream (const mix_vm_cmd_dispatcher_t *dis); + +extern FILE * +mix_vm_cmd_dispatcher_get_err_stream (const mix_vm_cmd_dispatcher_t *dis); + +extern FILE * /* old output stream */ +mix_vm_cmd_dispatcher_set_out_stream (mix_vm_cmd_dispatcher_t *dis, + FILE *out); + +extern FILE * /* old error stream */ +mix_vm_cmd_dispatcher_set_error_stream (mix_vm_cmd_dispatcher_t *dis, + FILE *err); + +/* set editor and compiler templates */ +extern void +mix_vm_cmd_dispatcher_set_editor (mix_vm_cmd_dispatcher_t *dis, + const gchar *edit_tplt); + +extern void +mix_vm_cmd_dispatcher_set_assembler (mix_vm_cmd_dispatcher_t *dis, + const gchar *asm_tplt); + +extern const gchar * +mix_vm_cmd_dispatcher_get_src_file_path (const mix_vm_cmd_dispatcher_t *dis); + +extern const gchar * +mix_vm_cmd_dispatcher_get_program_path (const mix_vm_cmd_dispatcher_t *dis); + +extern const gchar * +mix_vm_cmd_dispatcher_get_editor (const mix_vm_cmd_dispatcher_t *dis); + +extern const gchar * +mix_vm_cmd_dispatcher_get_assembler (const mix_vm_cmd_dispatcher_t *dis); + +/* dispatch a command */ +extern gboolean /* TRUE if success, FALSE otherwise */ +mix_vm_cmd_dispatcher_dispatch (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, const gchar *arg); + +/* dispatch a command in text format */ +extern gboolean +mix_vm_cmd_dispatcher_dispatch_text (mix_vm_cmd_dispatcher_t *dis, + const gchar *text); + +/* dispatch a command in text format, with command and arg split */ +extern gboolean +mix_vm_cmd_dispatcher_dispatch_split_text (mix_vm_cmd_dispatcher_t *dis, + const gchar *command, + const gchar *arg); + +/* get the last dispatch's result */ +extern gboolean +mix_vm_cmd_dispatcher_get_last_result (const mix_vm_cmd_dispatcher_t *dis); + +/* get total uptime */ +extern mix_time_t +mix_vm_cmd_dispatcher_get_uptime (const mix_vm_cmd_dispatcher_t *dis); + +/* get program total time */ +extern mix_time_t +mix_vm_cmd_dispatcher_get_progtime (const mix_vm_cmd_dispatcher_t *dis); + +/* get time lapse */ +extern mix_time_t +mix_vm_cmd_dispatcher_get_laptime (const mix_vm_cmd_dispatcher_t *dis); + +/* src file info */ +extern gulong +mix_vm_cmd_dispatcher_get_src_file_lineno (const mix_vm_cmd_dispatcher_t *dis); + +extern const gchar * +mix_vm_cmd_dispatcher_get_src_file_line (const mix_vm_cmd_dispatcher_t *dis, + gulong line, gboolean cr); + +/* toggle time printing */ +extern void +mix_vm_cmd_dispatcher_print_time (mix_vm_cmd_dispatcher_t * dis, + gboolean print); + +/* install hooks */ +extern void +mix_vm_cmd_dispatcher_pre_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, + mix_vm_cmd_hook_t hook, gpointer data); + +extern void +mix_vm_cmd_dispatcher_post_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_command_t cmd, + mix_vm_cmd_hook_t hook, gpointer data); + +extern void +mix_vm_cmd_dispatcher_global_pre_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_cmd_global_hook_t hook, + gpointer data); + +extern void +mix_vm_cmd_dispatcher_global_post_hook (mix_vm_cmd_dispatcher_t *dis, + mix_vm_cmd_global_hook_t hook, + gpointer data); + +/* get the mix vm */ +extern const mix_vm_t * +mix_vm_cmd_dispatcher_get_vm (const mix_vm_cmd_dispatcher_t *dis); + + +#endif /* MIX_VM_COMMAND_H */ + diff --git a/mixlib/mix_vm_dump.c b/mixlib/mix_vm_dump.c new file mode 100644 index 0000000..5c0bc32 --- /dev/null +++ b/mixlib/mix_vm_dump.c @@ -0,0 +1,147 @@ +/* ---------------------- mix_vm_dump.c : + * Implementation of the functions declared in mix_vm_dump.h + * ------------------------------------------------------------------ +** Copyright (C) 2000 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 "mix.h" +#include <unistd.h> +#include "xmix_vm.h" +#include "mix_vm_dump.h" + +/* Create/destroy a dump context */ +mix_dump_context_t * +mix_dump_context_new(FILE *fd, mix_address_t begin, mix_address_t end, + guint32 options) +{ + mix_dump_context_t *result = NULL; + + g_return_val_if_fail (fd != NULL, NULL); + + if ( begin > end ) begin = end; + if ( end >= MEM_CELLS_NO_ ) end = MEM_CELLS_NO_; + + result = g_new (mix_dump_context_t,1); + result->options = options; + result->begin = begin; + result->end = end; + result->channel = fd; + + return result; +} + +void +mix_dump_context_delete (mix_dump_context_t *dc) +{ + g_return_if_fail (dc != NULL); + g_free (dc); +} + +/* Use the dump context */ +#define WORD_FMT_ "%s %02d %02d %02d %02d %02d (%010ld)" +#define SHORT_FMT_ "%s %02d %02d (%04d)" +#define WORD_SIGN_(w) mix_word_is_negative (w)? "-":"+" +#define SHORT_SIGN_(s) mix_short_is_negative (s)? "-":"+" +#define WORD_ABS_(w) mix_word_magnitude (w) +#define SHORT_ABS_(s) mix_short_magnitude (s) +#define WORD_BYTE_(w,i) mix_byte_new (mix_word_magnitude (w)>>(6*(5-i))) +#define SHORT_BYTE_(s,i) mix_byte_new (mix_short_magnitude (s)>>(6*(2-i))) +#define WORD_ARGS_(w) WORD_SIGN_ (w), WORD_BYTE_ (w,1), WORD_BYTE_ (w,2), \ + WORD_BYTE_ (w,3), WORD_BYTE_ (w,4), WORD_BYTE_ (w,5), WORD_ABS_ (w) +#define SHORT_ARGS_(s) SHORT_SIGN_ (s), SHORT_BYTE_ (s,1), SHORT_BYTE_ (s,2), \ + SHORT_ABS_ (s) + +void +mix_vm_dump (const mix_vm_t *vm, const mix_dump_context_t *dc) +{ + guint j, i; + FILE *f; + + g_return_if_fail (vm != NULL); + g_return_if_fail (dc != NULL); + + f = dc->channel; + + if ( (dc->options & MIX_DUMP_rA) == MIX_DUMP_rA ) + { + mix_word_t rA = get_rA_ (vm); + fprintf (f, "rA: " WORD_FMT_ "\n", WORD_ARGS_ (rA)); + } + + if ( (dc->options & MIX_DUMP_rX) == MIX_DUMP_rX ) + { + mix_word_t rX = get_rX_ (vm); + fprintf (f, "rX: " WORD_FMT_ "\n", WORD_ARGS_ (rX)); + } + + if ( (dc->options & MIX_DUMP_rJ) == MIX_DUMP_rJ ) + { + mix_short_t rJ = get_rJ_ (vm); + fprintf (f, "rJ: " SHORT_FMT_ "\n", SHORT_ARGS_ (rJ)); + } + + for (j = 0, i = 0; j < IREG_NO_; ++j) + { + if ( (dc->options & (MIX_DUMP_rI1<<j)) == (MIX_DUMP_rI1<<j) ) + { + mix_short_t rI = mix_word_to_short_fast (get_rI_ (vm, j+1)); + fprintf (f, "rI%d: " SHORT_FMT_ "\t", j+1, SHORT_ARGS_ (rI)); + i++; + } + if ( i%2 == 0 && i != 0 ) fprintf (f, "\n"); + } + + if ( i%2 == 1 ) fprintf (f, "\n"); + + if ( (dc->options & MIX_DUMP_OVER) == MIX_DUMP_OVER ) + { + fprintf (f, _("Overflow: %s\n"), get_over_ (vm)? "T":"F"); + } + + if ( (dc->options & MIX_DUMP_CMP) == MIX_DUMP_CMP ) + { + const gchar *val = "?"; + switch (get_cmp_ (vm)) + { + case mix_LESS: + val = "L"; + break; + case mix_GREAT: + val = "G"; + break; + case mix_EQ: + val = "E"; + break; + default: + g_assert_not_reached (); + break; + } + fprintf (f, _("Cmp: %s\n"), val); + } + + if ( (dc->options & MIX_DUMP_CELLS) == MIX_DUMP_CELLS ) + { + for (j = dc->begin; j < dc->end; ++j) + { + mix_word_t cell = get_cell_ (vm,j); + fprintf (f, "%04d: " WORD_FMT_ "\n", j, WORD_ARGS_ (cell)); + } + } + fflush (f); +} + diff --git a/mixlib/mix_vm_dump.h b/mixlib/mix_vm_dump.h new file mode 100644 index 0000000..7f8b0c3 --- /dev/null +++ b/mixlib/mix_vm_dump.h @@ -0,0 +1,91 @@ +/* ---------------------- mix_vm_dump.h : + * This file declares types and functions for dumping the contents + * of a mix virtual machine. + * ------------------------------------------------------------------ +** Copyright (C) 2000, 2001 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. +** +*/ + + +#ifndef MIX_VM_DUMP_H +#define MIX_VM_DUMP_H + +#include <stdio.h> +#include "mix_vm.h" + +/* Dump context: a structure defining the properties of dumping */ +typedef struct mix_dump_context_t mix_dump_context_t; +struct mix_dump_context_t +{ + /* flags activating dumps (see enum below) */ + guint32 options; + /* range of addresses dumped: [begin,end) */ + mix_address_t begin; + mix_address_t end; + /* IO channel for dumping */ + FILE *channel; +}; + +/* Flags for activating dumps */ +#define MIX_DUMP_NONE 0 +#define MIX_DUMP_rA 1 +#define MIX_DUMP_rX (1<<1) +#define MIX_DUMP_rJ (1<<2) +#define MIX_DUMP_rI1 (1<<3) +#define MIX_DUMP_rI2 (1<<4) +#define MIX_DUMP_rI3 (1<<5) +#define MIX_DUMP_rI4 (1<<6) +#define MIX_DUMP_rI5 (1<<7) +#define MIX_DUMP_rI6 (1<<8) +#define MIX_DUMP_rIa MIX_DUMP_rI1|MIX_DUMP_rI2|MIX_DUMP_rI3 \ + |MIX_DUMP_rI4|MIX_DUMP_rI5|MIX_DUMP_rI6 +#define MIX_DUMP_rALL MIX_DUMP_rA|MIX_DUMP_rX|MIX_DUMP_rJ|MIX_DUMP_rIa +#define MIX_DUMP_OVER (1<<9) +#define MIX_DUMP_CMP (1<<10) +#define MIX_DUMP_CELLS (1<<11) +#define MIX_DUMP_ALL MIX_DUMP_rALL|MIX_DUMP_OVER|MIX_DUMP_CMP|MIX_DUMP_CELLS +#define MIX_DUMP_ALL_NOMEM MIX_DUMP_rALL|MIX_DUMP_OVER|MIX_DUMP_CMP + +/* Default output channel (stdout) */ +#define MIX_DUMP_DEF_CHANNEL (stdout) + +/* Create/destroy a dump context */ +extern mix_dump_context_t * +mix_dump_context_new (FILE *fd, mix_address_t begin, mix_address_t end, + guint32 options); + +extern void +mix_dump_context_delete(mix_dump_context_t *dc); + +/* Modify an existing dump context */ +#define mix_dump_context_add_opt(dc,opt) ((dc)->options |= (opt)) +#define mix_dump_context_del_opt(dc,opt) ((dc)->options &= ~(opt)) +#define mix_dump_context_set_opt(dc,opt) ((dc)->options = (opt)) +#define mix_dump_context_range(dc,first,last) \ +do { \ + (dc)->begin = first; \ + (dc)->end = last; \ +} while (FALSE) + + +/* Use the dump context */ +extern void +mix_vm_dump(const mix_vm_t *vm, const mix_dump_context_t *dc); + + +#endif /* MIX_VM_DUMP_H */ + diff --git a/mixlib/testsuite/Makefile.am b/mixlib/testsuite/Makefile.am new file mode 100644 index 0000000..834f833 --- /dev/null +++ b/mixlib/testsuite/Makefile.am @@ -0,0 +1,32 @@ +## Process this file with automake to produce Makefile.in + +# Copyright (C) 2000, 2001 Free Software Foundation, Inc. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# $Id: Makefile.am,v 1.2 2001/09/29 01:53:51 jao Exp $ + +INCLUDES = -I$(includedir) -I$(top_srcdir) -I$(top_srcdir)/mixlib +LDADD = $(top_builddir)/mixlib/libmix.a + +check_PROGRAMS = mixtypest mixinstest mixvminstest mixparsertest mixdevtest mixevaltest +TESTS = $(check_PROGRAMS) + +mixtypest_SOURCES = test.h mix_types_t.c +mixinstest_SOURCES = test.h mix_ins_t.c +mixvminstest_SOURCES = test.h mix_vm_ins_t.c +mixparsertest_SOURCES = test.h mix_parser_t.c +mixdevtest_SOURCES = test.h mix_device_t.c +mixevaltest_SOURCES = test.h mix_eval_t.c + + + + + + diff --git a/mixlib/testsuite/mix_device_t.c b/mixlib/testsuite/mix_device_t.c new file mode 100644 index 0000000..8638d2f --- /dev/null +++ b/mixlib/testsuite/mix_device_t.c @@ -0,0 +1,76 @@ +/* -*-c-*- -------------- mix_device_t.c : + * Implementation of the functions declared in mix_device_t.h + * ------------------------------------------------------------------ + * Copyright (C) 2000 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 <stdlib.h> +#include <mix_device.h> + +/* Define VERBOSE_TEST if you want to get prints of the test */ +/* #define VERBOSE_TEST */ +#include "test.h" + +static int S_ = MIX_CHAR_MAX +1; + +int +main (int argc, char **argv) +{ + mix_device_t *console; + size_t s; + mix_word_t **block; + mix_char_t mchars[S_]; + gchar chars[S_]; + int i, j; + int bno; + + + INIT_TEST; + + console = mix_device_new (mix_dev_CONSOLE); + s = mix_device_block_size (console); + bno = S_/(s*5); + if (bno == 0) bno = 1; + + block = g_new (mix_word_t *, bno); + for (i = 0; i < bno; ++i) + block[i] = g_new (mix_word_t, s); + + for (i = 0; i < S_; ++i) { + chars[i] = mix_char_to_ascii (i); + mchars[i] = mix_ascii_to_char (chars[i]); + g_assert (mchars[i] == i); + } + + for (i = 0; i < bno; ++i) { + for (j = 0; j < s; ++j) { + int n = i*s + 5*j; + if (n < S_) + block[i][j] = mix_bytes_to_word (mchars + n, 5); + else + block[i][j] = 0; + } + } + + for (i = 0; i < bno; ++i) { + mix_device_write (console, block[i]); + } + + + return EXIT_SUCCESS; +} diff --git a/mixlib/testsuite/mix_eval_t.c b/mixlib/testsuite/mix_eval_t.c new file mode 100644 index 0000000..9b62fd6 --- /dev/null +++ b/mixlib/testsuite/mix_eval_t.c @@ -0,0 +1,119 @@ +/* -*-c-*- -------------- mix_eval_t.c : + * Test of mix_eval_t + * ------------------------------------------------------------------ + * Last change: Time-stamp: "01/02/20 00:26:15 jose" + * ------------------------------------------------------------------ + * Copyright (C) 2000 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 <stdlib.h> +#include <mix_eval.h> +/* Define VERBOSE_TEST if you want to get prints of the test */ +/* #define VERBOSE_TEST */ +#include "test.h" + +typedef struct test_case_t +{ + const gchar *expr; + gint value; + mix_eval_result_t result; +} test_case_t; + +#define ok_case(exp,val) {exp, val, MIX_EVAL_OK} +#define err_case(exp,err) {exp, 0, err} + +typedef struct sym_val_t +{ + const gchar *sym; + mix_word_t value; +} sym_val_t; + +#define new_symbol(sym,val) {sym, mix_word_new (val)} + +int +main(int argc, char **argv) +{ + size_t k; + mix_eval_t *eval; + mix_short_t loc = mix_short_new (30); + sym_val_t symbols[] = { + new_symbol ("s0", 43), + new_symbol ("s1", -1234), + new_symbol ("s2", 0), + new_symbol ("s3", -20), + new_symbol (NULL, 0) + }; + + test_case_t cases[] = { + ok_case ("2343", 2343), + ok_case ("-890", -890), + ok_case ("15+1015", 1030), + ok_case ("1-481", -480), + ok_case ("2300/10", 230), + ok_case ("24*3", 72), + ok_case ("2:5", 21), + ok_case ("1//3", 357913941), + ok_case ("12+*", 42), + ok_case ("***", 900), + ok_case ("1:3*2-4", 18), + ok_case ("-1+5*20/6", 13), + ok_case ("-1000(0,2),1", 1), + ok_case ("s0-s2*3", 129), + ok_case ("s3**", -600), + ok_case ("s3(3:5)", 20), + ok_case ("-s1", 1234), + ok_case ("s1/10+s0", 166), + err_case ("foo", MIX_EVAL_UNDEF_SYM), + err_case ("11--2", MIX_EVAL_SYNTAX), + err_case ("s2*foo*3", MIX_EVAL_UNDEF_SYM), + err_case ("12/32),1", MIX_EVAL_MIS_PAREN), + err_case ("2000(88)", MIX_EVAL_INV_FSPEC), + ok_case (NULL, 0) + }; + + INIT_TEST; + g_print ("Entering mix_eval test..."); + eval = mix_eval_new (); + + for (k = 0; symbols[k].sym; ++k) + mix_eval_set_symbol (eval, symbols[k].sym, symbols[k].value); + + for (k = 0; cases[k].expr; ++k) { + mix_eval_result_t r = cases[k].result, s; + g_print ("Evaluating \"%s\" = %d ...", + cases[k].expr, cases[k].value); + + s = mix_eval_expression_with_loc (eval, cases[k].expr, loc); + g_assert (s == r); + if ( s == MIX_EVAL_OK ) { + mix_word_print (mix_eval_value (eval), "... "); + g_print ("\n"); + g_assert (mix_eval_value (eval) == mix_word_new(cases[k].value)); + } else { + g_print ("\n------->%s, at pos %d\n", + mix_eval_last_error_string (eval), + mix_eval_last_error_pos (eval)); + } + } + mix_eval_delete (eval); + + return EXIT_SUCCESS; +} + + diff --git a/mixlib/testsuite/mix_ins_t.c b/mixlib/testsuite/mix_ins_t.c new file mode 100644 index 0000000..8dcc619 --- /dev/null +++ b/mixlib/testsuite/mix_ins_t.c @@ -0,0 +1,79 @@ +/*----------------------- mix_ins_t.c ------------------------------- + * Tests for mix_ins.h + *------------------------------------------------------------------- +** Copyright (C) 1999 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 <stdlib.h> +#include <mix_ins.h> + +/* Define VERBOSE_TEST if you want to get prints of the test */ +/* #define VERBOSE_TEST */ +#include "test.h" + + +int +main(int argc, const char**argv) +{ + mix_word_t words[6]; + mix_ins_t ins[3]; + guint k; + + INIT_TEST; + + g_print("\n...ok.\nTesting mix_ins_id_t properties...\n"); + for ( k = 0; k < mix_INVALID_INS; ++k ) + { + mix_opcode_t c = mix_get_opcode_from_id(k); + mix_fspec_t f = mix_get_fspec_from_id(k); + mix_ins_id_t id = mix_get_ins_id(c,f); + + g_print("%02d:%s (%1d:%1d), ", + c, mix_get_string_from_id(k), + mix_fspec_left(f), mix_fspec_right(f)); + if ( (k+1)%3 == 0 ) g_print("\n"); + + g_assert(id==k); + } + + g_print("\n...ok.\nTesting mix_ins_t properties...\n"); + for ( k = 1; k < mix_INVALID_INS; ++k ) + { + g_print("%d ",k); + mix_ins_fill_from_id(ins[0], k); + g_assert(mix_ins_id_from_ins(ins[0]) == k); + ins[0].address = 0x0123; + ins[0].index = mix_I2; + words[2] = mix_ins_to_word(ins); + g_assert(ins[0].address == mix_get_ins_address(words[2])); + g_assert(ins[0].index == mix_get_ins_index(words[2])); + g_assert(ins[0].fspec == mix_get_ins_fspec(words[2])); + g_assert(ins[0].opcode == mix_get_ins_opcode(words[2])); + g_assert(mix_word_to_ins(words[2],ins+1) == k); + g_assert(ins[0].address == ins[1].address); + g_assert(ins[0].index == ins[1].index); + g_assert(ins[0].fspec == ins[1].fspec); + g_assert(ins[0].opcode == ins[1].opcode); + } + + g_print("\n...ok.\n"); + + + + return EXIT_SUCCESS; +} diff --git a/mixlib/testsuite/mix_parser_t.c b/mixlib/testsuite/mix_parser_t.c new file mode 100644 index 0000000..215fc70 --- /dev/null +++ b/mixlib/testsuite/mix_parser_t.c @@ -0,0 +1,85 @@ +/* -*-c-*- -------------- mix_parser_t.c : + * Test of mix_parser_t + * ------------------------------------------------------------------ + * Copyright (C) 2000 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 <stdlib.h> +#include <mix_parser.h> + +/* Define VERBOSE_TEST if you want to get prints of the test */ +/* #define VERBOSE_TEST */ +#include "test.h" + +static const gchar * const FILES_[] = { "test1" , "taocp145" +}; + +static const size_t FILE_NO_ = sizeof(FILES_)/sizeof(FILES_[0]); + +static void +test_code_(const gchar *name) +{ + mix_parser_t *parser = mix_parser_new(name); + mix_parser_err_t err; + mix_code_file_t *code; + mix_ins_desc_t ins; + + g_assert(parser); + err = mix_parser_compile(parser); + if ( err != MIX_PERR_OK ) { + g_print(mix_parser_err_string(err)); + g_print("\n"); + } + g_assert(err == MIX_PERR_OK); + err = mix_parser_write_code(parser, name, FALSE); + code = mix_code_file_new_read(name); + g_assert(code); + g_message("%s: Version: %d.%d", name, mix_code_file_major_version(code), + mix_code_file_minor_version(code)); + mix_short_print(mix_code_file_get_start_addr(code), "Start address: "); + g_print("\n"); + while ( mix_code_file_get_ins(code, &ins) ) { + mix_ins_t i; + mix_word_to_ins_uncheck(ins.ins, i); + mix_short_print(ins.address, "addr: "); + g_print(" : "); + mix_ins_print(&i); + g_print("\n"); + } + + mix_parser_delete(parser); + mix_code_file_delete(code); +} + +int +main(int argc, char **argv) +{ + size_t k; + + INIT_TEST; + + for (k = 0; k < FILE_NO_; ++k) + // test_code_(FILES_[k]); + ; + + + return EXIT_SUCCESS; +} + diff --git a/mixlib/testsuite/mix_types_t.c b/mixlib/testsuite/mix_types_t.c new file mode 100644 index 0000000..ababead --- /dev/null +++ b/mixlib/testsuite/mix_types_t.c @@ -0,0 +1,369 @@ +/*----------------------- mix_types_t.c ----------------------------- + * Tests for mix_types.h + * ------------------------------------------------------------------ +* +** Copyright (C) 1999, 2004 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 <stdlib.h> +#include <mix_types.h> + +/* Define VERBOSE_TEST if you want to get prints of the test */ +/* #define VERBOSE_TEST */ +#include "test.h" + +/* compare two words */ +static gboolean +word_compare_(mix_word_t w1, mix_word_t w2) +{ + if ( mix_word_magnitude(w1) == 0 ) + return ( mix_word_magnitude(w2) == 0 ); + else + return ( w1 == w2 ); +} + + +/* create a word from an array of bytes and check the result */ +static void +test_word_from_bytes_(mix_word_t *word, + mix_byte_t *bytes, + unsigned byteno, + const char *message) +{ + mix_byte_t r; + unsigned int k; + + *word = mix_bytes_to_word(bytes,byteno); + mix_word_print(*word,message); + g_print("\n"); + for ( k = 5-byteno; k < 5; ++k ) { + PRINT_BYTE(r = mix_word_get_byte(*word,k+1)); + g_print(" (k = %d)\n",k); + g_assert( r == bytes[k-5+byteno] ); + } +} + + +/* test field access */ +static void +test_field_access_(mix_fspec_t l, mix_fspec_t r, + mix_word_t from, mix_word_t to) +{ + mix_fspec_t f = mix_fspec_new(l,r); + mix_word_t result; + + PRINT_BYTE(l); g_print(", "); + PRINT_BYTE(r); g_print(", "); + PRINT_BYTE(f); g_print("\n "); + mix_word_print(from, "from: "); + mix_word_print(to, " to: "); + g_assert( mix_fspec_left(f) == l ); + g_assert( mix_fspec_right(f) == r ); + result = mix_word_set_field(f,from,to); + mix_word_print(result,"\n\tresult: "); + g_assert( mix_word_get_field(f,from) == mix_word_get_field(f,result) ); + g_print("\n"); +} + +/* test word addition */ +static void +test_word_add_(mix_word_t w1, mix_word_t w2) +{ + mix_word_t r; + + r = mix_word_add(w1,w2); + mix_word_print(w1,"\n"); + mix_word_print(w2,NULL); + mix_word_print(r," = "); + g_assert( word_compare_(mix_word_sub(r,w1), w2) ); + g_assert( word_compare_(mix_word_sub(r,w2), w1) ); + /* while asserting the following, take into account that 0 != -0 + for mix words, although they are logically equivalent + */ + g_assert( word_compare_(mix_word_sub(w1,r), mix_word_negative(w2)) ); + g_assert( word_compare_(mix_word_sub(w2,r), mix_word_negative(w1)) ); + g_assert( word_compare_(mix_word_add(w2,w1), r) ); +} + +/* test word multiplication */ +static void +test_word_mul_(mix_word_t w1, mix_word_t w2) +{ + mix_word_t h, l, q, r = 0; + mix_word_mul(w1,w2,&h,&l); + mix_word_print(w1,"\n"); + mix_word_print(w2,"*"); + mix_word_print(h,"\n ="); + mix_word_print(l,NULL); + if ( w1 != 0 ) { + g_assert( mix_word_div(h,l,w1,&q,&r) == FALSE ); + g_assert( mix_word_magnitude(r) == 0 ); + g_assert( q == w2 ); + } else { + g_assert( mix_word_magnitude(l) == 0 && mix_word_magnitude(h) == 0 ); + } + if ( w2 != 0 ) { + g_assert( mix_word_div(h,l,w2,&q,&r) == FALSE ); + g_assert( mix_word_magnitude(r) == 0 ); + g_assert( q == w1 ); + } else { + g_assert( mix_word_magnitude(l) == 0 && mix_word_magnitude(h) == 0 ); + } + +} + +/* test word division */ +static void +test_word_div_(mix_word_t h, mix_word_t l, mix_word_t by) +{ + mix_word_t q,r; + gboolean overflow; + + overflow = mix_word_div(h,l,by,&q,&r); + + mix_word_print(h,"\n\n"); + mix_word_print(l,NULL); + mix_word_print(by,"\n div by "); + + if ( !overflow ) { + mix_word_t h1, l1, h2; + mix_word_print(q,"\n q = "); + mix_word_print(r," r = "); + mix_word_mul(by,q,&h1,&l1); + mix_word_add_and_carry(l1,r,&h2,&l1); + h1 = mix_word_add(h1,h2); + g_assert( mix_word_magnitude(r) < mix_word_magnitude(by) ); + g_assert( word_compare_(h1,h) ); + g_assert( mix_word_magnitude(l1) == mix_word_magnitude(l) ); + } else + g_print("\n\t = overflow"); + +} + +static void +test_mix_char_(void) +{ + mix_char_t mchar; + guchar achar; + guint k; + g_print("\nTesting mix_char_t. Table of mix_chars:\n"); + + for (k = 0; k < MIX_CHAR_MAX + 1; ++k) { + mchar = k; + achar = mix_char_to_ascii(mchar); + g_print("%02d: %c, ", k, achar); + if ( (k+1)%5 == 0 ) g_print("\n"); + g_assert( mchar == mix_ascii_to_char(achar) ); + } + + g_print("\nchar <-> byte conversions...\n"); + + for (k = 0; k < MIX_CHAR_MAX + 1; ++k) { + mix_byte_t c = mix_byte_new (k); + g_assert (c == mix_byte_to_char (mix_char_to_byte (c))); + } + + g_print("\n"); +} + + +/* main test driver for mix_types.h/c */ + +int main(int argc, char **argv) +{ + unsigned int j,k; + mix_byte_t bytes[5] = { 0, 3, 20, 30, 40 }; + mix_byte_t r; + mix_short_t ss[6]; + mix_word_t words[6]; + + INIT_TEST; + + g_print("Testing mix_byte_t arithmetics...\n"); + PRINT_BYTE(bytes[0]); g_print(", "); + PRINT_BYTE(bytes[1]); g_print(", "); + PRINT_BYTE(bytes[2]); g_print(", "); + PRINT_BYTE(bytes[3]); g_print(", "); + PRINT_BYTE(bytes[4]); g_print("\n"); + PRINT_BYTE(r = mix_byte_add(bytes[1],bytes[2])); + g_print("\n"); g_assert(r == 23); + PRINT_BYTE(r = mix_byte_add(bytes[3],bytes[4])); + g_print("\n"); g_assert(r == 6); + PRINT_BYTE(r = mix_byte_sub(bytes[0],bytes[1])); + g_print("\n"); g_assert(r == 61); + PRINT_BYTE(r = mix_byte_sub(bytes[4],bytes[3])); + g_print("\n"); g_assert(r == 10); + PRINT_BYTE(r = mix_byte_sub(bytes[1],bytes[4])); + g_print("\n"); g_assert(r == 27); + PRINT_BYTE(r = mix_byte_mul(bytes[0],bytes[1])); + g_print("\n"); g_assert(r == 0); + PRINT_BYTE(r = mix_byte_mul(bytes[1],bytes[2])); + g_print("\n"); g_assert(r == 60); + PRINT_BYTE(r = mix_byte_mul(bytes[1],bytes[4])); + g_print("\n"); g_assert(r == 56); + PRINT_BYTE(r = mix_byte_mul(bytes[4],bytes[1])); + g_print("\n"); g_assert(r == 56); + PRINT_BYTE(r = mix_byte_div(bytes[4],bytes[2])); + g_print("\n"); g_assert(r == 2); + PRINT_BYTE(r = mix_byte_div(bytes[3],bytes[2])); + g_print("\n"); g_assert(r == 1); + + test_mix_char_(); + + g_print("\nTesting word<->short conversions..."); + words[0] = mix_bytes_to_word(bytes+1,5); + words[1] = mix_word_negative(words[0]); + ss[0] = mix_word_to_short(words[0]); + ss[1] = mix_word_to_short(words[1]); + mix_word_print(words[0],"\nwords[0]="); + mix_word_print(words[1],"\nwords[1]="); + mix_short_print(ss[0],"\nss[0]="); + mix_short_print(ss[1],"\nss[1]="); + g_assert(mix_short_is_positive(ss[0])); + g_assert(mix_short_is_negative(ss[1])); + words[2] = mix_short_to_word(ss[0]); + words[3] = mix_short_to_word(ss[1]); + mix_word_print(words[2],"\nwords[2]="); + mix_word_print(words[3],"\nwords[3]="); + g_assert(mix_word_sign(words[0]) == mix_word_sign(words[2])); + g_assert(mix_word_sign(words[1]) == mix_word_sign(words[3])); + g_assert(mix_short_magnitude(ss[0]) == mix_word_magnitude(words[2])); + g_assert(mix_short_magnitude(ss[1]) == mix_word_magnitude(words[3])); + g_assert(mix_word_get_byte(words[0],4) == mix_word_get_byte(words[2],4)); + g_assert(mix_word_get_byte(words[0],5) == mix_word_get_byte(words[2],5)); + g_assert(mix_word_get_byte(words[1],4) == mix_word_get_byte(words[3],4)); + g_assert(mix_word_get_byte(words[1],5) == mix_word_get_byte(words[3],5)); + words[4] = mix_word_extract_field(mix_fspec_new(4,5),words[0]); + words[5] = mix_word_extract_field(mix_fspec_new(4,5),words[1]); + mix_word_reverse_sign(words[5]); + g_assert(words[4] == words[2]); + g_assert(words[5] == words[3]); + + g_print("Testing mix_word_t creation and byte access...\n"); + test_word_from_bytes_(words,bytes,5,"word[0] created from bytes[0-4]"); + test_word_from_bytes_(words+1,bytes,4,"\nword[1] created from bytes[0-3]"); + test_word_from_bytes_(words+2,bytes,3,"\nword[2] created from bytes[0-2]"); + words[3] = mix_word_negative(words[2]); + g_assert( mix_word_negative(words[3]) == words[2] ); + g_assert( mix_word_is_negative(words[3]) && !mix_word_is_negative(words[2])); + mix_word_print(words[3],"\nword[3] created from -word[2]"); + test_word_from_bytes_(words+4,bytes+2,2,"\nword[2] created from bytes[2-3]"); + + g_print("\nTesting mix_word_t field access...\n"); + mix_word_set_byte(words+3,1,12); + mix_word_set_byte(words+3,2,58); + g_assert( mix_word_get_byte(words[3],1) == 12 ); + g_assert( mix_word_get_byte(words[3],2) == 58 ); + test_field_access_(0,5,words[3],words[4]); + test_field_access_(1,5,words[3],words[4]); + test_field_access_(2,5,words[3],words[4]); + test_field_access_(3,5,words[3],words[4]); + test_field_access_(4,5,words[3],words[4]); + test_field_access_(5,5,words[3],words[4]); + test_field_access_(0,0,words[3],words[4]); + + g_print("\n\nTesting mix_word_t arithmetics...\n"); + words[0] = MIX_WORD_MAX; + words[1] = mix_word_negative(words[0]); + for ( k = 1; k < 6; ++k ) { + mix_word_set_byte(words+2,k,5*k); + mix_word_set_byte(words+4,k,10*(5-k)); + mix_word_set_byte(words+3,k,21 + 3*k); + } + words[5] = 0; + + g_print("\n***addition***"); + for ( k = 0; k < 6; ++k ) + for ( j = 0; j <= k; ++j ) { + test_word_add_(words[k],mix_word_negative(words[j])); + test_word_add_(words[k],words[j]); + } + g_print("\n***product***"); + for ( k = 0; k < 6; ++k ) + for ( j = 0; j <= k; ++j ) { + test_word_mul_(words[k],words[j]); + } + g_print("\n***division***"); + for ( k = 0; k < 6; ++k ) { + test_word_div_(words[k],0,words[0]); + for ( j = 0; j <= k; ++j ) { + test_word_div_(k,words[j],words[j]); + test_word_div_(0,mix_word_add(mix_word_magnitude(words[j]),j),words[j]); + test_word_div_(mix_word_negative(k),words[j],words[j]); + } + } + + g_print("\nTesting shift operations...\n"); + for ( k = 0; k < 10; ++k ) + mix_word_set_byte(words+(k/5),1+(k%5),k+1); + + mix_word_print(words[0],"A = "); + mix_word_print(words[1],"X = "); + for ( k = 0; k < 11; ++k ) { + mix_word_t A, X; + unsigned int m; + + mix_word_shift_left(words[0],words[1],k,&A,&X); + g_print("\nShift left %d:\n",k); + mix_word_print(A,"A "); + mix_word_print(X,"X "); + for ( m = 0; m < 10 - k; ++m ) + g_assert( mix_word_get_byte( m < 5 ? A:X, (m%5)+1 ) == + mix_word_get_byte(words[(m+k)/5], ((m+k)%5)+1) ); + for ( ; m < 10; ++m ) + g_assert( mix_word_get_byte( m < 5 ? A:X, (m%5)+1 ) == 0 ); + + mix_word_shift_right(words[0],words[1],k,&A,&X); + g_print("\nShift right %d:\n",k); + mix_word_print(A,"A "); + mix_word_print(X,"X "); + for ( m = 0; m < k; ++m ) + g_assert( mix_word_get_byte( m < 5 ? A:X, (m%5)+1 ) == 0 ); + for ( ; m < 10; ++m ) + g_assert( mix_word_get_byte( m < 5 ? A:X, (m%5)+1 ) == + mix_word_get_byte(words[(m-k)/5], ((m-k)%5)+1) ); + + mix_word_shift_left_circular(words[0],words[1],k,&A,&X); + g_print("\nShift left circular %d:\n",k); + mix_word_print(A,"A "); + mix_word_print(X,"X "); + for ( m = 0; m < 10 - k; ++m ) + g_assert( mix_word_get_byte( m < 5 ? A:X, (m%5)+1 ) == + mix_word_get_byte(words[(m+k)/5], ((m+k)%5)+1) ); + for ( ; m < 10; ++m ) + g_assert( mix_word_get_byte( m < 5 ? A:X, (m%5)+1 ) == + mix_word_get_byte(words[(m-10+k)/5], 1+((m-10+k)%5)) ); + mix_word_shift_right_circular(A, X, k, &A, &X); + g_print("\nRe-shiftting right...\n"); + mix_word_print(A, "A "); + mix_word_print(X, "X "); + g_assert(A == words[0]); + g_assert(X == words[1]); + } + + + g_print("\n"); + return EXIT_SUCCESS; +} + + + + + + diff --git a/mixlib/testsuite/mix_vm_ins_t.c b/mixlib/testsuite/mix_vm_ins_t.c new file mode 100644 index 0000000..458cbba --- /dev/null +++ b/mixlib/testsuite/mix_vm_ins_t.c @@ -0,0 +1,505 @@ +/* ---------------------- mix_vm_ins_t.c : + * Tests for the virtual machine instruction handlers. + * ------------------------------------------------------------------ +** Copyright (C) 2000, 2004 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 <stdlib.h> +/* #define VERBOSE_TEST */ +#include "test.h" +#include "mix_vm.h" +#include "mix_vm_dump.h" + +typedef struct +{ + mix_word_t rA_b, rA_a; + mix_word_t rX_b, rX_a; + mix_short_t rJ_b, rJ_a; + mix_short_t rI_b[6], rI_a[6]; + gboolean over_b, over_a; + mix_cmpflag_t cmp_b, cmp_a; + mix_address_t begin, end; + mix_word_t *cells_b, *cells_a; + const mix_ins_t *ins; +} test_desc_t; + +static void +set_cells_(test_desc_t *t, mix_address_t begin, mix_address_t end) +{ + g_assert(begin <= end); + t->begin = begin; + t->end = end; + t->cells_b = g_new(mix_word_t,end-begin); + t->cells_a = g_new(mix_word_t,end-begin); +} + +static void +free_cells_(test_desc_t *t) +{ + g_assert(t); + g_free(t->cells_a); + g_free(t->cells_b); + t->cells_a = t->cells_b = NULL; + t->begin = t->end = 0; +} + + +static void +fill_test_desc_(test_desc_t *t, const mix_vm_t *vm, const mix_ins_t *ins) +{ + guint k; + g_assert(t); + g_assert(vm); + + t->rA_b = t->rA_a = mix_vm_get_rA(vm); + t->rX_b = t->rX_a = mix_vm_get_rX(vm); + t->rJ_b = t->rJ_a = mix_vm_get_rJ(vm); + for ( k = 0; k < 6; ++k ) + t->rI_b[k] = t->rI_a[k] = mix_vm_get_rI(vm,k+1); + t->cmp_b = t->cmp_a = mix_vm_get_cmpflag(vm); + t->over_b = t->over_a = mix_vm_get_overflow(vm); + + for (k = 0; k < t->end-t->begin; ++k) + t->cells_a[k] = t->cells_b[k] = + mix_vm_get_addr_contents(vm,t->begin+k); + t->ins = ins; +} + + +static void +run_test_(test_desc_t *t, mix_vm_t *vm, mix_dump_context_t *dc) +{ + guint k; + g_assert(t); + g_assert(vm); + + mix_vm_set_rA(vm, t->rA_b); + mix_vm_set_rX(vm, t->rX_b); + mix_vm_set_rJ(vm, t->rJ_b); + for (k = 0; k < 6; ++k) mix_vm_set_rI(vm, k+1, t->rI_b[k]); + for (k = t->begin; k < t->end; ++k) + mix_vm_set_addr_contents(vm, k, t->cells_b[k-t->begin]); + mix_vm_set_cmpflag(vm, t->cmp_b); + mix_vm_set_overflow(vm, t->over_b); + + mix_ins_print(t->ins); + if (dc) { + mix_dump_context_range(dc, t->begin, t->end); + mix_vm_dump(vm,dc); + } + k = mix_vm_exec_ins(vm, t->ins); + if (dc) mix_vm_dump(vm, dc); + g_assert(k == TRUE); + g_assert(mix_vm_get_rA(vm) == t->rA_a); + g_assert(mix_vm_get_rX(vm) == t->rX_a); + for (k = 0; k < 6; ++k) g_assert(mix_vm_get_rI(vm, k+1) == t->rI_a[k]); + g_assert(mix_vm_get_cmpflag(vm) == t->cmp_a); + g_assert(mix_vm_get_overflow(vm) == t->over_a); + for (k = t->begin; k < t->end; ++k) + g_assert(mix_vm_get_addr_contents(vm, k) == t->cells_a[k-t->begin]); +} + + + +static void +test_arithmetics_(mix_vm_t *vm, mix_dump_context_t *dc) +{ + test_desc_t test; + mix_ins_t ins; + + g_print("\nTesting arithmetic instructions...\n"); + mix_vm_reset(vm); + mix_ins_fill_from_id(ins,mix_ADD); + ins.index = 0; + ins.address = 1000; + mix_vm_set_rA(vm,mix_word_new_b(19,18,1,2,22)); + mix_vm_set_addr_contents(vm,1000,mix_word_new_b(1,36,5,0,50)); + set_cells_(&test,1000,1001); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(20,54,6,3,8); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins,mix_SUB); + mix_vm_set_rA(vm,mix_word_new_bn(19,18,0,0,9)); + mix_vm_set_addr_contents(vm,1000,mix_word_new_bn(31,16,2,22,0)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(11,62,2,21,55); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins,mix_MUL); + mix_vm_set_rA(vm,mix_word_new_b(1,1,1,1,1)); + mix_vm_set_addr_contents(vm,1000, mix_word_new_b(1,1,1,1,1)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(0,1,2,3,4); + test.rX_a = mix_word_new_b(5,4,3,2,1); + run_test_(&test, vm, dc); + + ins.fspec = mix_fspec_new(1,1); + mix_vm_set_rA(vm,mix_word_new_bn(0,0,0,1,48)); + mix_vm_set_addr_contents(vm,1000,mix_word_new_bn(2,16,2,22,0)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = MIX_WORD_MINUS_ZERO; + test.rX_a = mix_word_new_bn(0,0,0,3,32); + run_test_(&test, vm, dc); + + mix_vm_set_rA(vm,mix_word_new_bn(0,0,0,1,48)); + mix_vm_set_addr_contents(vm,1000,mix_word_new_b(2,0,34,33,1)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = MIX_WORD_MINUS_ZERO; + test.rX_a = mix_word_new_bn(0,0,0,3,32); + run_test_(&test, vm, dc); + + ins.fspec = mix_fspec_new(0,5); + mix_vm_set_rA(vm,mix_word_new_bn(50,0,1,48,4)); + mix_vm_set_addr_contents(vm,1000,mix_word_new_bn(2,0,0,0,0)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(1,36,0,3,32); + test.rX_a = mix_word_new_b(8,0,0,0,0); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins,mix_DIV); + mix_vm_set_rA(vm,MIX_WORD_ZERO); + mix_vm_set_rX(vm,mix_word_new_b(0,0,0,0,17)); + mix_vm_set_addr_contents(vm,1000, mix_word_new_b(0,0,0,0,3)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(0,0,0,0,5); + test.rX_a = mix_word_new_b(0,0,0,0,2); + run_test_(&test, vm, dc); + + mix_vm_set_rA(vm,MIX_WORD_ZERO); + mix_vm_set_rX(vm,mix_word_new_bn(0,0,0,0,17)); + mix_vm_set_addr_contents(vm,1000, mix_word_new_b(0,0,0,0,3)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(0,0,0,0,5); + test.rX_a = mix_word_new_b(0,0,0,0,2); + run_test_(&test, vm, dc); + + mix_vm_set_rA(vm, MIX_WORD_MINUS_ZERO); + mix_vm_set_rX(vm, mix_word_new_b(19,19,0,3,1)); + mix_vm_set_addr_contents(vm,1000, mix_word_new_bn(0,0,0,2,0)); + fill_test_desc_(&test,vm,&ins); + test.rA_a = mix_word_new_b(0,9,41,32,1); + test.rX_a = mix_word_new_bn(0,0,0,1,1); + run_test_(&test, vm, dc); + + free_cells_(&test); +} + +static void +test_shift_(mix_vm_t *vm, mix_dump_context_t *dc) +{ + test_desc_t test; + mix_ins_t ins; + + g_print("Testing shift instructions...\n"); + mix_vm_reset(vm); + set_cells_(&test,0,0); + fill_test_desc_(&test,vm,&ins); + mix_ins_fill_from_id(ins,mix_SRAX); + ins.index = 0; + ins.address = 1; + test.rA_b = mix_word_new_b(1,2,3,4,5); + test.rX_b = mix_word_new_bn(6,7,8,9,10); + test.rA_a = mix_word_new_b(0,1,2,3,4); + test.rX_a = mix_word_new_bn(5,6,7,8,9); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_SLA); + ins.address = 2; + fill_test_desc_(&test, vm, &ins); + test.rA_a = mix_word_new_b(2,3,4,0,0); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_SRC); + ins.address = 4; + fill_test_desc_(&test, vm, &ins); + test.rA_a = mix_word_new_b(6,7,8,9,2); + test.rX_a = mix_word_new_bn(3,4,0,0,5); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_SRA); + ins.address = 2; + fill_test_desc_(&test, vm, &ins); + test.rA_a = mix_word_new_b(0,0,6,7,8); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_SLC); + ins.address = 501; + fill_test_desc_(&test, vm, &ins); + test.rA_a = mix_word_new_b(0,6,7,8,3); + test.rX_a = mix_word_new_bn(4,0,0,5,0); + run_test_(&test, vm, dc); +} + +static void +test_spc_(mix_vm_t *vm, mix_dump_context_t *dc) +{ + test_desc_t test; + mix_ins_t ins; + + g_print("Testing special instructions...\n"); + mix_vm_reset(vm); + set_cells_(&test,0,0); + fill_test_desc_(&test,vm,&ins); + mix_ins_fill_from_id(ins,mix_NUM); + ins.index = 0; + ins.address = 0; + test.rA_b = mix_word_new_bn(0,0,31,32,39); + test.rX_b = mix_word_new_b(37,57,47,30,30); + test.rA_a = mix_word_negative(12977700); + test.rX_a = test.rX_b; + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_INCA); + ins.address = 1; + fill_test_desc_(&test, vm, &ins); + test.rA_a = mix_word_negative(12977699); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_CHAR); + fill_test_desc_(&test, vm, &ins); + test.rA_a = mix_word_new_bn(30,30,31,32,39); + test.rX_a = mix_word_new_b(37,37,36,39,39); + run_test_(&test, vm, dc); + + mix_ins_fill_from_id(ins, mix_HLT); + fill_test_desc_(&test, vm, &ins); + run_test_(&test, vm, dc); + g_assert(mix_vm_is_halted(vm)); + +} + +static void +test_move_(mix_vm_t *vm, mix_dump_context_t *dc) +{ + test_desc_t test; + mix_ins_t ins; + guint k; + + g_print("Testing move instruction...\n"); + mix_vm_reset(vm); + set_cells_(&test,0,10); + fill_test_desc_(&test,vm,&ins); + mix_ins_fill_from_id(ins,mix_MOVE); + + ins.index = 0; + ins.address = 0; + ins.fspec = 5; + for ( k = 0; k < 5; ++k ) + test.cells_b[k] = test.cells_a[k+5] = test.cells_a[k] =mix_word_new(100*k); + + test.rI_b[0] = 5; + test.rI_a[0] = 10; + run_test_(&test,vm,dc); + + free_cells_(&test); +} + + + +static void +test_load_(mix_vm_t *vm, mix_dump_context_t *dc) +{ + test_desc_t test; + mix_ins_t ins; + mix_ins_id_t ids[4] = {mix_LDA, mix_LDX, mix_LDAN, mix_LDXN}; + mix_word_t r_a[14] = { + mix_word_new_bn(1,16,3,5,4), + mix_word_new_b(1,16,3,5,4), + mix_word_new_b(0,0,3,5,4), + mix_word_new_bn(0,0,1,16,3), + mix_word_new_b(0,0,0,0,5), + mix_word_new_b(0,0,0,0,1), + MIX_WORD_MINUS_ZERO, + mix_word_new_b(1,16,3,5,4), + mix_word_new_bn(1,16,3,5,4), + mix_word_new_bn(0,0,3,5,4), + mix_word_new_b(0,0,1,16,3), + mix_word_new_bn(0,0,0,0,5), + mix_word_new_bn(0,0,0,0,1), + MIX_WORD_ZERO + }; + mix_fspec_t fs[11] = {5,13,29,3,36,9,0,0,0,0,0}; + mix_address_t a_a[11] = { + MIX_SHORT_MINUS_ZERO, + mix_short_new_bn(0,1), + mix_short_new_bn(1,16), + mix_short_new_bn(16,3), + mix_short_new_bn(3,5), + mix_short_new_bn(5,4), + mix_short_new_b(1,16), + mix_short_new_b(16,3), + mix_short_new_b(5,4), + mix_short_new_b(5,4), + mix_short_new_b(3,5) + }; + mix_word_t val = mix_word_new_bn(1,16,3,5,4); + gint j; + + g_print("Testing load instructions...\n"); + set_cells_(&test,2000,2001); + ins.index = 1; + ins.address = mix_short_negative(50); + + mix_vm_reset(vm); + mix_vm_set_addr_contents(vm, 2000, val); + + for (j = 0; j < 4; ++j) + { + gint k; + mix_ins_fill_from_id(ins,ids[j]); + for ( k = 0; k < 7; ++k ) { + fill_test_desc_(&test,vm,&ins); + ins.fspec = fs[k]; + switch (ids[j]) + { + case mix_LDA: test.rA_a = r_a[k]; break; + case mix_LDX: test.rX_a = r_a[k]; break; + case mix_LDAN: test.rA_a = r_a[k + 7]; break; + case mix_LDXN: test.rX_a = r_a[k + 7]; break; + default: g_assert_not_reached(); + } + test.rI_b[0] = test.rI_a[0] = 2050; + run_test_(&test, vm, dc); + } + } + + ins.index = 0; + ins.address = 2000; + fs[0] = 0; fs[1] = 1; fs[2] = 2; fs[3] = 3; fs[4] = 4; fs[5] = 5; + fs[6] = 10; fs[7] = 11; fs[8] = 37; fs[9] = 29; fs[10] = 12; + + mix_vm_reset(vm); + mix_vm_set_addr_contents(vm, 2000, val); + for ( j = 0; j < 14; j++ ) + { + guint k; + if (j == 6 || j == 7 ) continue; /* mix_LDX, mix_LDAN */ + mix_ins_fill_from_id(ins, mix_LD1 + j); + for (k = 0; k < 11; ++k) + { + fill_test_desc_(&test, vm, &ins); + ins.fspec = fs[k]; + if ( j < 6 ) + test.rI_a[j] = a_a[k]; + else /* mix_LDiN */ + test.rI_a[j-8] = mix_short_negative (a_a[k]); + run_test_(&test, vm, dc); + } + } + + free_cells_(&test); +} + + +static void +test_store_(mix_vm_t *vm, mix_dump_context_t *dc) +{ + test_desc_t test; + mix_ins_t ins; + mix_word_t reg = mix_word_new_b(6,7,8,9,0); + mix_word_t add = mix_word_new_bn(1,2,3,4,5); + mix_word_t addr[6] = { mix_word_new_b(6,7,8,9,0), + mix_word_new_bn(6,7,8,9,0), + mix_word_new_bn(1,2,3,4,0), + mix_word_new_bn(1,0,3,4,5), + mix_word_new_bn(1,9,0,4,5), + mix_word_new_b(0,2,3,4,5)}; + mix_word_t addri[6] = { mix_word_new_b(0,0,0,9,0), + mix_word_new_bn(0,0,0,9,0), + mix_word_new_bn(1,2,3,4,0), + mix_word_new_bn(1,0,3,4,5), + mix_word_new_bn(1,9,0,4,5), + mix_word_new_b(0,2,3,4,5)}; + mix_word_t addrz[6] = { mix_word_new_b(0,0,0,0,0), + mix_word_new_bn(0,0,0,0,0), + mix_word_new_bn(1,2,3,4,0), + mix_word_new_bn(1,0,3,4,5), + mix_word_new_bn(1,0,0,4,5), + mix_word_new_b(0,2,3,4,5)}; + mix_fspec_t fs[6] = {5,13,45,18,19,1}; + gint i,j; + + g_print("Testing store instructions...\n"); + + set_cells_(&test,2000,2001); + ins.index = 0; + ins.address = 2000; + + mix_vm_reset(vm); + fill_test_desc_(&test,vm,&ins); + test.rA_a = test.rA_b = test.rX_a = test.rX_b = reg; + test.rJ_a = test.rJ_b = mix_word_to_short(reg); + for (j = 0; j < 6; ++j) + test.rI_a[j] = test.rI_b[j] = test.rJ_a; + test.cells_b[0] = add; + + for (i = 0; i < 10; ++i) + { + mix_ins_fill_from_id(ins,mix_STA+i); + for (j = 0; j < 6; ++j) + { + ins.fspec = fs[j]; + if (i == 0 || i == 7 ) /* mix_STA, mix_STX */ + test.cells_a[0] = addr[j]; + else if ( i < 9 ) /* mix_STi, mix_STJ */ + test.cells_a[0] = addri[j]; + else /* mix_STZ */ + test.cells_a[0] = addrz[j]; + run_test_(&test,vm,dc); + } + } + + free_cells_(&test); +} + + + +int +main(int argc, const char **argv) +{ + mix_vm_t *vm; + mix_dump_context_t *dc; + + INIT_TEST; + + vm = mix_vm_new(); + +#ifdef VERBOSE_TEST + dc = mix_dump_context_new(MIX_DUMP_DEF_CHANNEL, 0, 0, MIX_DUMP_ALL); +#else + dc = NULL; +#endif + + test_arithmetics_(vm, dc); + test_shift_(vm, dc); + test_spc_(vm,dc); + test_move_(vm,dc); + test_load_(vm,dc); + test_store_(vm,dc); + + mix_vm_delete(vm); + +#ifdef VERBOSE_TEST + mix_dump_context_delete(dc); +#endif + + return EXIT_SUCCESS; +} + diff --git a/mixlib/testsuite/test.h b/mixlib/testsuite/test.h new file mode 100644 index 0000000..7f8eb0c --- /dev/null +++ b/mixlib/testsuite/test.h @@ -0,0 +1,44 @@ +/* +** Copyright (C) 1999 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. +** +*/ + +/* Common definitions for test programs */ + +#ifndef TEST_H +#define TEST_H + +#include <glib.h> +#include <mix.h> + +#define PRINT_BYTE(byte) g_print("%s = %02d",#byte,byte) + +#ifdef VERBOSE_TEST /* get printed information */ +#define INIT_TEST \ + do { g_set_print_handler(NULL); mix_init_lib(); } while(FALSE); +#else /* no printed information */ +static void +dummy_print_f_(const gchar *m) +{ + /* no output */ +} +#define INIT_TEST \ +do { g_set_print_handler(dummy_print_f_); mix_init_lib(); } while(FALSE); +#endif /* VERBOSE_TEST */ + + +#endif /* TEST_H */ diff --git a/mixlib/xmix_device.c b/mixlib/xmix_device.c new file mode 100644 index 0000000..c61ab55 --- /dev/null +++ b/mixlib/xmix_device.c @@ -0,0 +1,199 @@ +/* -*-c-*- -------------- xmix_device.c : + * Implementation of the functions declared in xmix_device.h + * ------------------------------------------------------------------ + * Last change: Time-stamp: "2001-05-10 01:10:25 jao" + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2004 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 "xmix_device.h" +#include "mix_types.h" + +gchar *DEV_DIR_ = NULL; + +const char *DEV_EXT_ = ".dev"; + +const char *DEF_NAMES_[] = { + "tape0", "tape1", "tape2", "tape3", "tape4", "tape5", "tape6", "tape7", + "disk0", "disk1", "disk2", "disk3", "disk4", "disk5", "disk6", "disk7", + "cardrd", "cardwr", "printer", "console", "paper" +}; + +const size_t SIZES_[] = { + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 16, 16, 24, 14, 14 +}; + +const mix_device_mode_t MODES_[] = { + mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, + mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, + mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, mix_dev_BIN, + mix_dev_BIN, mix_dev_CHAR, mix_dev_CHAR, mix_dev_CHAR, mix_dev_CHAR, + mix_dev_CHAR +}; + +const mix_fmode_t FMODES_[] = { + mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, + mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, + mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, + mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, mix_io_RDWRT, + mix_io_READ, mix_io_WRITE, mix_io_WRITE, mix_io_RDWRT, mix_io_READ +}; + +/* constructors */ +void +construct_device_ (mix_device_t *result, mix_device_type_t type) +{ + gchar *name; + name = DEV_DIR_ ? g_strdup_printf ("%s/%s", DEV_DIR_, DEF_NAMES_[type]) + : g_strdup (DEF_NAMES_[type]); + construct_device_with_name_ (result, type, name); + g_free (name); +} + + +void +construct_device_with_name_ (mix_device_t *result, + mix_device_type_t type, const gchar *name) +{ + result->type = type; + if (type != mix_dev_CONSOLE) + { + result->file = MIX_IOCHANNEL(mix_file_new_with_def_ext (name, + FMODES_[type], + DEV_EXT_)); + result->vtable = DEF_DEV_VTABLE_; + } + else + { + result->file = mix_io_new (stdout); + result->vtable = CONSOLE_DEV_VTABLE_; + } +} + + +void +construct_device_with_file_ (mix_device_t *result, + mix_device_type_t type, FILE *file) +{ + result->type = type; + result->file = mix_io_new (file); + result->vtable = DEF_DEV_VTABLE_; +} + + +/* + Write a block to the device. +*/ +static gboolean +write_ (mix_device_t *dev, const mix_word_t *block) +{ + gboolean result; + + if (FMODES_[dev->type] == mix_io_READ) return FALSE; + if (MODES_[dev->type] == mix_dev_CHAR) + result = mix_io_write_word_array_as_char (GET_CHANNEL_ (dev), + block, SIZES_[dev->type]); + else + result = mix_io_write_word_array (GET_CHANNEL_ (dev), + block, SIZES_[dev->type]); + fflush (mix_io_to_FILE (GET_CHANNEL_ (dev))); + + return result; +} + +static gboolean +read_cons_ (mix_device_t *dev, mix_word_t *block) +{ + return mix_io_read_word_array_as_char (mix_io_new (stdin), block, + SIZES_[mix_dev_CONSOLE]); +} + +static gboolean +read_ (mix_device_t *dev, mix_word_t *block) +{ + gboolean result; + + if (FMODES_[dev->type] == mix_io_WRITE) return FALSE; + if (MODES_[dev->type] == mix_dev_CHAR) + { + result = mix_io_read_word_array_as_char (GET_CHANNEL_ (dev), + block, SIZES_[dev->type]); + } + else + result = mix_io_read_word_array (GET_CHANNEL_ (dev), + block, SIZES_[dev->type]); + + return result; +} + +static gboolean +ioc_ (mix_device_t *dev, mix_short_t arg) +{ + int m; + FILE *file; + + m = mix_short_magnitude(arg); + if (mix_short_is_negative(arg)) m = -m; + m *= sizeof (mix_word_t) * SIZES_[dev->type]; + file = mix_io_to_FILE (GET_CHANNEL_(dev)); + + if (dev->type >= mix_dev_TAPE_0 && dev->type <= mix_dev_TAPE_7) + { + if (m == 0) rewind (file); + else fseek (file, m, SEEK_CUR); + } + if (dev->type >= mix_dev_DISK_0 && dev->type <= mix_dev_DISK_7) + { + if (m == 0) return FALSE; + // position disk + } + if (dev->type == mix_dev_PAPER_TAPE) + { + if (m == 0) return FALSE; + rewind (file); + } + return TRUE; +} + +static gboolean +busy_ (const mix_device_t *dev) +{ + return (!mix_io_is_ready (GET_CHANNEL_(dev))); +} + +static void +destroy_ (mix_device_t *dev) +{ + if (dev->type != mix_dev_CONSOLE && GET_FILE_(dev) != NULL) + mix_file_delete (GET_FILE_(dev)); +} + +static mix_device_vtable_t VTABLE_ = { + write_, read_, ioc_, busy_, destroy_ +}; + +const mix_device_vtable_t * DEF_DEV_VTABLE_ = &VTABLE_; + +static mix_device_vtable_t CVTABLE_ = { + write_, read_cons_, ioc_, busy_, destroy_ +}; + +const mix_device_vtable_t * CONSOLE_DEV_VTABLE_ = &CVTABLE_; diff --git a/mixlib/xmix_device.h b/mixlib/xmix_device.h new file mode 100644 index 0000000..0eb3fa8 --- /dev/null +++ b/mixlib/xmix_device.h @@ -0,0 +1,96 @@ +/* -*-c-*- ---------------- xmix_device.h : + * Protected declarations for mix_device_t + * ------------------------------------------------------------------ + * Last change: Time-stamp: <2001-05-07 23:59:35 jao> + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2004 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. + * + */ + + +#ifndef XMIX_DEVICE_H +#define XMIX_DEVICE_H + +#include "mix_file.h" +#include "mix_device.h" + +/* device file directory */ +extern gchar *DEV_DIR_; + +/* table of overridable device operations */ +typedef gboolean (*mix_dev_write_func_t) (mix_device_t *, const mix_word_t *); +typedef gboolean (*mix_dev_read_func_t) (mix_device_t *, mix_word_t *); +typedef gboolean (*mix_dev_ioc_func_t) (mix_device_t *, mix_short_t); +typedef gboolean (*mix_dev_busy_func_t) (const mix_device_t *); +typedef void (*mix_dev_destroy_t) (mix_device_t *); + +typedef struct mix_device_vtable_t +{ + mix_dev_write_func_t write; + mix_dev_read_func_t read; + mix_dev_ioc_func_t ioc; + mix_dev_busy_func_t busy; + mix_dev_destroy_t destroy; +} mix_device_vtable_t; + +/* default vtables */ +extern const mix_device_vtable_t *DEF_DEV_VTABLE_; +extern const mix_device_vtable_t *CONSOLE_DEV_VTABLE_; + +/* + Actual definition of a mix device, which can be cast to + a mix file. +*/ +struct mix_device_t +{ + mix_iochannel_t *file; + mix_device_type_t type; + const mix_device_vtable_t *vtable; +}; + +/* constructors */ +extern void +construct_device_ (mix_device_t *dev, mix_device_type_t type); + +extern void +construct_device_with_name_ (mix_device_t *dev, + mix_device_type_t type, const gchar *name); + +extern void +construct_device_with_file_ (mix_device_t *dev, + mix_device_type_t type, FILE *file); + + +#define GET_CHANNEL_(dev) (dev->file) +#define GET_FILE_(dev) ((mix_file_t *)(dev->file)) + +/* default extension for device files */ +extern const char *DEV_EXT_; +/* default names for device files */ +extern const char *DEF_NAMES_[]; +/* block sizes for devices */ +extern const size_t SIZES_[]; +/* io modes for devices */ +extern const mix_device_mode_t MODES_[]; +/* files modes for devices */ +extern const mix_fmode_t FMODES_[]; + + + + +#endif /* XMIX_DEVICE_H */ + diff --git a/mixlib/xmix_eval.h b/mixlib/xmix_eval.h new file mode 100644 index 0000000..7516ed3 --- /dev/null +++ b/mixlib/xmix_eval.h @@ -0,0 +1,55 @@ +/* -*-c-*- ---------------- xmix_eval.h : + * Definition of opaque types in mix_eval.h + * ------------------------------------------------------------------ + * Last change: Time-stamp: <01/02/20 00:25:14 jose> + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef XMIX_EVAL_H +#define XMIX_EVAL_H + +#include "mix_eval.h" + +/* the evaluator type */ +struct mix_eval_t +{ + mix_symbol_table_t *table; /* symbol table */ + gboolean towner; /* true if owns the table */ + mix_eval_result_t result; /* last evaluation result */ + gint errpos; /* location of last error */ + mix_word_t value; /* last computed value */ +}; + +/* flex scanner data/result struct */ +typedef struct mix_eval_data_ +{ + gchar *expr; + const mix_symbol_table_t *table; + mix_word_t value; + mix_short_t loc; + gint errpos; +} mix_eval_data_; + +/* flex scanner prototype */ +extern +mix_eval_result_t mix_eval_expr (mix_eval_data_ *data); + +#endif /* XMIX_EVAL_H */ + diff --git a/mixlib/xmix_io.c b/mixlib/xmix_io.c new file mode 100644 index 0000000..a43d284 --- /dev/null +++ b/mixlib/xmix_io.c @@ -0,0 +1,57 @@ +/* -*-c-*- ------------------ xmix_io.c : + * Implementation of the functions declared in xmix_io.h + * ------------------------------------------------------------------ + * Copyright (C) 2000 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 <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "xmix_io.h" + +const char *io_OPENTYPE_[5] = { "r", "w", "w+", "a", "a+" }; + + +/* initialise a mix_iochannel from a file descriptor */ +gboolean +io_init_from_fdesc_(mix_iochannel_t *ioc, int fdesc) +{ + FILE *file; + mix_fmode_t mode; + int flags = fcntl(fdesc, F_GETFL); + + if ( (flags&O_RDONLY) == O_RDONLY ) + mode = mix_io_READ; + else if ( (flags&O_WRONLY) == O_WRONLY ) + mode = mix_io_WRITE; + else if ( (flags&O_RDWR) == O_RDWR ) + mode = mix_io_RDWRT; + else + return FALSE; + + file = fdopen(fdesc, fmode_to_type_(mode)); + g_return_val_if_fail(file != NULL, FALSE); + if (mode == mix_io_RDWRT) rewind (file); + io_init_from_file_(ioc, file); + + return TRUE; +} + + + diff --git a/mixlib/xmix_io.h b/mixlib/xmix_io.h new file mode 100644 index 0000000..1346559 --- /dev/null +++ b/mixlib/xmix_io.h @@ -0,0 +1,64 @@ +/* -*-c-*- ------------------ xmix_io.h : + * Implementation of mix_iochannel_t and mix_file_t + * ------------------------------------------------------------------ + * Copyright (C) 2000 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. + * + */ + + +#ifndef XMIX_IOCHANNEL_H +#define XMIX_IOCHANNEL_H + +#include <stdio.h> +#include "mix_io.h" + +/* the actual definition of mix_iochannel_t */ +struct mix_iochannel_t +{ + FILE *file; +}; + + +extern const char * io_OPENTYPE_[5]; + +#define fmode_to_type_(mode) ( (mode) < 6 ? io_OPENTYPE_[(mode)]:NULL ) + +/* initialisation */ +extern gboolean +io_init_from_fdesc_(mix_iochannel_t *ioc, int fdesc); + +#define io_init_from_file_(ioc,f) (ioc)->file = f + +#define io_close_(ioc) fclose((ioc)->file) + +/* write/read data */ +#define write_data_(ioc,data,no) \ + ( (no) == fwrite((const void*)(data), sizeof(*(data)), (no), (ioc)->file) ) + +#define read_data_(ioc,data,no) \ + ( (no) == fread((void*)(data), sizeof(*(data)), (no), (ioc)->file) ) + +/* state */ +#define is_eof_(ioc) ( feof((ioc)->file) != 0 ) +#define is_ready_(ioc) ( ferror((ioc)->file) == 0 ) + +/* conversions */ +#define io_get_FILE_(ioc) (MIX_IOCHANNEL(ioc))->file + + +#endif /* XMIX_IOCHANNEL_H */ + diff --git a/mixlib/xmix_parser.h b/mixlib/xmix_parser.h new file mode 100644 index 0000000..51f1560 --- /dev/null +++ b/mixlib/xmix_parser.h @@ -0,0 +1,111 @@ +/* -*-c-*- ---------------- xmix_parser.h : + * Declarations for the implementation of mix_parser_t + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2004 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. + * + */ + + +#ifndef XMIX_PARSER_H +#define XMIX_PARSER_H + +#include "mix_ins.h" +#include "mix_file.h" +#include "mix_symbol_table.h" +#include "mix_parser.h" + +/* mix_parser_t definition */ +struct mix_parser_t +{ + mix_file_t *in_file; /* the source file to be compiled */ + mix_symbol_table_t *symbol_table; + mix_symbol_table_t *ls_table; /* literal strings symbols */ + guint cur_ls; /* current literal string symbol */ + GHashTable *future_refs; /* a map from symbol name to list of addresses */ + GTree *ins_table; /* a table of compiled instructions */ + GSList *con_list; /* CON instructions */ + GSList *alf_list; /* ALF instructions */ + mix_address_t loc_count; /* current memory location during compilation */ + mix_parser_err_t status; /* outcome of compilation */ + guint err_line; /* line of the last error */ + guint err_count; /* no. of errors during compilation */ + guint warn_count; /* no. of warnings during compilation */ + mix_address_t start; /* start address of the compiled code */ + mix_address_t end; /* end address of the compiled code */ +}; + +/* each node of the ins_table stores a mix_word_t with the instruction + and a source code line (for debugging and listing) +*/ +typedef struct ins_node_ +{ + mix_word_t ins; + guint lineno; +} ins_node_; + + +/* functions to manipulate mix_parser_t during compilation */ + +/* access loc counter */ +#define get_ploc_(parser) ((parser)->loc_count) + +/* symbol table */ +/* Define a new symbol with given value + * and update previously set refs to this symbol + */ +extern mix_parser_err_t +mix_parser_define_symbol_value (mix_parser_t *parser, const gchar *name, + mix_word_t value); + +/* Define a new symbol with value equal to the current loc_count + * and update previously set refs to this symbol + */ +extern mix_parser_err_t +mix_parser_define_symbol_here (mix_parser_t *parser, const gchar *name); + +/* Set a reference to future symbol here */ +extern void +mix_parser_set_future_ref (mix_parser_t *parser, const gchar *name); + +/* Redefine the value of a local symbol as the current loc_count */ +extern void +mix_parser_manage_local_symbol (mix_parser_t *parser, const gchar *name, + mix_short_t value); + +/* Literal strings symbols */ +extern void +mix_parser_define_ls (mix_parser_t *parser, mix_word_t value); + +/* Compilation */ +/* Add instruction with address the current loc_count */ +extern void +mix_parser_add_ins (mix_parser_t *parser, const mix_ins_t *new_ins, + guint lineno); +extern void +mix_parser_add_raw (mix_parser_t *parser, mix_word_t word, guint lineno, + gboolean is_con); + +/* Error handling */ +extern void +mix_parser_log_error (mix_parser_t *parser, mix_parser_err_t error, + gint lineno, const gchar *comment, gboolean warn); + + + + +#endif /* XMIX_PARSER_H */ + diff --git a/mixlib/xmix_vm.c b/mixlib/xmix_vm.c new file mode 100644 index 0000000..d5b4e4b --- /dev/null +++ b/mixlib/xmix_vm.c @@ -0,0 +1,598 @@ +/* ---------------------- xmix_vm.c : + * Implementation of the functions declared in xmix_vm.h + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2003, 2004 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 "xmix_vm.h" + +/* auxiliar functions */ +G_INLINE_FUNC mix_address_t +get_M_ (const mix_vm_t *vm, const mix_ins_t *ins); +G_INLINE_FUNC mix_word_t +get_V_ (const mix_vm_t *vm, const mix_ins_t *ins); +G_INLINE_FUNC mix_device_t * +get_dev_ (mix_vm_t *vm, mix_fspec_t type); + +G_INLINE_FUNC mix_address_t +get_M_ (const mix_vm_t *vm, const mix_ins_t *ins) +{ + if ( ins->index == 0 ) + return ins->address; + else + return mix_short_add (ins->address, + mix_word_to_short_fast (get_rI_ (vm, ins->index))); +} + +G_INLINE_FUNC mix_word_t +get_V_ (const mix_vm_t *vm, const mix_ins_t *ins) +{ + return mix_word_get_field (ins->fspec, get_cell_ (vm, get_M_ (vm,ins))); +} + +G_INLINE_FUNC mix_device_t * +get_dev_ (mix_vm_t *vm, mix_fspec_t type) +{ + if (type >= BD_NO_) return NULL; + if (vm->devices[type] == NULL) + vm->devices[type] = vm->factory (type); + return vm->devices[type]; +} + +/* error macro */ +#define fail_if_not_(vm,cond,error) \ +do { \ + if (!(cond)) \ + { \ + set_last_error_ (vm, error); \ + return FALSE; \ + } \ +} while (FALSE) + +#define fail_(vm,error) fail_if_not_ (vm, FALSE, error) + +#define fail_unexpected_(vm) fail_ (vm, MIX_VM_ERROR_UNEXPECTED) + +/* Instruction handlers */ + +static gboolean +nop_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode == mix_opNOP); + inc_loc_ (vm); + return TRUE; +} + +static gboolean +add_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_word_t val = get_V_ (vm, ins); + g_assert (ins->opcode == mix_opADD || ins->opcode == mix_opSUB); + if ( ins->opcode == mix_opSUB ) mix_word_reverse_sign (val); + if ( mix_word_add_and_carry (get_rA_ (vm), val, NULL, &get_rA_ (vm)) ) + set_over_ (vm,TRUE); + inc_loc_ (vm); + return TRUE; +} + + +static gboolean +mul_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode == mix_opMUL); + mix_word_mul (get_rA_ (vm), get_V_ (vm,ins), &get_rA_ (vm), &get_rX_ (vm)); + inc_loc_ (vm); + return TRUE; +} + +static gboolean +div_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode == mix_opDIV); + if ( mix_word_div (get_rA_ (vm), get_rX_ (vm), get_V_ (vm,ins), + &get_rA_ (vm), &get_rX_ (vm)) ) + set_over_ (vm,TRUE); + inc_loc_ (vm); + return TRUE; +} + +static gboolean +spc_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode == mix_opSPC); + + switch (mix_ins_id_from_ins (*ins)) { + case mix_HLT: halt_ (vm, TRUE); break; + case mix_CHAR: + { + guint32 num = mix_word_magnitude (get_rA_ (vm)); + mix_char_t z = mix_ascii_to_char ('0'); + guint i; + for (i = 5; 0 < i; --i, num /= 10) + mix_word_set_byte (&get_rX_ (vm), i, z + num % 10); + for (i = 5; 0 < i; --i, num /= 10) + mix_word_set_byte (&get_rA_ (vm), i, z + num % 10); + break; + } + case mix_NUM: + { + guint i; + mix_word_t num = MIX_WORD_ZERO; + mix_word_t ten = 10; + for (i = 1; i <= 5; ++i) { + mix_word_mul (ten, num, NULL, &num); + mix_word_add_and_carry (num, mix_word_get_byte (get_rA_ (vm),i)%10, + NULL, &num); + } + for (i = 1; i <= 5; ++i) { + mix_word_mul (ten, num, NULL, &num); + mix_word_add_and_carry (num, mix_word_get_byte (get_rX_ (vm),i)%10, + NULL, &num); + } + set_rA_ (vm, mix_word_is_negative (get_rA_ (vm)) ? + mix_word_negative (num) : num); + break; + } + default: fail_ (vm, MIX_VM_ERROR_BAD_FSPEC); + } + inc_loc_ (vm); + return TRUE; +} + +static gboolean +sla_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_short_t n = get_M_ (vm,ins); + + g_assert (ins->opcode == mix_opSLx); + + fail_if_not_ (vm, mix_short_is_positive (n), MIX_VM_ERROR_BAD_M); + + switch ( mix_ins_id_from_ins (*ins) ) { + case mix_SLA: + mix_word_shift_left (get_rA_ (vm), MIX_WORD_ZERO, n, &get_rA_ (vm), NULL); + break; + case mix_SRA: + mix_word_shift_right (get_rA_ (vm), MIX_WORD_ZERO, n, &get_rA_ (vm), NULL); + break; + case mix_SLAX: + mix_word_shift_left (get_rA_ (vm), get_rX_ (vm), n, + &get_rA_ (vm), &get_rX_ (vm)); + break; + case mix_SRAX: + mix_word_shift_right (get_rA_ (vm), get_rX_ (vm), n, + &get_rA_ (vm), &get_rX_ (vm)); + break; + case mix_SLC: + mix_word_shift_left_circular (get_rA_ (vm), get_rX_ (vm), n, + &get_rA_ (vm), &get_rX_ (vm)); + break; + case mix_SRC: + mix_word_shift_right_circular (get_rA_ (vm), get_rX_ (vm), n, + &get_rA_ (vm), &get_rX_ (vm)); + break; + default: + fail_unexpected_ (vm); + } + + inc_loc_ (vm); + return TRUE; +} + +static gboolean +mov_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_short_t from = get_M_ (vm,ins), + to = mix_word_to_short_fast (get_rI_ (vm,1)); + guint k, delta = ins->fspec; + + g_assert (ins->opcode == mix_opMOVE); + if (mix_short_is_positive (from) + && mix_short_is_positive (to) + && MEMOK_ (from + delta -1) + && MEMOK_ (to + delta - 1)) + { + for (k = 0; k < delta; ++k) + set_cell_ (vm, to+k, get_cell_ (vm, from+k)); + set_rI_ (vm, 1, to+delta); + inc_loc_ (vm); + return TRUE; + } + else + return FALSE; +} + +static gboolean +lda_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + gint r = 0; + mix_word_t val; + mix_ins_id_t id = mix_ins_id_from_ins (*ins); + + g_assert (id >= mix_LDA && id <= mix_LDXN); + + val = get_V_ (vm, ins); + if (id > mix_LDX) mix_word_reverse_sign (val); + if ( (id > mix_LDA && id < mix_LDX) || (id > mix_LDAN && id < mix_LDXN) ) + /* Bytes 1-3 of I regs are always null */ + val = mix_word_set_field (mix_fspec_new (1,3),MIX_WORD_ZERO,val); + + switch (id) { + case mix_LDA: case mix_LDAN: r = A_; break; + case mix_LDX: case mix_LDXN: r = X_; break; + case mix_LD1: case mix_LD1N: r = I1_; break; + case mix_LD2: case mix_LD2N: r = I2_; break; + case mix_LD3: case mix_LD3N: r = I3_; break; + case mix_LD4: case mix_LD4N: r = I4_; break; + case mix_LD5: case mix_LD5N: r = I5_; break; + case mix_LD6: case mix_LD6N: r = I6_; break; + default: g_assert_not_reached (); + } + set_reg_ (vm, r, val); + inc_loc_ (vm); + return TRUE; +} + + +static gboolean +sta_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_address_t addr = get_M_ (vm, ins); + mix_ins_id_t id = mix_ins_id_from_ins (*ins); + mix_word_t from; + + g_assert (id >= mix_STA && id <= mix_STZ); + switch (id) { + case mix_STA: from = get_rA_ (vm); break; + case mix_STX: from = get_rX_ (vm); break; + case mix_STJ: from = get_rJ_ (vm); break; + case mix_STZ: from = MIX_WORD_ZERO; break; + default: from = get_rI_ (vm, id - mix_ST1 + 1); break; + } + set_cell_ (vm, addr, + mix_word_store_field (ins->fspec, from, get_cell_ (vm, addr))); + inc_loc_ (vm); + return TRUE; +} + +static gboolean +jbs_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode == mix_opJBUS); + fail_if_not_ (vm, ins->fspec < BD_NO_, MIX_VM_ERROR_BAD_DEVICE_NO); + fail_if_not_ (vm, get_dev_ (vm, ins->fspec) != NULL, + MIX_VM_ERROR_BAD_DEVICE_NO); + + if ( mix_device_busy (get_dev_ (vm, ins->fspec)) ) { + set_rJ_ (vm, get_loc_ (vm)); + set_loc_ (vm, get_M_ (vm, ins)); + } else inc_loc_ (vm); + return TRUE; +} + +static gboolean +ioc_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_address_t addr; + mix_device_t *dev; + + g_assert (ins->opcode == mix_opIOC); + + addr = get_M_ (vm, ins); + fail_if_not_ (vm, ins->fspec < BD_NO_, MIX_VM_ERROR_BAD_DEVICE_NO); + + dev = get_dev_ (vm, ins->fspec); + fail_if_not_ (vm, dev != NULL, MIX_VM_ERROR_BAD_DEVICE_NO); + + fail_if_not_ (vm, mix_device_ioc (dev, addr), MIX_VM_ERROR_DEV_CTL); + + inc_loc_ (vm); + return TRUE; +} + +static gboolean +inp_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_address_t addr; + mix_device_t *dev; + + g_assert (ins->opcode == mix_opIN); + fail_if_not_ (vm, ins->fspec < BD_NO_, MIX_VM_ERROR_BAD_DEVICE_NO); + + addr = get_M_ (vm, ins); + fail_if_not_ (vm, MEMOK_ (addr), MIX_VM_ERROR_BAD_ACCESS); + + dev = get_dev_ (vm, ins->fspec); + fail_if_not_ (vm, dev != NULL, MIX_VM_ERROR_BAD_DEVICE_NO); + + fail_if_not_ (vm, MEM_CELLS_NO_ - addr > mix_device_block_size (dev), + MIX_VM_ERROR_BAD_ACCESS); + + fail_if_not_ (vm, mix_device_read (dev, get_cell_ptr_ (vm, addr)), + MIX_VM_ERROR_DEV_READ); + + inc_loc_ (vm); + return TRUE; +} + +static gboolean +out_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_address_t addr; + mix_device_t *dev; + + g_assert (ins->opcode == mix_opOUT); + + fail_if_not_ (vm, ins->fspec < BD_NO_, MIX_VM_ERROR_BAD_DEVICE_NO); + + addr = get_M_ (vm, ins); + fail_if_not_ (vm, MEMOK_ (addr), MIX_VM_ERROR_BAD_ACCESS); + + dev = get_dev_ (vm, ins->fspec); + fail_if_not_ (vm, dev != NULL, MIX_VM_ERROR_BAD_DEVICE_NO); + + fail_if_not_ (vm, MEM_CELLS_NO_ - addr > mix_device_block_size (dev), + MIX_VM_ERROR_BAD_ACCESS); + + fail_if_not_ (vm, mix_device_write (dev, get_cell_ptr_ (vm, addr)), + MIX_VM_ERROR_DEV_WRITE); + + inc_loc_ (vm); + return TRUE; +} + +static gboolean +jrd_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode == mix_opJRED); + fail_if_not_ (vm, ins->fspec < BD_NO_, MIX_VM_ERROR_BAD_DEVICE_NO); + fail_if_not_ (vm, get_dev_ (vm, ins->fspec) != NULL, + MIX_VM_ERROR_BAD_DEVICE_NO); + + inc_loc_ (vm); + if ( !mix_device_busy (get_dev_ (vm, ins->fspec)) ) + { + set_rJ_ (vm, get_loc_ (vm)); + set_loc_ (vm, get_M_ (vm, ins)); + } + return TRUE; +} + +static gboolean +jmp_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + gboolean jump = FALSE; + mix_address_t addr = get_M_ (vm, ins); + mix_ins_id_t id = mix_ins_id_from_ins (*ins); + + g_assert (ins->opcode == mix_opJMP); + fail_if_not_ (vm, MEMOK_ (addr), MIX_VM_ERROR_BAD_ACCESS); + + switch ( id ) { + case mix_JMP: + case mix_JSJ: + jump = TRUE; + break; + case mix_JOV: + jump = get_over_ (vm); + if (jump) set_over_ (vm, FALSE); + break; + case mix_JNOV: + jump = !get_over_ (vm); + set_over_ (vm, FALSE); + break; + case mix_JL: + jump = ( get_cmp_ (vm) == mix_LESS ); + break; + case mix_JE: + jump = ( get_cmp_ (vm) == mix_EQ ); + break; + case mix_JG: + jump = ( get_cmp_ (vm) == mix_GREAT ); + break; + case mix_JGE: + jump = ( get_cmp_ (vm) != mix_LESS ); + break; + case mix_JNE: + jump = ( get_cmp_ (vm) != mix_EQ ); + break; + case mix_JLE: + jump = ( get_cmp_ (vm) != mix_GREAT ); + break; + default: + fail_unexpected_ (vm); + } + + inc_loc_ (vm); + if ( jump ) { + if ( id != mix_JSJ ) set_rJ_ (vm, get_loc_ (vm)); + set_loc_ (vm, addr); + } + return TRUE; +} + +static gboolean +jpx_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + gboolean jump = FALSE; + mix_address_t addr = get_M_ (vm, ins); + mix_ins_id_t id = mix_ins_id_from_ins (*ins); + mix_word_t val; + + g_assert (ins->opcode >= mix_opJAx || ins->opcode <= mix_opJXx); + fail_if_not_ (vm, MEMOK_ (addr), MIX_VM_ERROR_BAD_ACCESS); + + switch (ins->opcode) { + case mix_opJAx: val = get_rA_ (vm); break; + case mix_opJXx: val = get_rX_ (vm); break; + default: val = get_rI_ (vm, ins->opcode - mix_opJAx); + } + + switch (id) { + case mix_JAN: case mix_JXN: + case mix_J1N: case mix_J2N: case mix_J3N: + case mix_J4N: case mix_J5N: case mix_J6N: + jump = mix_word_is_negative (val) && val != MIX_WORD_MINUS_ZERO; + break; + case mix_JAZ: case mix_JXZ: + case mix_J1Z: case mix_J2Z: case mix_J3Z: + case mix_J4Z: case mix_J5Z: case mix_J6Z: + jump = mix_word_magnitude (val) == MIX_WORD_ZERO; + break; + case mix_JAP: case mix_JXP: + case mix_J1P: case mix_J2P: case mix_J3P: + case mix_J4P: case mix_J5P: case mix_J6P: + jump = mix_word_is_positive (val) && val != MIX_WORD_ZERO; + break; + case mix_JANN: case mix_JXNN: + case mix_J1NN: case mix_J2NN: case mix_J3NN: + case mix_J4NN: case mix_J5NN: case mix_J6NN: + jump = mix_word_magnitude (val) == MIX_WORD_ZERO + || mix_word_is_positive (val); + break; + case mix_JANZ: case mix_JXNZ: + case mix_J1NZ: case mix_J2NZ: case mix_J3NZ: + case mix_J4NZ: case mix_J5NZ: case mix_J6NZ: + jump = mix_word_magnitude (val) != MIX_WORD_ZERO; + break; + case mix_JANP: case mix_JXNP: + case mix_J1NP: case mix_J2NP: case mix_J3NP: + case mix_J4NP: case mix_J5NP: case mix_J6NP: + jump = mix_word_magnitude (val) == MIX_WORD_ZERO + || mix_word_is_negative (val); + break; + default: + fail_unexpected_ (vm); + } + + inc_loc_ (vm); + if ( jump ) { + set_rJ_ (vm, get_loc_ (vm)); + set_loc_ (vm, addr); + } + return TRUE; +} + +static gboolean +ina_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + mix_word_t val = mix_short_to_word_fast (get_M_ (vm, ins)); + mix_ins_id_t id = mix_ins_id_from_ins (*ins); + gint r; + + g_assert (id >= mix_INCA && id <= mix_ENNX); + + switch (ins->opcode) { + case mix_opINCA: r = A_; break; + case mix_opINCX: r = X_; break; + default: r = I1_ + ins->opcode - mix_opINC1; + } + + switch (id) { + case mix_ENTA: case mix_ENTX: + break; + case mix_ENT1: case mix_ENT2: case mix_ENT3: + case mix_ENT4: case mix_ENT5: case mix_ENT6: + val = mix_word_set_field (mix_fspec_new (1,3), MIX_WORD_ZERO, val); + break; + case mix_INCA: case mix_INCX: + if ( mix_word_add_and_carry (val, get_reg_ (vm, r), NULL, &val) ) + set_over_ (vm, TRUE); + break; + case mix_INC1: case mix_INC2: case mix_INC3: + case mix_INC4: case mix_INC5: case mix_INC6: + mix_word_add_and_carry (val, get_reg_ (vm,r), NULL, &val); + val = mix_word_set_field (mix_fspec_new (1,3), MIX_WORD_ZERO, val); + break; + case mix_DECA: case mix_DECX: + if ( mix_word_add_and_carry (mix_word_negative (val), get_reg_ (vm, r), + NULL, &val) ) + set_over_ (vm, TRUE); + break; + case mix_DEC1: case mix_DEC2: case mix_DEC3: + case mix_DEC4: case mix_DEC5: case mix_DEC6: + mix_word_add_and_carry (mix_word_negative (val), get_reg_ (vm,r), + NULL, &val); + val = mix_word_set_field (mix_fspec_new (1,3), MIX_WORD_ZERO, val); + break; + case mix_ENN1: case mix_ENN2: case mix_ENN3: + case mix_ENN4: case mix_ENN5: case mix_ENN6: + val = mix_word_set_field (mix_fspec_new (1,3), MIX_WORD_ZERO, val); + /* fallthrough */ + case mix_ENNA: case mix_ENNX: + mix_word_reverse_sign (val); + break; + default: + fail_unexpected_ (vm); + } + set_reg_ (vm, r, val); + inc_loc_ (vm); + return TRUE; +} + +static gboolean +cmp_handler_ (mix_vm_t *vm, const mix_ins_t *ins) +{ + g_assert (ins->opcode >= mix_opCMPA && ins->opcode <= mix_opCMPX); + + if ( ins->fspec == 0 ) { /* shortcut: +0 == -0 */ + set_cmp_ (vm, mix_EQ); + } else { + mix_word_t v = get_V_ (vm, ins); + mix_word_t reg; + mix_cmpflag_t flag; + + switch (ins->opcode) { + case mix_opCMPA: + reg = get_rA_ (vm); + break; + case mix_opCMPX: + reg = get_rX_ (vm); + break; + default: + reg = get_rI_ (vm, ins->opcode - mix_opCMPA); + break; + } + reg = mix_word_get_field (ins->fspec, reg); + mix_word_add_and_carry (reg, mix_word_negative (v), NULL, ®); + if ( mix_word_magnitude (reg) == MIX_WORD_ZERO ) flag = mix_EQ; + else if ( mix_word_is_positive (reg) ) flag = mix_GREAT; + else flag = mix_LESS; + set_cmp_ (vm, flag); + } + inc_loc_ (vm); + return TRUE; +} + +ins_handler_t_ ins_handlers_[MIX_BYTE_MAX + 1] = { + nop_handler_, add_handler_, add_handler_, mul_handler_, div_handler_, + spc_handler_, sla_handler_, mov_handler_, lda_handler_, lda_handler_, + lda_handler_, lda_handler_, lda_handler_, lda_handler_, lda_handler_, + lda_handler_, lda_handler_, lda_handler_, lda_handler_, lda_handler_, + lda_handler_, lda_handler_, lda_handler_, lda_handler_, sta_handler_, + sta_handler_, sta_handler_, sta_handler_, sta_handler_, sta_handler_, + sta_handler_, sta_handler_, sta_handler_, sta_handler_, jbs_handler_, + ioc_handler_, inp_handler_, out_handler_, jrd_handler_, jmp_handler_, + jpx_handler_, jpx_handler_, jpx_handler_, jpx_handler_, jpx_handler_, + jpx_handler_, jpx_handler_, jpx_handler_, ina_handler_, ina_handler_, + ina_handler_, ina_handler_, ina_handler_, ina_handler_, ina_handler_, + ina_handler_, cmp_handler_, cmp_handler_, cmp_handler_, cmp_handler_, + cmp_handler_, cmp_handler_, cmp_handler_, cmp_handler_, +}; diff --git a/mixlib/xmix_vm.h b/mixlib/xmix_vm.h new file mode 100644 index 0000000..7a634d4 --- /dev/null +++ b/mixlib/xmix_vm.h @@ -0,0 +1,139 @@ +/* ---------------------- xmix_vm.h : + * This file contains internal declarations used in the implementation + * of the mix_vm_t type. + * ------------------------------------------------------------------ + * $Id: xmix_vm.h,v 1.10 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2000, 2002, 2003, 2004 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. + * + */ + + +#ifndef XMIX_VM_H +#define XMIX_VM_H + +#include <string.h> + +#include "mix_symbol_table.h" +#include "mix_device.h" +#include "mix_src_file.h" +#include "mix_predicate_list.h" +#include "mix_vm.h" + +/* The mix_vm_t type */ +enum { + IREG_NO_ = 6, + BD_NO_ = 21, + MEM_CELLS_NO_ = MIX_VM_CELL_NO, + MEM_CELLS_MAX_ = MIX_VM_CELL_NO - 1 +}; + +struct mix_vm_t +{ + mix_word_t reg[IREG_NO_+3]; + mix_word_t cell[MEM_CELLS_NO_]; + gboolean overflow; + mix_cmpflag_t cmpflag; + mix_short_t loc_count; + mix_vm_status_t status; + mix_vm_error_t last_error; + mix_device_t * devices[BD_NO_]; + mix_address_t start_addr; /* start address of loaded file */ + GTree *line_table; /* source line no -> address */ + GTree *address_table; /* adress -> source line no */ + gint8 bp[MEM_CELLS_NO_/8]; /* each bit signals a break point */ + mix_vm_clock_t *clock; /* the vm clock */ + mix_symbol_table_t *symbol_table; + mix_src_file_t *src_file; /* source of last loaded code file */ + mix_device_factory_t factory; /* the factory for new devices */ + mix_predicate_list_t *pred_list; /* predicates for conditional bps */ + GSList *address_list; /* list of executed addresses */ +}; + +/* Macros for accessing/modifying the above structure. + * Warning: the arguments of these macros must not have side-effects. + */ +#define IOK_(idx) ( (idx) > 0 && (idx) < IREG_NO_+1 ) +#define MEMOK_(addr) ( mix_short_is_positive(addr) && (addr) < MEM_CELLS_NO_ ) +#define REGOK_(r) ( (r) >= 0 && (r) < IREG_NO_ + 3 ) + +enum { A_ = 0, X_, J_, I1_, I2_, I3_, I4_, I5_, I6_ }; + +#define get_reg_(vm, r) ((vm)->reg[r]) +#define get_rA_(vm) get_reg_(vm, A_) +#define get_rX_(vm) get_reg_(vm, X_) +#define get_rJ_(vm) get_reg_(vm, J_) +#define get_rI_(vm,idx) get_reg_(vm, I1_ + (idx) - 1) +#define get_cell_(vm,addr) ( MEMOK_(addr) ? vm->cell[addr] : MIX_WORD_ZERO ) +#define get_cell_ptr_(vm,addr) ( MEMOK_(addr) ? (vm->cell) + addr : NULL ) +#define get_cmp_(vm) (vm->cmpflag) +#define get_over_(vm) (vm->overflow) +#define get_loc_(vm) (vm->loc_count) +#define get_clock_(vm) (vm->clock) +#define get_pred_list_(vm) (vm->pred_list) +#define get_address_list_(vm) (vm->address_list) +#define get_status_(vm) (vm->status) +#define get_last_error_(vm) (vm->last_error) +#define set_last_error_(vm,error) ((vm)->last_error = (error)) + +#define set_reg_(vm,r,x) \ +do { \ + if ( REGOK_(r) ) vm->reg[r] = (x); \ +} while (FALSE) + +#define set_rA_(vm,x) set_reg_(vm,A_,x) +#define set_rX_(vm,x) set_reg_(vm,X_,x) +#define set_rJ_(vm,x) set_reg_(vm,J_,(x)&MIX_SHORT_MAX) +#define set_rI_(vm,idx,x) set_reg_(vm,(idx) + I1_ - 1,x) + +#define set_cell_(vm,addr,x) \ +do { \ + if ( MEMOK_(addr) ) (vm)->cell[addr] = (x); \ +} while (FALSE) + +#define set_cmp_(vm,x) (vm)->cmpflag = (x) +#define set_over_(vm,x) (vm)->overflow = (x) +#define set_loc_(vm,x) (vm)->loc_count = (MEMOK_(x)? (x) : MIX_SHORT_ZERO) + +#define set_status_(vm,s) ((vm)->status = (s)) +#define is_halted_(vm) ((vm)->status == MIX_VM_HALT) +#define halt_(vm,val) ((vm)->status = (val)? MIX_VM_HALT : MIX_VM_RUNNING) + +#define inc_loc_(vm) \ +do { \ + if (++(vm->loc_count) == MEM_CELLS_NO_) \ + { vm->loc_count--; halt_(vm, TRUE); } \ +} while(FALSE) + +#define set_start_(vm,val) ((vm)->start_addr = (val)) +#define reset_loc_(vm) set_loc_ (vm, vm->start_addr) +#define update_time_(vm,ins) mix_vm_clock_add_lapse (get_clock_(vm), ins) + +/* Breakpoints handling */ +#define bp_clear_all_(vm) memset (vm->bp, 0, MEM_CELLS_NO_/8) +#define bp_set_(vm,addr) vm->bp[(addr)>>3] |= 1 << ((addr)&7) +#define bp_clear_(vm,addr) vm->bp[(addr)>>3] &= ~(1 << ((addr)&7)) +#define bp_is_set_(vm,addr) vm->bp[(addr)>>3] & (1 << ((addr)&7)) + +/* Instruction handlers */ +typedef gboolean (*ins_handler_t_)(mix_vm_t *,const mix_ins_t *); + +extern ins_handler_t_ ins_handlers_[MIX_BYTE_MAX + 1]; + + +#endif /* XMIX_VM_H */ + diff --git a/mixlib/xmix_vm_command.c b/mixlib/xmix_vm_command.c new file mode 100644 index 0000000..370ffb7 --- /dev/null +++ b/mixlib/xmix_vm_command.c @@ -0,0 +1,64 @@ +/* -*-c-*- -------------- xmix_vm_command.c : + * Implementation of the functions declared in xmix_vm_command.h + * ------------------------------------------------------------------ + * $Id: xmix_vm_command.c,v 1.4 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2002 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 <stdarg.h> +#include <stdio.h> + +#include "xmix_vm_command.h" + +/* configuration keys */ +const gchar *TRACING_KEY_ = "Tracing"; +const gchar *TIMING_KEY_ = "Timing"; +const gchar *EDITOR_KEY_ = "Editor"; +const gchar *ASM_KEY_ = "Assembler"; +const gchar *LOGGING_KEY_ = "Logs"; + +void +log_message_ (mix_vm_cmd_dispatcher_t *dis, const gchar *fmt, ...) +{ + if (dis && fmt && dis->log_msg && dis->out) + { + va_list args; + va_start (args, fmt); + vfprintf (dis->out, fmt, args); + fprintf (dis->out, "\n"); + va_end (args); + } +} + + +extern void +log_error_ (mix_vm_cmd_dispatcher_t *dis, const gchar *fmt, ...) +{ + enum {BUFF_SIZE = 256}; + static gchar BUFFER[256]; + + if (dis && fmt && dis->err) + { + va_list args; + va_start (args, fmt); + g_snprintf (BUFFER, BUFF_SIZE, "ERROR: %s\n", fmt); + vfprintf (dis->err, BUFFER, args); + va_end (args); + } +} diff --git a/mixlib/xmix_vm_command.h b/mixlib/xmix_vm_command.h new file mode 100644 index 0000000..ae2c2e7 --- /dev/null +++ b/mixlib/xmix_vm_command.h @@ -0,0 +1,102 @@ +/* -*-c-*- ---------------- xmix_vm_command.h : + * Private type declarations form mix_vm_command + * ------------------------------------------------------------------ + * $Id: xmix_vm_command.h,v 1.5 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2004 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. + * + */ + + +#ifndef XMIX_VM_COMMAND_H +#define XMIX_VM_COMMAND_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "mix.h" +#include "mix_device.h" +#include "mix_vm.h" +#include "mix_vm_dump.h" +#include "mix_eval.h" +#include "mix_predicate.h" +#include "mix_vm_command.h" + + +/* configuration keys */ +extern const gchar *TRACING_KEY_; +extern const gchar *TIMING_KEY_; +extern const gchar *EDITOR_KEY_; +extern const gchar *ASM_KEY_; +extern const gchar *LOGGING_KEY_; + +/* hooks */ +typedef struct +{ + mix_vm_cmd_hook_t func; + gpointer data; +} hook_; + +typedef struct +{ + mix_vm_cmd_global_hook_t func; + gpointer data; +} global_hook_; + +enum {PRNO_ = MIX_PRED_MEM, HOOKNO_ = MIX_CMD_INVALID}; + +struct mix_vm_cmd_dispatcher_t +{ + mix_vm_t *vm; /* the virtual machine */ + gboolean result; /* last command's outcome */ + gchar *program; /* the name of the last loaded program */ + gchar *editor; /* edit command line template */ + gchar *assembler; /* compile command line template */ + FILE *out; /* message output file */ + FILE *err; /* error output file */ + mix_dump_context_t *dump; /* dump context for output */ + mix_eval_t *eval; /* evaluator for w-expressions */ + gboolean trace; /* tracing flag */ + gboolean printtime; /* printing times flag */ + mix_time_t uptime; /* total running time */ + mix_time_t laptime; /* last run time */ + mix_time_t progtime; /* current program running time */ + GHashTable *commands; /* local commands */ + GCompletion *completions; /* command completion list */ + GSList *pre_hooks[HOOKNO_]; /* Pre-command hooks */ + GSList *post_hooks[HOOKNO_]; /* Post-command hooks */ + GSList *global_pre; /* global pre-command hook */ + GSList *global_post; /* global post-command hook */ + mix_config_t *config; /* externally provided configuration */ + mix_predicate_t *preds[PRNO_]; /* predicates for conditional breakpoints */ + GHashTable *mem_preds; /* predicates for memory conditional bps */ + gboolean log_msg; /* message logging activation flag*/ +}; + +extern void +log_message_ (mix_vm_cmd_dispatcher_t *dis, const gchar *fmt, ...); + +extern void +log_error_ (mix_vm_cmd_dispatcher_t *dis, const gchar *fmt, ...); + +#define wants_logs_(dis) (dis)->log_msg + +#endif /* XMIX_VM_COMMAND_H */ + diff --git a/mixlib/xmix_vm_handlers.c b/mixlib/xmix_vm_handlers.c new file mode 100644 index 0000000..ba7052b --- /dev/null +++ b/mixlib/xmix_vm_handlers.c @@ -0,0 +1,1387 @@ +/* -*-c-*- -------------- xmix_vm_handlers.c : + * Implementation of the functions declared in xmix_vm_handlers.h + * ------------------------------------------------------------------ + * $Id: xmix_vm_handlers.c,v 1.10 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001, 2002, 2003, 2004 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 "xmix_vm_handlers.h" + +/* available commands (in the same order as the type enum) */ +mix_vm_command_info_t commands_[] = { + { "help", cmd_help_, N_("Display this text"), "help [COMMAND]"}, + { "load", cmd_load_, N_("Load a MIX code file"), "load FILENAME"}, + { "edit", cmd_edit_, N_("Edit a MIXAL source file"), "edit [FILENAME]"}, + { "pedit", cmd_pedit_, N_("Print the external editor command"), "pedit"}, + { "sedit", cmd_sedit_, N_("Set the external editor command"), + "sedit COMMAND (e.g. emacs %s)"}, + { "compile", cmd_compile_, N_("Compile a MIXAL source file"), + "compile [FILENAME]"}, + { "pasm", cmd_pasm_, N_("Print the compile command"), "pasm"}, + { "sasm", cmd_sasm_, N_("Set the compile command"), + "sasm COMMAND (e.g. mixasm -g -l %s)"}, + { "run", cmd_run_, N_("Run loaded or given MIX code file"), + "run [FILENAME]"}, + { "next", cmd_next_, N_("Execute next instruction(s)"), + "next [NO_OF_INS]"}, + { "pstat", cmd_pstat_, N_("Print current vm status"), "pstat"}, + { "pc", cmd_pc_, N_("Print program counter value"), "pc" }, + { "psym", cmd_psym_, N_("Print symbol value"), "psym [SYMBOLNAME]"}, + { "preg", cmd_preg_, N_("Print register value"), + "preg [A | X | J | I[1-6]]"}, + { "pflags", cmd_pflags_, N_("Print comparison and overflow flags"), + "pflags"}, + { "pall", cmd_pall_, N_("Print all registers and flags"), "pall"}, + { "pmem", cmd_pmem_, N_("Print memory contents in address range"), + "pmem FROM[-TO]"}, + { "sreg", cmd_sreg_, N_("Set register value"), + "sreg A | X | J | I[1-6] VALUE"}, + { "scmp", cmd_scmp_, N_("Set comparison flag value"), "scmp L | E | G"}, + { "sover", cmd_sover_, N_("Set overflow flag value"), "sover T | F" }, + { "smem", cmd_smem_, N_("Set memory contents in given address"), + "smem ADDRESS VALUE"}, + { "ssym", cmd_ssym_, N_("Set a symbol\'s value"), "ssym SYMBOL WEXPR"}, + { "sbp", cmd_sbp_, N_("Set break point at given line"), "sbp LINENO"}, + { "cbp", cmd_cbp_, N_("Clear break point at given line"), "cbp LINENO"}, + { "sbpa", cmd_sbpa_, N_("Set break point at given address"), + "sbpa ADDRESS"}, + { "cbpa", cmd_cbpa_, N_("Clear break point at given address"), + "cbpa ADDRESS"}, + { "sbpr", cmd_sbpr_, N_("Set conditional breakpoint on register change"), + "sbpr A | X | J | I[1-6]"}, + { "cbpr", cmd_cbpr_, N_("Clear conditional breakpoint on register change"), + "sbpr A | X | J | I[1-6]"}, + { "sbpm", cmd_sbpm_, N_("Set conditional breakpoint on mem cell change"), + "sbpm ADDRESS"}, + { "cbpm", cmd_cbpm_, N_("Clear conditional breakpoint on mem cell change"), + "cbpm ADDRESS"}, + { "sbpc", cmd_sbpc_, + N_("Set conditional breakpoint on comparison flag change"), "sbpc"}, + { "cbpc", cmd_cbpc_, + N_("Clear conditional breakpoint on comparison flag change"), "cbpc"}, + { "sbpo", cmd_sbpo_, + N_("Set conditional breakpoint on overflow toggled"), "sbpo"}, + { "cbpo", cmd_cbpo_, + N_("Set conditional breakpoint on overflow toggled"), "cbpo"}, + { "cabp", cmd_cabp_, N_("Clear all breakpoints"), "cabp"}, + { "weval", cmd_weval_, N_("Evaluate a given W-expression"), "weval WEXPR"}, + { "w2d", cmd_w2d_, N_("Convert a MIX word to its decimal value"), + "w2d WORD"}, + { "strace", cmd_strace_, N_("Turn on/off instruction tracing"), + "strace on|off"}, + { "pbt", cmd_pbt_, N_("Print backtrace of executed instructions"), + "pbt [INS_NO] (e.g pbt 5)"}, + { "stime", cmd_stime_, N_("Turn on/off timing statistics"), + "stime on|off"}, + { "ptime", cmd_ptime_, N_("Print current time statistics"), "ptime"}, + { "sddir", cmd_sddir_, N_("Set devices directory"),"sddir NEWDIR"}, + { "pddir", cmd_pddir_, N_("Print current devices directory"),"pddir"}, + { "slog", cmd_slog_, N_("Turn on/off message logging"), "slog on|off"}, + { "pprog", cmd_pprog_, N_("Print the current program path"), "pprog"}, + { "psrc", cmd_psrc_, N_("Print the current program source path"), "psrc"}, + { "pline", cmd_pline_, + N_("Print the current (or a given) program source line"), "pline [LINENO]"}, + { NULL, NULL, NULL, NULL}, +}; + +/* trace current instruction */ +static void +do_trace_ (mix_vm_cmd_dispatcher_t *dis, gboolean error) +{ + enum {BUFFER_LEN = 128}; + static gchar STRINS[BUFFER_LEN]; + + const mix_src_file_t *file = mix_vm_get_src_file (dis->vm); + const gchar *line = "\n"; + mix_address_t loc = mix_vm_get_prog_count (dis->vm); + mix_word_t ins = mix_vm_get_addr_contents (dis->vm, loc); + mix_ins_t fins; + mix_word_to_ins_uncheck (ins, fins); + mix_ins_to_string_in_buffer (&fins, STRINS, BUFFER_LEN); + + if (file != NULL) + { + gulong b = mix_vm_get_break_lineno (dis->vm); + if (b > 0) line = mix_src_file_get_line (file, b); + } + + if (!error) + log_message_ (dis, "%d: [%-15s]\t%s", (gint)loc, STRINS, line); + else + log_error_ (dis, "%d: [%-15s]\t%s", (gint)loc, STRINS, line); +} + +static void +trace_ (mix_vm_cmd_dispatcher_t *dis) +{ + if (wants_logs_ (dis)) + { + do_trace_ (dis, FALSE); + } +} + +#define trace_error_(dis) do_trace_ (dis, TRUE) + +/* run a program tracing executed instructions */ +static int +run_and_trace_ (mix_vm_cmd_dispatcher_t *dis) +{ + int k = MIX_VM_RUNNING; + if (!dis->trace) + return mix_vm_run (dis->vm); + else while (k == MIX_VM_RUNNING) + { + trace_ (dis); + k = mix_vm_exec_next (dis->vm); + } + return k; +} + +/* print time statistics */ +static void +print_time_ (mix_vm_cmd_dispatcher_t *dis) +{ + dis->laptime = mix_vm_get_uptime (dis->vm) - dis->uptime; + dis->uptime += dis->laptime; + dis->progtime += dis->laptime; + if (dis->printtime) + fprintf + (dis->out, + _("Elapsed time: %ld /Total program time: %ld (Total uptime: %ld)\n"), + dis->laptime, dis->progtime, dis->uptime); +} + + +/* commands */ +static void +print_help_ (gpointer key, gpointer val, gpointer data) +{ + mix_vm_command_info_t *info = (mix_vm_command_info_t *)val; + if (info) + fprintf ((FILE *)data, "%-15s%s\n", info->name, info->doc); +} + +gboolean +cmd_help_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (arg && strlen (arg) > 0) + { + int i; + mix_vm_command_info_t *info = + (mix_vm_command_info_t *)g_hash_table_lookup + (dis->commands, (gpointer)arg); + for (i = 0; info == NULL && commands_[i].name; i++) + if (!strcmp (commands_[i].name, arg)) info = commands_ + i; + if (info) + { + fprintf (dis->out , _("%-15s%s.\n%-15sUsage: %s\n"), + info->name, info->doc, "", info->usage); + return TRUE; + } + fprintf (dis->out, _("No commands match `%s'\n"), arg); + return FALSE; + } + else + { + int i = 0; + fprintf (dis->out, _("Possible commands are:\n")); + for (i = 0; commands_[i].name; ++i) + fprintf (dis->out, "%-15s%s\n", commands_[i].name, commands_[i].doc); + g_hash_table_foreach (dis->commands, print_help_, (gpointer)dis->out); + return TRUE; + } +} + +gboolean +cmd_load_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + errno = 0; + if (arg == NULL || *arg == '\0') + { + log_error_ (dis, _("Missing file name")); + return FALSE; + } + mix_eval_remove_symbols_from_table (dis->eval, + mix_vm_get_symbol_table (dis->vm)); + if (!mix_vm_load_file (dis->vm, arg) ) + { + log_error_ (dis, _("Cannot load %s: "), arg); + if ( errno == 0 ) + log_error_ (dis, _("Wrong file format")); + else + log_error_ (dis, "%s", strerror (errno)); + return FALSE; + } + + if (dis->program != arg) + { + if (dis->program) g_free (dis->program); + dis->program = mix_file_complete_name (arg, MIX_CODE_DEFEXT); + } + + mix_eval_set_symbols_from_table (dis->eval, + mix_vm_get_symbol_table (dis->vm)); + if (wants_logs_ (dis)) + log_message_ (dis, _("Program loaded. Start address: %d"), + mix_vm_get_prog_count (dis->vm)); + + dis->laptime = dis->progtime = 0; + return TRUE; +} + +gboolean +cmd_edit_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (dis->editor == NULL) + { + log_error_ (dis, _("Editor not specified (use sedit)")); + return FALSE; + } + if (!arg || *arg == '\0') arg = mix_vm_cmd_dispatcher_get_src_file_path (dis); + if (!arg) + { + log_error_ (dis, _("MIXAL source file path not found")); + return FALSE; + } + else + { + gchar *cmd = g_strdup_printf (dis->editor, arg); + if (wants_logs_ (dis)) log_message_ (dis, cmd); + system (cmd); + if (wants_logs_ (dis)) log_message_ (dis, _(" ...done")); + g_free (cmd); + return TRUE; + } +} + +gboolean +cmd_compile_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (dis->assembler == NULL) + { + log_error_ (dis, _("MIX assembler not specified (use sasm)")); + return FALSE; + } + if (!arg || *arg == '\0') arg = mix_vm_cmd_dispatcher_get_src_file_path (dis); + if (!arg) + { + log_error_ (dis, _("MIXAL source file path not found")); + return FALSE; + } + else + { + gchar *cmd = g_strdup_printf (dis->assembler, arg); + if (wants_logs_ (dis)) log_message_ (dis, cmd); + if (system (cmd) == EXIT_SUCCESS && wants_logs_ (dis)) + log_message_ (dis, _("Successful compilation")); + g_free (cmd); + return TRUE; + } +} + +gboolean +cmd_run_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (arg != NULL && *arg != '\0' && cmd_load_ (dis, arg) != TRUE) + return FALSE; + + if (mix_vm_is_halted (dis->vm)) cmd_load_ (dis, dis->program); + + if (wants_logs_ (dis)) log_message_ (dis, _("Running ...")); + + switch (run_and_trace_ (dis)) + { + case MIX_VM_HALT: + if (wants_logs_ (dis)) log_message_ (dis, _("... done")); + break; + case MIX_VM_BREAK: + if (wants_logs_ (dis)) + { + gulong line = mix_vm_get_break_lineno (dis->vm); + if (line != 0) + log_message_ + (dis, _("... stopped: breakpoint at line %ld (address %d)"), + line, mix_vm_get_prog_count (dis->vm)); + else + log_message_ (dis, _("... stopped: breakpoint at address %d"), + mix_vm_get_prog_count (dis->vm)); + } + break; + case MIX_VM_COND_BREAK: + if (wants_logs_ (dis)) + { + gulong line = mix_vm_get_break_lineno (dis->vm); + if (line != 0) + log_message_ (dis, _("... stopped: %s (line %ld, address %d)"), + mix_vm_get_last_breakpoint_message (dis->vm), + line, mix_vm_get_prog_count (dis->vm)); + else + log_message_ (dis, _("... stopped: %s (address %d)"), + mix_vm_get_last_breakpoint_message (dis->vm), + mix_vm_get_prog_count (dis->vm)); + } + break; + case MIX_VM_ERROR: + log_error_ (dis, _("%s:"), mix_vm_get_last_error_string (dis->vm)); + trace_error_ (dis); + break; + default: + g_assert_not_reached (); + break; + } + + if (wants_logs_ (dis)) print_time_ (dis); + + return TRUE; +} + +gboolean +cmd_next_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + int ins_no = 1; + int k; + + if ( strlen (arg) != 0 ) + { + int k = 0; + while (isdigit (arg[k])) + k++; + if (arg[k] != '\0') + { + log_error_ (dis, _("Invalid argument: %s"), arg); + return FALSE; + } + ins_no = atoi (arg); + } + + if (mix_vm_is_halted (dis->vm)) cmd_load_ (dis, dis->program); + + while ( ins_no-- > 0 ) + { + if (dis->trace) trace_ (dis); + k = mix_vm_exec_next (dis->vm); + if (k == MIX_VM_HALT) + { + if (wants_logs_ (dis)) + log_message_(dis, _("End of program reached at address %d"), + mix_vm_get_prog_count (dis->vm)); + break; + } + else if (k == MIX_VM_ERROR) + { + log_error_ (dis, _("%s:"), mix_vm_get_last_error_string (dis->vm)); + trace_error_ (dis); + break; + } + } + if (wants_logs_ (dis)) print_time_ (dis); + + return TRUE; +} + +gboolean +cmd_pc_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + fprintf (dis->out, "Current address: %d\n", mix_vm_get_prog_count (dis->vm)); + return TRUE; +} + +gboolean +cmd_psym_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gboolean result = FALSE; + const mix_symbol_table_t *table = mix_eval_symbol_table (dis->eval); + if ( table == NULL ) + log_error_ (dis, _("Symbol table not available")); + else if (arg != NULL && *arg != '\0') + { + if ( mix_symbol_table_is_defined (table, arg) ) + { + mix_word_print_to_file (mix_symbol_table_value (table, arg), + NULL, dis->out); + putc ('\n', dis->out); + result = TRUE; + } + else + fprintf (dis->out, _("%s: symbol not defined\n"), arg); + } + else + { + mix_symbol_table_print (table, MIX_SYM_ROWS, dis->out, TRUE); + result = TRUE; + } + + return result; +} + +gboolean +cmd_preg_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + mix_dump_context_set_opt (dis->dump, MIX_DUMP_NONE); + if ( strlen (arg) == 0 ) + mix_dump_context_add_opt (dis->dump, MIX_DUMP_rALL); + else switch (*arg) + { + case 'A': + mix_dump_context_add_opt (dis->dump, MIX_DUMP_rA); + break; + case 'X': + mix_dump_context_add_opt (dis->dump, MIX_DUMP_rX); + break; + case 'J': + mix_dump_context_add_opt (dis->dump, MIX_DUMP_rJ); + break; + case 'I': + { + if ( strlen (arg) == 1 ) + mix_dump_context_add_opt (dis->dump, MIX_DUMP_rIa); + else + { + static gint32 opt[] = { MIX_DUMP_rI1, MIX_DUMP_rI2, + MIX_DUMP_rI3, MIX_DUMP_rI4, + MIX_DUMP_rI5, MIX_DUMP_rI6 + }; + int i = arg[1] - '1'; + if ( i < 0 || i > 5 ) + { + log_error_ (dis, _("Invalid I index: %d"), i); + return FALSE; + } + mix_dump_context_add_opt (dis->dump, opt[i]); + } + } + break; + default: + log_error_ (dis, _("Invalid argument: %s"), arg); + return FALSE; + } + mix_vm_dump (dis->vm, dis->dump); + return TRUE; +} + +gboolean +cmd_pflags_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + mix_dump_context_set_opt (dis->dump, MIX_DUMP_CMP | MIX_DUMP_OVER); + mix_vm_dump (dis->vm, dis->dump); + return TRUE; +} + +gboolean +cmd_pall_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + mix_dump_context_set_opt (dis->dump, MIX_DUMP_ALL_NOMEM); + mix_vm_dump (dis->vm, dis->dump); + return TRUE; +} + +gboolean +cmd_pmem_ (mix_vm_cmd_dispatcher_t *dis, const gchar *carg) +{ + glong begin = MIX_SHORT_ZERO, end = MIX_SHORT_ZERO; + int i = 0; + gboolean error = FALSE; + gchar *arg = NULL; + + if ( strlen (carg) == 0 ) + { + log_error_ (dis, _("Missing memory address")); + return FALSE; + } + arg = g_strdup (carg); + while (isdigit (arg[i])) + i++; + while (isspace (arg[i])) + i++; + if (arg[i] == '\0') + begin = end = atol (arg); + else if (arg[i] == '-') + { + gchar *narg; + arg[i++] = '\0'; + begin = atol (arg); + narg = arg + i; + i = 0; + while (isdigit (narg[i])) + i++; + while (isspace (narg[i])) + i++; + if (narg[i] != '\0') + error = TRUE; + else + end = atol (narg); + } + else + error = TRUE; + + if (error) + { + log_error_ (dis, _("Invalid argument: %s"), arg); + } + else if ( end < begin || end > MIX_VM_CELL_NO - 1 ) + { + log_error_ (dis, _("Invalid range: %ld-%ld"), begin, end); + error = TRUE; + } + else + { + mix_dump_context_set_opt (dis->dump, MIX_DUMP_CELLS); + mix_dump_context_range (dis->dump, mix_short_new (begin), + mix_short_new (end + 1)); + mix_vm_dump (dis->vm, dis->dump); + } + g_free (arg); + return !error; +} + +gboolean +cmd_sreg_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + int i = 0; + char reg = arg[0]; + gboolean ok = TRUE; + long value; + + i = (reg == 'I') ? 2 : 1; + ok = strlen (arg) > 2 && isspace (arg[i]); + if (ok) + { + while (isspace (arg[i])) i++; + ok = isdigit (arg[i]) || arg[i] == '+' || arg[i] == '-'; + if (ok) + { + value = atol (arg + i); + if (arg[i] == '+' || arg[i] == '-') i++; + while (isdigit (arg[i])) i++; + ok = (arg[i] == '\0'); + if (ok) + switch (reg) + { + case 'A': + mix_vm_set_rA (dis->vm, mix_word_new (value)); + break; + case 'X': + mix_vm_set_rX (dis->vm, mix_word_new (value)); + break; + case 'J': + if ( value >= 0 ) + mix_vm_set_rJ (dis->vm, mix_short_new (value)); + else + ok = FALSE; + break; + case 'I': + { + guint k = arg[1] - '0'; + if ( k < 7 ) + mix_vm_set_rI (dis->vm, k, mix_short_new (value)); + else + ok = FALSE; + } + break; + default: + ok = FALSE; + } + } + } + if (!ok) + { + log_error_ (dis, _("Invalid argument: %s"), arg); + } + + return ok; +} + +gboolean +cmd_scmp_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gboolean ok = (strlen (arg) == 1); + if (ok) switch (arg[0]) + { + case 'L': + mix_vm_set_cmpflag (dis->vm, mix_LESS); + break; + case 'E': + mix_vm_set_cmpflag (dis->vm, mix_EQ); + break; + case 'G': + mix_vm_set_cmpflag (dis->vm, mix_GREAT); + break; + default: + ok = FALSE; + } + if (!ok) + { + log_error_ (dis, _("Invalid argument: %s"), arg); + } + + return ok; +} + +gboolean +cmd_sover_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gboolean ok = (strlen (arg) == 1); + if (ok) switch (arg[0]) + { + case 'T': + mix_vm_set_overflow (dis->vm, TRUE); + break; + case 'F': + mix_vm_set_overflow (dis->vm, FALSE); + break; + default: + ok = FALSE; + } + if (!ok) + { + log_error_ (dis, _("Invalid argument: %s"), arg); + } + + return ok; +} + +gboolean +cmd_smem_ (mix_vm_cmd_dispatcher_t *dis, const gchar *carg) +{ + gboolean ok = (strlen (carg) > 2 && isdigit (carg[0])); + glong addr = -1; + glong value = 0; + int k = 0; + gchar *arg = NULL; + + if (ok) + { + arg = g_strdup (carg); + while (isdigit (arg[k])) k++; + ok = isspace (arg[k]); + if (ok) + { + arg[k++] = '\0'; + addr = atol (arg); + ok = addr < MIX_VM_CELL_NO; + } + if (ok) + { + while (isspace (arg[k])) k++; + value = atol (arg + k); + if ( arg[k] == '+' || arg[k] == '-' ) k++; + while (isdigit (arg[k])) k++; + ok = arg[k] == '\0'; + } + } + + if (ok) + mix_vm_set_addr_contents (dis->vm, mix_short_new (addr), + mix_word_new (value)); + else + { + log_error_ (dis, _("Invalid argument: %s"), arg); + } + if (arg) g_free (arg); + + return ok; +} + +gboolean +cmd_ssym_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gboolean result = FALSE; + if (arg == NULL || strlen(arg) == 0) + { + log_error_ (dis, _("Missing arguments")); + } + else + { + gchar *a = g_strdup (arg); + gchar *s = strtok (a, " \t"); + gchar *w = strtok (NULL, " \t"); + if (w != NULL && strtok (NULL, " \t") == NULL) + { + cmd_weval_ (dis, w); + if (mix_eval_last_error (dis->eval) == MIX_EVAL_OK) { + mix_eval_set_symbol (dis->eval, s, mix_eval_value (dis->eval)); + result = TRUE; + } + } + else + { + log_error_ (dis, _("Wrong argument number")); + } + g_free (a); + } + return result; +} + +gboolean +cmd_sbp_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + glong lineno; + glong k = 0; + while (isdigit (arg[k])) k++; + if (arg[k] != '\0') + { + log_error_ (dis, _("Invalid argument: %s"), arg); + return FALSE; + } + lineno = atol (arg); + switch (k = mix_vm_set_breakpoint (dis->vm, lineno)) + { + case MIX_VM_BP_INV_LINE: + log_error_ (dis, _("Line number %ld too high"), lineno); + break; + case MIX_VM_BP_ERROR: + log_error_ (dis, _("Could not set breakpoint: internal error")); + break; + case MIX_VM_BP_NDEBUG: + log_error_ (dis, _("Could not set breakpoint: no debug info available"), + dis->err); + break; + default: + if (wants_logs_ (dis)) + log_message_ (dis, _("Breakpoint set at line %ld"), k); + return TRUE; + } + return FALSE; +} + +gboolean +cmd_sbpa_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + glong address; + glong k = 0; + while (isdigit (arg[k])) k++; + if (arg[k] != '\0') + { + log_error_ (dis, _("Invalid argument: %s"), arg); + return FALSE; + } + address = atol (arg); + switch (mix_vm_set_breakpoint_address (dis->vm, address)) + { + case MIX_VM_BP_INV_ADDRESS: + log_error_ (dis, _("Invalid address %ld"), address); + break; + case MIX_VM_BP_ERROR: + log_error_ (dis, _("Could not set breakpoint: internal error")); + break; + default: + if (wants_logs_ (dis)) + log_message_ (dis, _("Breakpoint set at address %ld"), address); + return TRUE; + } + return FALSE; +} + +gboolean +cmd_cbp_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + glong lineno; + int k = 0; + while (isdigit (arg[k])) k++; + if (arg[k] != '\0') + { + log_error_ (dis, _("Invalid argument: %s"), arg); + return FALSE; + } + lineno = atol (arg); + switch (mix_vm_clear_breakpoint (dis->vm, lineno)) + { + case MIX_VM_BP_INV_LINE: + log_error_ (dis, _("No breakpoint set at line %ld"), lineno); + break; + case MIX_VM_BP_ERROR: + log_error_ (dis, _("Could not set breakpoint: internal error")); + break; + case MIX_VM_BP_NDEBUG: + log_error_ (dis, _("No debug info available")); + break; + case MIX_VM_BP_OK: + if (wants_logs_ (dis)) + log_message_ (dis, _("Breakpoint cleared at line %ld"), lineno); + return TRUE; + default: + g_assert_not_reached (); + break; + } + return FALSE; +} + +gboolean +cmd_cbpa_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + glong address; + glong k = 0; + while (isdigit (arg[k])) k++; + if (arg[k] != '\0') + { + log_error_ (dis, _("Invalid argument: %s"), arg); + return FALSE; + } + address = atol (arg); + switch (mix_vm_clear_breakpoint_address (dis->vm, address)) + { + case MIX_VM_BP_INV_ADDRESS: + log_error_ (dis, _("Invalid address %ld"), address); + break; + case MIX_VM_BP_ERROR: + log_error_ (dis, _("Could not clear breakpoint: internal error")); + break; + default: + if (wants_logs_ (dis)) + log_message_ (dis, _("Breakpoint cleared at address %ld"), address); + return TRUE; + } + return FALSE; +} + + +gboolean +cmd_cabp_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (strlen (arg) != 0) + { + log_error_ (dis, _("Unexpected argument: %s"), arg); + return FALSE; + } + mix_vm_clear_all_breakpoints (dis->vm); + return TRUE; +} + +gboolean +cmd_weval_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if ( strlen (arg) == 0 ) + { + log_error_ (dis, _("Missing expression")); + return FALSE; + } + + if (mix_eval_expression_with_loc (dis->eval, arg, + mix_vm_get_prog_count (dis->vm)) == + MIX_EVAL_OK) + { + mix_word_print_to_file (mix_eval_value (dis->eval), NULL, dis->out); + putc ('\n', dis->out); + return TRUE; + } + else + { + gint pos = mix_eval_last_error_pos (dis->eval); + gint k, len = strlen (arg); + g_assert(pos > -1 && pos <= len); + for (k = 0; k<pos; ++k) fputc (arg[k], dis->err); + fputc ('\n', dis->err); + for (k = 0; k<pos; ++k) fputc (' ', dis->err); + for (k = pos; k < len; ++k) fputc (arg[k], dis->err); + fprintf (dis->err, _("\nEvaluation error: %s\n"), + mix_eval_last_error_string (dis->eval)); + return FALSE; + } +} + +gboolean +cmd_w2d_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if ( strlen (arg) == 0 ) + { + log_error_ (dis, _("Missing expression")); + return FALSE; + } + else + { + gchar *cp = g_strdup (arg), *a = cp; + mix_byte_t bytes[5] = {0, 0, 0, 0, 0}; + gchar *b; + guint k = 0; + gboolean is_n = (a[0] == '-'), success = TRUE; + if (a[0] == '+' || a[0] == '-') ++a; + b = strtok (a, " \t"); + while (b != NULL && k < 5) + { + if (strlen (b) != 2 || !isdigit(b[0]) || !isdigit(b[1])) + { + log_error_ (dis, _("Incorrect byte specification: %s"), b); + success = FALSE; + b = NULL; + } + else + { + bytes[k++] = mix_byte_new (atoi (b)); + b = strtok (NULL, " \t"); + } + } + if (success) + { + if (strtok (NULL, "\t") != NULL) + { + log_error_ (dis, + _("The expression %s does not fit in a word"), arg); + success = FALSE; + } + else + { + mix_word_t w = mix_bytes_to_word (bytes, k); + fprintf (dis->out, "%s%ld\n", is_n? "-":"+", + mix_word_magnitude (w)); + } + } + g_free (cp); + + return success; + } +} + +gboolean +cmd_strace_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + static const gchar *ON = "on"; + static const gchar *OFF = "off"; + if (!arg || !strlen (arg)) + { + log_error_ (dis, _("Missing argument")); + } + else if (!strcmp (arg, ON)) + { + dis->trace = TRUE; + if (dis->config) mix_config_update (dis->config, TRACING_KEY_, ON); + } + else if (!strcmp (arg, OFF)) + { + dis->trace = FALSE; + if (dis->config) mix_config_update (dis->config, TRACING_KEY_, OFF); + } + else + log_error_ (dis, _("Wrong argument: "), arg); + return TRUE; +} + +gboolean +cmd_stime_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + static const gchar *ON = "on"; + static const gchar *OFF = "off"; + if (!arg || !strlen (arg)) + { + log_error_ (dis, _("Missing argument")); + } + else if (!strcmp (arg, ON)) + { + dis->printtime = TRUE; + if (dis->config) mix_config_update (dis->config, TIMING_KEY_, ON); + } + else if (!strcmp (arg, OFF)) + { + dis->printtime = FALSE; + if (dis->config) mix_config_update (dis->config, TIMING_KEY_, OFF); + } + else + log_error_ (dis, _("Wrong argument: "), arg); + return TRUE; +} + +gboolean +cmd_ptime_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + print_time_ (dis); + return TRUE; +} + +gboolean +cmd_pedit_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + const gchar *ed = mix_vm_cmd_dispatcher_get_editor (dis); + if (dis) + fprintf (dis->out, _("Edit command: %s\n"), ed); + else + fprintf (dis->out, _("Edit command not set (use sedit)\n")); + return TRUE; +} + +gboolean +cmd_sedit_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (!arg || !strlen (arg)) + { + log_error_ (dis, _("Missing argument")); + return FALSE; + } + mix_vm_cmd_dispatcher_set_editor (dis, arg); + return TRUE; +} + +gboolean +cmd_pasm_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + const gchar *ed = mix_vm_cmd_dispatcher_get_assembler (dis); + if (dis) + fprintf (dis->out, _("Compile command: %s\n"), ed); + else + fprintf (dis->out, _("Compile command not set (use sasm)\n")); + return TRUE; +} + +gboolean +cmd_sasm_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (!arg || !strlen (arg)) + { + log_error_ (dis, _("Missing argument")); + return FALSE; + } + mix_vm_cmd_dispatcher_set_assembler (dis, arg); + return TRUE; +} + +gboolean +cmd_sddir_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (!arg || !strlen (arg)) log_error_ (dis, _("Missing argument")); + else if (mix_device_set_dir (arg) && dis->config) + mix_config_set_devices_dir (dis->config, arg); + return TRUE; +} + +gboolean +cmd_pddir_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + fprintf (dis->out, _("Device directory: %s\n"), mix_device_get_dir ()); + return TRUE; +} + +static const gint INVALID_REG_ = -2; + +static mix_predicate_type_t +get_reg_pred_ (const gchar *arg) +{ + mix_predicate_type_t pred = INVALID_REG_; + + switch (*arg) + { + case 'A': + pred = MIX_PRED_REG_A; + break; + case 'X': + pred = MIX_PRED_REG_X; + break; + case 'J': + pred = MIX_PRED_REG_J; + break; + case 'I': + { + if ( strlen (arg) == 2 ) + { + int i = arg[1] - '1'; + if (i >= 0 && i < 6) + pred = MIX_PRED_REG_I1 - 1 + i; + } + } + break; + default: + break; + } + return pred; +} + +gboolean +cmd_sbpr_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + mix_predicate_type_t pred = get_reg_pred_ (arg); + if (pred != INVALID_REG_) + { + mix_vm_set_conditional_breakpoint (dis->vm, dis->preds[pred]); + if (wants_logs_ (dis)) + log_message_ (dis, + _("Conditional breakpoint on r%s change set"), arg); + return TRUE; + } + else + { + log_error_ (dis, _("Invalid argument %s"), arg); + return FALSE; + } +} + +gboolean +cmd_cbpr_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + mix_predicate_type_t pred = get_reg_pred_ (arg); + if (pred != INVALID_REG_) + { + if (mix_vm_clear_conditional_breakpoint (dis->vm, dis->preds[pred])) + { + if (wants_logs_(dis)) + log_message_ (dis, + _("Conditional breakpoint on r%s change removed"), + arg); + } + else + log_error_ (dis, _("No breakpoint set on r%s change"), arg); + return TRUE; + } + else + { + log_error_ (dis, _("Invalid argument %s"), arg); + return FALSE; + } +} + +gboolean +cmd_sbpm_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gint add = atoi (arg); + gpointer key, value; + if (add < 0 || add > MIX_VM_CELL_NO) + { + log_error_ (dis, _("Invalid memory address: %s"), arg); + return FALSE; + } + if (!g_hash_table_lookup_extended (dis->mem_preds, GINT_TO_POINTER (add), + &key, &value)) + { + mix_predicate_t *new_pred = mix_predicate_new (MIX_PRED_MEM); + mix_predicate_set_mem_address (new_pred, add); + g_hash_table_insert (dis->mem_preds, + GINT_TO_POINTER (add), (gpointer)new_pred); + mix_vm_set_conditional_breakpoint (dis->vm, new_pred); + } + if (wants_logs_ (dis)) + log_message_ (dis, _("Conditional breakpoint on mem cell no. %d set"), + add); + return TRUE; +} + +gboolean +cmd_cbpm_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gint add = atoi (arg); + gpointer key, value; + if (add < 0 || add > MIX_VM_CELL_NO) + { + log_error_ (dis, _("Invalid memory address: %s"), arg); + return FALSE; + } + if (g_hash_table_lookup_extended (dis->mem_preds, GINT_TO_POINTER (add), + &key, &value)) + { + g_hash_table_remove (dis->mem_preds, key); + mix_vm_clear_conditional_breakpoint (dis->vm, (mix_predicate_t *)value); + mix_predicate_delete ((mix_predicate_t *)value); + if (wants_logs_ (dis)) + log_message_ (dis, + _("Conditional breakpoint on mem cell no. %d removed"), + add); + } + else + { + log_error_ (dis, _("No conditional breakpoint set at address %d"), + add); + } + return TRUE; +} + +gboolean +cmd_sbpo_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (arg && strlen (arg)) + log_error_ (dis, _("Unexpected argument: %s"), arg); + else + { + mix_vm_set_conditional_breakpoint (dis->vm, dis->preds[MIX_PRED_OVER]); + if (wants_logs_ (dis)) + log_message_ (dis, + _("Conditional breakpoint on overflow toggled set")); + } + return TRUE; +} + +gboolean +cmd_cbpo_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (arg && strlen (arg)) + log_error_ (dis, _("Unexpected argument: %s"), arg); + else + { + if (mix_vm_clear_conditional_breakpoint + (dis->vm, dis->preds[MIX_PRED_OVER])) + { + if (wants_logs_ (dis)) + log_message_ + (dis, _("Conditional breakpoint on overflow toggled removed.")); + } + else + log_error_ (dis, _("No breakpoint set on overflow toggle")); + } + return TRUE; +} + +gboolean +cmd_sbpc_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (arg && strlen (arg)) + log_error_ (dis, _("Unexpected argument: %s"), arg); + else + { + mix_vm_set_conditional_breakpoint (dis->vm, dis->preds[MIX_PRED_CMP]); + if (wants_logs_ (dis)) + log_message_ + (dis, _("Conditional breakpoint on comparison flag changed set")); + } + return TRUE; +} + +gboolean +cmd_cbpc_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + if (arg && strlen (arg)) + log_error_ (dis, _("Unexpected argument: %s"), arg); + else + { + if (mix_vm_clear_conditional_breakpoint + (dis->vm, dis->preds[MIX_PRED_CMP])) + { + if (wants_logs_ (dis)) + log_message_ + (dis, + _("Conditional breakpoint on comparison flag changed removed.")); + } + else + log_error_ (dis, _("No breakpoint set on comparison flag change")); + } + return TRUE; +} + +gboolean +cmd_pbt_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + enum {SIZE = 256}; + static gchar BUFFER[SIZE]; + gint no = atoi (arg); + gint k = 0, address; + guint line; + const mix_src_file_t *file = mix_vm_get_src_file (dis->vm); + char *name = + file ? g_path_get_basename (mix_src_file_get_path (file)) : NULL; + + const GSList *add = mix_vm_get_backtrace (dis->vm); + while (add && (no == 0 || k < no)) + { + BUFFER[0] = '\0'; + address = GPOINTER_TO_INT (add->data); + line = mix_vm_get_address_lineno (dis->vm, address); + if (line && file) + { + int j = 0; + g_snprintf (BUFFER, SIZE, "%s", mix_src_file_get_line (file, line)); + while (!isspace (BUFFER[j])) j++; + BUFFER[j] = '\0'; + } + if (strlen (BUFFER) == 0) g_snprintf (BUFFER, SIZE, "%d", address); + fprintf (dis->out, "#%d\t%s\tin %s%s:%d\n", k, BUFFER, name, + MIX_SRC_DEFEXT, line); + ++k; + add = add->next; + } + return TRUE; +} + +gboolean +cmd_slog_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + static const gchar *ON = "on"; + static const gchar *OFF = "off"; + if (arg && !strcmp (arg, ON)) + { + dis->log_msg = TRUE; + if (dis->config) mix_config_update (dis->config, LOGGING_KEY_, ON); + } + else if (arg && !strcmp (arg, OFF)) + { + dis->log_msg = FALSE; + if (dis->config) mix_config_update (dis->config, LOGGING_KEY_, OFF); + } + else + log_error_ (dis, _("Wrong argument: "), arg); + return TRUE; +} + +gboolean +cmd_pprog_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + const gchar *path = mix_vm_cmd_dispatcher_get_program_path (dis); + fprintf (dis->out, path? path : _("No program currently loaded")); + fprintf (dis->out, "\n"); + return (path != NULL); +} + +gboolean +cmd_psrc_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + const gchar *path = mix_vm_cmd_dispatcher_get_src_file_path (dis); + fprintf (dis->out, path? path : _("No program currently loaded\n")); + fprintf (dis->out, "\n"); + return (path != NULL); +} + +gboolean +cmd_pline_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + gulong line = 0; + const gchar *txt; + + if (arg && strlen (arg)) line = atoi (arg); + + if (line < 0) + { + log_error_ (dis, _("Invalid argument")); + return FALSE; + } + + if (line == 0) + line = mix_vm_cmd_dispatcher_get_src_file_lineno (dis); + + if (line == 0) + txt = "No such line (debug info not available)\n"; + else + txt = mix_vm_cmd_dispatcher_get_src_file_line (dis, line, FALSE); + + if (txt == NULL || strlen (txt) == 0) txt = "No such line\n"; + + fprintf (dis->out, "Line %ld: %s\n", line, txt); + + return TRUE; +} + +gboolean +cmd_pstat_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) +{ + static const gchar *MSGS[MIX_VM_EMPTY + 1] = { + N_("Error loading or executing file"), + N_("Execution stopped: breakpoint encountered"), + N_("Execution stopped: conditional breakpoint encountered"), + N_("Program successfully terminated"), + N_("Execution stopped"), + N_("Program successfully loaded"), + N_("No program loaded") + }; + mix_vm_status_t status = + mix_vm_get_run_status (mix_vm_cmd_dispatcher_get_vm (dis)); + fprintf (dis->out, "VM status: %s\n", MSGS[status]); + return TRUE; +} diff --git a/mixlib/xmix_vm_handlers.h b/mixlib/xmix_vm_handlers.h new file mode 100644 index 0000000..b3258f9 --- /dev/null +++ b/mixlib/xmix_vm_handlers.h @@ -0,0 +1,87 @@ +/* -*-c-*- ---------------- xmix_vm_handlers.h : + * mix_vm_cmd_dispatcher command handlers + * ------------------------------------------------------------------ + * $Id: xmix_vm_handlers.h,v 1.4 2005/09/20 19:43:13 jao Exp $ + * ------------------------------------------------------------------ + * Copyright (C) 2001 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. + * + */ + + +#ifndef XMIX_VM_HANDLERS_H +#define XMIX_VM_HANDLERS_H + +#include "xmix_vm_command.h" + +/* command handlers */ +#define DEC_FUN(name) \ + extern gboolean cmd_##name (mix_vm_cmd_dispatcher_t *dis, const gchar *arg) + +DEC_FUN (help_); +DEC_FUN (load_); +DEC_FUN (run_); +DEC_FUN (next_); +DEC_FUN (pc_); +DEC_FUN (psym_); +DEC_FUN (preg_); +DEC_FUN (pflags_); +DEC_FUN (pall_); +DEC_FUN (pmem_); +DEC_FUN (sreg_); +DEC_FUN (scmp_); +DEC_FUN (sover_); +DEC_FUN (smem_); +DEC_FUN (ssym_); +DEC_FUN (sbp_); +DEC_FUN (sbpa_); +DEC_FUN (cbp_); +DEC_FUN (cbpa_); +DEC_FUN (cabp_); +DEC_FUN (weval_); +DEC_FUN (w2d_); +DEC_FUN (strace_); +DEC_FUN (stime_); +DEC_FUN (ptime_); +DEC_FUN (edit_); +DEC_FUN (compile_); +DEC_FUN (pedit_); +DEC_FUN (sedit_); +DEC_FUN (pasm_); +DEC_FUN (sasm_); +DEC_FUN (pddir_); +DEC_FUN (sddir_); +DEC_FUN (sbpr_); +DEC_FUN (sbpm_); +DEC_FUN (sbpc_); +DEC_FUN (sbpo_); +DEC_FUN (cbpr_); +DEC_FUN (cbpm_); +DEC_FUN (cbpc_); +DEC_FUN (cbpo_); +DEC_FUN (pbt_); +DEC_FUN (slog_); +DEC_FUN (pprog_); +DEC_FUN (psrc_); +DEC_FUN (pline_); +DEC_FUN (pstat_); + +/* default command structure */ +extern mix_vm_command_info_t commands_[]; + + +#endif /* XMIX_VM_HANDLERS_H */ + |