| /* support routines for interpreted instructions |
| Copyright (C) 1992, 1993 Free Software Foundation, Inc. |
| |
| This file is part of Z8KSIM |
| |
| Z8KSIM 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, or (at your option) |
| any later version. |
| |
| Z8KSIM 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 Z8KZIM; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| #include "config.h" |
| |
| #include <ansidecl.h> |
| #include <signal.h> |
| #include <errno.h> |
| |
| #include "tm.h" |
| #include "sim.h" |
| #include "mem.h" |
| #include <stdio.h> |
| #ifdef HAVE_TIME_H |
| #include <time.h> |
| #endif |
| #ifdef HAVE_SYS_TIMES_H |
| #include <sys/times.h> |
| #endif |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/param.h> |
| #include "callback.h" |
| #include "remote-sim.h" |
| #include "syscall.h" |
| |
| static int get_now PARAMS ((void)); |
| static int now_persec PARAMS ((void)); |
| static int put_long PARAMS ((sim_state_type * context, int ptr, int value)); |
| static int put_short PARAMS ((sim_state_type * context, int ptr, int value)); |
| |
| int sim_z8001_mode; |
| |
| static int |
| get_now () |
| { |
| #ifdef HAVE_SYS_TIMES_H |
| struct tms b; |
| |
| times (&b); |
| return b.tms_utime + b.tms_stime; |
| #else |
| return time (0); |
| #endif |
| } |
| |
| static int |
| now_persec () |
| { |
| return 50; |
| } |
| |
| |
| /* #define LOG /* define this to print instruction use counts */ |
| |
| #ifdef __GNUC__ |
| #define INLINE __inline__ |
| #include "inlines.h" |
| #else |
| #include "inlines.h" |
| #endif |
| |
| /* This holds the entire cpu context */ |
| static sim_state_type the_state; |
| |
| int |
| fail (context, dummy) |
| sim_state_type *context; |
| int dummy; |
| { |
| context->exception = SIM_BAD_INST; |
| return 1; |
| } |
| |
| void |
| sfop_bad1 (context) |
| sim_state_type *context; |
| { |
| context->exception |
| = SIM_BAD_INST; |
| } |
| |
| void |
| bfop_bad1 (context) |
| sim_state_type *context; |
| { |
| context->exception |
| = SIM_BAD_INST; |
| } |
| |
| void |
| fop_bad (context) |
| sim_state_type *context; |
| { |
| context->exception = |
| SIM_BAD_INST; |
| } |
| |
| /* Table of bit counts for all byte values */ |
| |
| char the_parity[256] = |
| { |
| 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, |
| 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, |
| 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, |
| 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, |
| 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, |
| 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, |
| 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, |
| 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, |
| 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, |
| 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, |
| 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, |
| 7, 7, 8}; |
| |
| |
| int read (); |
| int write (); |
| int open (); |
| int close (); |
| int open (); |
| int close (); |
| |
| int link (); |
| int fstat (); |
| |
| static int |
| put_short (context, ptr, value) |
| sim_state_type *context; |
| int ptr; |
| int value; |
| { |
| put_word_mem_da (context, ptr, value); |
| return ptr + 2; |
| } |
| |
| static int |
| put_long (context, ptr, value) |
| sim_state_type *context; |
| int |
| ptr; |
| int value; |
| { |
| put_long_mem_da (context, ptr, value); |
| return ptr + 4; |
| } |
| |
| #define aptr(x) ((sitoptr(x)) + (char *)(context->memory)) |
| |
| static int args[3]; |
| static int arg_index; /* Translate a z8k system call into a host system call */ |
| void |
| support_call (context, sc) |
| sim_state_type *context; |
| int sc; |
| { |
| extern int errno; |
| int ret; |
| int retnext = 0; |
| int fd; |
| |
| int olderrno = errno; |
| errno = 0; |
| switch (sc) |
| { |
| case SYS_ARG: |
| args[arg_index++] = context->regs[0].word << 16 | context->regs[1].word; |
| break; |
| case SYS_exit: |
| context->exception = SIM_DONE; |
| ret = args[0]; |
| arg_index = 0; |
| break; |
| case SYS_close: |
| ret = close ((int) (args[0])); |
| arg_index = 0; |
| break; |
| case SYS_creat: |
| ret = creat (aptr (args[0]), args[1]); |
| arg_index = 0; |
| break; |
| case SYS_isatty: |
| ret = isatty (args[0]); |
| arg_index = 0; |
| break; |
| case SYS_open: |
| ret = open (aptr (args[0]), args[1], args[2]); |
| arg_index = 0; |
| break; |
| case SYS_lseek: |
| ret = lseek (args[0], (off_t) args[1], args[2]); |
| arg_index = 0; |
| break; |
| case SYS_read: |
| ret = read (args[0], aptr (args[1]), args[2]); |
| arg_index = 0; |
| break; |
| case SYS_write: |
| ret = write (args[0],aptr (args[1]), args[2]); |
| arg_index = 0; |
| break; |
| case SYS_time: |
| { |
| int dst = args[0]; |
| |
| ret = time (0); |
| if (dst) |
| { |
| put_long_mem_da (context, |
| sitoptr (dst), ret); |
| } |
| retnext = ret; |
| ret = retnext >> 16; |
| arg_index = 0; |
| } |
| break; |
| case SYS_fstat: |
| { |
| int buf; |
| struct stat host_stat; |
| fd = args[0]; |
| buf = sitoptr (args[1]); |
| ret = fstat (fd, &host_stat); |
| buf = put_short (context, buf, host_stat.st_dev); |
| buf = put_short (context, buf, host_stat.st_ino); |
| /* FIXME: Isn't mode_t 4 bytes? */ |
| buf = put_short (context, buf, host_stat.st_mode); |
| buf = put_short (context, buf, host_stat.st_nlink); |
| buf = put_short (context, buf, host_stat.st_uid); |
| buf = put_short (context, buf, host_stat.st_uid); |
| buf = put_short (context, buf, host_stat.st_rdev); |
| buf = put_long (context, buf, host_stat.st_size); |
| buf = put_long (context, buf, host_stat.st_atime); |
| arg_index = 0; |
| } break; |
| default: |
| case SYS_link: |
| context->exception = SIM_BAD_SYSCALL; |
| arg_index = 0; |
| break; |
| } |
| context->regs[2].word = ret; |
| context->regs[3].word = retnext; |
| context->regs[5].word = errno; |
| |
| |
| /* support for the stdcall calling convention */ |
| context->regs[6].word = retnext; |
| context->regs[7].word = ret; |
| |
| errno = olderrno; |
| } |
| |
| #undef get_word_mem_da |
| |
| int |
| get_word_mem_da (context, addr) |
| sim_state_type *context; |
| int addr; |
| { |
| return (get_byte_mem_da (context, addr) << 8) | (get_byte_mem_da (context, addr + 1)); |
| |
| } |
| |
| #undef get_word_reg |
| int |
| get_word_reg (context, reg) sim_state_type |
| * context; |
| int reg; |
| { |
| return context->regs[reg].word; |
| } |
| |
| #ifdef LOG |
| int log[64 * 1024]; |
| |
| #endif |
| |
| void |
| tm_store_register (regno, value) |
| int regno; |
| int value; |
| { |
| switch |
| (regno) |
| { |
| case REG_PC: |
| the_state.sometimes_pc = value; |
| break; |
| |
| default: |
| put_word_reg (&the_state, regno, value); |
| } |
| } |
| |
| void |
| swap_long (buf, val) |
| char *buf; |
| int val; |
| { |
| buf[0] = val >> 24; |
| buf[1] = val >> 16; |
| buf[2] = val >> 8; |
| buf[3] = val >> 0; |
| } |
| |
| void |
| swap_word (buf, val) |
| char *buf; |
| int val; |
| { |
| buf[0] = val >> 8; |
| buf[1] = val >> 0; |
| } |
| |
| void |
| tm_fetch_register (regno, buf) |
| int regno; |
| char *buf; |
| { |
| switch |
| (regno) |
| { |
| case REG_CYCLES: |
| swap_long (buf, the_state.cycles); |
| break; |
| case REG_INSTS: |
| swap_long (buf, the_state.insts); |
| break; |
| case |
| REG_TIME: |
| swap_long (buf, the_state.ticks); |
| break; |
| case REG_PC: |
| swap_long (buf, the_state.sometimes_pc); |
| break; |
| case REG_SP: |
| { |
| if (sim_z8001_mode) |
| { |
| swap_long (buf, get_long_reg (&the_state, 14)); |
| } |
| else |
| { |
| swap_long (buf, get_word_reg (&the_state, 15)); |
| } |
| } |
| break; |
| case |
| REG_FP: |
| { |
| if (sim_z8001_mode) |
| { |
| swap_long (buf, get_long_reg |
| (&the_state, 10)); |
| } |
| else |
| { |
| swap_long (buf, |
| get_word_reg (&the_state, 10)); |
| } |
| } |
| break; |
| default: |
| { |
| swap_word (buf, |
| get_word_reg (&the_state, regno)); |
| } |
| } |
| } |
| |
| void |
| tm_resume (step) |
| int step; |
| { |
| int now = get_now (); |
| struct op_info |
| *p; |
| int word; |
| int pc; |
| extern int (*(sfop_table[])) (); |
| extern int (*(bfop_table[])) (); |
| int (*((*table))) (); |
| sim_state_type *context = &the_state; |
| |
| if (step) |
| { |
| context->exception = SIM_SINGLE_STEP; |
| } |
| else |
| { |
| context->exception = 0; |
| } |
| |
| pc = context->sometimes_pc; |
| if (sim_z8001_mode) |
| { |
| table = bfop_table; |
| pc = MAP_PHYSICAL_TO_LOGICAL (pc); |
| } |
| else |
| { |
| table = sfop_table; |
| } |
| |
| |
| do |
| { |
| word = get_word_mem_da (context, pc); |
| p = op_info_table + word; |
| |
| #ifdef LOG |
| log[word]++; |
| #endif |
| pc = table[p->exec] (context, pc, word); |
| context->insts++; |
| |
| } |
| while (!context->exception); |
| |
| |
| |
| context->sometimes_pc = MAP_LOGICAL_TO_PHYSICAL (pc); |
| context->ticks += get_now () - now; |
| } |
| |
| int |
| tm_signal () |
| { |
| return the_state.exception; |
| } |
| |
| void |
| tm_info_print (x) |
| sim_state_type *x; |
| { |
| double timetaken = (double) x->ticks / (double) now_persec (); |
| double virttime = x->cycles / 4.0e6; |
| |
| printf ("instructions executed : %9d\n", x->insts); |
| printf ("cycles counted : %9d \n", x->cycles); |
| printf ("cycles / inst : %9.1f \n", (double) x->cycles / (double) x->insts); |
| printf ("virtual time taked (at 4 Mhz) : %9.1f \n", virttime); |
| printf ("real time taken : %9.1f \n", timetaken); |
| |
| if (timetaken) |
| { |
| printf ("virtual instructions per second : %9.1f\n", x->insts / timetaken); |
| printf ("emulation speed : %9.1f%%\n", virttime / timetaken * 100.0); |
| } |
| #ifdef LOG |
| { |
| extern int quick[]; |
| |
| for (i = 0; quick[i]; i++) |
| { |
| log[quick[i]] += 100000; |
| } |
| } |
| |
| for (i = 0; i < 64 * 1024; i++) |
| { |
| if (log[i]) |
| { |
| printf (" /*%7d*/ 0x%x,\n", log[i], i); |
| } |
| } |
| #endif |
| |
| } |
| |
| int |
| sim_trace (sd) |
| SIM_DESC sd; |
| { |
| int i; |
| char buffer[10]; |
| int r; |
| |
| printf ("\n"); |
| for (r = 0; r < 16; r++) |
| { |
| int m; |
| |
| printf ("r%2d", r); |
| printf ("=%04x ", get_word_reg (&the_state, |
| r)); |
| for (m = -4; m < 8; m++) |
| { |
| if (m == 0) |
| printf (">"); |
| printf ("%04x ", |
| get_word_mem_da (&the_state, (0xfffe & get_word_reg (&the_state, r)) + m * 2)); |
| } |
| printf ("\n"); |
| } |
| |
| printf ("\n"); |
| printf ("%9d %9d %08x ", the_state.cycles, |
| the_state.insts, the_state.sometimes_pc); |
| |
| for (i = 0; i < 6; i++) |
| { |
| buffer[i] = get_byte_mem_da (&the_state, |
| the_state.sometimes_pc + i); |
| } |
| |
| print_insn_z8001 (the_state.sometimes_pc, buffer, stdout); |
| printf |
| ("\n"); |
| tm_resume (1); |
| if (the_state.exception != SIM_SINGLE_STEP) |
| return 1; |
| return 0; |
| } |
| |
| void |
| tm_state (x) |
| sim_state_type *x; |
| { |
| *x = the_state; |
| } |
| |
| void |
| tm_exception (x) |
| int x; |
| { |
| the_state.exception = x; |
| } |
| |
| int |
| tm_read_byte (x) |
| int x; |
| { |
| x &= 0x3f00ffff; |
| return sim_read_byte (&the_state, x); |
| } |
| |
| void |
| tm_write_byte (x, y) |
| int x, y; |
| { |
| x &= 0x3f00ffff; |
| sim_write_byte (&the_state, x, y); |
| } |
| |
| #define SIGN(x) ((x) & MASK) |
| normal_flags_32(context,d,sa,sb,sub) |
| sim_state_type *context; |
| unsigned int d; |
| unsigned int sa; |
| unsigned int sb; |
| unsigned int sub; |
| { |
| #undef MASK |
| #define MASK (1<<31) |
| context->broken_flags = 0; |
| if (sub) |
| PSW_CARRY = sa < sb; |
| else |
| PSW_CARRY = d < sa; |
| if (sub) |
| PSW_OVERFLOW = (SIGN(sa) != SIGN(sb)) && (SIGN(d) == SIGN(sb)); |
| else |
| PSW_OVERFLOW = (SIGN(sa) == SIGN(sb)) && (SIGN(d) != SIGN(sb)); |
| |
| PSW_SIGN = ((int)d) <0; |
| PSW_ZERO = d == 0; |
| } |
| |
| normal_flags_16(context,d,sal,sbl,sub) |
| sim_state_type *context; |
| unsigned int d; |
| unsigned int sal; |
| unsigned int sbl; |
| unsigned short int sub; |
| { |
| unsigned short sa = sal; |
| unsigned short sb = sbl; |
| #undef MASK |
| #define MASK (1<<15) |
| context->broken_flags = 0; |
| if (sub) |
| PSW_CARRY = sal < sbl; |
| else |
| PSW_CARRY = (d & 0x10000) != 0; |
| |
| if (sub) |
| PSW_OVERFLOW = (SIGN(sa) != SIGN(sb)) && (SIGN(d) == SIGN(sb)); |
| else |
| PSW_OVERFLOW = (SIGN(sa) == SIGN(sb)) && (SIGN(d) != SIGN(sb)); |
| |
| PSW_SIGN = ((short int)d) <0; |
| PSW_ZERO = ((short)d) == 0; |
| } |
| |
| normal_flags_8(context,d,sa,sb,sub) |
| sim_state_type *context; |
| unsigned char d; |
| unsigned char sa; |
| unsigned char sb; |
| unsigned char sub; |
| { |
| #undef MASK |
| #define MASK (1<<7) |
| context->broken_flags = 0; |
| if (sub) |
| PSW_CARRY = sa < sb; |
| else |
| PSW_CARRY = d < sa; |
| if (sub) |
| PSW_OVERFLOW = (SIGN(sa) != SIGN(sb)) && (SIGN(d) == SIGN(sb)); |
| else |
| PSW_OVERFLOW = (SIGN(sa) == SIGN(sb)) && (SIGN(d) != SIGN(sb)); |
| PSW_SIGN = ((char)d) <0; |
| PSW_ZERO = d == 0; |
| } |
| |
| |
| static int |
| is_cond_true (context, c) |
| sim_state_type *context; |
| int c; |
| { |
| switch (c) |
| { |
| case T: |
| return 1; |
| case F: |
| return 0; /* F */ |
| case LE: |
| return (PSW_ZERO | (PSW_SIGN ^ PSW_OVERFLOW)) & 1; /*LE */ |
| case GT: |
| return (~(PSW_ZERO | (PSW_SIGN ^ PSW_OVERFLOW))) & 1; /*GT */ |
| case 0x5: |
| return (PSW_SIGN & 1); /* sign */ |
| case 0xd: |
| return (~(PSW_SIGN)) & 1; /* not sign */ |
| case 0x3: |
| return ((PSW_CARRY | PSW_ZERO) & 1); /* ule*/ |
| case UGT: |
| return ((~(PSW_CARRY | PSW_ZERO)) & 1); /* ugt */ |
| case 0x4: |
| return (PSW_OVERFLOW & 1);/* overflow */ |
| case 0xc: |
| return (~(PSW_OVERFLOW)) & 1; /* not overflow */ |
| case LT: |
| return (PSW_SIGN ^ PSW_OVERFLOW) & 1; /* LT */ |
| case GE: |
| return (~(PSW_SIGN ^ PSW_OVERFLOW)) & 1; /* GE */ |
| case EQ: |
| return (PSW_ZERO) & 1; /* zero */ |
| case NE: |
| return ((~PSW_ZERO) & 1); /* not zero */ |
| case 0x7: |
| return (PSW_CARRY) & 1; /* carry */ |
| case 0xf: |
| return (~PSW_CARRY) & 1; /* not carry */ |
| default: |
| abort (); |
| } |
| } |
| |
| int |
| COND (context, c) |
| sim_state_type *context; |
| int c; |
| { |
| if (c == 8) |
| return 1; |
| |
| /* We can calculate what the flags would have been by |
| looking at the src and dst and size of the operation */ |
| |
| if (context->broken_flags) |
| { |
| int slow = 0; |
| int size; |
| int dst; |
| int srca; |
| int srcb; |
| int mask; |
| int ans; |
| |
| /* see if we can short-cut the nasty flag calcs */ |
| |
| switch (size = context->size) |
| { |
| default: |
| abort(); |
| return 0; |
| case 8: |
| srca = (char) (context->srca); |
| srcb = (char) (context->srcb); |
| dst = (char) (context->dst); |
| mask = 0xff; |
| break; |
| case 16: |
| srca = (short) (context->srca); |
| srcb = (short) (context->srcb); |
| dst = (short) (context->dst); |
| mask = 0xffff; |
| break; |
| case 32: |
| srca = (long) (context->srca); |
| srcb = (long) (context->srcb); |
| dst = (long) (context->dst); |
| mask = 0xffffffff; |
| break; |
| } |
| |
| switch (c) |
| { |
| case T: |
| return 1; |
| case F: |
| return 0; |
| case EQ: |
| return !dst; |
| case NE: |
| return dst; |
| case GT: |
| ans = ((dst)) > 0; |
| if (slow) |
| { |
| if (is_cond_true (context, c) != ans) |
| abort (); |
| } |
| return ans; |
| case LE: |
| ans = ((dst)) <= 0; |
| if (slow) |
| { |
| if (is_cond_true (context, c) != ans) |
| abort (); |
| } |
| return ans; |
| case GE: |
| ans = ((dst)) >= 0; |
| if (slow) |
| { |
| if (is_cond_true (context, c) != ans) |
| abort (); |
| } |
| return ans; |
| case LT: |
| ans = ((dst)) < 0; |
| if (slow) |
| { |
| if (is_cond_true (context, c) != ans) |
| abort (); |
| } |
| return ans; |
| default: |
| break; |
| } |
| |
| /* Can't fake it, we'll have to work out the flags the |
| hard way */ |
| |
| makeflags (context, mask); |
| } |
| |
| /* don't know how to fake a test, inspect the flags |
| the hard way */ |
| |
| return is_cond_true (context, c); |
| } |