/* 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 #include #include #include "tm.h" #include "sim.h" #include "mem.h" #include #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #endif #include #include #include #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); }