/* -*-c-*- ------------------ mix_vm.c : * Implementation of the functions declared in mix_vm.h * ------------------------------------------------------------------ * Copyright (C) 2000 jose antonio ortega ruiz * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "mix.h" #include "xmix_vm.h" static void vm_reset_ (mix_vm_t *vm) { guint k; 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); for ( k = 0; k < MEM_CELLS_NO_; ++k) set_cell_ (vm, k, MIX_WORD_ZERO); halt_ (vm, FALSE); 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; } bp_clear_all_ (vm); } /* 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; for (i = 0; i < BD_NO_; ++i) vm->devices[i] = NULL; vm->clock = mix_vm_clock_new (); vm_reset_ (vm); return vm; } void mix_vm_delete (mix_vm_t * vm) { int i; 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); for (i = 0; i < BD_NO_; ++i) mix_device_delete (vm->devices[i]); mix_vm_clock_delete (vm->clock); g_free (vm); } /* 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); g_return_val_if_fail (MEMOK_ (addr), MIX_WORD_ZERO); return get_cell_ (vm, addr); } void mix_vm_set_addr_contents (mix_vm_t *vm, mix_address_t addr, mix_word_t value) { g_return_if_fail (vm != NULL); g_return_if_fail (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_ins_desc_t ins; const gchar *sp; g_return_val_if_fail (vm != NULL, FALSE); file = mix_code_file_new_read (name); if (file == NULL) return FALSE; vm_reset_ (vm); 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_); } sp = mix_code_file_get_source_path (file); if (sp != NULL) vm->src_file = mix_src_file_new_for_read (sp); 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); 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); } /* Reposition program counter and reset state so that a loaded program can be restarted. */ void mix_vm_reset_program (mix_vm_t *vm) { guint k; g_return_if_fail (vm != NULL); reset_loc_ (vm); halt_ (vm, FALSE); 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); } /* continue execution of instructions in memory */ int 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); if ( !(*ins_handlers_[ins.opcode]) (vm,&ins) ) return MIX_VM_ERROR; else update_time_ (vm, &ins); if (bp_is_set_ (vm, get_loc_ (vm))) return MIX_VM_BREAK; if (get_loc_ (vm) >= MIX_VM_CELL_NO) halt_ (vm, TRUE); } return MIX_VM_HALT; } /* execute next memory instruction */ int 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 MIX_VM_HALT; mix_word_to_ins_uncheck (get_cell_ (vm, get_loc_ (vm)), ins); if (!(*ins_handlers_[ins.opcode]) (vm, &ins)) return MIX_VM_ERROR; else update_time_ (vm, &ins); if (is_halted_ (vm)) return MIX_VM_HALT; return bp_is_set_ (vm, get_loc_ (vm)) ? MIX_VM_BREAK : MIX_VM_OK; } /* 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_traverse (vm->line_table, set_break_, G_IN_ORDER, (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; } 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_traverse (vm->line_table, clear_break_, G_IN_ORDER, (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); } /* 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)); }