text: /* IQ2000 simulator support code
     Copyright (C) 2000-2014 Free Software Foundation, Inc.
     Contributed by Cygnus Support.
  
     This file is part of the GNU simulators.
  
     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 3 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, see <http://www.gnu.org/licenses/>.  */
  
  #define WANT_CPU
  #define WANT_CPU_IQ2000BF
  
  #include "sim-main.h"
  #include "cgen-mem.h"
  #include "cgen-ops.h"
  
  enum
  {
    GPR0_REGNUM = 0,
    NR_GPR = 32,
    PC_REGNUM = 32
  };
  
  enum libgloss_syscall
  {
    SYS_exit = 1,
    SYS_open = 2, 
    SYS_close = 3, 
    SYS_read = 4,
    SYS_write = 5, 
    SYS_lseek = 6, 
    SYS_unlink = 7,
    SYS_getpid = 8,
    SYS_kill = 9,
    SYS_fstat = 10, 
    SYS_argvlen = 12, 
    SYS_argv = 13,
    SYS_chdir = 14, 
    SYS_stat = 15, 
    SYS_chmod = 16, 
    SYS_utime = 17,
    SYS_time = 18,
    SYS_gettimeofday = 19,
    SYS_times = 20
  };
  
  /* Read a null terminated string from memory, return in a buffer */
  static char *
  fetch_str (current_cpu, pc, addr)
       SIM_CPU *current_cpu;
       PCADDR pc;
       DI addr;
  {
    char *buf;
    int nr = 0;
    while (sim_core_read_1 (current_cpu,
                            pc, read_map, CPU2DATA(addr + nr)) != 0)
      nr++;
    buf = NZALLOC (char, nr + 1);
    sim_read (CPU_STATE (current_cpu), CPU2DATA(addr), buf, nr);
    return buf;
  }
  
  void
  do_syscall (SIM_CPU *current_cpu, PCADDR pc)
  {
  #if 0
    int syscall = H2T_4 (iq2000bf_h_gr_get (current_cpu, 11));
  #endif
    int syscall_function = iq2000bf_h_gr_get (current_cpu, 4);
    int i;
    char *buf;
    int PARM1 = iq2000bf_h_gr_get (current_cpu, 5);
    int PARM2 = iq2000bf_h_gr_get (current_cpu, 6);
    int PARM3 = iq2000bf_h_gr_get (current_cpu, 7);
    const int ret_reg = 2;
  	
    switch (syscall_function)
      {
      case 0:
        switch (H2T_4 (iq2000bf_h_gr_get (current_cpu, 11)))
  	{
  	case 0:
  	  /* Pass.  */
  	  puts ("pass");
  	  exit (0);
  	case 1:
  	  /* Fail.  */
  	  puts ("fail");
  	  exit (1);
  	}
  
      case SYS_write:
        buf = zalloc (PARM3);
        sim_read (CPU_STATE (current_cpu), CPU2DATA(PARM2), buf, PARM3);
        SET_H_GR (ret_reg,
  		sim_io_write (CPU_STATE (current_cpu),
  			      PARM1, buf, PARM3));
        free (buf);
        break;
  
      case SYS_lseek:
        SET_H_GR (ret_reg,
  		sim_io_lseek (CPU_STATE (current_cpu),
  			      PARM1, PARM2, PARM3));
        break;
  	    
      case SYS_exit:
        sim_engine_halt (CPU_STATE (current_cpu), current_cpu,
  		       NULL, pc, sim_exited, PARM1);
        break;
  
      case SYS_read:
        buf = zalloc (PARM3);
        SET_H_GR (ret_reg,
  		sim_io_read (CPU_STATE (current_cpu),
  			     PARM1, buf, PARM3));
        sim_write (CPU_STATE (current_cpu), CPU2DATA(PARM2), buf, PARM3);
        free (buf);
        break;
  	    
      case SYS_open:
        buf = fetch_str (current_cpu, pc, PARM1);
        SET_H_GR (ret_reg,
  		sim_io_open (CPU_STATE (current_cpu),
  			     buf, PARM2));
        free (buf);
        break;
  
      case SYS_close:
        SET_H_GR (ret_reg,
  		sim_io_close (CPU_STATE (current_cpu), PARM1));
        break;
  
      case SYS_time:
        SET_H_GR (ret_reg, time (0));
        break;
  
      default:
        SET_H_GR (ret_reg, -1);
      }
  }
  
  void 
  do_break (SIM_CPU *current_cpu, PCADDR pc)
  {
    SIM_DESC sd = CPU_STATE (current_cpu);
    sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
  }
  
  /* The semantic code invokes this for invalid (unrecognized) instructions.  */
  
  SEM_PC
  sim_engine_invalid_insn (SIM_CPU *current_cpu, IADDR cia, SEM_PC vpc)
  {
    SIM_DESC sd = CPU_STATE (current_cpu);
    sim_engine_halt (sd, current_cpu, NULL, cia, sim_stopped, SIM_SIGILL);
  
    return vpc;
  }
  
  
  /* Process an address exception.  */
  
  void
  iq2000_core_signal (SIM_DESC sd, SIM_CPU *current_cpu, sim_cia cia,
                    unsigned int map, int nr_bytes, address_word addr,
                    transfer_type transfer, sim_core_signals sig)
  {
    sim_core_signal (sd, current_cpu, cia, map, nr_bytes, addr,
  		   transfer, sig);
  }
  
  
  /* Initialize cycle counting for an insn.
     FIRST_P is non-zero if this is the first insn in a set of parallel
     insns.  */
  
  void
  iq2000bf_model_insn_before (SIM_CPU *cpu, int first_p)
  {
    /* Do nothing.  */
  }
  
  
  /* Record the cycles computed for an insn.
     LAST_P is non-zero if this is the last insn in a set of parallel insns,
     and we update the total cycle count.
     CYCLES is the cycle count of the insn.  */
  
  void
  iq2000bf_model_insn_after(SIM_CPU *cpu, int last_p, int cycles)
  {
    /* Do nothing.  */
  }
  
  
  int
  iq2000bf_model_iq2000_u_exec (SIM_CPU *cpu, const IDESC *idesc,
  			    int unit_num, int referenced)
  {
    return idesc->timing->units[unit_num].done;
  }
  
  PCADDR
  get_h_pc (SIM_CPU *cpu)
  {
    return CPU_CGEN_HW(cpu)->h_pc;
  }
  
  void
  set_h_pc (SIM_CPU *cpu, PCADDR addr)
  {
    CPU_CGEN_HW(cpu)->h_pc = addr | IQ2000_INSN_MASK;
  }
  
  int
  iq2000bf_fetch_register (SIM_CPU *cpu, int nr, unsigned char *buf, int len)
  {
    if (nr >= GPR0_REGNUM
        && nr < (GPR0_REGNUM + NR_GPR)
        && len == 4)
      {
        *((unsigned32*)buf) =
  	H2T_4 (iq2000bf_h_gr_get (cpu, nr - GPR0_REGNUM));
        return 4;
      }
    else if (nr == PC_REGNUM
  	   && len == 4)
      {
        *((unsigned32*)buf) = H2T_4 (get_h_pc (cpu));
        return 4;
      }
    else
      return 0;
  }
  
  int
  iq2000bf_store_register (SIM_CPU *cpu, int nr, unsigned char *buf, int len)
  {
    if (nr >= GPR0_REGNUM
        && nr < (GPR0_REGNUM + NR_GPR)
        && len == 4)
      {
        iq2000bf_h_gr_set (cpu, nr - GPR0_REGNUM, T2H_4 (*((unsigned32*)buf)));
        return 4;
      }
    else if (nr == PC_REGNUM
  	   && len == 4)
      {
        set_h_pc (cpu, T2H_4 (*((unsigned32*)buf)));
        return 4;
      }
    else
      return 0;
  }
  