|  | /* This must come before any other includes.  */ | 
|  | #include "defs.h" | 
|  |  | 
|  | #include "sim-main.h" | 
|  | #include "sim-options.h" | 
|  | #include "sim-hw.h" | 
|  |  | 
|  | #include "bfd.h" | 
|  | #include "sim-assert.h" | 
|  | #include "sim-fpu.h" | 
|  | #include "sim-signal.h" | 
|  |  | 
|  | #include "mn10300-sim.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  |  | 
|  | struct _state State; | 
|  |  | 
|  |  | 
|  | /* simulation target board.  NULL=default configuration */ | 
|  | static char* board = NULL; | 
|  |  | 
|  | static DECLARE_OPTION_HANDLER (mn10300_option_handler); | 
|  |  | 
|  | enum { | 
|  | OPTION_BOARD = OPTION_START, | 
|  | }; | 
|  |  | 
|  | static SIM_RC | 
|  | mn10300_option_handler (SIM_DESC sd, | 
|  | sim_cpu *cpu, | 
|  | int opt, | 
|  | char *arg, | 
|  | int is_command) | 
|  | { | 
|  | int cpu_nr; | 
|  | switch (opt) | 
|  | { | 
|  | case OPTION_BOARD: | 
|  | { | 
|  | if (arg) | 
|  | { | 
|  | board = zalloc(strlen(arg) + 1); | 
|  | strcpy(board, arg); | 
|  | } | 
|  | return SIM_RC_OK; | 
|  | } | 
|  | } | 
|  |  | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  | static const OPTION mn10300_options[] = | 
|  | { | 
|  | #define BOARD_AM32 "stdeval1" | 
|  | { {"board", required_argument, NULL, OPTION_BOARD}, | 
|  | '\0', "none" /* rely on compile-time string concatenation for other options */ | 
|  | "|" BOARD_AM32 | 
|  | , "Customize simulation for a particular board.", mn10300_option_handler }, | 
|  |  | 
|  | { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | /* For compatibility */ | 
|  | SIM_DESC simulator; | 
|  |  | 
|  | static sim_cia | 
|  | mn10300_pc_get (sim_cpu *cpu) | 
|  | { | 
|  | return PC; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mn10300_pc_set (sim_cpu *cpu, sim_cia pc) | 
|  | { | 
|  | PC = pc; | 
|  | } | 
|  |  | 
|  | static int mn10300_reg_fetch (SIM_CPU *, int, void *, int); | 
|  | static int mn10300_reg_store (SIM_CPU *, int, const void *, int); | 
|  |  | 
|  | /* These default values correspond to expected usage for the chip.  */ | 
|  |  | 
|  | SIM_DESC | 
|  | sim_open (SIM_OPEN_KIND kind, | 
|  | host_callback *cb, | 
|  | struct bfd *abfd, | 
|  | char * const *argv) | 
|  | { | 
|  | int i; | 
|  | SIM_DESC sd = sim_state_alloc (kind, cb); | 
|  |  | 
|  | SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); | 
|  |  | 
|  | /* Set default options before parsing user options.  */ | 
|  | current_target_byte_order = BFD_ENDIAN_LITTLE; | 
|  |  | 
|  | /* The cpu data is kept in a separately allocated chunk of memory.  */ | 
|  | if (sim_cpu_alloc_all (sd, 0) != SIM_RC_OK) | 
|  | return 0; | 
|  |  | 
|  | /* for compatibility */ | 
|  | simulator = sd; | 
|  |  | 
|  | /* FIXME: should be better way of setting up interrupts.  For | 
|  | moment, only support watchpoints causing a breakpoint (gdb | 
|  | halt). */ | 
|  | STATE_WATCHPOINTS (sd)->interrupt_handler = NULL; | 
|  | STATE_WATCHPOINTS (sd)->interrupt_names = NULL; | 
|  |  | 
|  | if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) | 
|  | return 0; | 
|  | sim_add_option_table (sd, NULL, mn10300_options); | 
|  |  | 
|  | /* Allocate core managed memory */ | 
|  | sim_do_command (sd, "memory region 0,0x100000"); | 
|  | sim_do_command (sd, "memory region 0x40000000,0x200000"); | 
|  |  | 
|  | /* The parser will print an error message for us, so we silently return.  */ | 
|  | if (sim_parse_args (sd, argv) != SIM_RC_OK) | 
|  | { | 
|  | /* Uninstall the modules to avoid memory leaks, | 
|  | file descriptor leaks, etc.  */ | 
|  | sim_module_uninstall (sd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if ( NULL != board | 
|  | && (strcmp(board, BOARD_AM32) == 0 ) ) | 
|  | { | 
|  | /* environment */ | 
|  | STATE_ENVIRONMENT (sd) = OPERATING_ENVIRONMENT; | 
|  |  | 
|  | sim_do_command (sd, "memory region 0x44000000,0x40000"); | 
|  | sim_do_command (sd, "memory region 0x48000000,0x400000"); | 
|  |  | 
|  | /* device support for mn1030002 */ | 
|  | /* interrupt controller */ | 
|  |  | 
|  | sim_hw_parse (sd, "/mn103int@0x34000100/reg 0x34000100 0x7C 0x34000200 0x8 0x34000280 0x8"); | 
|  |  | 
|  | /* DEBUG: NMI input's */ | 
|  | sim_hw_parse (sd, "/glue@0x30000000/reg 0x30000000 12"); | 
|  | sim_hw_parse (sd, "/glue@0x30000000 > int0 nmirq /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30000000 > int1 watchdog /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30000000 > int2 syserr /mn103int"); | 
|  |  | 
|  | /* DEBUG: ACK input */ | 
|  | sim_hw_parse (sd, "/glue@0x30002000/reg 0x30002000 4"); | 
|  | sim_hw_parse (sd, "/glue@0x30002000 > int ack /mn103int"); | 
|  |  | 
|  | /* DEBUG: LEVEL output */ | 
|  | sim_hw_parse (sd, "/glue@0x30004000/reg 0x30004000 8"); | 
|  | sim_hw_parse (sd, "/mn103int > nmi int0 /glue@0x30004000"); | 
|  | sim_hw_parse (sd, "/mn103int > level int1 /glue@0x30004000"); | 
|  |  | 
|  | /* DEBUG: A bunch of interrupt inputs */ | 
|  | sim_hw_parse (sd, "/glue@0x30006000/reg 0x30006000 32"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int0 irq-0 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int1 irq-1 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int2 irq-2 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int3 irq-3 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int4 irq-4 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int5 irq-5 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int6 irq-6 /mn103int"); | 
|  | sim_hw_parse (sd, "/glue@0x30006000 > int7 irq-7 /mn103int"); | 
|  |  | 
|  | /* processor interrupt device */ | 
|  |  | 
|  | /* the device */ | 
|  | sim_hw_parse (sd, "/mn103cpu@0x20000000"); | 
|  | sim_hw_parse (sd, "/mn103cpu@0x20000000/reg 0x20000000 0x42"); | 
|  |  | 
|  | /* DEBUG: ACK output wired upto a glue device */ | 
|  | sim_hw_parse (sd, "/glue@0x20002000"); | 
|  | sim_hw_parse (sd, "/glue@0x20002000/reg 0x20002000 4"); | 
|  | sim_hw_parse (sd, "/mn103cpu > ack int0 /glue@0x20002000"); | 
|  |  | 
|  | /* DEBUG: RESET/NMI/LEVEL wired up to a glue device */ | 
|  | sim_hw_parse (sd, "/glue@0x20004000"); | 
|  | sim_hw_parse (sd, "/glue@0x20004000/reg 0x20004000 12"); | 
|  | sim_hw_parse (sd, "/glue@0x20004000 > int0 reset /mn103cpu"); | 
|  | sim_hw_parse (sd, "/glue@0x20004000 > int1 nmi /mn103cpu"); | 
|  | sim_hw_parse (sd, "/glue@0x20004000 > int2 level /mn103cpu"); | 
|  |  | 
|  | /* REAL: The processor wired up to the real interrupt controller */ | 
|  | sim_hw_parse (sd, "/mn103cpu > ack ack /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103int > level level /mn103cpu"); | 
|  | sim_hw_parse (sd, "/mn103int > nmi nmi /mn103cpu"); | 
|  |  | 
|  |  | 
|  | /* PAL */ | 
|  |  | 
|  | /* the device */ | 
|  | sim_hw_parse (sd, "/pal@0x31000000"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000/reg 0x31000000 64"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000/poll? true"); | 
|  |  | 
|  | /* DEBUG: PAL wired up to a glue device */ | 
|  | sim_hw_parse (sd, "/glue@0x31002000"); | 
|  | sim_hw_parse (sd, "/glue@0x31002000/reg 0x31002000 16"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000 > countdown int0 /glue@0x31002000"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000 > timer int1 /glue@0x31002000"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000 > int int2 /glue@0x31002000"); | 
|  | sim_hw_parse (sd, "/glue@0x31002000 > int0 int3 /glue@0x31002000"); | 
|  | sim_hw_parse (sd, "/glue@0x31002000 > int1 int3 /glue@0x31002000"); | 
|  | sim_hw_parse (sd, "/glue@0x31002000 > int2 int3 /glue@0x31002000"); | 
|  |  | 
|  | /* REAL: The PAL wired up to the real interrupt controller */ | 
|  | sim_hw_parse (sd, "/pal@0x31000000 > countdown irq-0 /mn103int"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000 > timer irq-1 /mn103int"); | 
|  | sim_hw_parse (sd, "/pal@0x31000000 > int irq-2 /mn103int"); | 
|  |  | 
|  | /* 8 and 16 bit timers */ | 
|  | sim_hw_parse (sd, "/mn103tim@0x34001000/reg 0x34001000 36 0x34001080 100 0x34004000 16"); | 
|  |  | 
|  | /* Hook timer interrupts up to interrupt controller */ | 
|  | sim_hw_parse (sd, "/mn103tim > timer-0-underflow timer-0-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-1-underflow timer-1-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-2-underflow timer-2-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-3-underflow timer-3-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-4-underflow timer-4-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-5-underflow timer-5-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-6-underflow timer-6-underflow /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-6-compare-a timer-6-compare-a /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103tim > timer-6-compare-b timer-6-compare-b /mn103int"); | 
|  |  | 
|  |  | 
|  | /* Serial devices 0,1,2 */ | 
|  | sim_hw_parse (sd, "/mn103ser@0x34000800/reg 0x34000800 48"); | 
|  | sim_hw_parse (sd, "/mn103ser@0x34000800/poll? true"); | 
|  |  | 
|  | /* Hook serial interrupts up to interrupt controller */ | 
|  | sim_hw_parse (sd, "/mn103ser > serial-0-receive serial-0-receive /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103ser > serial-0-transmit serial-0-transmit /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103ser > serial-1-receive serial-1-receive /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103ser > serial-1-transmit serial-1-transmit /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103ser > serial-2-receive serial-2-receive /mn103int"); | 
|  | sim_hw_parse (sd, "/mn103ser > serial-2-transmit serial-2-transmit /mn103int"); | 
|  |  | 
|  | sim_hw_parse (sd, "/mn103iop@0x36008000/reg 0x36008000 8 0x36008020 8 0x36008040 0xc 0x36008060 8 0x36008080 8"); | 
|  |  | 
|  | /* Memory control registers */ | 
|  | sim_do_command (sd, "memory region 0x32000020,0x30"); | 
|  | /* Cache control register */ | 
|  | sim_do_command (sd, "memory region 0x20000070,0x4"); | 
|  | /* Cache purge regions */ | 
|  | sim_do_command (sd, "memory region 0x28400000,0x800"); | 
|  | sim_do_command (sd, "memory region 0x28401000,0x800"); | 
|  | /* DMA registers */ | 
|  | sim_do_command (sd, "memory region 0x32000100,0xF"); | 
|  | sim_do_command (sd, "memory region 0x32000200,0xF"); | 
|  | sim_do_command (sd, "memory region 0x32000400,0xF"); | 
|  | sim_do_command (sd, "memory region 0x32000800,0xF"); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (board != NULL) | 
|  | { | 
|  | sim_io_eprintf (sd, "Error: Board `%s' unknown.\n", board); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* check for/establish the a reference program image */ | 
|  | if (sim_analyze_program (sd, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK) | 
|  | { | 
|  | sim_module_uninstall (sd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* establish any remaining configuration options */ | 
|  | if (sim_config (sd) != SIM_RC_OK) | 
|  | { | 
|  | sim_module_uninstall (sd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (sim_post_argv_init (sd) != SIM_RC_OK) | 
|  | { | 
|  | /* Uninstall the modules to avoid memory leaks, | 
|  | file descriptor leaks, etc.  */ | 
|  | sim_module_uninstall (sd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* set machine specific configuration */ | 
|  | /*   STATE_CPU (sd, 0)->psw_mask = (PSW_NP | PSW_EP | PSW_ID | PSW_SAT */ | 
|  | /* 			     | PSW_CY | PSW_OV | PSW_S | PSW_Z); */ | 
|  |  | 
|  | /* CPU specific initialization.  */ | 
|  | for (i = 0; i < MAX_NR_PROCESSORS; ++i) | 
|  | { | 
|  | SIM_CPU *cpu = STATE_CPU (sd, i); | 
|  |  | 
|  | CPU_REG_FETCH (cpu) = mn10300_reg_fetch; | 
|  | CPU_REG_STORE (cpu) = mn10300_reg_store; | 
|  | CPU_PC_FETCH (cpu) = mn10300_pc_get; | 
|  | CPU_PC_STORE (cpu) = mn10300_pc_set; | 
|  | } | 
|  |  | 
|  | return sd; | 
|  | } | 
|  |  | 
|  | SIM_RC | 
|  | sim_create_inferior (SIM_DESC sd, | 
|  | struct bfd *prog_bfd, | 
|  | char * const *argv, | 
|  | char * const *env) | 
|  | { | 
|  | memset (&State, 0, sizeof (State)); | 
|  | if (prog_bfd != NULL) { | 
|  | PC = bfd_get_start_address (prog_bfd); | 
|  | } else { | 
|  | PC = 0; | 
|  | } | 
|  | CPU_PC_SET (STATE_CPU (sd, 0), (uint64_t) PC); | 
|  |  | 
|  | if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_am33_2) | 
|  | PSW |= PSW_FE; | 
|  |  | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  | /* FIXME These would more efficient to use than load_mem/store_mem, | 
|  | but need to be changed to use the memory map.  */ | 
|  |  | 
|  | static int | 
|  | mn10300_reg_fetch (SIM_CPU *cpu, int rn, void *memory, int length) | 
|  | { | 
|  | reg_t reg = State.regs[rn]; | 
|  | uint8_t *a = memory; | 
|  | a[0] = reg; | 
|  | a[1] = reg >> 8; | 
|  | a[2] = reg >> 16; | 
|  | a[3] = reg >> 24; | 
|  | return length; | 
|  | } | 
|  |  | 
|  | static int | 
|  | mn10300_reg_store (SIM_CPU *cpu, int rn, const void *memory, int length) | 
|  | { | 
|  | const uint8_t *a = memory; | 
|  | State.regs[rn] = (a[3] << 24) + (a[2] << 16) + (a[1] << 8) + a[0]; | 
|  | return length; | 
|  | } | 
|  |  | 
|  | void | 
|  | mn10300_core_signal (SIM_DESC sd, | 
|  | sim_cpu *cpu, | 
|  | sim_cia cia, | 
|  | unsigned map, | 
|  | int nr_bytes, | 
|  | address_word addr, | 
|  | transfer_type transfer, | 
|  | sim_core_signals sig) | 
|  | { | 
|  | const char *copy = (transfer == read_transfer ? "read" : "write"); | 
|  | address_word ip = CIA_ADDR (cia); | 
|  |  | 
|  | switch (sig) | 
|  | { | 
|  | case sim_core_unmapped_signal: | 
|  | sim_io_eprintf (sd, "mn10300-core: %d byte %s to unmapped address 0x%lx at 0x%lx\n", | 
|  | nr_bytes, copy, | 
|  | (unsigned long) addr, (unsigned long) ip); | 
|  | program_interrupt(sd, cpu, cia, SIM_SIGSEGV); | 
|  | break; | 
|  |  | 
|  | case sim_core_unaligned_signal: | 
|  | sim_io_eprintf (sd, "mn10300-core: %d byte %s to unaligned address 0x%lx at 0x%lx\n", | 
|  | nr_bytes, copy, | 
|  | (unsigned long) addr, (unsigned long) ip); | 
|  | program_interrupt(sd, cpu, cia, SIM_SIGBUS); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | sim_engine_abort (sd, cpu, cia, | 
|  | "mn10300_core_signal - internal error - bad switch"); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | program_interrupt (SIM_DESC sd, | 
|  | sim_cpu *cpu, | 
|  | sim_cia cia, | 
|  | SIM_SIGNAL sig) | 
|  | { | 
|  | int status; | 
|  | struct hw *device; | 
|  | static int in_interrupt = 0; | 
|  |  | 
|  | #ifdef SIM_CPU_EXCEPTION_TRIGGER | 
|  | SIM_CPU_EXCEPTION_TRIGGER(sd,cpu,cia); | 
|  | #endif | 
|  |  | 
|  | /* avoid infinite recursion */ | 
|  | if (in_interrupt) | 
|  | sim_io_printf (sd, "ERROR: recursion in program_interrupt during software exception dispatch."); | 
|  | else | 
|  | { | 
|  | in_interrupt = 1; | 
|  | /* copy NMI handler code from dv-mn103cpu.c */ | 
|  | store_word (SP - 4, CPU_PC_GET (cpu)); | 
|  | store_half (SP - 8, PSW); | 
|  |  | 
|  | /* Set the SYSEF flag in NMICR by backdoor method.  See | 
|  | dv-mn103int.c:write_icr().  This is necessary because | 
|  | software exceptions are not modelled by actually talking to | 
|  | the interrupt controller, so it cannot set its own SYSEF | 
|  | flag. */ | 
|  | if ((NULL != board) && (strcmp(board, BOARD_AM32) == 0)) | 
|  | store_byte (0x34000103, 0x04); | 
|  | } | 
|  |  | 
|  | PSW &= ~PSW_IE; | 
|  | SP = SP - 8; | 
|  | CPU_PC_SET (cpu, 0x40000008); | 
|  |  | 
|  | in_interrupt = 0; | 
|  | sim_engine_halt(sd, cpu, NULL, cia, sim_stopped, sig); | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | mn10300_cpu_exception_trigger(SIM_DESC sd, sim_cpu* cpu, address_word cia) | 
|  | { | 
|  | ASSERT(cpu != NULL); | 
|  |  | 
|  | if(State.exc_suspended > 0) | 
|  | sim_io_eprintf(sd, "Warning, nested exception triggered (%d)\n", State.exc_suspended); | 
|  |  | 
|  | CPU_PC_SET (cpu, cia); | 
|  | memcpy(State.exc_trigger_regs, State.regs, sizeof(State.exc_trigger_regs)); | 
|  | State.exc_suspended = 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | mn10300_cpu_exception_suspend(SIM_DESC sd, sim_cpu* cpu, int exception) | 
|  | { | 
|  | ASSERT(cpu != NULL); | 
|  |  | 
|  | if(State.exc_suspended > 0) | 
|  | sim_io_eprintf(sd, "Warning, nested exception signal (%d then %d)\n", | 
|  | State.exc_suspended, exception); | 
|  |  | 
|  | memcpy(State.exc_suspend_regs, State.regs, sizeof(State.exc_suspend_regs)); | 
|  | memcpy(State.regs, State.exc_trigger_regs, sizeof(State.regs)); | 
|  | CPU_PC_SET (cpu, PC); /* copy PC back from new State.regs */ | 
|  | State.exc_suspended = exception; | 
|  | } | 
|  |  | 
|  | void | 
|  | mn10300_cpu_exception_resume(SIM_DESC sd, sim_cpu* cpu, int exception) | 
|  | { | 
|  | ASSERT(cpu != NULL); | 
|  |  | 
|  | if(exception == 0 && State.exc_suspended > 0) | 
|  | { | 
|  | #ifndef SIGTRAP | 
|  | # define SIGTRAP 5 | 
|  | #endif | 
|  | if(State.exc_suspended != SIGTRAP) /* warn not for breakpoints */ | 
|  | sim_io_eprintf(sd, "Warning, resuming but ignoring pending exception signal (%d)\n", | 
|  | State.exc_suspended); | 
|  | } | 
|  | else if(exception != 0 && State.exc_suspended > 0) | 
|  | { | 
|  | if(exception != State.exc_suspended) | 
|  | sim_io_eprintf(sd, "Warning, resuming with mismatched exception signal (%d vs %d)\n", | 
|  | State.exc_suspended, exception); | 
|  |  | 
|  | memcpy(State.regs, State.exc_suspend_regs, sizeof(State.regs)); | 
|  | CPU_PC_SET (cpu, PC); /* copy PC back from new State.regs */ | 
|  | } | 
|  | else if(exception != 0 && State.exc_suspended == 0) | 
|  | { | 
|  | sim_io_eprintf(sd, "Warning, ignoring spontanous exception signal (%d)\n", exception); | 
|  | } | 
|  | State.exc_suspended = 0; | 
|  | } | 
|  |  | 
|  | /* This is called when an FP instruction is issued when the FP unit is | 
|  | disabled, i.e., the FE bit of PSW is zero.  It raises interrupt | 
|  | code 0x1c0.  */ | 
|  | void | 
|  | fpu_disabled_exception (SIM_DESC sd, sim_cpu *cpu, sim_cia cia) | 
|  | { | 
|  | sim_io_eprintf(sd, "FPU disabled exception\n"); | 
|  | program_interrupt (sd, cpu, cia, SIM_SIGFPE); | 
|  | } | 
|  |  | 
|  | /* This is called when the FP unit is enabled but one of the | 
|  | unimplemented insns is issued.  It raises interrupt code 0x1c8.  */ | 
|  | void | 
|  | fpu_unimp_exception (SIM_DESC sd, sim_cpu *cpu, sim_cia cia) | 
|  | { | 
|  | sim_io_eprintf(sd, "Unimplemented FPU instruction exception\n"); | 
|  | program_interrupt (sd, cpu, cia, SIM_SIGFPE); | 
|  | } | 
|  |  | 
|  | /* This is called at the end of any FP insns that may have triggered | 
|  | FP exceptions.  If no exception is enabled, it returns immediately. | 
|  | Otherwise, it raises an exception code 0x1d0.  */ | 
|  | void | 
|  | fpu_check_signal_exception (SIM_DESC sd, sim_cpu *cpu, sim_cia cia) | 
|  | { | 
|  | if ((FPCR & EC_MASK) == 0) | 
|  | return; | 
|  |  | 
|  | sim_io_eprintf(sd, "FPU %s%s%s%s%s exception\n", | 
|  | (FPCR & EC_V) ? "V" : "", | 
|  | (FPCR & EC_Z) ? "Z" : "", | 
|  | (FPCR & EC_O) ? "O" : "", | 
|  | (FPCR & EC_U) ? "U" : "", | 
|  | (FPCR & EC_I) ? "I" : ""); | 
|  | program_interrupt (sd, cpu, cia, SIM_SIGFPE); | 
|  | } | 
|  |  | 
|  | /* Convert a 32-bit single-precision FP value in the target platform | 
|  | format to a sim_fpu value.  */ | 
|  | static void | 
|  | reg2val_32 (const void *reg, sim_fpu *val) | 
|  | { | 
|  | FS2FPU (*(reg_t *)reg, *val); | 
|  | } | 
|  |  | 
|  | /* Round the given sim_fpu value to single precision, following the | 
|  | target platform rounding and denormalization conventions.  On | 
|  | AM33/2.0, round_near is the only rounding mode.  */ | 
|  | static int | 
|  | round_32 (sim_fpu *val) | 
|  | { | 
|  | return sim_fpu_round_32 (val, sim_fpu_round_near, sim_fpu_denorm_zero); | 
|  | } | 
|  |  | 
|  | /* Convert a sim_fpu value to the 32-bit single-precision target | 
|  | representation.  */ | 
|  | static void | 
|  | val2reg_32 (const sim_fpu *val, void *reg) | 
|  | { | 
|  | FPU2FS (*val, *(reg_t *)reg); | 
|  | } | 
|  |  | 
|  | /* Define the 32-bit single-precision conversion and rounding uniform | 
|  | interface.  */ | 
|  | const struct fp_prec_t | 
|  | fp_single_prec = { | 
|  | reg2val_32, round_32, val2reg_32 | 
|  | }; | 
|  |  | 
|  | /* Convert a 64-bit double-precision FP value in the target platform | 
|  | format to a sim_fpu value.  */ | 
|  | static void | 
|  | reg2val_64 (const void *reg, sim_fpu *val) | 
|  | { | 
|  | FD2FPU (*(dword *)reg, *val); | 
|  | } | 
|  |  | 
|  | /* Round the given sim_fpu value to double precision, following the | 
|  | target platform rounding and denormalization conventions.  On | 
|  | AM33/2.0, round_near is the only rounding mode.  */ | 
|  | static int | 
|  | round_64 (sim_fpu *val) | 
|  | { | 
|  | return sim_fpu_round_64 (val, sim_fpu_round_near, sim_fpu_denorm_zero); | 
|  | } | 
|  |  | 
|  | /* Convert a sim_fpu value to the 64-bit double-precision target | 
|  | representation.  */ | 
|  | static void | 
|  | val2reg_64 (const sim_fpu *val, void *reg) | 
|  | { | 
|  | FPU2FD (*val, *(dword *)reg); | 
|  | } | 
|  |  | 
|  | /* Define the 64-bit single-precision conversion and rounding uniform | 
|  | interface.  */ | 
|  | const struct fp_prec_t | 
|  | fp_double_prec = { | 
|  | reg2val_64, round_64, val2reg_64 | 
|  | }; | 
|  |  | 
|  | /* Define shortcuts to the uniform interface operations.  */ | 
|  | #define REG2VAL(reg,val) (*ops->reg2val) (reg,val) | 
|  | #define ROUND(val) (*ops->round) (val) | 
|  | #define VAL2REG(val,reg) (*ops->val2reg) (val,reg) | 
|  |  | 
|  | /* Check whether overflow, underflow or inexact exceptions should be | 
|  | raised.  */ | 
|  | static int | 
|  | fpu_status_ok (sim_fpu_status stat) | 
|  | { | 
|  | if ((stat & sim_fpu_status_overflow) | 
|  | && (FPCR & EE_O)) | 
|  | FPCR |= EC_O; | 
|  | else if ((stat & (sim_fpu_status_underflow | sim_fpu_status_denorm)) | 
|  | && (FPCR & EE_U)) | 
|  | FPCR |= EC_U; | 
|  | else if ((stat & (sim_fpu_status_inexact | sim_fpu_status_rounded)) | 
|  | && (FPCR & EE_I)) | 
|  | FPCR |= EC_I; | 
|  | else if (stat & ~ (sim_fpu_status_overflow | 
|  | | sim_fpu_status_underflow | 
|  | | sim_fpu_status_denorm | 
|  | | sim_fpu_status_inexact | 
|  | | sim_fpu_status_rounded)) | 
|  | abort (); | 
|  | else | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit reciprocal square root, signaling FP | 
|  | exceptions when appropriate.  */ | 
|  | void | 
|  | fpu_rsqrt (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in, void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu in, med, out; | 
|  |  | 
|  | REG2VAL (reg_in, &in); | 
|  | ROUND (&in); | 
|  | FPCR &= ~ EC_MASK; | 
|  | switch (sim_fpu_is (&in)) | 
|  | { | 
|  | case SIM_FPU_IS_SNAN: | 
|  | case SIM_FPU_IS_NNUMBER: | 
|  | case SIM_FPU_IS_NINF: | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | break; | 
|  |  | 
|  | case SIM_FPU_IS_QNAN: | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | break; | 
|  |  | 
|  | case SIM_FPU_IS_PINF: | 
|  | VAL2REG (&sim_fpu_zero, reg_out); | 
|  | break; | 
|  |  | 
|  | case SIM_FPU_IS_PNUMBER: | 
|  | { | 
|  | /* Since we don't have a function to compute rsqrt directly, | 
|  | use sqrt and inv.  */ | 
|  | sim_fpu_status stat = 0; | 
|  | stat |= sim_fpu_sqrt (&med, &in); | 
|  | stat |= sim_fpu_inv (&out, &med); | 
|  | stat |= ROUND (&out); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&out, reg_out); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case SIM_FPU_IS_NZERO: | 
|  | case SIM_FPU_IS_PZERO: | 
|  | if (FPCR & EE_Z) | 
|  | FPCR |= EC_Z; | 
|  | else | 
|  | { | 
|  | /* Generate an INF with the same sign.  */ | 
|  | sim_fpu_inv (&out, &in); | 
|  | VAL2REG (&out, reg_out); | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | abort (); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | static inline reg_t | 
|  | cmp2fcc (int res) | 
|  | { | 
|  | switch (res) | 
|  | { | 
|  | case SIM_FPU_IS_SNAN: | 
|  | case SIM_FPU_IS_QNAN: | 
|  | return FCC_U; | 
|  |  | 
|  | case SIM_FPU_IS_NINF: | 
|  | case SIM_FPU_IS_NNUMBER: | 
|  | case SIM_FPU_IS_NDENORM: | 
|  | return FCC_L; | 
|  |  | 
|  | case SIM_FPU_IS_PINF: | 
|  | case SIM_FPU_IS_PNUMBER: | 
|  | case SIM_FPU_IS_PDENORM: | 
|  | return FCC_G; | 
|  |  | 
|  | case SIM_FPU_IS_NZERO: | 
|  | case SIM_FPU_IS_PZERO: | 
|  | return FCC_E; | 
|  |  | 
|  | default: | 
|  | abort (); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP compare, setting the FPCR status and/or | 
|  | exception bits as specified.  */ | 
|  | void | 
|  | fpu_cmp (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, | 
|  | const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m, n; | 
|  |  | 
|  | REG2VAL (reg_in1, &m); | 
|  | REG2VAL (reg_in2, &n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | FPCR &= ~ FCC_MASK; | 
|  | ROUND (&m); | 
|  | ROUND (&n); | 
|  | if (sim_fpu_is_snan (&m) || sim_fpu_is_snan (&n)) | 
|  | { | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | FPCR |= FCC_U; | 
|  | } | 
|  | else | 
|  | FPCR |= cmp2fcc (sim_fpu_cmp (&m, &n)); | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP add, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_add (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m); | 
|  | REG2VAL (reg_in2, &n); | 
|  | ROUND (&m); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is (&m) == SIM_FPU_IS_PINF | 
|  | && sim_fpu_is (&n) == SIM_FPU_IS_NINF) | 
|  | || (sim_fpu_is (&m) == SIM_FPU_IS_NINF | 
|  | && sim_fpu_is (&n) == SIM_FPU_IS_PINF)) | 
|  | { | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_add (&r, &m, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP sub, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_sub (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m); | 
|  | REG2VAL (reg_in2, &n); | 
|  | ROUND (&m); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is (&m) == SIM_FPU_IS_PINF | 
|  | && sim_fpu_is (&n) == SIM_FPU_IS_PINF) | 
|  | || (sim_fpu_is (&m) == SIM_FPU_IS_NINF | 
|  | && sim_fpu_is (&n) == SIM_FPU_IS_NINF)) | 
|  | { | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_sub (&r, &m, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP mul, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_mul (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m); | 
|  | REG2VAL (reg_in2, &n); | 
|  | ROUND (&m); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is_infinity (&m) && sim_fpu_is_zero (&n)) | 
|  | || (sim_fpu_is_zero (&m) && sim_fpu_is_infinity (&n))) | 
|  | { | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_mul (&r, &m, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP div, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_div (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m); | 
|  | REG2VAL (reg_in2, &n); | 
|  | ROUND (&m); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is_infinity (&m) && sim_fpu_is_infinity (&n)) | 
|  | || (sim_fpu_is_zero (&m) && sim_fpu_is_zero (&n))) | 
|  | { | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else if (sim_fpu_is_number (&m) && sim_fpu_is_zero (&n) | 
|  | && (FPCR & EE_Z)) | 
|  | FPCR |= EC_Z; | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_div (&r, &m, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP madd, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_fmadd (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, const void *reg_in3, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m1, m2, m, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m1); | 
|  | REG2VAL (reg_in2, &m2); | 
|  | REG2VAL (reg_in3, &n); | 
|  | ROUND (&m1); | 
|  | ROUND (&m2); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m1) || sim_fpu_is_snan (&m2) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is_infinity (&m1) && sim_fpu_is_zero (&m2)) | 
|  | || (sim_fpu_is_zero (&m1) && sim_fpu_is_infinity (&m2))) | 
|  | { | 
|  | invalid_operands: | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_mul (&m, &m1, &m2); | 
|  |  | 
|  | if (sim_fpu_is_infinity (&m) && sim_fpu_is_infinity (&n) | 
|  | && sim_fpu_sign (&m) != sim_fpu_sign (&n)) | 
|  | goto invalid_operands; | 
|  |  | 
|  | stat |= sim_fpu_add (&r, &m, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP msub, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_fmsub (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, const void *reg_in3, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m1, m2, m, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m1); | 
|  | REG2VAL (reg_in2, &m2); | 
|  | REG2VAL (reg_in3, &n); | 
|  | ROUND (&m1); | 
|  | ROUND (&m2); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m1) || sim_fpu_is_snan (&m2) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is_infinity (&m1) && sim_fpu_is_zero (&m2)) | 
|  | || (sim_fpu_is_zero (&m1) && sim_fpu_is_infinity (&m2))) | 
|  | { | 
|  | invalid_operands: | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_mul (&m, &m1, &m2); | 
|  |  | 
|  | if (sim_fpu_is_infinity (&m) && sim_fpu_is_infinity (&n) | 
|  | && sim_fpu_sign (&m) == sim_fpu_sign (&n)) | 
|  | goto invalid_operands; | 
|  |  | 
|  | stat |= sim_fpu_sub (&r, &m, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP nmadd, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_fnmadd (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, const void *reg_in3, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m1, m2, m, mm, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m1); | 
|  | REG2VAL (reg_in2, &m2); | 
|  | REG2VAL (reg_in3, &n); | 
|  | ROUND (&m1); | 
|  | ROUND (&m2); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m1) || sim_fpu_is_snan (&m2) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is_infinity (&m1) && sim_fpu_is_zero (&m2)) | 
|  | || (sim_fpu_is_zero (&m1) && sim_fpu_is_infinity (&m2))) | 
|  | { | 
|  | invalid_operands: | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_mul (&m, &m1, &m2); | 
|  |  | 
|  | if (sim_fpu_is_infinity (&m) && sim_fpu_is_infinity (&n) | 
|  | && sim_fpu_sign (&m) == sim_fpu_sign (&n)) | 
|  | goto invalid_operands; | 
|  |  | 
|  | stat |= sim_fpu_neg (&mm, &m); | 
|  | stat |= sim_fpu_add (&r, &mm, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } | 
|  |  | 
|  | /* Implement a 32/64 bit FP nmsub, setting FP exception bits when | 
|  | appropriate.  */ | 
|  | void | 
|  | fpu_fnmsub (SIM_DESC sd, sim_cpu *cpu, sim_cia cia, | 
|  | const void *reg_in1, const void *reg_in2, const void *reg_in3, | 
|  | void *reg_out, const struct fp_prec_t *ops) | 
|  | { | 
|  | sim_fpu m1, m2, m, mm, n, r; | 
|  |  | 
|  | REG2VAL (reg_in1, &m1); | 
|  | REG2VAL (reg_in2, &m2); | 
|  | REG2VAL (reg_in3, &n); | 
|  | ROUND (&m1); | 
|  | ROUND (&m2); | 
|  | ROUND (&n); | 
|  | FPCR &= ~ EC_MASK; | 
|  | if (sim_fpu_is_snan (&m1) || sim_fpu_is_snan (&m2) || sim_fpu_is_snan (&n) | 
|  | || (sim_fpu_is_infinity (&m1) && sim_fpu_is_zero (&m2)) | 
|  | || (sim_fpu_is_zero (&m1) && sim_fpu_is_infinity (&m2))) | 
|  | { | 
|  | invalid_operands: | 
|  | if (FPCR & EE_V) | 
|  | FPCR |= EC_V; | 
|  | else | 
|  | VAL2REG (&sim_fpu_qnan, reg_out); | 
|  | } | 
|  | else | 
|  | { | 
|  | sim_fpu_status stat = sim_fpu_mul (&m, &m1, &m2); | 
|  |  | 
|  | if (sim_fpu_is_infinity (&m) && sim_fpu_is_infinity (&n) | 
|  | && sim_fpu_sign (&m) != sim_fpu_sign (&n)) | 
|  | goto invalid_operands; | 
|  |  | 
|  | stat |= sim_fpu_neg (&mm, &m); | 
|  | stat |= sim_fpu_sub (&r, &mm, &n); | 
|  | stat |= ROUND (&r); | 
|  | if (fpu_status_ok (stat)) | 
|  | VAL2REG (&r, reg_out); | 
|  | } | 
|  |  | 
|  | fpu_check_signal_exception (sd, cpu, cia); | 
|  | } |