diff options
Diffstat (limited to 'trunk/src/emu/cpu/i860/i860dec.c')
-rw-r--r-- | trunk/src/emu/cpu/i860/i860dec.c | 4718 |
1 files changed, 4718 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/i860/i860dec.c b/trunk/src/emu/cpu/i860/i860dec.c new file mode 100644 index 00000000000..4277eecda8a --- /dev/null +++ b/trunk/src/emu/cpu/i860/i860dec.c @@ -0,0 +1,4718 @@ +/*************************************************************************** + + i860dec.c + + Execution engine for the Intel i860 emulator. + + Copyright (C) 1995-present Jason Eckhardt (jle@rice.edu) + Released for general non-commercial use under the MAME license + with the additional requirement that you are free to use and + redistribute this code in modified or unmodified form, provided + you list me in the credits. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +/* + * References: + * `i860 Microprocessor Programmer's Reference Manual', Intel, 1990. + * + * This code was originally written by Jason Eckhardt as part of an + * emulator for some i860-based Unix workstations (early 1990's) such + * as the Stardent Vistra 800 series and the OkiStation/i860 7300 series. + * The code you are reading now is the i860 CPU portion only, which has + * been adapted to (and simplified for) MAME. + * MAME-specific notes: + * - i860XR emulation only (i860XP unnecessary for MAME). + * - No emulation of data and instruction caches (unnecessary for MAME version). + * - No emulation of DIM mode or CS8 mode (unnecessary for MAME version). + * - No BL/IL/locked sequences (unnecessary for MAME). + * - Emulate only the i860's LSB-first mode (BE = 0). + * Generic notes: + * - There is some amount of code duplication (e.g., see the + * various insn_* routines for the branches and FP routines) that + * could be eliminated. + * - The host's floating point types are used to emulate the i860's + * floating point. Should probably be made machine independent by + * using an IEEE FP emulation library. On the other hand, most machines + * today also use IEEE FP. + * + */ +#include "i860.h" +#include <math.h> + + +#undef HOST_MSB + +#undef TRACE_RDWR_MEM +#undef TRACE_ADDR_TRANSLATION +#undef TRACE_PAGE_FAULT +#define TRACE_UNDEFINED_I860 +#undef TRACE_EXT_INT +#define TRACE_UNALIGNED_MEM + + + +#define i860s i860_state_t + + +/* Prototypes. */ +static void decode_exec (i860s *, UINT32, UINT32); +static UINT32 get_address_translation (i860s *, UINT32, int, int); +static UINT32 readmemi_emu (i860s *cpustate, UINT32, int); + +//static void debugger (i860s *cpustate); +//static void disasm (i860s *cpustate, UINT32, int); +//static void dump_state (i860s *cpustate); + + + + +/* Defines for pending_trap. */ +enum { + TRAP_NORMAL = 0x01, + TRAP_IN_DELAY_SLOT = 0x02, + TRAP_WAS_EXTERNAL = 0x04 +}; + + + + +/* Get/set general register value -- watch for r0 on writes. */ +#define get_iregval(gr) (cpustate->iregs[(gr)]) +#define set_iregval(gr, val) (cpustate->iregs[(gr)] = ((gr) == 0 ? 0 : (val))) + +INLINE float get_fregval_s (i860s *cpustate, int fr) +{ + float f; + UINT32 x; + UINT8 *tp; + fr = 31 - fr; + tp = (UINT8 *)(&cpustate->frg[fr * 4]); + x = ((UINT32)tp[0] << 24) | ((UINT32)tp[1] << 16) | + ((UINT32)tp[2] << 8) | ((UINT32)tp[3]); + f = *(float *)(&x); + return f; +} + +INLINE double get_fregval_d (i860s *cpustate, int fr) +{ + double d; + UINT64 x; + UINT8 *tp; + fr = 31 - (fr + 1); + tp = (UINT8 *)(&cpustate->frg[fr * 4]); + x = ((UINT64)tp[0] << 56) | ((UINT64)tp[1] << 48) | + ((UINT64)tp[2] << 40) | ((UINT64)tp[3] << 32) | + ((UINT64)tp[4] << 24) | ((UINT64)tp[5] << 16) | + ((UINT64)tp[6] << 8) | ((UINT64)tp[7]); + d = *(double *)(&x); + return d; +} + +INLINE void set_fregval_s (i860s *cpustate, int fr, float s) +{ + UINT8 *f = (UINT8 *)&s; + UINT8 *tp; + int newfr = 31 - fr; + float jj = s; + tp = (UINT8 *)(&cpustate->frg[newfr * 4]); + + f = (UINT8 *)(&jj); + if (fr == 0 || fr == 1) + { + tp[0] = 0; tp[1] = 0; tp[2] = 0; tp[3] = 0; + } + else + { +#ifndef HOST_MSB + tp[0] = f[3]; tp[1] = f[2]; tp[2] = f[1]; tp[3] = f[0]; +#else + tp[0] = f[0]; tp[1] = f[1]; tp[2] = f[2]; tp[3] = f[3]; +#endif + } +} + +INLINE void set_fregval_d (i860s *cpustate, int fr, double d) +{ + UINT8 *f = (UINT8 *)&d; + UINT8 *tp; + int newfr = 31 - (fr + 1); + double jj = d; + tp = (UINT8 *)(&cpustate->frg[newfr * 4]); + + f = (UINT8 *)(&jj); + + if (fr == 0) + { + tp[0] = 0; tp[1] = 0; tp[2] = 0; tp[3] = 0; + tp[4] = 0; tp[5] = 0; tp[6] = 0; tp[7] = 0; + } + else + { +#ifndef HOST_MSB + tp[0] = f[7]; tp[1] = f[6]; tp[2] = f[5]; tp[3] = f[4]; + tp[4] = f[3]; tp[5] = f[2]; tp[6] = f[1]; tp[7] = f[0]; +#else + tp[0] = f[0]; tp[1] = f[1]; tp[2] = f[2]; tp[3] = f[3]; + tp[4] = f[4]; tp[5] = f[5]; tp[6] = f[6]; tp[7] = f[7]; +#endif + } +} + + +/* Macros for accessing register fields in instruction word. */ +#define get_isrc1(bits) (((bits) >> 11) & 0x1f) +#define get_isrc2(bits) (((bits) >> 21) & 0x1f) +#define get_idest(bits) (((bits) >> 16) & 0x1f) +#define get_fsrc1(bits) (((bits) >> 11) & 0x1f) +#define get_fsrc2(bits) (((bits) >> 21) & 0x1f) +#define get_fdest(bits) (((bits) >> 16) & 0x1f) +#define get_creg(bits) (((bits) >> 21) & 0x7) + +/* Macros for accessing immediate fields. */ +/* 16-bit immediate. */ +#define get_imm16(insn) ((insn) & 0xffff) + +/* Control register numbers. */ +enum { + CR_FIR = 0, + CR_PSR = 1, + CR_DIRBASE = 2, + CR_DB = 3, + CR_FSR = 4, + CR_EPSR = 5 +}; + +/* A mask for all the trap bits of the PSR (FT, DAT, IAT, IN, IT, or + bits [12..8]). */ +#define PSR_ALL_TRAP_BITS_MASK 0x00001f00 + +/* A mask for PSR bits which can only be changed from supervisor level. */ +#define PSR_SUPERVISOR_ONLY_MASK 0x0000fff3 + + +/* PSR: BR flag (PSR[0]): set/get. */ +#define GET_PSR_BR() ((cpustate->cregs[CR_PSR] >> 0) & 1) +#define SET_PSR_BR(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 0)) | (((val) & 1) << 0)) + +/* PSR: BW flag (PSR[1]): set/get. */ +#define GET_PSR_BW() ((cpustate->cregs[CR_PSR] >> 1) & 1) +#define SET_PSR_BW(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 1)) | (((val) & 1) << 1)) + +/* PSR: Shift count (PSR[21..17]): set/get. */ +#define GET_PSR_SC() ((cpustate->cregs[CR_PSR] >> 17) & 0x1f) +#define SET_PSR_SC(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~0x003e0000) | (((val) & 0x1f) << 17)) + +/* PSR: CC flag (PSR[2]): set/get. */ +#define GET_PSR_CC() ((cpustate->cregs[CR_PSR] >> 2) & 1) +#define SET_PSR_CC(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 2)) | (((val) & 1) << 2)) + +/* PSR: IT flag (PSR[8]): set/get. */ +#define GET_PSR_IT() ((cpustate->cregs[CR_PSR] >> 8) & 1) +#define SET_PSR_IT(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 8)) | (((val) & 1) << 8)) + +/* PSR: IN flag (PSR[9]): set/get. */ +#define GET_PSR_IN() ((cpustate->cregs[CR_PSR] >> 9) & 1) +#define SET_PSR_IN(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 9)) | (((val) & 1) << 9)) + +/* PSR: IAT flag (PSR[10]): set/get. */ +#define GET_PSR_IAT() ((cpustate->cregs[CR_PSR] >> 10) & 1) +#define SET_PSR_IAT(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 10)) | (((val) & 1) << 10)) + +/* PSR: DAT flag (PSR[11]): set/get. */ +#define GET_PSR_DAT() ((cpustate->cregs[CR_PSR] >> 11) & 1) +#define SET_PSR_DAT(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 11)) | (((val) & 1) << 11)) + +/* PSR: FT flag (PSR[12]): set/get. */ +#define GET_PSR_FT() ((cpustate->cregs[CR_PSR] >> 12) & 1) +#define SET_PSR_FT(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 12)) | (((val) & 1) << 12)) + +/* PSR: DS flag (PSR[13]): set/get. */ +#define GET_PSR_DS() ((cpustate->cregs[CR_PSR] >> 13) & 1) +#define SET_PSR_DS(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 13)) | (((val) & 1) << 13)) + +/* PSR: DIM flag (PSR[14]): set/get. */ +#define GET_PSR_DIM() ((cpustate->cregs[CR_PSR] >> 14) & 1) +#define SET_PSR_DIM(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 14)) | (((val) & 1) << 14)) + +/* PSR: LCC (PSR[3]): set/get. */ +#define GET_PSR_LCC() ((cpustate->cregs[CR_PSR] >> 3) & 1) +#define SET_PSR_LCC(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 3)) | (((val) & 1) << 3)) + +/* PSR: IM (PSR[4]): set/get. */ +#define GET_PSR_IM() ((cpustate->cregs[CR_PSR] >> 4) & 1) +#define SET_PSR_IM(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 4)) | (((val) & 1) << 4)) + +/* PSR: PIM (PSR[5]): set/get. */ +#define GET_PSR_PIM() ((cpustate->cregs[CR_PSR] >> 5) & 1) +#define SET_PSR_PIM(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 5)) | (((val) & 1) << 5)) + +/* PSR: U (PSR[6]): set/get. */ +#define GET_PSR_U() ((cpustate->cregs[CR_PSR] >> 6) & 1) +#define SET_PSR_U(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 6)) | (((val) & 1) << 6)) + +/* PSR: PU (PSR[7]): set/get. */ +#define GET_PSR_PU() ((cpustate->cregs[CR_PSR] >> 7) & 1) +#define SET_PSR_PU(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~(1 << 7)) | (((val) & 1) << 7)) + +/* PSR: Pixel size (PSR[23..22]): set/get. */ +#define GET_PSR_PS() ((cpustate->cregs[CR_PSR] >> 22) & 0x3) +#define SET_PSR_PS(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~0x00c00000) | (((val) & 0x3) << 22)) + +/* PSR: Pixel mask (PSR[31..24]): set/get. */ +#define GET_PSR_PM() ((cpustate->cregs[CR_PSR] >> 24) & 0xff) +#define SET_PSR_PM(val) (cpustate->cregs[CR_PSR] = (cpustate->cregs[CR_PSR] & ~0xff000000) | (((val) & 0xff) << 24)) + +/* EPSR: WP bit (EPSR[14]): set/get. */ +#define GET_EPSR_WP() ((cpustate->cregs[CR_EPSR] >> 14) & 1) +#define SET_EPSR_WP(val) (cpustate->cregs[CR_EPSR] = (cpustate->cregs[CR_EPSR] & ~(1 << 14)) | (((val) & 1) << 14)) + +/* EPSR: INT bit (EPSR[17]): set/get. */ +#define GET_EPSR_INT() ((cpustate->cregs[CR_EPSR] >> 17) & 1) +#define SET_EPSR_INT(val) (cpustate->cregs[CR_EPSR] = (cpustate->cregs[CR_EPSR] & ~(1 << 17)) | (((val) & 1) << 17)) + + +/* EPSR: OF flag (EPSR[24]): set/get. */ +#define GET_EPSR_OF() ((cpustate->cregs[CR_EPSR] >> 24) & 1) +#define SET_EPSR_OF(val) (cpustate->cregs[CR_EPSR] = (cpustate->cregs[CR_EPSR] & ~(1 << 24)) | (((val) & 1) << 24)) + +/* EPSR: BE flag (EPSR[23]): set/get. */ +#define GET_EPSR_BE() ((cpustate->cregs[CR_EPSR] >> 23) & 1) +#define SET_EPSR_BE(val) (cpustate->cregs[CR_EPSR] = (cpustate->cregs[CR_EPSR] & ~(1 << 23)) | (((val) & 1) << 23)) + +/* DIRBASE: ATE bit (DIRBASE[0]): get. */ +#define GET_DIRBASE_ATE() (cpustate->cregs[CR_DIRBASE] & 1) + +/* DIRBASE: CS8 bit (DIRBASE[7]): get. */ +#define GET_DIRBASE_CS8() ((cpustate->cregs[CR_DIRBASE] >> 7) & 1) + +/* FSR: FTE bit (FSR[5]): set/get. */ +#define GET_FSR_FTE() ((cpustate->cregs[CR_FSR] >> 5) & 1) +#define SET_FSR_FTE(val) (cpustate->cregs[CR_FSR] = (cpustate->cregs[CR_FSR] & ~(1 << 5)) | (((val) & 1) << 5)) + +/* FSR: SE bit (FSR[8]): set/get. */ +#define GET_FSR_SE() ((cpustate->cregs[CR_FSR] >> 8) & 1) +#define SET_FSR_SE(val) (cpustate->cregs[CR_FSR] = (cpustate->cregs[CR_FSR] & ~(1 << 8)) | (((val) & 1) << 8)) + + +/* +static int has_delay_slot(UINT32 insn) +{ + int opc = (insn >> 26) & 0x3f; + if (opc == 0x10 || opc == 0x1a || opc == 0x1b || opc == 0x1d || + opc == 0x1f || opc == 0x2d || (opc == 0x13 && (insn & 3) == 2)) + return 1; + return 0; +} +*/ + +/* This is the external interface for asserting/deasserting pins on + the i860. */ +void i860_set_pin (device_t *device, int pin, int val) +{ + i860s *cpustate = get_safe_token(device); + if (pin == DEC_PIN_BUS_HOLD) + cpustate->pin_bus_hold = val; + else if (pin == DEC_PIN_RESET) + cpustate->pin_reset = val; + else + assert (0); +} + + +/* This is the external interface for indicating an external interrupt + to the i860. */ +void i860_gen_interrupt (i860s *cpustate) +{ + /* If interrupts are enabled, then set PSR.IN and prepare for trap. + Otherwise, the external interrupt is ignored. We also set + bit EPSR.INT (which tracks the INT pin). */ + if (GET_PSR_IM ()) + { + SET_PSR_IN (1); + SET_EPSR_INT (1); + cpustate->pending_trap = TRAP_WAS_EXTERNAL; + } + +#ifdef TRACE_EXT_INT + fprintf (stderr, "i860_gen_interrupt: External interrupt received "); + if (GET_PSR_IM ()) + fprintf (stderr, "[PSR.IN set, preparing to trap]\n"); + else + fprintf (stderr, "[ignored (interrupts disabled)]\n"); +#endif +} + + +/* Fetch instructions from instruction cache. + Note: The instruction cache is not implemented for MAME version, + this just fetches and returns 1 instruction from memory. */ +static UINT32 ifetch (i860s *cpustate, UINT32 pc) +{ + UINT32 phys_pc = 0; + UINT32 w1 = 0; + + /* If virtual mode, get translation. */ + if (GET_DIRBASE_ATE ()) + { + phys_pc = get_address_translation (cpustate, pc, 0 /* is_dataref */, 0 /* is_write */); + cpustate->exiting_ifetch = 0; + if (cpustate->pending_trap && (GET_PSR_DAT () || GET_PSR_IAT ())) + { + cpustate->exiting_ifetch = 1; + return 0xffeeffee; + } + } + else + phys_pc = pc; + + /* Since i860 instructions are always stored LSB first (regardless of + the BE bit), we need to adjust the instruction below on MSB hosts. */ + w1 = cpustate->program->read_dword(phys_pc); +#ifdef HOST_MSB + BYTE_REV32 (w1); +#endif /* HOST_MSB. */ + return w1; +} + + +/* Given a virtual address, perform the i860 address translation and + return the corresponding physical address. + vaddr: virtual address + is_dataref: 1 = load/store, 0 = instruction fetch. + is_write: 1 = writing to vaddr, 0 = reading from vaddr + The last two arguments are only used to determine what types + of traps should be taken. + + Page tables must always be in memory (not cached). So the routine + here only accesses memory. */ +static UINT32 get_address_translation (i860s *cpustate, UINT32 vaddr, int is_dataref, int is_write) +{ + UINT32 vdir = (vaddr >> 22) & 0x3ff; + UINT32 vpage = (vaddr >> 12) & 0x3ff; + UINT32 voffset = vaddr & 0xfff; + UINT32 dtb = (cpustate->cregs[CR_DIRBASE]) & 0xfffff000; + UINT32 pg_dir_entry_a = 0; + UINT32 pg_dir_entry = 0; + UINT32 pg_tbl_entry_a = 0; + UINT32 pg_tbl_entry = 0; + UINT32 pfa1 = 0; + UINT32 pfa2 = 0; + UINT32 ret = 0; + UINT32 ttpde = 0; + UINT32 ttpte = 0; + + assert (GET_DIRBASE_ATE ()); + + /* Get page directory entry at DTB:DIR:00. */ + pg_dir_entry_a = dtb | (vdir << 2); + pg_dir_entry = cpustate->program->read_dword(pg_dir_entry_a); +#ifdef HOST_MSB + BYTE_REV32 (pg_dir_entry); +#endif + + /* Check for non-present PDE. */ + if (!(pg_dir_entry & 1)) + { + /* PDE is not present, generate DAT or IAT. */ + if (is_dataref) + SET_PSR_DAT (1); + else + SET_PSR_IAT (1); + cpustate->pending_trap = 1; + + /* Dummy return. */ + return 0; + } + + /* PDE Check for write protection violations. */ + if (is_write && is_dataref + && !(pg_dir_entry & 2) /* W = 0. */ + && (GET_PSR_U () || GET_EPSR_WP ())) /* PSR_U = 1 or EPSR_WP = 1. */ + { + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + /* Dummy return. */ + return 0; + } + + /* PDE Check for user-mode access to supervisor pages. */ + if (GET_PSR_U () + && !(pg_dir_entry & 4)) /* U = 0. */ + { + if (is_dataref) + SET_PSR_DAT (1); + else + SET_PSR_IAT (1); + cpustate->pending_trap = 1; + /* Dummy return. */ + return 0; + } + + /* FIXME: How exactly to handle A check/update?. */ + + /* Get page table entry at PFA1:PAGE:00. */ + pfa1 = pg_dir_entry & 0xfffff000; + pg_tbl_entry_a = pfa1 | (vpage << 2); + pg_tbl_entry = cpustate->program->read_dword(pg_tbl_entry_a); +#ifdef HOST_MSB + BYTE_REV32 (pg_tbl_entry); +#endif + + /* Check for non-present PTE. */ + if (!(pg_tbl_entry & 1)) + { + /* PTE is not present, generate DAT or IAT. */ + if (is_dataref) + SET_PSR_DAT (1); + else + SET_PSR_IAT (1); + cpustate->pending_trap = 1; + + /* Dummy return. */ + return 0; + } + + /* PTE Check for write protection violations. */ + if (is_write && is_dataref + && !(pg_tbl_entry & 2) /* W = 0. */ + && (GET_PSR_U () || GET_EPSR_WP ())) /* PSR_U = 1 or EPSR_WP = 1. */ + { + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + /* Dummy return. */ + return 0; + } + + /* PTE Check for user-mode access to supervisor pages. */ + if (GET_PSR_U () + && !(pg_tbl_entry & 4)) /* U = 0. */ + { + if (is_dataref) + SET_PSR_DAT (1); + else + SET_PSR_IAT (1); + cpustate->pending_trap = 1; + /* Dummy return. */ + return 0; + } + + /* Update A bit and check D bit. */ + ttpde = pg_dir_entry | 0x20; + ttpte = pg_tbl_entry | 0x20; +#ifdef HOST_MSB + BYTE_REV32 (ttpde); + BYTE_REV32 (ttpte); +#endif + cpustate->program->write_dword(pg_dir_entry_a, ttpde); + cpustate->program->write_dword(pg_tbl_entry_a, ttpte); + + if (is_write && is_dataref && (pg_tbl_entry & 0x40) == 0) + { + /* fprintf(stderr, "DAT trap on write without dirty bit v0x%08x/p0x%08x\n", + vaddr, (pg_tbl_entry & ~0xfff)|voffset); */ + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + /* Dummy return. */ + return 0; + } + + pfa2 = (pg_tbl_entry & 0xfffff000); + ret = pfa2 | voffset; + +#ifdef TRACE_ADDR_TRANSLATION + fprintf (stderr, "get_address_translation: virt(0x%08x) -> phys(0x%08x)\n", + vaddr, ret); +#endif + + return ret; +} + + +/* Read memory emulation. + addr = address to read. + size = size of read in bytes. */ +static UINT32 readmemi_emu (i860s *cpustate, UINT32 addr, int size) +{ +#ifdef TRACE_RDWR_MEM + fprintf (stderr, "readmemi_emu: (ATE=%d) addr = 0x%08x, size = %d\n", + GET_DIRBASE_ATE (), addr, size); +#endif + + /* If virtual mode, do translation. */ + if (GET_DIRBASE_ATE ()) + { + UINT32 phys = get_address_translation (cpustate, addr, 1 /* is_dataref */, 0 /* is_write */); + if (cpustate->pending_trap && (GET_PSR_IAT () || GET_PSR_DAT ())) + { +#ifdef TRACE_PAGE_FAULT + fprintf (stderr, "0x%08x: ## Page fault (readmemi_emu).\n", + cpustate->pc); +#endif + cpustate->exiting_readmem = 1; + return 0; + } + addr = phys; + } + + /* First check for match to db register (before read). */ + if (((addr & ~(size - 1)) == cpustate->cregs[CR_DB]) && GET_PSR_BR ()) + { + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return 0; + } + + /* Now do the actual read. */ + if (size == 1) + { + UINT32 ret = cpustate->program->read_byte(addr); + return ret & 0xff; + } + else if (size == 2) + { + UINT32 ret = cpustate->program->read_word(addr); +#ifdef HOST_MSB + BYTE_REV16 (ret); +#endif + return ret & 0xffff; + } + else if (size == 4) + { + UINT32 ret = cpustate->program->read_dword(addr); +#ifdef HOST_MSB + BYTE_REV32 (ret); +#endif + return ret; + } + else + assert (0); + + return 0; +} + + +/* Write memory emulation. + addr = address to write. + size = size of write in bytes. + data = data to write. */ +static void writememi_emu (i860s *cpustate, UINT32 addr, int size, UINT32 data) +{ +#ifdef TRACE_RDWR_MEM + fprintf (stderr, "writememi_emu: (ATE=%d) addr = 0x%08x, size = %d, data = 0x%08x\n", + GET_DIRBASE_ATE (), addr, size, data); +#endif + + /* If virtual mode, do translation. */ + if (GET_DIRBASE_ATE ()) + { + UINT32 phys = get_address_translation (cpustate, addr, 1 /* is_dataref */, 1 /* is_write */); + if (cpustate->pending_trap && (GET_PSR_IAT () || GET_PSR_DAT ())) + { +#ifdef TRACE_PAGE_FAULT + fprintf (stderr, "0x%08x: ## Page fault (writememi_emu).\n", + cpustate->pc); +#endif + cpustate->exiting_readmem = 2; + return; + } + addr = phys; + } + + /* First check for match to db register (before write). */ + if (((addr & ~(size - 1)) == cpustate->cregs[CR_DB]) && GET_PSR_BW ()) + { + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } + + /* Now do the actual write. */ + if (size == 1) + cpustate->program->write_byte(addr, data); + else if (size == 2) + { +#ifdef HOST_MSB + BYTE_REV16 (data); +#endif + cpustate->program->write_word(addr, data); + } + else if (size == 4) + { +#ifdef HOST_MSB + BYTE_REV32 (data); +#endif + cpustate->program->write_dword(addr, data); + } + else + assert (0); +} + + +/* Floating-point read mem routine. + addr = address to read. + size = size of read in bytes. + dest = memory to put read data. */ +static void fp_readmem_emu (i860s *cpustate, UINT32 addr, int size, UINT8 *dest) +{ +#ifdef TRACE_RDWR_MEM + fprintf (stderr, "fp_readmem_emu: (ATE=%d) addr = 0x%08x, size = %d\n", + GET_DIRBASE_ATE (), addr, size); +#endif + + assert (size == 4 || size == 8 || size == 16); + + /* If virtual mode, do translation. */ + if (GET_DIRBASE_ATE ()) + { + UINT32 phys = get_address_translation (cpustate, addr, 1 /* is_dataref */, 0 /* is_write */); + if (cpustate->pending_trap && (GET_PSR_IAT () || GET_PSR_DAT ())) + { +#ifdef TRACE_PAGE_FAULT + fprintf (stderr, "0x%08x: ## Page fault (fp_readmem_emu).\n", + cpustate->pc); +#endif + cpustate->exiting_readmem = 3; + return; + } + addr = phys; + } + + /* First check for match to db register (before read). */ + if (((addr & ~(size - 1)) == cpustate->cregs[CR_DB]) && GET_PSR_BR ()) + { + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } + + if (size == 4) + { + dest[0] = cpustate->program->read_byte(addr+3); + dest[1] = cpustate->program->read_byte(addr+2); + dest[2] = cpustate->program->read_byte(addr+1); + dest[3] = cpustate->program->read_byte(addr+0); + } + else if (size == 8) + { + dest[0] = cpustate->program->read_byte(addr+7); + dest[1] = cpustate->program->read_byte(addr+6); + dest[2] = cpustate->program->read_byte(addr+5); + dest[3] = cpustate->program->read_byte(addr+4); + dest[4] = cpustate->program->read_byte(addr+3); + dest[5] = cpustate->program->read_byte(addr+2); + dest[6] = cpustate->program->read_byte(addr+1); + dest[7] = cpustate->program->read_byte(addr+0); + } + else if (size == 16) + { + int i; + for (i = 0; i < 16; i++) + { + dest[i] = cpustate->program->read_byte(addr+15-i); + } + } +} + + +/* Floating-point write mem routine. + addr = address to read. + size = size of read in bytes. + data = pointer to the data. + wmask = bit mask of bytes to write (only for pst.d). */ +static void fp_writemem_emu (i860s *cpustate, UINT32 addr, int size, UINT8 *data, UINT32 wmask) +{ +#ifdef TRACE_RDWR_MEM + fprintf (stderr, "fp_writemem_emu: (ATE=%d) addr = 0x%08x, size = %d\n", + GET_DIRBASE_ATE (), addr, size); +#endif + + assert (size == 4 || size == 8 || size == 16); + + /* If virtual mode, do translation. */ + if (GET_DIRBASE_ATE ()) + { + UINT32 phys = get_address_translation (cpustate, addr, 1 /* is_dataref */, 1 /* is_write */); + if (cpustate->pending_trap && GET_PSR_DAT ()) + { +#ifdef TRACE_PAGE_FAULT + fprintf (stderr, "0x%08x: ## Page fault (fp_writememi_emu).\n", + cpustate->pc); +#endif + cpustate->exiting_readmem = 4; + return; + } + addr = phys; + } + + /* First check for match to db register (before read). */ + if (((addr & ~(size - 1)) == cpustate->cregs[CR_DB]) && GET_PSR_BW ()) + { + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } + + if (size == 4) + { +#if 1 + cpustate->program->write_byte(addr+3, data[0]); + cpustate->program->write_byte(addr+2, data[1]); + cpustate->program->write_byte(addr+1, data[2]); + cpustate->program->write_byte(addr+0, data[3]); +#else + UINT32 ddd = (data[3]) | (data[2] << 8) | (data[1] << 16) |(data[0] << 24); + cpustate->program->write_dword(addr+0, ddd); +#endif + } + else if (size == 8) + { + /* Special: watch for wmask != 0xff, which means we're doing pst.d. */ + if (wmask == 0xff) + { + cpustate->program->write_byte(addr+7, data[0]); + cpustate->program->write_byte(addr+6, data[1]); + cpustate->program->write_byte(addr+5, data[2]); + cpustate->program->write_byte(addr+4, data[3]); + cpustate->program->write_byte(addr+3, data[4]); + cpustate->program->write_byte(addr+2, data[5]); + cpustate->program->write_byte(addr+1, data[6]); + cpustate->program->write_byte(addr+0, data[7]); + } + else + { + if (wmask & 0x80) cpustate->program->write_byte(addr+7, data[0]); + if (wmask & 0x40) cpustate->program->write_byte(addr+6, data[1]); + if (wmask & 0x20) cpustate->program->write_byte(addr+5, data[2]); + if (wmask & 0x10) cpustate->program->write_byte(addr+4, data[3]); + if (wmask & 0x08) cpustate->program->write_byte(addr+3, data[4]); + if (wmask & 0x04) cpustate->program->write_byte(addr+2, data[5]); + if (wmask & 0x02) cpustate->program->write_byte(addr+1, data[6]); + if (wmask & 0x01) cpustate->program->write_byte(addr+0, data[7]); + } + } + else if (size == 16) + { + int i; + for (i = 0; i < 16; i++) + { + cpustate->program->write_byte(addr+15-i, data[i]); + } + } + +} + + +#if 0 +/* Do a pipeline dump. + type: 0 (all), 1 (add), 2 (mul), 3 (load), 4 (graphics). */ +static void dump_pipe (i860s *cpustate, int type) +{ + int i = 0; + + fprintf (stderr, "pipeline state:\n"); + /* Dump the adder pipeline, if requested. */ + if (type == 0 || type == 1) + { + fprintf (stderr, " A: "); + for (i = 0; i < 3; i++) + { + if (cpustate->A[i].stat.arp) + fprintf (stderr, "[%dd] 0x%016llx ", i + 1, + *(UINT64 *)(&cpustate->A[i].val.d)); + else + fprintf (stderr, "[%ds] 0x%08x ", i + 1, + *(UINT32 *)(&cpustate->A[i].val.s)); + } + fprintf (stderr, "\n"); + } + + + /* Dump the multiplier pipeline, if requested. */ + if (type == 0 || type == 2) + { + fprintf (stderr, " M: "); + for (i = 0; i < 3; i++) + { + if (cpustate->M[i].stat.mrp) + fprintf (stderr, "[%dd] 0x%016llx ", i + 1, + *(UINT64 *)(&cpustate->M[i].val.d)); + else + fprintf (stderr, "[%ds] 0x%08x ", i + 1, + *(UINT32 *)(&cpustate->M[i].val.s)); + } + fprintf (stderr, "\n"); + } + + /* Dump the load pipeline, if requested. */ + if (type == 0 || type == 3) + { + fprintf (stderr, " L: "); + for (i = 0; i < 3; i++) + { + if (cpustate->L[i].stat.lrp) + fprintf (stderr, "[%dd] 0x%016llx ", i + 1, + *(UINT64 *)(&cpustate->L[i].val.d)); + else + fprintf (stderr, "[%ds] 0x%08x ", i + 1, + *(UINT32 *)(&cpustate->L[i].val.s)); + } + fprintf (stderr, "\n"); + } + + /* Dump the graphics pipeline, if requested. */ + if (type == 0 || type == 4) + { + fprintf (stderr, " I: "); + if (cpustate->G.stat.irp) + fprintf (stderr, "[1d] 0x%016llx\n", + *(UINT64 *)(&cpustate->G.val.d)); + else + fprintf (stderr, "[1s] 0x%08x\n", + *(UINT32 *)(&cpustate->G.val.s)); + } +} + + +/* Do a register/state dump. */ +static void dump_state (i860s *cpustate) +{ + int rn; + + /* GR's first, 4 per line. */ + for (rn = 0; rn < 32; rn++) + { + if ((rn % 4) == 0) + fprintf (stderr, "\n"); + fprintf (stderr, "%%r%-3d: 0x%08x ", rn, get_iregval (rn)); + } + fprintf (stderr, "\n"); + + /* FR's (as 32-bits), 4 per line. */ + for (rn = 0; rn < 32; rn++) + { + float ff = get_fregval_s (cpustate, rn); + if ((rn % 4) == 0) + fprintf (stderr, "\n"); + fprintf (stderr, "%%f%-3d: 0x%08x ", rn, *(UINT32 *)&ff); + } + fprintf (stderr, "\n"); + + fprintf (stderr, " psr: CC = %d, LCC = %d, SC = %d, IM = %d, U = %d\n", + GET_PSR_CC (), GET_PSR_LCC (), GET_PSR_SC (), GET_PSR_IM (), + GET_PSR_U ()); + fprintf (stderr, " IT/FT/IAT/DAT/IN = %d/%d/%d/%d/%d\n", + GET_PSR_IT (), GET_PSR_FT (), GET_PSR_IAT (), + GET_PSR_DAT (), GET_PSR_IN ()); + fprintf (stderr, "epsr: INT = %d, OF = %d, BE = %d\n", + GET_EPSR_INT (), GET_EPSR_OF (), GET_EPSR_BE ()); + fprintf (stderr, " fir: 0x%08x dirbase: 0x%08x fsr: 0x%08x\n", + cpustate->cregs[CR_FIR], cpustate->cregs[CR_DIRBASE], + cpustate->cregs[CR_FSR]); + fprintf (stderr, " pc: 0x%08x\n", cpustate->pc); +} +#endif + +/* Sign extend N-bit number. */ +INLINE INT32 sign_ext (UINT32 x, int n) +{ + INT32 t; + t = x >> (n - 1); + t = ((-t) << n) | x; + return t; +} + + +static void unrecog_opcode (UINT32 pc, UINT32 insn) +{ + fprintf (stderr, "0x%08x: 0x%08x (unrecognized opcode)\n", pc, insn); +} + + +/* Execute "ld.c csrc2,idest" instruction. */ +static void insn_ld_ctrl (i860s *cpustate, UINT32 insn) +{ + UINT32 csrc2 = get_creg (insn); + UINT32 idest = get_idest (insn); + +#ifdef TRACE_UNDEFINED_I860 + if (csrc2 > 5) + { + /* Control register not between 0..5. Undefined i860XR behavior. */ + fprintf (stderr, "WARNING: insn_ld_from_ctrl (pc=0x%08x): bad creg in ld.c (ignored)\n", cpustate->pc); + return; + } +#endif + + /* If this is a load of the fir, then there are two cases: + 1. First load of fir after a trap = usual value. + 2. Not first load of fir after a trap = address of the ld.c insn. */ + if (csrc2 == CR_FIR) + { + if (cpustate->fir_gets_trap_addr) + set_iregval (idest, cpustate->cregs[csrc2]); + else + { + cpustate->cregs[csrc2] = cpustate->pc; + set_iregval (idest, cpustate->cregs[csrc2]); + } + cpustate->fir_gets_trap_addr = 0; + } + else + set_iregval (idest, cpustate->cregs[csrc2]); +} + + +/* Execute "st.c isrc1,csrc2" instruction. */ +static void insn_st_ctrl (i860s *cpustate, UINT32 insn) +{ + UINT32 csrc2 = get_creg (insn); + UINT32 isrc1 = get_isrc1 (insn); + +#ifdef TRACE_UNDEFINED_I860 + if (csrc2 > 5) + { + /* Control register not between 0..5. Undefined i860XR behavior. */ + fprintf (stderr, "WARNING: insn_st_to_ctrl (pc=0x%08x): bad creg in st.c (ignored)\n", cpustate->pc); + return; + } +#endif + + /* Look for ITI bit turned on (but it never actually is written -- + it always appears to be 0). */ + if (csrc2 == CR_DIRBASE && (get_iregval (isrc1) & 0x20)) + { + /* NOTE: The actual icache and TLB flush are unimplemented for + the MAME version. */ + + /* Make sure ITI isn't actually written. */ + set_iregval (isrc1, (get_iregval (isrc1) & ~0x20)); + } + + if (csrc2 == CR_DIRBASE && (get_iregval (isrc1) & 1) + && GET_DIRBASE_ATE () == 0) + { + fprintf (stderr, "0x%08x: ** ATE going high!\n", cpustate->pc); + } + + /* Update the register -- unless it is fir which cannot be updated. */ + if (csrc2 == CR_EPSR) + { + UINT32 enew = 0, tmp = 0; + /* Make sure unchangeable EPSR bits stay unchanged (DCS, stepping, + and type). Also, some bits are only writeable in supervisor + mode. */ + if (GET_PSR_U ()) + { + enew = get_iregval (isrc1) & ~(0x003e1fff | 0x00c06000); + tmp = cpustate->cregs[CR_EPSR] & (0x003e1fff | 0x00c06000); + } + else + { + enew = get_iregval (isrc1) & ~0x003e1fff; + tmp = cpustate->cregs[CR_EPSR] & 0x003e1fff; + } + cpustate->cregs[CR_EPSR] = enew | tmp; + } + else if (csrc2 == CR_PSR) + { + /* Some PSR bits are only writeable in supervisor mode. */ + if (GET_PSR_U ()) + { + UINT32 enew = get_iregval (isrc1) & ~PSR_SUPERVISOR_ONLY_MASK; + UINT32 tmp = cpustate->cregs[CR_PSR] & PSR_SUPERVISOR_ONLY_MASK; + cpustate->cregs[CR_PSR] = enew | tmp; + } + else + cpustate->cregs[CR_PSR] = get_iregval (isrc1); + } + else if (csrc2 == CR_FSR) + { + /* I believe that only 21..17, 8..5, and 3..0 should be updated. */ + UINT32 enew = get_iregval (isrc1) & 0x003e01ef; + UINT32 tmp = cpustate->cregs[CR_FSR] & ~0x003e01ef; + cpustate->cregs[CR_FSR] = enew | tmp; + } + else if (csrc2 != CR_FIR) + cpustate->cregs[csrc2] = get_iregval (isrc1); +} + + +/* Execute "ld.{s,b,l} isrc1(isrc2),idest" or + "ld.{s,b,l} #const(isrc2),idest". */ +static void insn_ldx (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + INT32 immsrc1 = sign_ext (get_imm16 (insn), 16); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 eff = 0; + /* Operand size, in bytes. */ + int sizes[4] = { 1, 1, 2, 4}; + int size = 0; + int form_disp_reg = 0; + + /* Bits 28 and 0 determine the operand size. */ + size = sizes[((insn >> 27) & 2) | (insn & 1)]; + + /* Bit 26 determines the addressing mode (reg+reg or disp+reg). */ + form_disp_reg = (insn & 0x04000000); + + /* Get effective address depending on disp+reg or reg+reg form. */ + if (form_disp_reg) + { + /* Chop off lower bits of displacement. */ + immsrc1 &= ~(size - 1); + eff = (UINT32)(immsrc1 + (INT32)(get_iregval (isrc2))); + } + else + eff = get_iregval (isrc1) + get_iregval (isrc2); + +#ifdef TRACE_UNALIGNED_MEM + if (eff & (size - 1)) + { + fprintf (stderr, "0x%08x: Unaligned access detected (0x%08x).\n", + cpustate->pc, eff); + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } +#endif + + /* The i860 sign-extends 8- or 16-bit integer loads. + + Below, the readmemi_emu() needs to happen outside of the + set_iregval macro (otherwise the readmem won't occur if r0 + is the target register). */ + if (size < 4) + { + UINT32 readval = sign_ext (readmemi_emu (cpustate, eff, size), size * 8); + /* Do not update register on page fault. */ + if (cpustate->exiting_readmem) + { + return; + } + set_iregval (idest, readval); + } + else + { + UINT32 readval = readmemi_emu (cpustate, eff, size); + /* Do not update register on page fault. */ + if (cpustate->exiting_readmem) + { + return; + } + set_iregval (idest, readval); + } +} + + +/* Execute "st.x isrc1ni,#const(isrc2)" instruction (there is no + (reg + reg form). Store uses the split immediate, not the normal + 16-bit immediate as in ld.x. */ +static void insn_stx (i860s *cpustate, UINT32 insn) +{ + INT32 immsrc = sign_ext ((((insn >> 5) & 0xf800) | (insn & 0x07ff)), 16); + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 eff = 0; + /* Operand size, in bytes. */ + int sizes[4] = { 1, 1, 2, 4}; + int size = 0; + + /* Bits 28 and 0 determine the operand size. */ + size = sizes[((insn >> 27) & 2) | (insn & 1)]; + + /* FIXME: Do any necessary traps. */ + + /* Get effective address. Chop off lower bits of displacement. */ + immsrc &= ~(size - 1); + eff = (UINT32)(immsrc + (INT32)get_iregval (isrc2)); + + /* Write data (value of reg isrc1) to memory at eff. */ + writememi_emu (cpustate, eff, size, get_iregval (isrc1)); + if (cpustate->exiting_readmem) + return; +} + + +/* Execute "fst.y fdest,isrc1(isrc2)", "fst.y fdest,isrc1(isrc2)++", + "fst.y fdest,#const(isrc2)" or "fst.y fdest,#const(isrc2)++" + instruction. */ +static void insn_fsty (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + INT32 immsrc1 = sign_ext (get_imm16 (insn), 16); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 fdest = get_fdest (insn); + UINT32 eff = 0; + /* Operand size, in bytes. */ + int sizes[4] = { 8, 4, 16, 4}; + int size = 0; + int form_disp_reg = 0; + int auto_inc = (insn & 1); + + /* Bits 2 and 1 determine the operand size. */ + size = sizes[((insn >> 1) & 3)]; + + /* Bit 26 determines the addressing mode (reg+reg or disp+reg). */ + form_disp_reg = (insn & 0x04000000); + + /* FIXME: Check for undefined behavior, non-even or non-quad + register operands for fst.d and fst.q respectively. */ + + /* Get effective address depending on disp+reg or reg+reg form. */ + if (form_disp_reg) + { + /* Chop off lower bits of displacement. */ + immsrc1 &= ~(size - 1); + eff = (UINT32)(immsrc1 + (INT32)(get_iregval (isrc2))); + } + else + eff = get_iregval (isrc1) + get_iregval (isrc2); + +#ifdef TRACE_UNALIGNED_MEM + if (eff & (size - 1)) + { + fprintf (stderr, "0x%08x: Unaligned access detected (0x%08x).\n", + cpustate->pc, eff); + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } +#endif + + /* Do (post) auto-increment. */ + if (auto_inc) + { + set_iregval (isrc2, eff); +#ifdef TRACE_UNDEFINED_I860 + /* When auto-inc, isrc1 and isrc2 regs can't be the same. */ + if (isrc1 == isrc2) + { + /* Undefined i860XR behavior. */ + fprintf (stderr, "WARNING: insn_fsty (pc=0x%08x): isrc1 = isrc2 in fst with auto-inc (ignored)\n", cpustate->pc); + return; + } +#endif + } + + /* Write data (value of freg fdest) to memory at eff. */ + if (size == 4) + fp_writemem_emu (cpustate, eff, size, (UINT8 *)(&cpustate->frg[4 * (31 - fdest)]), 0xff); + else if (size == 8) + fp_writemem_emu (cpustate, eff, size, (UINT8 *)(&cpustate->frg[4 * (31 - (fdest + 1))]), 0xff); + else + fp_writemem_emu (cpustate, eff, size, (UINT8 *)(&cpustate->frg[4 * (31 - (fdest + 3))]), 0xff); + +} + + +/* Execute "fld.y isrc1(isrc2),fdest", "fld.y isrc1(isrc2)++,idest", + "fld.y #const(isrc2),fdest" or "fld.y #const(isrc2)++,idest". + Where y = {l,d,q}. Note, there is no pfld.q, though. */ +static void insn_fldy (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + INT32 immsrc1 = sign_ext (get_imm16 (insn), 16); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 fdest = get_fdest (insn); + UINT32 eff = 0; + /* Operand size, in bytes. */ + int sizes[4] = { 8, 4, 16, 4}; + int size = 0; + int form_disp_reg = 0; + int auto_inc = (insn & 1); + int piped = (insn & 0x40000000); + + /* Bits 2 and 1 determine the operand size. */ + size = sizes[((insn >> 1) & 3)]; + + /* Bit 26 determines the addressing mode (reg+reg or disp+reg). */ + form_disp_reg = (insn & 0x04000000); + + /* There is no pipelined load quad. */ + if (piped && size == 16) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* FIXME: Check for undefined behavior, non-even or non-quad + register operands for fld.d and fld.q respectively. */ + + /* Get effective address depending on disp+reg or reg+reg form. */ + if (form_disp_reg) + { + /* Chop off lower bits of displacement. */ + immsrc1 &= ~(size - 1); + eff = (UINT32)(immsrc1 + (INT32)(get_iregval (isrc2))); + } + else + eff = get_iregval (isrc1) + get_iregval (isrc2); + + /* Do (post) auto-increment. */ + if (auto_inc) + { + set_iregval (isrc2, eff); +#ifdef TRACE_UNDEFINED_I860 + /* When auto-inc, isrc1 and isrc2 regs can't be the same. */ + if (isrc1 == isrc2) + { + /* Undefined i860XR behavior. */ + fprintf (stderr, "WARNING: insn_fldy (pc=0x%08x): isrc1 = isrc2 in fst with auto-inc (ignored)\n", cpustate->pc); + return; + } +#endif + } + +#ifdef TRACE_UNALIGNED_MEM + if (eff & (size - 1)) + { + fprintf (stderr, "0x%08x: Unaligned access detected (0x%08x).\n", + cpustate->pc, eff); + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } +#endif + + /* Update the load pipe if necessary. */ + /* FIXME: Copy result-status bits to fsr from last stage. */ + if (!piped) + { + /* Scalar version writes the current result to fdest. */ + /* Read data at 'eff' into freg 'fdest' (reads to f0 or f1 are + thrown away). */ + if (fdest > 1) + { + if (size == 4) + fp_readmem_emu (cpustate, eff, size, (UINT8 *)&(cpustate->frg[4 * (31 - fdest)])); + else if (size == 8) + fp_readmem_emu (cpustate, eff, size, (UINT8 *)&(cpustate->frg[4 * (31 - (fdest + 1))])); + else if (size == 16) + fp_readmem_emu (cpustate, eff, size, (UINT8 *)&(cpustate->frg[4 * (31 - (fdest + 3))])); + } + } + else + { + /* Read the data into a temp space first. This way we can test + for any traps before updating the pipeline. The pipeline must + stay unaffected after a trap so that the instruction can be + properly restarted. */ + UINT8 bebuf[8]; + fp_readmem_emu (cpustate, eff, size, bebuf); + if (cpustate->pending_trap && cpustate->exiting_readmem) + goto ab_op; + + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the LRP + bit of the stage's result-status bits. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage LRP to FSR. */ + if (cpustate->L[1 /* 2 */].stat.lrp) + cpustate->cregs[CR_FSR] |= 0x04000000; + else + cpustate->cregs[CR_FSR] &= ~0x04000000; +#endif + if (cpustate->L[2].stat.lrp) /* 3rd (last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->L[2].val.d); + else + set_fregval_s (cpustate, fdest, cpustate->L[2].val.s); + + /* Now advance pipeline and write loaded data to first stage. */ + cpustate->L[2] = cpustate->L[1]; + cpustate->L[1] = cpustate->L[0]; + if (size == 8) + { + UINT8 *t = (UINT8 *)&(cpustate->L[0].val.d); +#ifndef HOST_MSB + t[7] = bebuf[0]; t[6] = bebuf[1]; t[5] = bebuf[2]; t[4] = bebuf[3]; + t[3] = bebuf[4]; t[2] = bebuf[5]; t[1] = bebuf[6]; t[0] = bebuf[7]; +#else + t[0] = bebuf[0]; t[1] = bebuf[1]; t[2] = bebuf[2]; t[3] = bebuf[3]; + t[4] = bebuf[4]; t[5] = bebuf[5]; t[6] = bebuf[6]; t[7] = bebuf[7]; +#endif + cpustate->L[0].stat.lrp = 1; + } + else + { + UINT8 *t = (UINT8 *)&(cpustate->L[0].val.s); +#ifndef HOST_MSB + t[3] = bebuf[0]; t[2] = bebuf[1]; t[1] = bebuf[2]; t[0] = bebuf[3]; +#else + t[0] = bebuf[0]; t[1] = bebuf[1]; t[2] = bebuf[2]; t[3] = bebuf[3]; +#endif + cpustate->L[0].stat.lrp = 0; + } + } + + ab_op:; +} + + +/* Execute "pst.d fdest,#const(isrc2)" or "fst.d fdest,#const(isrc2)++" + instruction. */ +static void insn_pstd (i860s *cpustate, UINT32 insn) +{ + INT32 immsrc1 = sign_ext (get_imm16 (insn), 16); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 fdest = get_fdest (insn); + UINT32 eff = 0; + int auto_inc = (insn & 1); + UINT8 *bebuf = 0; + int pm = GET_PSR_PM (); + int i; + UINT32 wmask; + int orig_pm = pm; + + /* Get the pixel size, where: + PS: 0 = 8 bits, 1 = 16 bits, 2 = 32-bits. */ + int ps = GET_PSR_PS (); + +#ifdef TRACE_UNDEFINED_I860 + if (!(ps == 0 || ps == 1 || ps == 2)) + fprintf (stderr, "insn_pstd: Undefined i860XR behavior, invalid value %d for pixel size.\n", ps); +#endif + +#ifdef TRACE_UNDEFINED_I860 + /* Bits 2 and 1 determine the operand size, which must always be + zero (indicating a 64-bit operand). */ + if (insn & 0x6) + { + /* Undefined i860XR behavior. */ + fprintf (stderr, "WARNING: insn_pstd (pc=0x%08x): bad operand size specifier\n", cpustate->pc); + } +#endif + + /* FIXME: Check for undefined behavior, non-even register operands. */ + + /* Get effective address. Chop off lower bits of displacement. */ + immsrc1 &= ~(8 - 1); + eff = (UINT32)(immsrc1 + (INT32)(get_iregval (isrc2))); + +#ifdef TRACE_UNALIGNED_MEM + if (eff & (8 - 1)) + { + fprintf (stderr, "0x%08x: Unaligned access detected (0x%08x).\n", + cpustate->pc, eff); + SET_PSR_DAT (1); + cpustate->pending_trap = 1; + return; + } +#endif + + /* Do (post) auto-increment. */ + if (auto_inc) + set_iregval (isrc2, eff); + + /* Update the the pixel mask depending on the pixel size. Shift PM + right by 8/2^ps bits. */ + if (ps == 0) + pm = (pm >> 8) & 0x00; + else if (ps == 1) + pm = (pm >> 4) & 0x0f; + else if (ps == 2) + pm = (pm >> 2) & 0x3f; + SET_PSR_PM (pm); + + /* Write data (value of freg fdest) to memory at eff-- but only those + bytes that are enabled by the bits in PSR.PM. Bit 0 of PM selects + the pixel at the lowest address. */ + wmask = 0; + for (i = 0; i < 8; ) + { + if (ps == 0) + { + if (orig_pm & 0x80) + wmask |= 1 << (7-i); + i += 1; + } + else if (ps == 1) + { + if (orig_pm & 0x08) + wmask |= 0x3 << (6-i); + i += 2; + } + else if (ps == 2) + { + if (orig_pm & 0x02) + wmask |= 0xf << (4-i); + i += 4; + } + else + { + wmask = 0xff; + break; + } + orig_pm <<= 1; + } + bebuf = (UINT8 *)(&cpustate->frg[4 * (31 - (fdest + 1))]); + fp_writemem_emu (cpustate, eff, 8, bebuf, wmask); +} + + +/* Execute "ixfr isrc1ni,fdest" instruction. */ +static void insn_ixfr (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 fdest = get_fdest (insn); + UINT32 iv = 0; + + /* This is a bit-pattern transfer, not a conversion. */ + iv = get_iregval (isrc1); + set_fregval_s (cpustate, fdest, *(float *)&iv); +} + + +/* Execute "addu isrc1,isrc2,idest". */ +static void insn_addu (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + UINT64 tmp = 0; + + src1val = get_iregval (get_isrc1 (insn)); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val + get_iregval (isrc2); + + /* Set OF and CC flags. + For unsigned: + OF = bit 31 carry + CC = bit 31 carry. + */ + tmp = (UINT64)src1val + (UINT64)(get_iregval (isrc2)); + if ((tmp >> 32) & 1) + { + SET_PSR_CC (1); + SET_EPSR_OF (1); + } + else + { + SET_PSR_CC (0); + SET_EPSR_OF (0); + } + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "addu #const,isrc2,idest". */ +static void insn_addu_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + UINT64 tmp = 0; + + src1val = sign_ext (get_imm16 (insn), 16); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val + get_iregval (isrc2); + + /* Set OF and CC flags. + For unsigned: + OF = bit 31 carry + CC = bit 31 carry. + */ + tmp = (UINT64)src1val + (UINT64)(get_iregval (isrc2)); + if ((tmp >> 32) & 1) + { + SET_PSR_CC (1); + SET_EPSR_OF (1); + } + else + { + SET_PSR_CC (0); + SET_EPSR_OF (0); + } + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "adds isrc1,isrc2,idest". */ +static void insn_adds (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + int sa, sb, sres; + + src1val = get_iregval (get_isrc1 (insn)); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val + get_iregval (isrc2); + + /* Set OF and CC flags. + For signed: + OF = standard signed overflow. + CC set if isrc2 < -isrc1 + CC clear if isrc2 >= -isrc1 + */ + sa = src1val & 0x80000000; + sb = get_iregval (isrc2) & 0x80000000; + sres = tmp_dest_val & 0x80000000; + if (sa != sb && sa != sres) + SET_EPSR_OF (1); + else + SET_EPSR_OF (0); + + if ((INT32)get_iregval (isrc2) < -(INT32)(src1val)) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "adds #const,isrc2,idest". */ +static void insn_adds_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + int sa, sb, sres; + + src1val = sign_ext (get_imm16 (insn), 16); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val + get_iregval (isrc2); + + /* Set OF and CC flags. + For signed: + OF = standard signed overflow. + CC set if isrc2 < -isrc1 + CC clear if isrc2 >= -isrc1 + */ + sa = src1val & 0x80000000; + sb = get_iregval (isrc2) & 0x80000000; + sres = tmp_dest_val & 0x80000000; + if (sa != sb && sa != sres) + SET_EPSR_OF (1); + else + SET_EPSR_OF (0); + + if ((INT32)get_iregval (isrc2) < -(INT32)(src1val)) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "subu isrc1,isrc2,idest". */ +static void insn_subu (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + + src1val = get_iregval (get_isrc1 (insn)); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val - get_iregval (isrc2); + + /* Set OF and CC flags. + For unsigned: + OF = NOT(bit 31 carry) + CC = bit 31 carry. + (i.e. CC set if isrc2 <= isrc1 + CC clear if isrc2 > isrc1 + */ + if ((UINT32)get_iregval (isrc2) <= (UINT32)src1val) + { + SET_PSR_CC (1); + SET_EPSR_OF (0); + } + else + { + SET_PSR_CC (0); + SET_EPSR_OF (1); + } + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "subu #const,isrc2,idest". */ +static void insn_subu_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + + src1val = sign_ext (get_imm16 (insn), 16); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val - get_iregval (isrc2); + + /* Set OF and CC flags. + For unsigned: + OF = NOT(bit 31 carry) + CC = bit 31 carry. + (i.e. CC set if isrc2 <= isrc1 + CC clear if isrc2 > isrc1 + */ + if ((UINT32)get_iregval (isrc2) <= (UINT32)src1val) + { + SET_PSR_CC (1); + SET_EPSR_OF (0); + } + else + { + SET_PSR_CC (0); + SET_EPSR_OF (1); + } + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "subs isrc1,isrc2,idest". */ +static void insn_subs (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + int sa, sb, sres; + + src1val = get_iregval (get_isrc1 (insn)); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val - get_iregval (isrc2); + + /* Set OF and CC flags. + For signed: + OF = standard signed overflow. + CC set if isrc2 > isrc1 + CC clear if isrc2 <= isrc1 + */ + sa = src1val & 0x80000000; + sb = get_iregval (isrc2) & 0x80000000; + sres = tmp_dest_val & 0x80000000; + if (sa != sb && sa != sres) + SET_EPSR_OF (1); + else + SET_EPSR_OF (0); + + if ((INT32)get_iregval (isrc2) > (INT32)(src1val)) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "subs #const,isrc2,idest". */ +static void insn_subs_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 tmp_dest_val = 0; + int sa, sb, sres; + + src1val = sign_ext (get_imm16 (insn), 16); + + /* We don't update the actual idest register now because below we + need to test the original src1 and src2 if either happens to + be the destination register. */ + tmp_dest_val = src1val - get_iregval (isrc2); + + /* Set OF and CC flags. + For signed: + OF = standard signed overflow. + CC set if isrc2 > isrc1 + CC clear if isrc2 <= isrc1 + */ + sa = src1val & 0x80000000; + sb = get_iregval (isrc2) & 0x80000000; + sres = tmp_dest_val & 0x80000000; + if (sa != sb && sa != sres) + SET_EPSR_OF (1); + else + SET_EPSR_OF (0); + + if ((INT32)get_iregval (isrc2) > (INT32)(src1val)) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + /* Now update the destination register. */ + set_iregval (idest, tmp_dest_val); +} + + +/* Execute "shl isrc1,isrc2,idest". */ +static void insn_shl (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + + src1val = get_iregval (get_isrc1 (insn)); + set_iregval (idest, get_iregval (isrc2) << src1val); +} + + +/* Execute "shl #const,isrc2,idest". */ +static void insn_shl_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + + src1val = sign_ext (get_imm16 (insn), 16); + set_iregval (idest, get_iregval (isrc2) << src1val); +} + + +/* Execute "shr isrc1,isrc2,idest". */ +static void insn_shr (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + + src1val = get_iregval (get_isrc1 (insn)); + + /* The iregs array is UINT32, so this is a logical shift. */ + set_iregval (idest, get_iregval (isrc2) >> src1val); + + /* shr also sets the SC in psr (shift count). */ + SET_PSR_SC (src1val); +} + + +/* Execute "shr #const,isrc2,idest". */ +static void insn_shr_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + + src1val = sign_ext (get_imm16 (insn), 16); + + /* The iregs array is UINT32, so this is a logical shift. */ + set_iregval (idest, get_iregval (isrc2) >> src1val); + + /* shr also sets the SC in psr (shift count). */ + SET_PSR_SC (src1val); +} + + +/* Execute "shra isrc1,isrc2,idest". */ +static void insn_shra (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + + src1val = get_iregval (get_isrc1 (insn)); + + /* The iregs array is UINT32, so cast isrc2 to get arithmetic shift. */ + set_iregval (idest, (INT32)get_iregval (isrc2) >> src1val); +} + + +/* Execute "shra #const,isrc2,idest". */ +static void insn_shra_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + + src1val = sign_ext (get_imm16 (insn), 16); + + /* The iregs array is UINT32, so cast isrc2 to get arithmetic shift. */ + set_iregval (idest, (INT32)get_iregval (isrc2) >> src1val); +} + + +/* Execute "shrd isrc1ni,isrc2,idest" instruction. */ +static void insn_shrd (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 sc = GET_PSR_SC (); + UINT32 tmp; + + /* Do the operation: + idest = low_32(isrc1ni:isrc2 >> sc). */ + if (sc == 0) + tmp = get_iregval (isrc2); + else + { + tmp = get_iregval (isrc1) << (32 - sc); + tmp |= (get_iregval (isrc2) >> sc); + } + set_iregval (idest, tmp); +} + + +/* Execute "and isrc1,isrc2,idest". */ +static void insn_and (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + res = get_iregval (isrc1) & get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "and #const,isrc2,idest". */ +static void insn_and_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = src1val & get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "andh #const,isrc2,idest". */ +static void insn_andh_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = (src1val << 16) & get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "andnot isrc1,isrc2,idest". */ +static void insn_andnot (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + res = (~get_iregval (isrc1)) & get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "andnot #const,isrc2,idest". */ +static void insn_andnot_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = (~src1val) & get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "andnoth #const,isrc2,idest". */ +static void insn_andnoth_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = (~(src1val << 16)) & get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "or isrc1,isrc2,idest". */ +static void insn_or (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + res = get_iregval (isrc1) | get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "or #const,isrc2,idest". */ +static void insn_or_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = src1val | get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "orh #const,isrc2,idest". */ +static void insn_orh_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = (src1val << 16) | get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "xor isrc1,isrc2,idest". */ +static void insn_xor (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + res = get_iregval (isrc1) ^ get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "xor #const,isrc2,idest". */ +static void insn_xor_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = src1val ^ get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "xorh #const,isrc2,idest". */ +static void insn_xorh_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 idest = get_idest (insn); + UINT32 res = 0; + + /* Do the operation. */ + src1val = get_imm16 (insn); + res = (src1val << 16) ^ get_iregval (isrc2); + + /* Set flags. */ + if (res == 0) + SET_PSR_CC (1); + else + SET_PSR_CC (0); + + set_iregval (idest, res); +} + + +/* Execute "trap isrc1ni,isrc2,idest" instruction. */ +static void insn_trap (i860s *cpustate, UINT32 insn) +{ + SET_PSR_IT (1); + cpustate->pending_trap = 1; +} + + +/* Execute "intovr" instruction. */ +static void insn_intovr (i860s *cpustate, UINT32 insn) +{ + if (GET_EPSR_OF ()) + { + SET_PSR_IT (1); + cpustate->pending_trap = 1; + } +} + + +/* Execute "bte isrc1,isrc2,sbroff". */ +static void insn_bte (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 target_addr = 0; + INT32 sbroff = 0; + int res = 0; + + src1val = get_iregval (get_isrc1 (insn)); + + /* Compute the target address from the sbroff field. */ + sbroff = sign_ext ((((insn >> 5) & 0xf800) | (insn & 0x07ff)), 16); + target_addr = (INT32)cpustate->pc + 4 + (sbroff << 2); + + /* Determine comparison result. */ + res = (src1val == get_iregval (isrc2)); + + /* Branch routines always update the PC. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 4; + + cpustate->pc_updated = 1; +} + + +/* Execute "bte #const5,isrc2,sbroff". */ +static void insn_bte_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 target_addr = 0; + INT32 sbroff = 0; + int res = 0; + + src1val = (insn >> 11) & 0x1f; /* 5-bit field, zero-extended. */ + + /* Compute the target address from the sbroff field. */ + sbroff = sign_ext ((((insn >> 5) & 0xf800) | (insn & 0x07ff)), 16); + target_addr = (INT32)cpustate->pc + 4 + (sbroff << 2); + + /* Determine comparison result. */ + res = (src1val == get_iregval (isrc2)); + + /* Branch routines always update the PC. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 4; + + cpustate->pc_updated = 1; +} + + +/* Execute "btne isrc1,isrc2,sbroff". */ +static void insn_btne (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 target_addr = 0; + INT32 sbroff = 0; + int res = 0; + + src1val = get_iregval (get_isrc1 (insn)); + + /* Compute the target address from the sbroff field. */ + sbroff = sign_ext ((((insn >> 5) & 0xf800) | (insn & 0x07ff)), 16); + target_addr = (INT32)cpustate->pc + 4 + (sbroff << 2); + + /* Determine comparison result. */ + res = (src1val != get_iregval (isrc2)); + + /* Branch routines always update the PC. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 4; + + cpustate->pc_updated = 1; +} + + +/* Execute "btne #const5,isrc2,sbroff". */ +static void insn_btne_imm (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = 0; + UINT32 isrc2 = get_isrc2 (insn); + UINT32 target_addr = 0; + INT32 sbroff = 0; + int res = 0; + + src1val = (insn >> 11) & 0x1f; /* 5-bit field, zero-extended. */ + + /* Compute the target address from the sbroff field. */ + sbroff = sign_ext ((((insn >> 5) & 0xf800) | (insn & 0x07ff)), 16); + target_addr = (INT32)cpustate->pc + 4 + (sbroff << 2); + + /* Determine comparison result. */ + res = (src1val != get_iregval (isrc2)); + + /* Branch routines always update the PC. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 4; + + cpustate->pc_updated = 1; +} + + +/* Execute "bc lbroff" instruction. */ +static void insn_bc (i860s *cpustate, UINT32 insn) +{ + UINT32 target_addr = 0; + INT32 lbroff = 0; + int res = 0; + + /* Compute the target address from the lbroff field. */ + lbroff = sign_ext ((insn & 0x03ffffff), 26); + target_addr = (INT32)cpustate->pc + 4 + (lbroff << 2); + + /* Determine comparison result. */ + res = (GET_PSR_CC () == 1); + + /* Branch routines always update the PC. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 4; + + cpustate->pc_updated = 1; +} + + +/* Execute "bnc lbroff" instruction. */ +static void insn_bnc (i860s *cpustate, UINT32 insn) +{ + UINT32 target_addr = 0; + INT32 lbroff = 0; + int res = 0; + + /* Compute the target address from the lbroff field. */ + lbroff = sign_ext ((insn & 0x03ffffff), 26); + target_addr = (INT32)cpustate->pc + 4 + (lbroff << 2); + + /* Determine comparison result. */ + res = (GET_PSR_CC () == 0); + + /* Branch routines always update the PC, since pc_updated is set + in the decode routine. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 4; + + cpustate->pc_updated = 1; +} + + +/* Execute "bc.t lbroff" instruction. */ +static void insn_bct (i860s *cpustate, UINT32 insn) +{ + UINT32 target_addr = 0; + INT32 lbroff = 0; + int res = 0; + UINT32 orig_pc = cpustate->pc; + + /* Compute the target address from the lbroff field. */ + lbroff = sign_ext ((insn & 0x03ffffff), 26); + target_addr = (INT32)cpustate->pc + 4 + (lbroff << 2); + + /* Determine comparison result. */ + res = (GET_PSR_CC () == 1); + + /* Careful. Unlike bla, the delay slot instruction is only executed + if the branch is taken. */ + if (res) + { + /* Execute delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + if (cpustate->pending_trap) + { + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + } + + /* Since this branch is delayed, we must jump 2 instructions if + if isn't taken. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 8; + + cpustate->pc_updated = 1; + + ab_op: + ; +} + + +/* Execute "bnc.t lbroff" instruction. */ +static void insn_bnct (i860s *cpustate, UINT32 insn) +{ + UINT32 target_addr = 0; + INT32 lbroff = 0; + int res = 0; + UINT32 orig_pc = cpustate->pc; + + /* Compute the target address from the lbroff field. */ + lbroff = sign_ext ((insn & 0x03ffffff), 26); + target_addr = (INT32)cpustate->pc + 4 + (lbroff << 2); + + /* Determine comparison result. */ + res = (GET_PSR_CC () == 0); + + /* Careful. Unlike bla, the delay slot instruction is only executed + if the branch is taken. */ + if (res) + { + /* Execute delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + if (cpustate->pending_trap) + { + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + } + + /* Since this branch is delayed, we must jump 2 instructions if + if isn't taken. */ + if (res) + cpustate->pc = target_addr; + else + cpustate->pc += 8; + + cpustate->pc_updated = 1; + + ab_op: + ; +} + + +/* Execute "call lbroff" instruction. */ +static void insn_call (i860s *cpustate, UINT32 insn) +{ + UINT32 target_addr = 0; + INT32 lbroff = 0; + UINT32 orig_pc = cpustate->pc; + + /* Compute the target address from the lbroff field. */ + lbroff = sign_ext ((insn & 0x03ffffff), 26); + target_addr = (INT32)cpustate->pc + 4 + (lbroff << 2); + + /* Execute the delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + if (cpustate->pending_trap) + { + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + + /* Sets the return pointer (r1). */ + set_iregval (1, orig_pc + 8); + + /* New target. */ + cpustate->pc = target_addr; + cpustate->pc_updated = 1; + + ab_op:; +} + + +/* Execute "br lbroff". */ +static void insn_br (i860s *cpustate, UINT32 insn) +{ + UINT32 target_addr = 0; + INT32 lbroff = 0; + UINT32 orig_pc = cpustate->pc; + + /* Compute the target address from the lbroff field. */ + lbroff = sign_ext ((insn & 0x03ffffff), 26); + target_addr = (INT32)cpustate->pc + 4 + (lbroff << 2); + + /* Execute the delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + if (cpustate->pending_trap) + { + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + + /* New target. */ + cpustate->pc = target_addr; + cpustate->pc_updated = 1; + + ab_op:; +} + + +/* Execute "bri isrc1ni" instruction. + Note: I didn't merge this code with calli because bri must do + a lot of flag manipulation if any trap bits are set. */ +static void insn_bri (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 orig_pc = cpustate->pc; + UINT32 orig_psr = cpustate->cregs[CR_PSR]; + UINT32 orig_src1_val = get_iregval (isrc1); + +#if 1 /* TURBO. */ + cpustate->cregs[CR_PSR] &= ~PSR_ALL_TRAP_BITS_MASK; +#endif + + /* Execute the delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + + /* Delay slot insn caused a trap, abort operation. */ + if (cpustate->pending_trap) + { + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + + /* If any trap bits are set, we need to do the return from + trap work. Note, we must use the PSR value that existed + before the delay slot instruction was executed since the + delay slot instruction might itself cause a trap bit to + be set. */ + if (orig_psr & PSR_ALL_TRAP_BITS_MASK) + { + /* Restore U and IM from their previous copies. */ + SET_PSR_U (GET_PSR_PU ()); + SET_PSR_IM (GET_PSR_PIM ()); + + cpustate->fir_gets_trap_addr = 0; + } + + /* Update PC. */ + cpustate->pc = orig_src1_val; + + cpustate->pc_updated = 1; + ab_op:; +} + +/* Execute "calli isrc1ni" instruction. */ +static void insn_calli (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 orig_pc = cpustate->pc; + UINT32 orig_src1_val = get_iregval (isrc1); + +#ifdef TRACE_UNDEFINED_I860 + /* Check for undefined behavior. */ + if (isrc1 == 1) + { + /* Src1 must not be r1. */ + fprintf (stderr, "WARNING: insn_calli (pc=0x%08x): isrc1 = r1 on a calli\n", cpustate->pc); + } +#endif + + /* Set return pointer before executing delay slot instruction. */ + set_iregval (1, cpustate->pc + 8); + + /* Execute the delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + if (cpustate->pending_trap) + { + set_iregval (1, orig_src1_val); + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + + /* Set new PC. */ + cpustate->pc = orig_src1_val; + cpustate->pc_updated = 1; + + ab_op:; +} + + +/* Execute "bla isrc1ni,isrc2,sbroff" instruction. */ +static void insn_bla (i860s *cpustate, UINT32 insn) +{ + UINT32 isrc1 = get_isrc1 (insn); + UINT32 isrc2 = get_isrc2 (insn); + UINT32 target_addr = 0; + INT32 sbroff = 0; + int lcc_tmp = 0; + UINT32 orig_pc = cpustate->pc; + UINT32 orig_isrc2val = get_iregval (isrc2); + +#ifdef TRACE_UNDEFINED_I860 + /* Check for undefined behavior. */ + if (isrc1 == isrc2) + { + /* Src1 and src2 the same is undefined i860XR behavior. */ + fprintf (stderr, "WARNING: insn_bla (pc=0x%08x): isrc1 and isrc2 are the same (ignored)\n", cpustate->pc); + return; + } +#endif + + /* Compute the target address from the sbroff field. */ + sbroff = sign_ext ((((insn >> 5) & 0xf800) | (insn & 0x07ff)), 16); + target_addr = (INT32)cpustate->pc + 4 + (sbroff << 2); + + /* Determine comparison result based on opcode. */ + lcc_tmp = ((INT32)get_iregval (isrc2) >= -(INT32)get_iregval (isrc1)); + + set_iregval (isrc2, get_iregval (isrc1) + orig_isrc2val); + + /* Execute the delay slot instruction. */ + cpustate->pc += 4; + decode_exec (cpustate, ifetch (cpustate, orig_pc + 4), 0); + cpustate->pc = orig_pc; + if (cpustate->pending_trap) + { + cpustate->pending_trap |= TRAP_IN_DELAY_SLOT; + goto ab_op; + } + + if (GET_PSR_LCC ()) + cpustate->pc = target_addr; + else + { + /* Since this branch is delayed, we must jump 2 instructions if + if isn't taken. */ + cpustate->pc += 8; + } + SET_PSR_LCC (lcc_tmp); + + cpustate->pc_updated = 1; + ab_op:; +} + + +/* Execute "flush #const(isrc2)" or "flush #const(isrc2)++" instruction. */ +static void insn_flush (i860s *cpustate, UINT32 insn) +{ + UINT32 src1val = sign_ext (get_imm16 (insn), 16); + UINT32 isrc2 = get_isrc2 (insn); + int auto_inc = (insn & 1); + UINT32 eff = 0; + + /* Technically, idest should be encoded as r0 because idest + is undefined after the instruction. We don't currently + check for this. + + Flush D$ block at address #const+isrc2. Block is undefined + after. The effective address must be 16-byte aligned. + + FIXME: Need to examine RB and RC and do this right. + */ + + /* Chop off lower bits of displacement to 16-byte alignment. */ + src1val &= ~(16-1); + eff = src1val + get_iregval (isrc2); + if (auto_inc) + set_iregval (isrc2, eff); + + /* In user mode, the flush is ignored. */ + if (GET_PSR_U () == 0) + { + /* If line is dirty, write it to memory and invalidate. + NOTE: The actual dirty write is unimplemented in the MAME version + as we don't emulate the dcache. */ + } +} + + +/* Execute "[p]fmul.{ss,sd,dd} fsrc1,fsrc2,fdest" instruction or + pfmul3.dd fsrc1,fsrc2,fdest. + + The pfmul3.dd differs from pfmul.dd in that it treats the pipeline + as 3 stages, even though it is a double precision multiply. */ +static void insn_fmul (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + double dbl_tmp_dest = 0.0; + float sgl_tmp_dest = 0.0; + double dbl_last_stage_contents = 0.0; + float sgl_last_stage_contents = 0.0; + int is_pfmul3 = insn & 0x4; + int num_stages = (src_prec && !is_pfmul3) ? 2 : 3; + + /* Only .dd is valid for pfmul. */ + if (is_pfmul3 && (insn & 0x180) != 0x180) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* Check for invalid .ds combination. */ + if ((insn & 0x180) == 0x100) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* For pipelined version, retrieve the contents of the last stage + of the pipeline, whose precision is specified by the MRP bit + of the stage's result-status bits. Note for pfmul, the number + of stages is determined by the source precision of the current + operation. */ + if (piped) + { + if (cpustate->M[num_stages - 1].stat.mrp) + dbl_last_stage_contents = cpustate->M[num_stages - 1].val.d; + else + sgl_last_stage_contents = cpustate->M[num_stages - 1].val.s; + } + + /* Do the operation, being careful about source and result + precision. */ + if (src_prec) + { + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + + /* For pipelined mul, if fsrc2 is the same as fdest, then the last + stage is bypassed to fsrc2 (rather than using the value in fsrc2). + This bypass is not available for fsrc1, and is undefined behavior. */ + if (0 && piped && fdest != 0 && fsrc1 == fdest) + v1 = dbl_last_stage_contents; + if (piped && fdest != 0 && fsrc2 == fdest) + v2 = dbl_last_stage_contents; + + if (res_prec) + dbl_tmp_dest = v1 * v2; + else + sgl_tmp_dest = (float)(v1 * v2); + } + else + { + float v1 = get_fregval_s (cpustate, fsrc1); + float v2 = get_fregval_s (cpustate, fsrc2); + + /* For pipelined mul, if fsrc2 is the same as fdest, then the last + stage is bypassed to fsrc2 (rather than using the value in fsrc2). + This bypass is not available for fsrc1, and is undefined behavior. */ + if (0 && piped && fdest != 0 && fsrc1 == fdest) + v1 = sgl_last_stage_contents; + if (piped && fdest != 0 && fsrc2 == fdest) + v2 = sgl_last_stage_contents; + + if (res_prec) + dbl_tmp_dest = (double)(v1 * v2); + else + sgl_tmp_dest = v1 * v2; + } + + /* FIXME: Set result-status bits besides MRP. And copy to fsr from + last stage. */ + /* FIXME: Scalar version flows through all stages. */ + /* FIXME: Mixed precision (only weird for pfmul). */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, with precision specified by the R bit. */ + if (res_prec) + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + else + set_fregval_s (cpustate, fdest, sgl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage MRP to FSR. */ + if (cpustate->M[num_stages - 2 /* 1 */].stat.mrp) + cpustate->cregs[CR_FSR] |= 0x10000000; + else + cpustate->cregs[CR_FSR] &= ~0x10000000; +#endif + + if (cpustate->M[num_stages - 1].stat.mrp) + set_fregval_d (cpustate, fdest, dbl_last_stage_contents); + else + set_fregval_s (cpustate, fdest, sgl_last_stage_contents); + + /* Now advance pipeline and write current calculation to + first stage. */ + if (num_stages == 3) + { + cpustate->M[2] = cpustate->M[1]; + cpustate->M[1] = cpustate->M[0]; + } + else + cpustate->M[1] = cpustate->M[0]; + + if (res_prec) + { + cpustate->M[0].val.d = dbl_tmp_dest; + cpustate->M[0].stat.mrp = 1; + } + else + { + cpustate->M[0].val.s = sgl_tmp_dest; + cpustate->M[0].stat.mrp = 0; + } + } +} + + +/* Execute "fmlow.dd fsrc1,fsrc2,fdest" instruction. */ +static void insn_fmlow (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + INT64 i1 = *(UINT64 *)&v1; + INT64 i2 = *(UINT64 *)&v2; + INT64 tmp = 0; + + /* Only .dd is valid for fmlow. */ + if ((insn & 0x180) != 0x180) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* The lower 32-bits are obvious. What exactly goes in the upper + bits? + Technically, the upper-most 10 bits are undefined, but i'd like + to be undefined in the same way as the real i860 if possible. */ + + /* Keep lower 53 bits of multiply. */ + tmp = i1 * i2; + tmp &= 0x001fffffffffffffULL; + tmp |= (i1 & 0x8000000000000000LL) ^ (i2 & 0x8000000000000000LL); + set_fregval_d (cpustate, fdest, *(double *)&tmp); +} + + +/* Execute [p]fadd.{ss,sd,dd} fsrc1,fsrc2,fdest (.ds disallowed above). */ +static void insn_fadd_sub (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + int is_sub = insn & 1; /* 1 = sub, 0 = add. */ + double dbl_tmp_dest = 0.0; + float sgl_tmp_dest = 0.0; + double dbl_last_stage_contents = 0.0; + float sgl_last_stage_contents = 0.0; + + /* Check for invalid .ds combination. */ + if ((insn & 0x180) == 0x100) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* For pipelined version, retrieve the contents of the last stage + of the pipeline, whose precision is specified by the ARP bit + of the stage's result-status bits. There are always three stages + for pfadd/pfsub. */ + if (piped) + { + if (cpustate->A[2].stat.arp) + dbl_last_stage_contents = cpustate->A[2].val.d; + else + sgl_last_stage_contents = cpustate->A[2].val.s; + } + + /* Do the operation, being careful about source and result + precision. */ + if (src_prec) + { + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + + /* For pipelined add/sub, if fsrc1 is the same as fdest, then the last + stage is bypassed to fsrc1 (rather than using the value in fsrc1). + Likewise for fsrc2. */ + if (piped && fdest != 0 && fsrc1 == fdest) + v1 = dbl_last_stage_contents; + if (piped && fdest != 0 && fsrc2 == fdest) + v2 = dbl_last_stage_contents; + + if (res_prec) + dbl_tmp_dest = is_sub ? v1 - v2 : v1 + v2; + else + sgl_tmp_dest = is_sub ? (float)(v1 - v2) : (float)(v1 + v2); + } + else + { + float v1 = get_fregval_s (cpustate, fsrc1); + float v2 = get_fregval_s (cpustate, fsrc2); + + /* For pipelined add/sub, if fsrc1 is the same as fdest, then the last + stage is bypassed to fsrc1 (rather than using the value in fsrc1). + Likewise for fsrc2. */ + if (piped && fdest != 0 && fsrc1 == fdest) + v1 = sgl_last_stage_contents; + if (piped && fdest != 0 && fsrc2 == fdest) + v2 = sgl_last_stage_contents; + + if (res_prec) + dbl_tmp_dest = is_sub ? (double)(v1 - v2) : (double)(v1 + v2); + else + sgl_tmp_dest = is_sub ? v1 - v2 : v1 + v2; + } + + /* FIXME: Set result-status bits besides ARP. And copy to fsr from + last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, with precision specified by the R bit. */ + if (res_prec) + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + else + set_fregval_s (cpustate, fdest, sgl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the ARP + bit of the stage's result-status bits. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage ARP to FSR. */ + if (cpustate->A[1 /* 2 */].stat.arp) + cpustate->cregs[CR_FSR] |= 0x20000000; + else + cpustate->cregs[CR_FSR] &= ~0x20000000; +#endif + if (cpustate->A[2].stat.arp) /* 3rd (last) stage. */ + set_fregval_d (cpustate, fdest, dbl_last_stage_contents); + else + set_fregval_s (cpustate, fdest, sgl_last_stage_contents); + + /* Now advance pipeline and write current calculation to + first stage. */ + cpustate->A[2] = cpustate->A[1]; + cpustate->A[1] = cpustate->A[0]; + if (res_prec) + { + cpustate->A[0].val.d = dbl_tmp_dest; + cpustate->A[0].stat.arp = 1; + } + else + { + cpustate->A[0].val.s = sgl_tmp_dest; + cpustate->A[0].stat.arp = 0; + } + } +} + + +/* Operand types for PFAM/PFMAM routine below. */ +enum { + OP_SRC1 = 0, + OP_SRC2 = 1, + OP_KI = 2, + OP_KR = 4, + OP_T = 8, + OP_MPIPE = 16, + OP_APIPE = 32, + FLAGM = 64 /* Indicates PFMAM uses M rather than A pipe result. */ +}; + +/* A table to map DPC value to source operands. + + The PFAM and PFMAM tables are nearly identical, and the only differences + are that every time PFAM uses the A pipe, PFMAM uses the M pipe instead. + So we only represent the PFAM table and use a special flag on any entry + where the PFMAM table would use the M pipe rather than the A pipe. + Also, entry 16 is not valid for PFMAM. */ +static const struct +{ + int M_unit_op1; + int M_unit_op2; + int A_unit_op1; + int A_unit_op2; + int T_loaded; + int K_loaded; +} src_opers[] = { + /* 0000 */ { OP_KR, OP_SRC2, OP_SRC1, OP_MPIPE, 0, 0}, + /* 0001 */ { OP_KR, OP_SRC2, OP_T, OP_MPIPE, 0, 1}, + /* 0010 */ { OP_KR, OP_SRC2, OP_SRC1, OP_APIPE|FLAGM, 1, 0}, + /* 0011 */ { OP_KR, OP_SRC2, OP_T, OP_APIPE|FLAGM, 1, 1}, + /* 0100 */ { OP_KI, OP_SRC2, OP_SRC1, OP_MPIPE, 0, 0}, + /* 0101 */ { OP_KI, OP_SRC2, OP_T, OP_MPIPE, 0, 1}, + /* 0110 */ { OP_KI, OP_SRC2, OP_SRC1, OP_APIPE|FLAGM, 1, 0}, + /* 0111 */ { OP_KI, OP_SRC2, OP_T, OP_APIPE|FLAGM, 1, 1}, + /* 1000 */ { OP_KR, OP_APIPE|FLAGM, OP_SRC1, OP_SRC2, 1, 0}, + /* 1001 */ { OP_SRC1, OP_SRC2, OP_APIPE|FLAGM, OP_MPIPE, 0, 0}, + /* 1010 */ { OP_KR, OP_APIPE|FLAGM, OP_SRC1, OP_SRC2, 0, 0}, + /* 1011 */ { OP_SRC1, OP_SRC2, OP_T, OP_APIPE|FLAGM, 1, 0}, + /* 1100 */ { OP_KI, OP_APIPE|FLAGM, OP_SRC1, OP_SRC2, 1, 0}, + /* 1101 */ { OP_SRC1, OP_SRC2, OP_T, OP_MPIPE, 0, 0}, + /* 1110 */ { OP_KI, OP_APIPE|FLAGM, OP_SRC1, OP_SRC2, 0, 0}, + /* 1111 */ { OP_SRC1, OP_SRC2, OP_T, OP_APIPE|FLAGM, 0, 0} +}; + +static float get_fval_from_optype_s (i860s *cpustate, UINT32 insn, int optype) +{ + float retval = 0.0; + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + + optype &= ~FLAGM; + switch (optype) + { + case OP_SRC1: + retval = get_fregval_s (cpustate, fsrc1); + break; + case OP_SRC2: + retval = get_fregval_s (cpustate, fsrc2); + break; + case OP_KI: + retval = cpustate->KI.s; + break; + case OP_KR: + retval = cpustate->KR.s; + break; + case OP_T: + retval = cpustate->T.s; + break; + case OP_MPIPE: + /* Last stage is 3rd stage for single precision input. */ + retval = cpustate->M[2].val.s; + break; + case OP_APIPE: + retval = cpustate->A[2].val.s; + break; + default: + assert (0); + } + + return retval; +} + + +static double get_fval_from_optype_d (i860s *cpustate, UINT32 insn, int optype) +{ + double retval = 0.0; + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + + optype &= ~FLAGM; + switch (optype) + { + case OP_SRC1: + retval = get_fregval_d (cpustate, fsrc1); + break; + case OP_SRC2: + retval = get_fregval_d (cpustate, fsrc2); + break; + case OP_KI: + retval = cpustate->KI.d; + break; + case OP_KR: + retval = cpustate->KR.d; + break; + case OP_T: + retval = cpustate->T.d; + break; + case OP_MPIPE: + /* Last stage is 2nd stage for double precision input. */ + retval = cpustate->M[1].val.d; + break; + case OP_APIPE: + retval = cpustate->A[2].val.d; + break; + default: + assert (0); + } + + return retval; +} + + +/* Execute pf[m]{a,s}m.{ss,sd,dd} fsrc1,fsrc2,fdest (FP dual ops). + + Since these are always pipelined, the P bit is used to distinguish + family pfam (P=1) from family pfmam (P=0), and the lower 4 bits + of the extended opcode is the DPC. + + Note also that the S and R bits are slightly different than normal + floating point operations. The S bit denotes the precision of the + multiplication source, while the R bit denotes the precision of + the addition source as well as precision of all results. */ +static void insn_dualop (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + int is_pfam = insn & 0x400; /* 1 = pfam, 0 = pfmam. */ + int is_sub = insn & 0x10; /* 1 = pf[m]sm, 0 = pf[m]am. */ + double dbl_tmp_dest_mul = 0.0; + float sgl_tmp_dest_mul = 0.0; + double dbl_tmp_dest_add = 0.0; + float sgl_tmp_dest_add = 0.0; + double dbl_last_Mstage_contents = 0.0; + float sgl_last_Mstage_contents = 0.0; + double dbl_last_Astage_contents = 0.0; + float sgl_last_Astage_contents = 0.0; + int num_mul_stages = src_prec ? 2 : 3; + + int dpc = insn & 0xf; + int M_unit_op1 = src_opers[dpc].M_unit_op1; + int M_unit_op2 = src_opers[dpc].M_unit_op2; + int A_unit_op1 = src_opers[dpc].A_unit_op1; + int A_unit_op2 = src_opers[dpc].A_unit_op2; + int T_loaded = src_opers[dpc].T_loaded; + int K_loaded = src_opers[dpc].K_loaded; + + /* Check for invalid .ds combination. */ + if ((insn & 0x180) == 0x100) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + if (is_pfam == 0) + { + /* Check for invalid DPC combination 16 for PFMAM. */ + if (dpc == 16) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* PFMAM table adjustments (M_unit_op1 is never a pipe stage, + so no adjustment made for it). */ + M_unit_op2 = (M_unit_op2 & FLAGM) ? OP_MPIPE : M_unit_op2; + A_unit_op1 = (A_unit_op1 & FLAGM) ? OP_MPIPE : A_unit_op1; + A_unit_op2 = (A_unit_op2 & FLAGM) ? OP_MPIPE : A_unit_op2; + } + + /* FIXME: Check for fsrc1/fdest overlap for some mul DPC combinations. */ + + /* Retrieve the contents of the last stage of the multiplier pipeline, + whose precision is specified by the MRP bit of the stage's result- + status bits. Note for multiply, the number of stages is determined + by the source precision of the current operation. */ + if (cpustate->M[num_mul_stages - 1].stat.mrp) + dbl_last_Mstage_contents = cpustate->M[num_mul_stages - 1].val.d; + else + sgl_last_Mstage_contents = cpustate->M[num_mul_stages - 1].val.s; + + /* Similarly, retrieve the last stage of the adder pipe. */ + if (cpustate->A[2].stat.arp) + dbl_last_Astage_contents = cpustate->A[2].val.d; + else + sgl_last_Astage_contents = cpustate->A[2].val.s; + + /* Do the mul operation, being careful about source and result + precision. */ + if (src_prec) + { + double v1 = get_fval_from_optype_d (cpustate, insn, M_unit_op1); + double v2 = get_fval_from_optype_d (cpustate, insn, M_unit_op2); + + /* For mul, if fsrc2 is the same as fdest, then the last stage + is bypassed to fsrc2 (rather than using the value in fsrc2). + This bypass is not available for fsrc1, and is undefined behavior. */ + if (0 && M_unit_op1 == OP_SRC1 && fdest != 0 && fsrc1 == fdest) + v1 = is_pfam ? dbl_last_Astage_contents : dbl_last_Mstage_contents; + if (M_unit_op2 == OP_SRC2 && fdest != 0 && fsrc2 == fdest) + v2 = is_pfam ? dbl_last_Astage_contents : dbl_last_Mstage_contents; + + if (res_prec) + dbl_tmp_dest_mul = v1 * v2; + else + sgl_tmp_dest_mul = (float)(v1 * v2); + } + else + { + float v1 = get_fval_from_optype_s (cpustate, insn, M_unit_op1); + float v2 = get_fval_from_optype_s (cpustate, insn, M_unit_op2); + + /* For mul, if fsrc2 is the same as fdest, then the last stage + is bypassed to fsrc2 (rather than using the value in fsrc2). + This bypass is not available for fsrc1, and is undefined behavior. */ + if (0 && M_unit_op1 == OP_SRC1 && fdest != 0 && fsrc1 == fdest) + v1 = is_pfam ? sgl_last_Astage_contents : sgl_last_Mstage_contents; + if (M_unit_op2 == OP_SRC2 && fdest != 0 && fsrc2 == fdest) + v2 = is_pfam ? sgl_last_Astage_contents : sgl_last_Mstage_contents; + + if (res_prec) + dbl_tmp_dest_mul = (double)(v1 * v2); + else + sgl_tmp_dest_mul = v1 * v2; + } + + /* Do the add operation, being careful about source and result + precision. Remember, the R bit indicates source and result precision + here. */ + if (res_prec) + { + double v1 = get_fval_from_optype_d (cpustate, insn, A_unit_op1); + double v2 = get_fval_from_optype_d (cpustate, insn, A_unit_op2); + + /* For add/sub, if fsrc1 is the same as fdest, then the last stage + is bypassed to fsrc1 (rather than using the value in fsrc1). + Likewise for fsrc2. */ + if (A_unit_op1 == OP_SRC1 && fdest != 0 && fsrc1 == fdest) + v1 = is_pfam ? dbl_last_Astage_contents : dbl_last_Mstage_contents; + if (A_unit_op2 == OP_SRC2 && fdest != 0 && fsrc2 == fdest) + v2 = is_pfam ? dbl_last_Astage_contents : dbl_last_Mstage_contents; + + if (res_prec) + dbl_tmp_dest_add = is_sub ? v1 - v2 : v1 + v2; + else + sgl_tmp_dest_add = is_sub ? (float)(v1 - v2) : (float)(v1 + v2); + } + else + { + float v1 = get_fval_from_optype_s (cpustate, insn, A_unit_op1); + float v2 = get_fval_from_optype_s (cpustate, insn, A_unit_op2); + + /* For add/sub, if fsrc1 is the same as fdest, then the last stage + is bypassed to fsrc1 (rather than using the value in fsrc1). + Likewise for fsrc2. */ + if (A_unit_op1 == OP_SRC1 && fdest != 0 && fsrc1 == fdest) + v1 = is_pfam ? sgl_last_Astage_contents : sgl_last_Mstage_contents; + if (A_unit_op2 == OP_SRC2 && fdest != 0 && fsrc2 == fdest) + v2 = is_pfam ? sgl_last_Astage_contents : sgl_last_Mstage_contents; + + if (res_prec) + dbl_tmp_dest_add = is_sub ? (double)(v1 - v2) : (double)(v1 + v2); + else + sgl_tmp_dest_add = is_sub ? v1 - v2 : v1 + v2; + } + + /* If necessary, load T. */ + if (T_loaded) + { + /* T is loaded from the result of the last stage of the multiplier. */ + if (cpustate->M[num_mul_stages - 1].stat.mrp) + cpustate->T.d = dbl_last_Mstage_contents; + else + cpustate->T.s = sgl_last_Mstage_contents; + } + + /* If necessary, load KR or KI. */ + if (K_loaded) + { + /* KI or KR is loaded from the first register input. */ + if (M_unit_op1 == OP_KI) + { + if (src_prec) + cpustate->KI.d = get_fregval_d (cpustate, fsrc1); + else + cpustate->KI.s = get_fregval_s (cpustate, fsrc1); + } + else if (M_unit_op1 == OP_KR) + { + if (src_prec) + cpustate->KR.d = get_fregval_d (cpustate, fsrc1); + else + cpustate->KR.s = get_fregval_s (cpustate, fsrc1); + } + else + assert (0); + } + + /* Now update fdest (either from adder pipe or multiplier pipe, + depending on whether the instruction is pfam or pfmam). */ + if (is_pfam) + { + /* Update fdest with the result from the last stage of the + adder pipeline, with precision specified by the ARP + bit of the stage's result-status bits. */ + if (cpustate->A[2].stat.arp) + set_fregval_d (cpustate, fdest, dbl_last_Astage_contents); + else + set_fregval_s (cpustate, fdest, sgl_last_Astage_contents); + } + else + { + /* Update fdest with the result from the last stage of the + multiplier pipeline, with precision specified by the MRP + bit of the stage's result-status bits. */ + if (cpustate->M[num_mul_stages - 1].stat.mrp) + set_fregval_d (cpustate, fdest, dbl_last_Mstage_contents); + else + set_fregval_s (cpustate, fdest, sgl_last_Mstage_contents); + } + + /* FIXME: Set result-status bits besides MRP. And copy to fsr from + last stage. */ + /* FIXME: Mixed precision (only weird for pfmul). */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage MRP to FSR. */ + if (cpustate->M[num_mul_stages - 2 /* 1 */].stat.mrp) + cpustate->cregs[CR_FSR] |= 0x10000000; + else + cpustate->cregs[CR_FSR] &= ~0x10000000; +#endif + + /* Now advance multiplier pipeline and write current calculation to + first stage. */ + if (num_mul_stages == 3) + { + cpustate->M[2] = cpustate->M[1]; + cpustate->M[1] = cpustate->M[0]; + } + else + cpustate->M[1] = cpustate->M[0]; + + if (res_prec) + { + cpustate->M[0].val.d = dbl_tmp_dest_mul; + cpustate->M[0].stat.mrp = 1; + } + else + { + cpustate->M[0].val.s = sgl_tmp_dest_mul; + cpustate->M[0].stat.mrp = 0; + } + + /* FIXME: Set result-status bits besides ARP. And copy to fsr from + last stage. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage ARP to FSR. */ + if (cpustate->A[1 /* 2 */].stat.arp) + cpustate->cregs[CR_FSR] |= 0x20000000; + else + cpustate->cregs[CR_FSR] &= ~0x20000000; +#endif + + /* Now advance adder pipeline and write current calculation to + first stage. */ + cpustate->A[2] = cpustate->A[1]; + cpustate->A[1] = cpustate->A[0]; + if (res_prec) + { + cpustate->A[0].val.d = dbl_tmp_dest_add; + cpustate->A[0].stat.arp = 1; + } + else + { + cpustate->A[0].val.s = sgl_tmp_dest_add; + cpustate->A[0].stat.arp = 0; + } +} + + +/* Execute frcp.{ss,sd,dd} fsrc2,fdest (.ds disallowed above). */ +static void insn_frcp (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + + /* Do the operation, being careful about source and result + precision. */ + if (src_prec) + { + double v = get_fregval_d (cpustate, fsrc2); + double res; + if (v == (double)0.0) + { + /* Generate source-exception trap if fsrc2 is 0. */ + if (0 /* && GET_FSR_FTE () */) + { + SET_PSR_FT (1); + SET_FSR_SE (1); + cpustate->pending_trap = GET_FSR_FTE (); + } + /* Set fdest to INF or some other exceptional value here? */ + } + else + { + /* Real i860 isn't a precise as a real divide, but this should + be okay. */ + SET_FSR_SE (0); + *((UINT64 *)&v) &= 0xfffff00000000000ULL; + res = (double)1.0/v; + *((UINT64 *)&res) &= 0xfffff00000000000ULL; + if (res_prec) + set_fregval_d (cpustate, fdest, res); + else + set_fregval_s (cpustate, fdest, (float)res); + } + } + else + { + float v = get_fregval_s (cpustate, fsrc2); + float res; + if (v == 0.0) + { + /* Generate source-exception trap if fsrc2 is 0. */ + if (0 /* GET_FSR_FTE () */) + { + SET_PSR_FT (1); + SET_FSR_SE (1); + cpustate->pending_trap = GET_FSR_FTE (); + } + /* Set fdest to INF or some other exceptional value here? */ + } + else + { + /* Real i860 isn't a precise as a real divide, but this should + be okay. */ + SET_FSR_SE (0); + *((UINT32 *)&v) &= 0xffff8000; + res = (float)1.0/v; + *((UINT32 *)&res) &= 0xffff8000; + if (res_prec) + set_fregval_d (cpustate, fdest, (double)res); + else + set_fregval_s (cpustate, fdest, res); + } + } +} + + +/* Execute frsqr.{ss,sd,dd} fsrc2,fdest (.ds disallowed above). */ +static void insn_frsqr (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + + /* Check for invalid .ds combination. */ + if ((insn & 0x180) == 0x100) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* Check for invalid .ds combination. */ + if ((insn & 0x180) == 0x100) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* Do the operation, being careful about source and result + precision. */ + if (src_prec) + { + double v = get_fregval_d (cpustate, fsrc2); + double res; + if (v == 0.0 || v < 0.0) + { + /* Generate source-exception trap if fsrc2 is 0 or negative. */ + if (0 /* GET_FSR_FTE () */) + { + SET_PSR_FT (1); + SET_FSR_SE (1); + cpustate->pending_trap = GET_FSR_FTE (); + } + /* Set fdest to INF or some other exceptional value here? */ + } + else + { + SET_FSR_SE (0); + *((UINT64 *)&v) &= 0xfffff00000000000ULL; + res = (double)1.0/sqrt (v); + *((UINT64 *)&res) &= 0xfffff00000000000ULL; + if (res_prec) + set_fregval_d (cpustate, fdest, res); + else + set_fregval_s (cpustate, fdest, (float)res); + } + } + else + { + float v = get_fregval_s (cpustate, fsrc2); + float res; + if (v == 0.0 || v < 0.0) + { + /* Generate source-exception trap if fsrc2 is 0 or negative. */ + if (0 /* GET_FSR_FTE () */) + { + SET_PSR_FT (1); + SET_FSR_SE (1); + cpustate->pending_trap = GET_FSR_FTE (); + } + /* Set fdest to INF or some other exceptional value here? */ + } + else + { + SET_FSR_SE (0); + *((UINT32 *)&v) &= 0xffff8000; + res = (float)1.0/sqrt (v); + *((UINT32 *)&res) &= 0xffff8000; + if (res_prec) + set_fregval_d (cpustate, fdest, (double)res); + else + set_fregval_s (cpustate, fdest, res); + } + } +} + + +/* Execute fxfr fsrc1,idest. */ +static void insn_fxfr (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 idest = get_idest (insn); + float fv = 0; + + /* This is a bit-pattern transfer, not a conversion. */ + fv = get_fregval_s (cpustate, fsrc1); + set_iregval (idest, *(UINT32 *)&fv); +} + + +/* Execute [p]ftrunc.{ss,sd,dd} fsrc1,idest. */ +/* FIXME: Is .ss really a valid combination? On the one hand, + the programmer's reference (1990) lists ftrunc.p where .p + is any of {ss,sd,dd}. On the other hand, a paragraph on the + same page states that [p]ftrunc must specify double-precision + results. Inconsistent. + Update: The vendor SVR4 assembler does not accept .ss combination, + so the latter sentence above appears to be the correct way. */ +static void insn_ftrunc (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + + /* Check for invalid .ds or .ss combinations. */ + if ((insn & 0x080) == 0) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* Do the operation, being careful about source and result + precision. Operation: fdest = integer part of fsrc1 in + lower 32-bits. */ + if (src_prec) + { + double v1 = get_fregval_d (cpustate, fsrc1); + INT32 iv = (INT32)v1; + /* We always write a single, since the lower 32-bits of fdest + get the result (and the even numbered reg is the lower). */ + set_fregval_s (cpustate, fdest, *(float *)&iv); + } + else + { + float v1 = get_fregval_s (cpustate, fsrc1); + INT32 iv = (INT32)v1; + /* We always write a single, since the lower 32-bits of fdest + get the result (and the even numbered reg is the lower). */ + set_fregval_s (cpustate, fdest, *(float *)&iv); + } + + /* FIXME: Handle updating of pipestages for pftrunc. */ + /* Includes looking at ARP (add result precision.) */ + if (piped) + { + fprintf (stderr, "insn_ftrunc: FIXME: pipelined not functional yet.\n"); + if (res_prec) + set_fregval_d (cpustate, fdest, 0.0); + else + set_fregval_s (cpustate, fdest, 0.0); + } +} + + +/* Execute [p]famov.{ss,sd,ds,dd} fsrc1,fdest. */ +static void insn_famov (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + double dbl_tmp_dest = 0.0; + double sgl_tmp_dest = 0.0; + + /* Do the operation, being careful about source and result + precision. */ + if (src_prec) + { + double v1 = get_fregval_d (cpustate, fsrc1); + if (res_prec) + dbl_tmp_dest = v1; + else + sgl_tmp_dest = (float)v1; + } + else + { + float v1 = get_fregval_s (cpustate, fsrc1); + if (res_prec) + dbl_tmp_dest = (double)v1; + else + sgl_tmp_dest = v1; + } + + /* FIXME: Set result-status bits besides ARP. And copy to fsr from + last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, with precision specified by the R bit. */ + if (res_prec) + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + else + set_fregval_s (cpustate, fdest, sgl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the ARP + bit of the stage's result-status bits. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage ARP to FSR. */ + if (cpustate->A[1 /* 2 */].stat.arp) + cpustate->cregs[CR_FSR] |= 0x20000000; + else + cpustate->cregs[CR_FSR] &= ~0x20000000; +#endif + if (cpustate->A[2].stat.arp) /* 3rd (last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->A[2].val.d); + else + set_fregval_s (cpustate, fdest, cpustate->A[2].val.s); + + /* Now advance pipeline and write current calculation to + first stage. */ + cpustate->A[2] = cpustate->A[1]; + cpustate->A[1] = cpustate->A[0]; + if (res_prec) + { + cpustate->A[0].val.d = dbl_tmp_dest; + cpustate->A[0].stat.arp = 1; + } + else + { + cpustate->A[0].val.s = sgl_tmp_dest; + cpustate->A[0].stat.arp = 0; + } + } +} + + +/* Execute [p]fiadd/sub.{ss,dd} fsrc1,fsrc2,fdest. */ +static void insn_fiadd_sub (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + int res_prec = insn & 0x080; /* 1 = double, 0 = single. */ + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + int is_sub = insn & 0x4; /* 1 = sub, 0 = add. */ + double dbl_tmp_dest = 0.0; + float sgl_tmp_dest = 0.0; + + /* Check for invalid .ds and .sd combinations. */ + if ((insn & 0x180) == 0x100 + || (insn & 0x180) == 0x080) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* Do the operation, being careful about source and result + precision. */ + if (src_prec) + { + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + UINT64 iv1 = *(UINT64 *)&v1; + UINT64 iv2 = *(UINT64 *)&v2; + UINT64 r; + if (is_sub) + r = iv1 - iv2; + else + r = iv1 + iv2; + if (res_prec) + dbl_tmp_dest = *(double *)&r; + else + assert (0); /* .ds not allowed. */ + } + else + { + float v1 = get_fregval_s (cpustate, fsrc1); + float v2 = get_fregval_s (cpustate, fsrc2); + UINT64 iv1 = (UINT64)(*(UINT32 *)&v1); + UINT64 iv2 = (UINT64)(*(UINT32 *)&v2); + UINT32 r; + if (is_sub) + r = (UINT32)(iv1 - iv2); + else + r = (UINT32)(iv1 + iv2); + if (res_prec) + assert (0); /* .sd not allowed. */ + else + sgl_tmp_dest = *(float *)&r; + } + + /* FIXME: Copy result-status bit IRP to fsr from last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, with precision specified by the R bit. */ + if (res_prec) + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + else + set_fregval_s (cpustate, fdest, sgl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the IRP + bit of the stage's result-status bits. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy stage IRP to FSR. */ + if (res_prec) + cpustate->cregs[CR_FSR] |= 0x08000000; + else + cpustate->cregs[CR_FSR] &= ~0x08000000; +#endif + if (cpustate->G.stat.irp) /* 1st (and last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->G.val.d); + else + set_fregval_s (cpustate, fdest, cpustate->G.val.s); + + /* Now write current calculation to first and only stage. */ + if (res_prec) + { + cpustate->G.val.d = dbl_tmp_dest; + cpustate->G.stat.irp = 1; + } + else + { + cpustate->G.val.s = sgl_tmp_dest; + cpustate->G.stat.irp = 0; + } + } +} + + +/* Execute pf{gt,le,eq}.{ss,dd} fsrc1,fsrc2,fdest. + Opcode pfgt has R bit cleared; pfle has R bit set. */ +static void insn_fcmp (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int src_prec = insn & 0x100; /* 1 = double, 0 = single. */ + double dbl_tmp_dest = 0.0; + double sgl_tmp_dest = 0.0; + /* int is_eq = insn & 1; */ + int is_gt = ((insn & 0x81) == 0x00); + int is_le = ((insn & 0x81) == 0x80); + + /* Do the operation. Source and result precision must be the same. + pfgt: CC set if fsrc1 > fsrc2, else cleared. + pfle: CC cleared if fsrc1 <= fsrc2, else set. + pfeq: CC set if fsrc1 = fsrc2, else cleared. + + Note that the compares write an undefined (but non-exceptional) + result into the first stage of the adder pipeline. We'll model + this by just pushing in dbl_ or sgl_tmp_dest which equal 0.0. */ + if (src_prec) + { + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + if (is_gt) /* gt. */ + SET_PSR_CC (v1 > v2 ? 1 : 0); + else if (is_le) /* le. */ + SET_PSR_CC (v1 <= v2 ? 0 : 1); + else /* eq. */ + SET_PSR_CC (v1 == v2 ? 1 : 0); + } + else + { + float v1 = get_fregval_s (cpustate, fsrc1); + float v2 = get_fregval_s (cpustate, fsrc2); + if (is_gt) /* gt. */ + SET_PSR_CC (v1 > v2 ? 1 : 0); + else if (is_le) /* le. */ + SET_PSR_CC (v1 <= v2 ? 0 : 1); + else /* eq. */ + SET_PSR_CC (v1 == v2 ? 1 : 0); + } + + /* FIXME: Set result-status bits besides ARP. And copy to fsr from + last stage. */ + /* These write fdest with the result from the last + stage of the pipeline, with precision specified by the ARP + bit of the stage's result-status bits. */ +#if 1 /* FIXME: WIP on FSR update. This may not be correct. */ + /* Copy 3rd stage ARP to FSR. */ + if (cpustate->A[1 /* 2 */].stat.arp) + cpustate->cregs[CR_FSR] |= 0x20000000; + else + cpustate->cregs[CR_FSR] &= ~0x20000000; +#endif + if (cpustate->A[2].stat.arp) /* 3rd (last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->A[2].val.d); + else + set_fregval_s (cpustate, fdest, cpustate->A[2].val.s); + + /* Now advance pipeline and write current calculation to + first stage. */ + cpustate->A[2] = cpustate->A[1]; + cpustate->A[1] = cpustate->A[0]; + if (src_prec) + { + cpustate->A[0].val.d = dbl_tmp_dest; + cpustate->A[0].stat.arp = 1; + } + else + { + cpustate->A[0].val.s = sgl_tmp_dest; + cpustate->A[0].stat.arp = 0; + } +} + + +/* Execute [p]fzchk{l,s} fsrc1,fsrc2,fdest. + The fzchk instructions have S and R bits set. */ +static void insn_fzchk (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + int is_fzchks = insn & 8; /* 1 = fzchks, 0 = fzchkl. */ + double dbl_tmp_dest = 0.0; + int i; + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + UINT64 iv1 = *(UINT64 *)&v1; + UINT64 iv2 = *(UINT64 *)&v2; + UINT64 r = 0; + char pm = GET_PSR_PM (); + + /* Check for S and R bits set. */ + if ((insn & 0x180) != 0x180) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + /* Do the operation. The fzchks version operates in parallel on + four 16-bit pixels, while the fzchkl operates on two 32-bit + pixels (pixels are unsigned ordinals in this context). */ + if (is_fzchks) + { + pm = (pm >> 4) & 0x0f; + for (i = 3; i >= 0; i--) + { + UINT16 ps1 = (iv1 >> (i * 16)) & 0xffff; + UINT16 ps2 = (iv2 >> (i * 16)) & 0xffff; + if (ps2 <= ps1) + { + r |= ((UINT64)ps2 << (i * 16)); + pm |= (1 << (7 - (3 - i))); + } + else + { + r |= ((UINT64)ps1 << (i * 16)); + pm &= ~(1 << (7 - (3 - i))); + } + } + } + else + { + pm = (pm >> 2) & 0x3f; + for (i = 1; i >= 0; i--) + { + UINT32 ps1 = (iv1 >> (i * 32)) & 0xffffffff; + UINT32 ps2 = (iv2 >> (i * 32)) & 0xffffffff; + if (ps2 <= ps1) + { + r |= ((UINT64)ps2 << (i * 32)); + pm |= (1 << (7 - (1 - i))); + } + else + { + r |= ((UINT64)ps1 << (i * 32)); + pm &= ~(1 << (7 - (1 - i))); + } + } + } + + dbl_tmp_dest = *(double *)&r; + SET_PSR_PM (pm); + cpustate->merge = 0; + + /* FIXME: Copy result-status bit IRP to fsr from last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, always with double precision. */ + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the IRP + bit of the stage's result-status bits. */ + if (cpustate->G.stat.irp) /* 1st (and last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->G.val.d); + else + set_fregval_s (cpustate, fdest, cpustate->G.val.s); + + /* Now write current calculation to first and only stage. */ + cpustate->G.val.d = dbl_tmp_dest; + cpustate->G.stat.irp = 1; + } +} + + +/* Execute [p]form.dd fsrc1,fdest. + The form.dd instructions have S and R bits set. */ +static void insn_form (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fdest = get_fdest (insn); + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + double dbl_tmp_dest = 0.0; + double v1 = get_fregval_d (cpustate, fsrc1); + UINT64 iv1 = *(UINT64 *)&v1; + + /* Check for S and R bits set. */ + if ((insn & 0x180) != 0x180) + { + unrecog_opcode (cpustate->pc, insn); + return; + } + + iv1 |= cpustate->merge; + dbl_tmp_dest = *(double *)&iv1; + cpustate->merge = 0; + + /* FIXME: Copy result-status bit IRP to fsr from last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, always with double precision. */ + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the IRP + bit of the stage's result-status bits. */ + if (cpustate->G.stat.irp) /* 1st (and last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->G.val.d); + else + set_fregval_s (cpustate, fdest, cpustate->G.val.s); + + /* Now write current calculation to first and only stage. */ + cpustate->G.val.d = dbl_tmp_dest; + cpustate->G.stat.irp = 1; + } +} + + +/* Execute [p]faddp fsrc1,fsrc2,fdest. */ +static void insn_faddp (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + double dbl_tmp_dest = 0.0; + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + UINT64 iv1 = *(UINT64 *)&v1; + UINT64 iv2 = *(UINT64 *)&v2; + UINT64 r = 0; + int ps = GET_PSR_PS (); + + r = iv1 + iv2; + dbl_tmp_dest = *(double *)&r; + + /* Update the merge register depending on the pixel size. + PS: 0 = 8 bits, 1 = 16 bits, 2 = 32-bits. */ + if (ps == 0) + { + cpustate->merge = ((cpustate->merge >> 8) & ~0xff00ff00ff00ff00ULL); + cpustate->merge |= (r & 0xff00ff00ff00ff00ULL); + } + else if (ps == 1) + { + cpustate->merge = ((cpustate->merge >> 6) & ~0xfc00fc00fc00fc00ULL); + cpustate->merge |= (r & 0xfc00fc00fc00fc00ULL); + } + else if (ps == 2) + { + cpustate->merge = ((cpustate->merge >> 8) & ~0xff000000ff000000ULL); + cpustate->merge |= (r & 0xff000000ff000000ULL); + } +#ifdef TRACE_UNDEFINED_I860 + else + fprintf (stderr, "insn_faddp: Undefined i860XR behavior, invalid value %d for pixel size.\n", ps); +#endif + + /* FIXME: Copy result-status bit IRP to fsr from last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, always with double precision. */ + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the IRP + bit of the stage's result-status bits. */ + if (cpustate->G.stat.irp) /* 1st (and last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->G.val.d); + else + set_fregval_s (cpustate, fdest, cpustate->G.val.s); + + /* Now write current calculation to first and only stage. */ + cpustate->G.val.d = dbl_tmp_dest; + cpustate->G.stat.irp = 1; + } +} + + +/* Execute [p]faddz fsrc1,fsrc2,fdest. */ +static void insn_faddz (i860s *cpustate, UINT32 insn) +{ + UINT32 fsrc1 = get_fsrc1 (insn); + UINT32 fsrc2 = get_fsrc2 (insn); + UINT32 fdest = get_fdest (insn); + int piped = insn & 0x400; /* 1 = pipelined, 0 = scalar. */ + double dbl_tmp_dest = 0.0; + double v1 = get_fregval_d (cpustate, fsrc1); + double v2 = get_fregval_d (cpustate, fsrc2); + UINT64 iv1 = *(UINT64 *)&v1; + UINT64 iv2 = *(UINT64 *)&v2; + UINT64 r = 0; + + r = iv1 + iv2; + dbl_tmp_dest = *(double *)&r; + + /* Update the merge register depending on the pixel size. */ + cpustate->merge = ((cpustate->merge >> 16) & ~0xffff0000ffff0000ULL); + cpustate->merge |= (r & 0xffff0000ffff0000ULL); + + /* FIXME: Copy result-status bit IRP to fsr from last stage. */ + /* FIXME: Scalar version flows through all stages. */ + if (!piped) + { + /* Scalar version writes the current calculation to the fdest + register, always with double precision. */ + set_fregval_d (cpustate, fdest, dbl_tmp_dest); + } + else + { + /* Pipelined version writes fdest with the result from the last + stage of the pipeline, with precision specified by the IRP + bit of the stage's result-status bits. */ + if (cpustate->G.stat.irp) /* 1st (and last) stage. */ + set_fregval_d (cpustate, fdest, cpustate->G.val.d); + else + set_fregval_s (cpustate, fdest, cpustate->G.val.s); + + /* Now write current calculation to first and only stage. */ + cpustate->G.val.d = dbl_tmp_dest; + cpustate->G.stat.irp = 1; + } +} + + +/* Flags for the decode table. */ +enum { + DEC_MORE = 1, /* More decoding necessary. */ + DEC_DECODED = 2 /* Fully decoded, go. */ +}; + + +typedef struct { + /* Execute function for this opcode. */ + void (*insn_exec)(i860s *, UINT32); + + /* Flags for this opcode. */ + char flags; +} decode_tbl_t; + + +/* First-level decode table (i.e., for the 6 primary opcode bits). */ +static const decode_tbl_t decode_tbl[64] = { + /* A slight bit of decoding for loads and stores is done in the + execution routines (operand size and addressing mode), which + is why their respective entries are identical. */ + { insn_ldx, DEC_DECODED}, /* ld.b isrc1(isrc2),idest. */ + { insn_ldx, DEC_DECODED}, /* ld.b #const(isrc2),idest. */ + { insn_ixfr, DEC_DECODED}, /* ixfr isrc1ni,fdest. */ + { insn_stx, DEC_DECODED}, /* st.b isrc1ni,#const(isrc2). */ + { insn_ldx, DEC_DECODED}, /* ld.{s,l} isrc1(isrc2),idest. */ + { insn_ldx, DEC_DECODED}, /* ld.{s,l} #const(isrc2),idest. */ + { 0, 0}, + { insn_stx, DEC_DECODED}, /* st.{s,l} isrc1ni,#const(isrc2),idest.*/ + { insn_fldy, DEC_DECODED}, /* fld.{l,d,q} isrc1(isrc2)[++],fdest. */ + { insn_fldy, DEC_DECODED}, /* fld.{l,d,q} #const(isrc2)[++],fdest. */ + { insn_fsty, DEC_DECODED}, /* fst.{l,d,q} fdest,isrc1(isrc2)[++] */ + { insn_fsty, DEC_DECODED}, /* fst.{l,d,q} fdest,#const(isrc2)[++] */ + { insn_ld_ctrl, DEC_DECODED}, /* ld.c csrc2,idest. */ + { insn_flush, DEC_DECODED}, /* flush #const(isrc2) (or autoinc). */ + { insn_st_ctrl, DEC_DECODED}, /* st.c isrc1,csrc2. */ + { insn_pstd, DEC_DECODED}, /* pst.d fdest,#const(isrc2)[++]. */ + { insn_bri, DEC_DECODED}, /* bri isrc1ni. */ + { insn_trap, DEC_DECODED}, /* trap isrc1ni,isrc2,idest. */ + { 0, DEC_MORE}, /* FP ESCAPE FORMAT, more decode. */ + { 0, DEC_MORE}, /* CORE ESCAPE FORMAT, more decode. */ + { insn_btne, DEC_DECODED}, /* btne isrc1,isrc2,sbroff. */ + { insn_btne_imm, DEC_DECODED}, /* btne #const,isrc2,sbroff. */ + { insn_bte, DEC_DECODED}, /* bte isrc1,isrc2,sbroff. */ + { insn_bte_imm, DEC_DECODED}, /* bte #const5,isrc2,idest. */ + { insn_fldy, DEC_DECODED}, /* pfld.{l,d,q} isrc1(isrc2)[++],fdest.*/ + { insn_fldy, DEC_DECODED}, /* pfld.{l,d,q} #const(isrc2)[++],fdest.*/ + { insn_br, DEC_DECODED}, /* br lbroff. */ + { insn_call, DEC_DECODED}, /* call lbroff . */ + { insn_bc, DEC_DECODED}, /* bc lbroff. */ + { insn_bct, DEC_DECODED}, /* bc.t lbroff. */ + { insn_bnc, DEC_DECODED}, /* bnc lbroff. */ + { insn_bnct, DEC_DECODED}, /* bnc.t lbroff. */ + { insn_addu, DEC_DECODED}, /* addu isrc1,isrc2,idest. */ + { insn_addu_imm, DEC_DECODED}, /* addu #const,isrc2,idest. */ + { insn_subu, DEC_DECODED}, /* subu isrc1,isrc2,idest. */ + { insn_subu_imm, DEC_DECODED}, /* subu #const,isrc2,idest. */ + { insn_adds, DEC_DECODED}, /* adds isrc1,isrc2,idest. */ + { insn_adds_imm, DEC_DECODED}, /* adds #const,isrc2,idest. */ + { insn_subs, DEC_DECODED}, /* subs isrc1,isrc2,idest. */ + { insn_subs_imm, DEC_DECODED}, /* subs #const,isrc2,idest. */ + { insn_shl, DEC_DECODED}, /* shl isrc1,isrc2,idest. */ + { insn_shl_imm, DEC_DECODED}, /* shl #const,isrc2,idest. */ + { insn_shr, DEC_DECODED}, /* shr isrc1,isrc2,idest. */ + { insn_shr_imm, DEC_DECODED}, /* shr #const,isrc2,idest. */ + { insn_shrd, DEC_DECODED}, /* shrd isrc1ni,isrc2,idest. */ + { insn_bla, DEC_DECODED}, /* bla isrc1ni,isrc2,sbroff. */ + { insn_shra, DEC_DECODED}, /* shra isrc1,isrc2,idest. */ + { insn_shra_imm, DEC_DECODED}, /* shra #const,isrc2,idest. */ + { insn_and, DEC_DECODED}, /* and isrc1,isrc2,idest. */ + { insn_and_imm, DEC_DECODED}, /* and #const,isrc2,idest. */ + { 0, 0}, + { insn_andh_imm, DEC_DECODED}, /* andh #const,isrc2,idest. */ + { insn_andnot, DEC_DECODED}, /* andnot isrc1,isrc2,idest. */ + { insn_andnot_imm, DEC_DECODED}, /* andnot #const,isrc2,idest. */ + { 0, 0}, + { insn_andnoth_imm, DEC_DECODED}, /* andnoth #const,isrc2,idest. */ + { insn_or, DEC_DECODED}, /* or isrc1,isrc2,idest. */ + { insn_or_imm, DEC_DECODED}, /* or #const,isrc2,idest. */ + { 0, 0}, + { insn_orh_imm, DEC_DECODED}, /* orh #const,isrc2,idest. */ + { insn_xor, DEC_DECODED}, /* xor isrc1,isrc2,idest. */ + { insn_xor_imm, DEC_DECODED}, /* xor #const,isrc2,idest. */ + { 0, 0}, + { insn_xorh_imm, DEC_DECODED}, /* xorh #const,isrc2,idest. */ +}; + + +/* Second-level decode table (i.e., for the 3 core escape opcode bits). */ +static const decode_tbl_t core_esc_decode_tbl[8] = { + { 0, 0}, + { 0, 0}, /* lock (FIXME: unimplemented). */ + { insn_calli, DEC_DECODED}, /* calli isrc1ni. */ + { 0, 0}, + { insn_intovr, DEC_DECODED}, /* intovr. */ + { 0, 0}, + { 0, 0}, + { 0, 0}, /* unlock (FIXME: unimplemented). */ +}; + + +/* Second-level decode table (i.e., for the 7 FP extended opcode bits). */ +static const decode_tbl_t fp_decode_tbl[128] = { + /* Floating point instructions. The least significant 7 bits are + the (extended) opcode and bits 10:7 are P,D,S,R respectively + ([p]ipelined, [d]ual, [s]ource prec., [r]esult prec.). + For some operations, I defer decoding the P,S,R bits to the + emulation routine for them. */ + { insn_dualop, DEC_DECODED}, /* 0x00 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x01 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x02 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x03 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x04 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x05 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x06 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x07 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x08 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x09 pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x0A pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x0B pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x0C pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x0D pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x0E pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x0F pf[m]am */ + { insn_dualop, DEC_DECODED}, /* 0x10 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x11 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x12 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x13 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x14 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x15 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x16 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x17 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x18 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x19 pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x1A pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x1B pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x1C pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x1D pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x1E pf[m]sm */ + { insn_dualop, DEC_DECODED}, /* 0x1F pf[m]sm */ + { insn_fmul, DEC_DECODED}, /* 0x20 [p]fmul */ + { insn_fmlow, DEC_DECODED}, /* 0x21 fmlow.dd */ + { insn_frcp, DEC_DECODED}, /* 0x22 frcp.{ss,sd,dd} */ + { insn_frsqr, DEC_DECODED}, /* 0x23 frsqr.{ss,sd,dd} */ + { insn_fmul, DEC_DECODED}, /* 0x24 pfmul3.dd */ + { 0, 0}, /* 0x25 */ + { 0, 0}, /* 0x26 */ + { 0, 0}, /* 0x27 */ + { 0, 0}, /* 0x28 */ + { 0, 0}, /* 0x29 */ + { 0, 0}, /* 0x2A */ + { 0, 0}, /* 0x2B */ + { 0, 0}, /* 0x2C */ + { 0, 0}, /* 0x2D */ + { 0, 0}, /* 0x2E */ + { 0, 0}, /* 0x2F */ + { insn_fadd_sub, DEC_DECODED}, /* 0x30, [p]fadd.{ss,sd,dd} */ + { insn_fadd_sub, DEC_DECODED}, /* 0x31, [p]fsub.{ss,sd,dd} */ + { 0, 0}, /* 0x32, [p]fix.{ss,sd,dd} FIXME: nyi. */ + { insn_famov, DEC_DECODED}, /* 0x33, [p]famov.{ss,sd,ds,dd} */ + { insn_fcmp, DEC_DECODED}, /* 0x34, pf{gt,le}.{ss,dd} */ + { insn_fcmp, DEC_DECODED}, /* 0x35, pfeq.{ss,dd} */ + { 0, 0}, /* 0x36 */ + { 0, 0}, /* 0x37 */ + { 0, 0}, /* 0x38 */ + { 0, 0}, /* 0x39 */ + { insn_ftrunc, DEC_DECODED}, /* 0x3A, [p]ftrunc.{ss,sd,dd} */ + { 0, 0}, /* 0x3B */ + { 0, 0}, /* 0x3C */ + { 0, 0}, /* 0x3D */ + { 0, 0}, /* 0x3E */ + { 0, 0}, /* 0x3F */ + { insn_fxfr, DEC_DECODED}, /* 0x40, fxfr */ + { 0, 0}, /* 0x41 */ + { 0, 0}, /* 0x42 */ + { 0, 0}, /* 0x43 */ + { 0, 0}, /* 0x44 */ + { 0, 0}, /* 0x45 */ + { 0, 0}, /* 0x46 */ + { 0, 0}, /* 0x47 */ + { 0, 0}, /* 0x48 */ + { insn_fiadd_sub, DEC_DECODED}, /* 0x49, [p]fiadd.{ss,dd} */ + { 0, 0}, /* 0x4A */ + { 0, 0}, /* 0x4B */ + { 0, 0}, /* 0x4C */ + { insn_fiadd_sub, DEC_DECODED}, /* 0x4D, [p]fisub.{ss,dd} */ + { 0, 0}, /* 0x4E */ + { 0, 0}, /* 0x4F */ + { insn_faddp, DEC_DECODED}, /* 0x50, [p]faddp */ + { insn_faddz, DEC_DECODED}, /* 0x51, [p]faddz */ + { 0, 0}, /* 0x52 */ + { 0, 0}, /* 0x53 */ + { 0, 0}, /* 0x54 */ + { 0, 0}, /* 0x55 */ + { 0, 0}, /* 0x56 */ + { insn_fzchk, DEC_DECODED}, /* 0x57, [p]fzchkl */ + { 0, 0}, /* 0x58 */ + { 0, 0}, /* 0x59 */ + { insn_form, DEC_DECODED}, /* 0x5A, [p]form.dd */ + { 0, 0}, /* 0x5B */ + { 0, 0}, /* 0x5C */ + { 0, 0}, /* 0x5D */ + { 0, 0}, /* 0x5E */ + { insn_fzchk, DEC_DECODED}, /* 0x5F, [p]fzchks */ + { 0, 0}, /* 0x60 */ + { 0, 0}, /* 0x61 */ + { 0, 0}, /* 0x62 */ + { 0, 0}, /* 0x63 */ + { 0, 0}, /* 0x64 */ + { 0, 0}, /* 0x65 */ + { 0, 0}, /* 0x66 */ + { 0, 0}, /* 0x67 */ + { 0, 0}, /* 0x68 */ + { 0, 0}, /* 0x69 */ + { 0, 0}, /* 0x6A */ + { 0, 0}, /* 0x6B */ + { 0, 0}, /* 0x6C */ + { 0, 0}, /* 0x6D */ + { 0, 0}, /* 0x6E */ + { 0, 0}, /* 0x6F */ + { 0, 0}, /* 0x70 */ + { 0, 0}, /* 0x71 */ + { 0, 0}, /* 0x72 */ + { 0, 0}, /* 0x73 */ + { 0, 0}, /* 0x74 */ + { 0, 0}, /* 0x75 */ + { 0, 0}, /* 0x76 */ + { 0, 0}, /* 0x77 */ + { 0, 0}, /* 0x78 */ + { 0, 0}, /* 0x79 */ + { 0, 0}, /* 0x7A */ + { 0, 0}, /* 0x7B */ + { 0, 0}, /* 0x7C */ + { 0, 0}, /* 0x7D */ + { 0, 0}, /* 0x7E */ + { 0, 0}, /* 0x7F */ +}; + + +/* + * Main decoder driver. + * insn = instruction at the current PC to execute. + * non_shadow = This insn is not in the shadow of a delayed branch). + */ +static void decode_exec (i860s *cpustate, UINT32 insn, UINT32 non_shadow) +{ + int upper_6bits = (insn >> 26) & 0x3f; + char flags = 0; + int unrecognized = 1; + + if (cpustate->exiting_ifetch) + return; + + if ((upper_6bits == 0x12 || upper_6bits == 0x2c) && insn & 0x0200) + logerror("D-bit seen.\n"); + if (GET_EPSR_BE ()) + logerror("BE-bit high.\n"); + if (GET_DIRBASE_CS8 ()) + logerror("CS8-bit high.\n"); + + flags = decode_tbl[upper_6bits].flags; + if (flags & DEC_DECODED) + { + decode_tbl[upper_6bits].insn_exec (cpustate, insn); + unrecognized = 0; + } + else if (flags & DEC_MORE) + { + if (upper_6bits == 0x12) + { + /* FP instruction format handled here. */ + char fp_flags = fp_decode_tbl[insn & 0x7f].flags; + if (fp_flags & DEC_DECODED) + { + fp_decode_tbl[insn & 0x7f].insn_exec (cpustate, insn); + unrecognized = 0; + } + } + else if (upper_6bits == 0x13) + { + /* Core escape instruction format handled here. */ + char esc_flags = core_esc_decode_tbl[insn & 0x3].flags; + if (esc_flags & DEC_DECODED) + { + core_esc_decode_tbl[insn & 0x3].insn_exec (cpustate, insn); + unrecognized = 0; + } + } + } + + if (unrecognized) + unrecog_opcode (cpustate->pc, insn); + + /* For now, just treat every instruction as taking the same number of + clocks-- a major oversimplification. */ + cpustate->icount -= 9; +} + + +/* Set-up all the default power-on/reset values. */ +void reset_i860 (i860s *cpustate) +{ + int i; + /* On power-up/reset, i860 has values: + PC = 0xffffff00. + Integer registers: r0 = 0, others = undefined. + FP registers: f0:f1 = 0, others undefined. + psr: U = IM = BR = BW = 0; others = undefined. + epsr: IL = WP = PBM = BE = 0; processor type, stepping, and + DCS are proper and read-only; others = undefined. + db: undefined. + dirbase: DPS, BL, ATE = 0 + fir, fsr, KR, KI, MERGE: undefined. (what about T?) + + I$: flushed. + D$: undefined (all modified bits = 0). + TLB: flushed. + + Note that any undefined values are set to 0x55aa55aa patterns to + try to detect defective i860 software. */ + + /* PC is at trap address after reset. */ + cpustate->pc = 0xffffff00; + + /* Set grs and frs to undefined/nonsense values, except r0. */ + for (i = 0; i < 32; i++) + { + set_iregval (i, 0x55aa55aa); + set_fregval_s (cpustate, i, 0.0); + } + set_iregval (0, 0); + set_fregval_s (cpustate, 0, 0.0); + set_fregval_s (cpustate, 1, 0.0); + + /* Set whole psr to 0. This sets the proper bits to 0 as specified + above, and zeroes the undefined bits. */ + cpustate->cregs[CR_PSR] = 0; + + /* Set most of the epsr bits to 0 (as specified above), leaving + undefined as zero as well. Then properly set processor type, + step, and DCS. Type = EPSR[7..0], step = EPSR[12..8], + DCS = EPSR[21..18] (2^[12+dcs] = cache size). + We'll pretend to be stepping D0, since it has the fewest bugs + (and I don't want to emulate the many defects in the earlier + steppings). + Proc type: 1 = XR, 2 = XP (XR has 8KB data cache -> DCS = 1). + Steppings (XR): 3,4,5,6,7 = (B2, C0, B3, C1, D0 respectively). + Steppings (XP): 0, 2, 3, 4 = (A0, B0, B1, B2) (any others?). */ + cpustate->cregs[CR_EPSR] = 0x00040701; + + /* Set DPS, BL, ATE = 0 and the undefined parts also to 0. */ + cpustate->cregs[CR_DIRBASE] = 0x00000000; + + /* Set fir, fsr, KR, KI, MERGE, T to undefined. */ + cpustate->cregs[CR_FIR] = 0xaa55aa55; + cpustate->cregs[CR_FSR] = /* 0xaa55aa55; */ 0; + cpustate->KR.d = 0.0; + cpustate->KI.d = 0.0; + cpustate->T.d = 0.0; + cpustate->merge = 0xaa55aa55; + + cpustate->fir_gets_trap_addr = 0; +} + + + + +/*=================================================================*/ +/* MAME execution hook for i860 emulator. */ +/*=================================================================*/ + +#include "emu.h" + +static CPU_EXECUTE( i860 ) +{ + i860_state_t *cpustate = get_safe_token(device); + + /* Check if the data bus is held by another device, and bail if so. + Also check for reset. */ + if (cpustate->pin_reset) + reset_i860 (cpustate); + if (cpustate->pin_bus_hold) + { + cpustate->icount = 0; + return; + } + + cpustate->exiting_readmem = 0; + cpustate->exiting_ifetch = 0; + + /* Decode and execute loop. */ + while (cpustate->icount > 0) + { + UINT32 savepc = cpustate->pc; + cpustate->pc_updated = 0; + cpustate->pending_trap = 0; + +#if 1 /* Delete me soon, for debugging VC inter-processor synch. */ + if (cpustate->pc == 0xfffc0370 || + cpustate->pc == 0xfffc03a4) + { + fprintf(stderr, "(%s) 0x%08x: snag 0x20000000\n", cpustate->device->tag(), cpustate->pc); + cpustate->single_stepping = 0; + } + else if (cpustate->pc == 0xfffc0384 || + cpustate->pc == 0xfffc03b8) + { + fprintf(stderr, "(%s) 0x%08x: passed 0x20000000\n", cpustate->device->tag(), cpustate->pc); + cpustate->single_stepping = 0; + } +#endif + + savepc = cpustate->pc; + debugger_instruction_hook(cpustate->device, cpustate->pc); + decode_exec (cpustate, ifetch (cpustate, cpustate->pc), 1); + + cpustate->exiting_ifetch = 0; + cpustate->exiting_readmem = 0; + + if (cpustate->pending_trap) + { + /* If we need to trap, change PC to trap address. + Also set supervisor mode, copy U and IM to their + previous versions, clear IM. */ + if ((cpustate->pending_trap & TRAP_WAS_EXTERNAL) || (GET_EPSR_INT () && GET_PSR_IN ())) + { + if (!cpustate->pc_updated) + cpustate->cregs[CR_FIR] = savepc + 4; + else + cpustate->cregs[CR_FIR] = cpustate->pc; + } + else if (cpustate->pending_trap & TRAP_IN_DELAY_SLOT) + { + cpustate->cregs[CR_FIR] = savepc + 4; + } + else + cpustate->cregs[CR_FIR] = savepc; + + cpustate->fir_gets_trap_addr = 1; + SET_PSR_PU (GET_PSR_U ()); + SET_PSR_PIM (GET_PSR_IM ()); + SET_PSR_U (0); + SET_PSR_IM (0); + SET_PSR_DIM (0); + SET_PSR_DS (0); + cpustate->pc = 0xffffff00; + cpustate->pending_trap = 0; + } + else if (!cpustate->pc_updated) + { + /* If the PC wasn't updated by a control flow instruction, just + bump to next sequential instruction. */ + cpustate->pc += 4; + } + + /*if (cpustate->single_stepping) + debugger (cpustate); */ + } +} +/*=================================================================*/ + + + + +#if 0 +/*=================================================================*/ +/* Internal debugger-related stuff. */ + +extern unsigned disasm_i860 (char *buf, unsigned int pc, unsigned int insn); + + +/* Disassemble `len' instructions starting at `addr'. */ +static void disasm (i860s *cpustate, UINT32 addr, int len) +{ + UINT32 insn; + int j; + for (j = 0; j < len; j++) + { + char buf[256]; + UINT32 phys_addr = addr; + if (GET_DIRBASE_ATE ()) + phys_addr = get_address_translation (cpustate, addr, 1 /* is_dataref */, 0 /* is_write */); + + /* Note that we print the incoming (possibly virtual) address as the + PC rather than the translated address. */ + fprintf (stderr, " (%s) 0x%08x: ", cpustate->device->tag(), addr); + insn = cpustate->program->read_dword(phys_addr); +#ifdef HOST_MSB + BYTE_REV32 (insn); +#endif /* HOST_MSB. */ + disasm_i860 (buf, addr, insn); fprintf (stderr, "%s", buf); + fprintf (stderr, "\n"); + addr += 4; +#if 1 + if (cpustate->single_stepping == 1 && has_delay_slot (insn)) + len += 1; +#endif + } +} + + +/* Dump `len' bytes starting at `addr'. */ +static void dbg_db (i860s *cpustate, UINT32 addr, int len) +{ + UINT8 b[16]; + int i; + /* This will always dump a multiple of 16 bytes, even if 'len' isn't. */ + while (len > 0) + { + /* Note that we print the incoming (possibly virtual) address + rather than the translated address. */ + fprintf (stderr, "0x%08x: ", addr); + for (i = 0; i < 16; i++) + { + UINT32 phys_addr = addr; + if (GET_DIRBASE_ATE ()) + phys_addr = get_address_translation (cpustate, addr, 1 /* is_dataref */, 0 /* is_write */); + + b[i] = cpustate->program->read_byte(phys_addr); + fprintf (stderr, "%02x ", b[i]); + addr++; + } + fprintf (stderr, "| "); + for (i = 0; i < 16; i++) + { + if (isprint (b[i])) + fprintf (stderr, "%c", b[i]); + else + fprintf (stderr, "."); + } + fprintf (stderr, "\n"); + len -= 16; + } +} + + +/* A simple internal debugger. */ +void debugger (i860s *cpustate) +{ + char buf[256]; + UINT32 curr_disasm = cpustate->pc; + UINT32 curr_dumpdb = 0; + int c = 0; + + if (cpustate->single_stepping > 1 && cpustate->single_stepping != cpustate->pc) + return; + + buf[0] = 0; + + /* Always disassemble the upcoming instruction when single-stepping. */ + if (cpustate->single_stepping) + { + disasm (cpustate, cpustate->pc, 1); + if (has_delay_slot (2)) + disasm (cpustate, cpustate->pc + 4, 1); + } + else + fprintf (stderr, "\nEmulator: internal debugger started (? for help).\n"); + + fflush (stdin); + + cpustate->single_stepping = 0; + while (!cpustate->single_stepping) + { + fprintf (stderr, "- "); +#if 0 /* Doesn't work on MacOSX BSD flavor. */ + fscanf (stdin, "%s", buf); +#else + while (1) + { + char it = 0; + if (read(STDIN_FILENO, &it, 1) == 1) + { + if (it == '\n') + { + buf[c] = 0; + c = 0; + break; + } + buf[c++] = it; + } + } +#endif + if (buf[0] == 'g') + { + if (buf[1] == '0') + sscanf (buf + 1, "%x", &cpustate->single_stepping); + else + break; + buf[1] = 0; + fprintf (stderr, "go until pc = 0x%08x.\n", + cpustate->single_stepping); + cpustate->single_stepping = 0; /* HACK */ + } + else if (buf[0] == 'r') + dump_state (cpustate); + else if (buf[0] == 'u') + { + if (buf[1] == '0') + sscanf (buf + 1, "%x", &curr_disasm); + disasm (cpustate, curr_disasm, 10); + curr_disasm += 10 * 4; + buf[1] = 0; + } + else if (buf[0] == 'p') + { + if (buf[1] >= '0' && buf[1] <= '4') + dump_pipe (cpustate, buf[1] - 0x30); + buf[1] = 0; + } + else if (buf[0] == 's') + cpustate->single_stepping = 1; + else if (buf[0] == 'l') + ; //cpustate->pc = elf_load(buf + 1); + else if (buf[0] == 'd' && buf[1] == 'b') + { + if (buf[2] == '0') + sscanf (buf + 2, "%x", &curr_dumpdb); + dbg_db (cpustate, curr_dumpdb, 32); + curr_dumpdb += 32; + } + else if (buf[0] == 'x' && buf[1] == '0') + { + UINT32 v; + sscanf (buf + 1, "%x", &v); + if (GET_DIRBASE_ATE ()) + fprintf (stderr, "vma 0x%08x ==> phys 0x%08x\n", v, + get_address_translation (cpustate, v, 1, 0)); + else + fprintf (stderr, "not in virtual address mode.\n"); + } + else if (buf[0] == 'B') + { + ;//cpustate->pc = elf_load("bins/bsd"); + break; + } + else if (buf[0] == '?') + { + fprintf (stderr, " db: dump bytes (db[0xaddress])\n r: dump registers\n s: single-step\n g: go back to emulator (g[0xaddress])\n u: disassemble (u[0xaddress])\n p: dump pipelines (p{0-4} for all, add, mul, load, graphics)\n l: load an ELF binary (lpath)\n x: give virt->phys translation (x{0xaddress})\n"); + } + else + fprintf (stderr, "Bad command '%s'.\n", buf); + } + + /* Less noise when single-stepping. */ + if (cpustate->single_stepping != 1) + fprintf (stderr, "Debugger done, continuing emulation.\n"); +} + +#endif |