|  | /* rl78.c --- opcode semantics for stand-alone RL78 simulator. | 
|  |  | 
|  | Copyright (C) 2008-2024 Free Software Foundation, Inc. | 
|  | Contributed by Red Hat, Inc. | 
|  |  | 
|  | This file is part of the GNU simulators. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  | */ | 
|  |  | 
|  | /* This must come before any other includes.  */ | 
|  | #include "defs.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <signal.h> | 
|  | #include <setjmp.h> | 
|  | #include <time.h> | 
|  |  | 
|  | #include "opcode/rl78.h" | 
|  | #include "cpu.h" | 
|  | #include "mem.h" | 
|  |  | 
|  | extern int skip_init; | 
|  | static int opcode_pc = 0; | 
|  |  | 
|  | jmp_buf decode_jmp_buf; | 
|  | #define DO_RETURN(x) longjmp (decode_jmp_buf, x) | 
|  |  | 
|  | #define tprintf if (trace) printf | 
|  |  | 
|  | #define WILD_JUMP_CHECK(new_pc)						\ | 
|  | do {									\ | 
|  | if (new_pc == 0 || new_pc > 0xfffff)				\ | 
|  | {									\ | 
|  | pc = opcode_pc;							\ | 
|  | fprintf (stderr, "Wild jump to 0x%x from 0x%x!\n", new_pc, pc); \ | 
|  | DO_RETURN (RL78_MAKE_HIT_BREAK ());				\ | 
|  | }									\ | 
|  | } while (0) | 
|  |  | 
|  | typedef struct { | 
|  | unsigned long dpc; | 
|  | } RL78_Data; | 
|  |  | 
|  | static int | 
|  | rl78_get_byte (void *vdata) | 
|  | { | 
|  | RL78_Data *rl78_data = (RL78_Data *)vdata; | 
|  | int rv = mem_get_pc (rl78_data->dpc); | 
|  | rl78_data->dpc ++; | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | static int | 
|  | op_addr (const RL78_Opcode_Operand *o, int for_data) | 
|  | { | 
|  | int v = o->addend; | 
|  | if (o->reg != RL78_Reg_None) | 
|  | v += get_reg (o->reg); | 
|  | if (o->reg2 != RL78_Reg_None) | 
|  | v += get_reg (o->reg2); | 
|  | if (o->use_es) | 
|  | v |= (get_reg (RL78_Reg_ES) & 0xf) << 16; | 
|  | else if (for_data) | 
|  | v |= 0xf0000; | 
|  | v &= 0xfffff; | 
|  | return v; | 
|  | } | 
|  |  | 
|  | static int | 
|  | get_op (const RL78_Opcode_Decoded *rd, int i, int for_data) | 
|  | { | 
|  | int v, r; | 
|  | const RL78_Opcode_Operand *o = rd->op + i; | 
|  |  | 
|  | switch (o->type) | 
|  | { | 
|  | case RL78_Operand_None: | 
|  | /* condition code does this. */ | 
|  | v = 0; | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_Immediate: | 
|  | tprintf (" #"); | 
|  | v = o->addend; | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_Register: | 
|  | tprintf (" %s=", reg_names[o->reg]); | 
|  | v = get_reg (o->reg); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_Bit: | 
|  | tprintf (" %s.%d=", reg_names[o->reg], o->bit_number); | 
|  | v = get_reg (o->reg); | 
|  | v = (v & (1 << o->bit_number)) ? 1 : 0; | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_Indirect: | 
|  | v = op_addr (o, for_data); | 
|  | tprintf (" [0x%x]=", v); | 
|  | if (rd->size == RL78_Word) | 
|  | v = mem_get_hi (v); | 
|  | else | 
|  | v = mem_get_qi (v); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_BitIndirect: | 
|  | v = op_addr (o, for_data); | 
|  | tprintf (" [0x%x].%d=", v, o->bit_number); | 
|  | v = (mem_get_qi (v) & (1 << o->bit_number)) ? 1 : 0; | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_PreDec: | 
|  | r = get_reg (o->reg); | 
|  | tprintf (" [--%s]", reg_names[o->reg]); | 
|  | if (rd->size == RL78_Word) | 
|  | { | 
|  | r -= 2; | 
|  | v = mem_get_hi (r | 0xf0000); | 
|  | } | 
|  | else | 
|  | { | 
|  | r -= 1; | 
|  | v = mem_get_qi (r | 0xf0000); | 
|  | } | 
|  | set_reg (o->reg, r); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_PostInc: | 
|  | tprintf (" [%s++]", reg_names[o->reg]); | 
|  | r = get_reg (o->reg); | 
|  | if (rd->size == RL78_Word) | 
|  | { | 
|  | v = mem_get_hi (r | 0xf0000); | 
|  | r += 2; | 
|  | } | 
|  | else | 
|  | { | 
|  | v = mem_get_qi (r | 0xf0000); | 
|  | r += 1; | 
|  | } | 
|  | set_reg (o->reg, r); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | abort (); | 
|  | } | 
|  | tprintf ("%d", v); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | static void | 
|  | put_op (const RL78_Opcode_Decoded *rd, int i, int for_data, int v) | 
|  | { | 
|  | int r, a; | 
|  | const RL78_Opcode_Operand *o = rd->op + i; | 
|  |  | 
|  | tprintf (" -> "); | 
|  |  | 
|  | switch (o->type) | 
|  | { | 
|  | case RL78_Operand_Register: | 
|  | tprintf ("%s", reg_names[o->reg]); | 
|  | set_reg (o->reg, v); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_Bit: | 
|  | tprintf ("%s.%d", reg_names[o->reg], o->bit_number); | 
|  | r = get_reg (o->reg); | 
|  | if (v) | 
|  | r |= (1 << o->bit_number); | 
|  | else | 
|  | r &= ~(1 << o->bit_number); | 
|  | set_reg (o->reg, r); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_Indirect: | 
|  | r = op_addr (o, for_data); | 
|  | tprintf ("[0x%x]", r); | 
|  | if (rd->size == RL78_Word) | 
|  | mem_put_hi (r, v); | 
|  | else | 
|  | mem_put_qi (r, v); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_BitIndirect: | 
|  | a = op_addr (o, for_data); | 
|  | tprintf ("[0x%x].%d", a, o->bit_number); | 
|  | r = mem_get_qi (a); | 
|  | if (v) | 
|  | r |= (1 << o->bit_number); | 
|  | else | 
|  | r &= ~(1 << o->bit_number); | 
|  | mem_put_qi (a, r); | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_PreDec: | 
|  | r = get_reg (o->reg); | 
|  | tprintf ("[--%s]", reg_names[o->reg]); | 
|  | if (rd->size == RL78_Word) | 
|  | { | 
|  | r -= 2; | 
|  | set_reg (o->reg, r); | 
|  | mem_put_hi (r | 0xf0000, v); | 
|  | } | 
|  | else | 
|  | { | 
|  | r -= 1; | 
|  | set_reg (o->reg, r); | 
|  | mem_put_qi (r | 0xf0000, v); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case RL78_Operand_PostInc: | 
|  | tprintf ("[%s++]", reg_names[o->reg]); | 
|  | r = get_reg (o->reg); | 
|  | if (rd->size == RL78_Word) | 
|  | { | 
|  | mem_put_hi (r | 0xf0000, v); | 
|  | r += 2; | 
|  | } | 
|  | else | 
|  | { | 
|  | mem_put_qi (r | 0xf0000, v); | 
|  | r += 1; | 
|  | } | 
|  | set_reg (o->reg, r); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | abort (); | 
|  | } | 
|  | tprintf ("\n"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | op_flags (int before, int after, int mask, RL78_Size size) | 
|  | { | 
|  | int vmask, cmask, amask, avmask; | 
|  | int psw; | 
|  |  | 
|  | if (size == RL78_Word) | 
|  | { | 
|  | cmask = 0x10000; | 
|  | vmask = 0xffff; | 
|  | amask = 0x100; | 
|  | avmask = 0x0ff; | 
|  | } | 
|  | else | 
|  | { | 
|  | cmask = 0x100; | 
|  | vmask = 0xff; | 
|  | amask = 0x10; | 
|  | avmask = 0x0f; | 
|  | } | 
|  |  | 
|  | psw = get_reg (RL78_Reg_PSW); | 
|  | psw &= ~mask; | 
|  |  | 
|  | if (mask & RL78_PSW_CY) | 
|  | { | 
|  | if ((after & cmask) != (before & cmask)) | 
|  | psw |= RL78_PSW_CY; | 
|  | } | 
|  | if (mask & RL78_PSW_AC) | 
|  | { | 
|  | if ((after & amask) != (before & amask) | 
|  | && (after & avmask) < (before & avmask)) | 
|  | psw |= RL78_PSW_AC; | 
|  | } | 
|  | if (mask & RL78_PSW_Z) | 
|  | { | 
|  | if (! (after & vmask)) | 
|  | psw |= RL78_PSW_Z; | 
|  | } | 
|  |  | 
|  | set_reg (RL78_Reg_PSW, psw); | 
|  | } | 
|  |  | 
|  | #define FLAGS(before,after) if (opcode.flags) op_flags (before, after, opcode.flags, opcode.size) | 
|  |  | 
|  | #define PD(x) put_op (&opcode, 0, 1, x) | 
|  | #define PS(x) put_op (&opcode, 1, 1, x) | 
|  | #define GD() get_op (&opcode, 0, 1) | 
|  | #define GS() get_op (&opcode, 1, 1) | 
|  |  | 
|  | #define GPC() gpc (&opcode, 0) | 
|  | static int | 
|  | gpc (RL78_Opcode_Decoded *opcode, int idx) | 
|  | { | 
|  | int a = get_op (opcode, 0, 1); | 
|  | if (opcode->op[idx].type == RL78_Operand_Register) | 
|  | a =(a & 0x0ffff) | ((get_reg (RL78_Reg_CS) & 0x0f) << 16); | 
|  | else | 
|  | a &= 0xfffff; | 
|  | return a; | 
|  | } | 
|  |  | 
|  | static int | 
|  | get_carry (void) | 
|  | { | 
|  | return (get_reg (RL78_Reg_PSW) & RL78_PSW_CY) ? 1 : 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_carry (int c) | 
|  | { | 
|  | int p = get_reg (RL78_Reg_PSW); | 
|  | tprintf ("set_carry (%d)\n", c ? 1 : 0); | 
|  | if (c) | 
|  | p |= RL78_PSW_CY; | 
|  | else | 
|  | p &= ~RL78_PSW_CY; | 
|  | set_reg (RL78_Reg_PSW, p); | 
|  | } | 
|  |  | 
|  | /* We simulate timer TM00 in interval mode, no clearing, with | 
|  | interrupts.  I.e. it's a cycle counter.  */ | 
|  |  | 
|  | unsigned int counts_per_insn[0x100000]; | 
|  |  | 
|  | int pending_clocks = 0; | 
|  | long long total_clocks = 0; | 
|  |  | 
|  | #define TCR0	0xf0180 | 
|  | #define	MK1	0xfffe6 | 
|  | static void | 
|  | process_clock_tick (void) | 
|  | { | 
|  | unsigned short cnt; | 
|  | unsigned short ivect; | 
|  | unsigned short mask; | 
|  | unsigned char psw; | 
|  | int save_trace; | 
|  |  | 
|  | save_trace = trace; | 
|  | trace = 0; | 
|  |  | 
|  | pending_clocks ++; | 
|  |  | 
|  | counts_per_insn[opcode_pc] += pending_clocks; | 
|  | total_clocks += pending_clocks; | 
|  |  | 
|  | while (pending_clocks) | 
|  | { | 
|  | pending_clocks --; | 
|  | cnt = mem_get_hi (TCR0); | 
|  | cnt --; | 
|  | mem_put_hi (TCR0, cnt); | 
|  | if (cnt != 0xffff) | 
|  | continue; | 
|  |  | 
|  | /* overflow.  */ | 
|  | psw = get_reg (RL78_Reg_PSW); | 
|  | ivect = mem_get_hi (0x0002c); | 
|  | mask = mem_get_hi (MK1); | 
|  |  | 
|  | if ((psw & RL78_PSW_IE) | 
|  | && (ivect != 0) | 
|  | && !(mask & 0x0010)) | 
|  | { | 
|  | unsigned short sp = get_reg (RL78_Reg_SP); | 
|  | set_reg (RL78_Reg_SP, sp - 4); | 
|  | sp --; | 
|  | mem_put_qi (sp | 0xf0000, psw); | 
|  | sp -= 3; | 
|  | mem_put_psi (sp | 0xf0000, pc); | 
|  | psw &= ~RL78_PSW_IE; | 
|  | set_reg (RL78_Reg_PSW, psw); | 
|  | pc = ivect; | 
|  | /* Spec says 9-14 clocks */ | 
|  | pending_clocks += 9; | 
|  | } | 
|  | } | 
|  |  | 
|  | trace = save_trace; | 
|  | } | 
|  |  | 
|  | void | 
|  | dump_counts_per_insn (const char * filename) | 
|  | { | 
|  | int i; | 
|  | FILE *f; | 
|  | f = fopen (filename, "w"); | 
|  | if (!f) | 
|  | { | 
|  | perror (filename); | 
|  | return; | 
|  | } | 
|  | for (i = 0; i < 0x100000; i ++) | 
|  | { | 
|  | if (counts_per_insn[i]) | 
|  | fprintf (f, "%05x %d\n", i, counts_per_insn[i]); | 
|  | } | 
|  | fclose (f); | 
|  | } | 
|  |  | 
|  | static void | 
|  | CLOCKS (int n) | 
|  | { | 
|  | pending_clocks += n - 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | decode_opcode (void) | 
|  | { | 
|  | RL78_Data rl78_data; | 
|  | RL78_Opcode_Decoded opcode; | 
|  | int opcode_size; | 
|  | int a, b, v, v2; | 
|  | unsigned int u, u2; | 
|  | int obits; | 
|  | RL78_Dis_Isa isa; | 
|  |  | 
|  | isa = (rl78_g10_mode ? RL78_ISA_G10 | 
|  | : g14_multiply ? RL78_ISA_G14 | 
|  | : g13_multiply ? RL78_ISA_G13 | 
|  | : RL78_ISA_DEFAULT); | 
|  |  | 
|  | rl78_data.dpc = pc; | 
|  | opcode_size = rl78_decode_opcode (pc, &opcode, | 
|  | rl78_get_byte, &rl78_data, isa); | 
|  |  | 
|  | opcode_pc = pc; | 
|  | pc += opcode_size; | 
|  |  | 
|  | trace_register_words = opcode.size == RL78_Word ? 1 : 0; | 
|  |  | 
|  | /* Used by shfit/rotate instructions */ | 
|  | obits = opcode.size == RL78_Word ? 16 : 8; | 
|  |  | 
|  | switch (opcode.id) | 
|  | { | 
|  | case RLO_add: | 
|  | tprintf ("ADD: "); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = a + b; | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_addc: | 
|  | tprintf ("ADDC: "); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = a + b + get_carry (); | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_and: | 
|  | tprintf ("AND: "); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = a & b; | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_branch_cond: | 
|  | case RLO_branch_cond_clear: | 
|  | tprintf ("BRANCH_COND: "); | 
|  | if (!condition_true (opcode.op[1].condition, GS ())) | 
|  | { | 
|  | tprintf (" false\n"); | 
|  | if (opcode.op[1].condition == RL78_Condition_T | 
|  | || opcode.op[1].condition == RL78_Condition_F) | 
|  | CLOCKS (3); | 
|  | else | 
|  | CLOCKS (2); | 
|  | break; | 
|  | } | 
|  | if (opcode.id == RLO_branch_cond_clear) | 
|  | PS (0); | 
|  | tprintf (" "); | 
|  | if (opcode.op[1].condition == RL78_Condition_T | 
|  | || opcode.op[1].condition == RL78_Condition_F) | 
|  | CLOCKS (3); /* note: adds two clocks, total 5 clocks */ | 
|  | else | 
|  | CLOCKS (2); /* note: adds one clock, total 4 clocks */ | 
|  | ATTRIBUTE_FALLTHROUGH; | 
|  | case RLO_branch: | 
|  | tprintf ("BRANCH: "); | 
|  | v = GPC (); | 
|  | WILD_JUMP_CHECK (v); | 
|  | pc = v; | 
|  | tprintf (" => 0x%05x\n", pc); | 
|  | CLOCKS (3); | 
|  | break; | 
|  |  | 
|  | case RLO_break: | 
|  | tprintf ("BRK: "); | 
|  | CLOCKS (5); | 
|  | if (rl78_in_gdb) | 
|  | DO_RETURN (RL78_MAKE_HIT_BREAK ()); | 
|  | else | 
|  | DO_RETURN (RL78_MAKE_EXITED (1)); | 
|  | break; | 
|  |  | 
|  | case RLO_call: | 
|  | tprintf ("CALL: "); | 
|  | a = get_reg (RL78_Reg_SP); | 
|  | set_reg (RL78_Reg_SP, a - 4); | 
|  | mem_put_psi ((a - 4) | 0xf0000, pc); | 
|  | v = GPC (); | 
|  | WILD_JUMP_CHECK (v); | 
|  | pc = v; | 
|  | #if 0 | 
|  | /* Enable this code to dump the arguments for each call.  */ | 
|  | if (trace) | 
|  | { | 
|  | int i; | 
|  | skip_init ++; | 
|  | for (i = 0; i < 8; i ++) | 
|  | printf (" %02x", mem_get_qi (0xf0000 | (a + i)) & 0xff); | 
|  | skip_init --; | 
|  | } | 
|  | #endif | 
|  | tprintf ("\n"); | 
|  | CLOCKS (3); | 
|  | break; | 
|  |  | 
|  | case RLO_cmp: | 
|  | tprintf ("CMP: "); | 
|  | a = GD (); | 
|  | b = GS (); | 
|  | v = a - b; | 
|  | FLAGS (b, v); | 
|  | tprintf (" (%d)\n", v); | 
|  | break; | 
|  |  | 
|  | case RLO_divhu: | 
|  | a = get_reg (RL78_Reg_AX); | 
|  | b = get_reg (RL78_Reg_DE); | 
|  | tprintf (" %d / %d = ", a, b); | 
|  | if (b == 0) | 
|  | { | 
|  | tprintf ("%d rem %d\n", 0xffff, a); | 
|  | set_reg (RL78_Reg_AX, 0xffff); | 
|  | set_reg (RL78_Reg_DE, a); | 
|  | } | 
|  | else | 
|  | { | 
|  | v = a / b; | 
|  | a = a % b; | 
|  | tprintf ("%d rem %d\n", v, a); | 
|  | set_reg (RL78_Reg_AX, v); | 
|  | set_reg (RL78_Reg_DE, a); | 
|  | } | 
|  | CLOCKS (9); | 
|  | break; | 
|  |  | 
|  | case RLO_divwu: | 
|  | { | 
|  | unsigned long bcax, hlde, quot, rem; | 
|  | bcax = get_reg (RL78_Reg_AX) + 65536 * get_reg (RL78_Reg_BC); | 
|  | hlde = get_reg (RL78_Reg_DE) + 65536 * get_reg (RL78_Reg_HL); | 
|  |  | 
|  | tprintf (" %lu / %lu = ", bcax, hlde); | 
|  | if (hlde == 0) | 
|  | { | 
|  | tprintf ("%lu rem %lu\n", 0xffffLU, bcax); | 
|  | set_reg (RL78_Reg_AX, 0xffffLU); | 
|  | set_reg (RL78_Reg_BC, 0xffffLU); | 
|  | set_reg (RL78_Reg_DE, bcax); | 
|  | set_reg (RL78_Reg_HL, bcax >> 16); | 
|  | } | 
|  | else | 
|  | { | 
|  | quot = bcax / hlde; | 
|  | rem = bcax % hlde; | 
|  | tprintf ("%lu rem %lu\n", quot, rem); | 
|  | set_reg (RL78_Reg_AX, quot); | 
|  | set_reg (RL78_Reg_BC, quot >> 16); | 
|  | set_reg (RL78_Reg_DE, rem); | 
|  | set_reg (RL78_Reg_HL, rem >> 16); | 
|  | } | 
|  | } | 
|  | CLOCKS (17); | 
|  | break; | 
|  |  | 
|  | case RLO_halt: | 
|  | tprintf ("HALT.\n"); | 
|  | DO_RETURN (RL78_MAKE_EXITED (get_reg (RL78_Reg_A))); | 
|  |  | 
|  | case RLO_mov: | 
|  | tprintf ("MOV: "); | 
|  | a = GS (); | 
|  | FLAGS (a, a); | 
|  | PD (a); | 
|  | break; | 
|  |  | 
|  | #define MACR 0xffff0 | 
|  | case RLO_mach: | 
|  | tprintf ("MACH:"); | 
|  | a = sign_ext (get_reg (RL78_Reg_AX), 16); | 
|  | b = sign_ext (get_reg (RL78_Reg_BC), 16); | 
|  | v = sign_ext (mem_get_si (MACR), 32); | 
|  | tprintf ("%08x %d + %d * %d = ", v, v, a, b); | 
|  | v2 = sign_ext (v + a * b, 32); | 
|  | tprintf ("%08x %d\n", v2, v2); | 
|  | mem_put_si (MACR, v2); | 
|  | a = get_reg (RL78_Reg_PSW); | 
|  | v ^= v2; | 
|  | if (v & (1<<31)) | 
|  | a |= RL78_PSW_CY; | 
|  | else | 
|  | a &= ~RL78_PSW_CY; | 
|  | if (v2 & (1 << 31)) | 
|  | a |= RL78_PSW_AC; | 
|  | else | 
|  | a &= ~RL78_PSW_AC; | 
|  | set_reg (RL78_Reg_PSW, a); | 
|  | CLOCKS (3); | 
|  | break; | 
|  |  | 
|  | case RLO_machu: | 
|  | tprintf ("MACHU:"); | 
|  | a = get_reg (RL78_Reg_AX); | 
|  | b = get_reg (RL78_Reg_BC); | 
|  | u = mem_get_si (MACR); | 
|  | tprintf ("%08x %u + %u * %u = ", u, u, a, b); | 
|  | u2 = (u + (unsigned)a * (unsigned)b) & 0xffffffffUL; | 
|  | tprintf ("%08x %u\n", u2, u2); | 
|  | mem_put_si (MACR, u2); | 
|  | a = get_reg (RL78_Reg_PSW); | 
|  | if (u2 < u) | 
|  | a |= RL78_PSW_CY; | 
|  | else | 
|  | a &= ~RL78_PSW_CY; | 
|  | a &= ~RL78_PSW_AC; | 
|  | set_reg (RL78_Reg_PSW, a); | 
|  | CLOCKS (3); | 
|  | break; | 
|  |  | 
|  | case RLO_mulu: | 
|  | tprintf ("MULU:"); | 
|  | a = get_reg (RL78_Reg_A); | 
|  | b = get_reg (RL78_Reg_X); | 
|  | v = a * b; | 
|  | tprintf (" %d * %d = %d\n", a, b, v); | 
|  | set_reg (RL78_Reg_AX, v); | 
|  | break; | 
|  |  | 
|  | case RLO_mulh: | 
|  | tprintf ("MUL:"); | 
|  | a = sign_ext (get_reg (RL78_Reg_AX), 16); | 
|  | b = sign_ext (get_reg (RL78_Reg_BC), 16); | 
|  | v = a * b; | 
|  | tprintf (" %d * %d = %d\n", a, b, v); | 
|  | set_reg (RL78_Reg_BC, v >> 16); | 
|  | set_reg (RL78_Reg_AX, v); | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_mulhu: | 
|  | tprintf ("MULHU:"); | 
|  | a = get_reg (RL78_Reg_AX); | 
|  | b = get_reg (RL78_Reg_BC); | 
|  | v = a * b; | 
|  | tprintf (" %d * %d = %d\n", a, b, v); | 
|  | set_reg (RL78_Reg_BC, v >> 16); | 
|  | set_reg (RL78_Reg_AX, v); | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_nop: | 
|  | tprintf ("NOP.\n"); | 
|  | break; | 
|  |  | 
|  | case RLO_or: | 
|  | tprintf ("OR:"); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = a | b; | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_ret: | 
|  | tprintf ("RET: "); | 
|  | a = get_reg (RL78_Reg_SP); | 
|  | v = mem_get_psi (a | 0xf0000); | 
|  | WILD_JUMP_CHECK (v); | 
|  | pc = v; | 
|  | set_reg (RL78_Reg_SP, a + 4); | 
|  | #if 0 | 
|  | /* Enable this code to dump the return values for each return.  */ | 
|  | if (trace) | 
|  | { | 
|  | int i; | 
|  | skip_init ++; | 
|  | for (i = 0; i < 8; i ++) | 
|  | printf (" %02x", mem_get_qi (0xffef0 + i) & 0xff); | 
|  | skip_init --; | 
|  | } | 
|  | #endif | 
|  | tprintf ("\n"); | 
|  | CLOCKS (6); | 
|  | break; | 
|  |  | 
|  | case RLO_reti: | 
|  | tprintf ("RETI: "); | 
|  | a = get_reg (RL78_Reg_SP); | 
|  | v = mem_get_psi (a | 0xf0000); | 
|  | WILD_JUMP_CHECK (v); | 
|  | pc = v; | 
|  | b = mem_get_qi ((a + 3) | 0xf0000); | 
|  | set_reg (RL78_Reg_PSW, b); | 
|  | set_reg (RL78_Reg_SP, a + 4); | 
|  | tprintf ("\n"); | 
|  | break; | 
|  |  | 
|  | case RLO_rol: | 
|  | tprintf ("ROL:"); /* d <<= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b << 1; | 
|  | v |= (b >> (obits - 1)) & 1; | 
|  | set_carry ((b >> (obits - 1)) & 1); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_rolc: | 
|  | tprintf ("ROLC:"); /* d <<= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b << 1; | 
|  | v |= get_carry (); | 
|  | set_carry ((b >> (obits - 1)) & 1); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_ror: | 
|  | tprintf ("ROR:"); /* d >>= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b >> 1; | 
|  | v |= (b & 1) << (obits - 1); | 
|  | set_carry (b & 1); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_rorc: | 
|  | tprintf ("RORC:"); /* d >>= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b >> 1; | 
|  | v |= (get_carry () << (obits - 1)); | 
|  | set_carry (b & 1); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_sar: | 
|  | tprintf ("SAR:"); /* d >>= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b >> 1; | 
|  | v |= b & (1 << (obits - 1)); | 
|  | set_carry (b & 1); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_sel: | 
|  | tprintf ("SEL:"); | 
|  | a = GS (); | 
|  | b = get_reg (RL78_Reg_PSW); | 
|  | b &= ~(RL78_PSW_RBS1 | RL78_PSW_RBS0); | 
|  | if (a & 1) | 
|  | b |= RL78_PSW_RBS0; | 
|  | if (a & 2) | 
|  | b |= RL78_PSW_RBS1; | 
|  | set_reg (RL78_Reg_PSW, b); | 
|  | tprintf ("\n"); | 
|  | break; | 
|  |  | 
|  | case RLO_shl: | 
|  | tprintf ("SHL%d:", obits); /* d <<= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b << 1; | 
|  | tprintf ("b = 0x%x & 0x%x\n", b, 1<<(obits - 1)); | 
|  | set_carry (b & (1<<(obits - 1))); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_shr: | 
|  | tprintf ("SHR:"); /* d >>= s */ | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b; | 
|  | while (a --) | 
|  | { | 
|  | v = b >> 1; | 
|  | set_carry (b & 1); | 
|  | b = v; | 
|  | } | 
|  | PD (v); | 
|  | break; | 
|  |  | 
|  | case RLO_skip: | 
|  | tprintf ("SKIP: "); | 
|  | if (!condition_true (opcode.op[1].condition, GS ())) | 
|  | { | 
|  | tprintf (" false\n"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | rl78_data.dpc = pc; | 
|  | opcode_size = rl78_decode_opcode (pc, &opcode, | 
|  | rl78_get_byte, &rl78_data, isa); | 
|  | pc += opcode_size; | 
|  | tprintf (" skipped: %s\n", opcode.syntax); | 
|  | break; | 
|  |  | 
|  | case RLO_stop: | 
|  | tprintf ("STOP.\n"); | 
|  | DO_RETURN (RL78_MAKE_EXITED (get_reg (RL78_Reg_A))); | 
|  | DO_RETURN (RL78_MAKE_HIT_BREAK ()); | 
|  |  | 
|  | case RLO_sub: | 
|  | tprintf ("SUB: "); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b - a; | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | tprintf ("%d (0x%x) - %d (0x%x) = %d (0x%x)\n", b, b, a, a, v, v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_subc: | 
|  | tprintf ("SUBC: "); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = b - a - get_carry (); | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | case RLO_xch: | 
|  | tprintf ("XCH: "); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | PD (a); | 
|  | PS (b); | 
|  | break; | 
|  |  | 
|  | case RLO_xor: | 
|  | tprintf ("XOR:"); | 
|  | a = GS (); | 
|  | b = GD (); | 
|  | v = a ^ b; | 
|  | FLAGS (b, v); | 
|  | PD (v); | 
|  | if (opcode.op[0].type == RL78_Operand_Indirect) | 
|  | CLOCKS (2); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | tprintf ("Unknown opcode?\n"); | 
|  | DO_RETURN (RL78_MAKE_HIT_BREAK ()); | 
|  | } | 
|  |  | 
|  | if (timer_enabled) | 
|  | process_clock_tick (); | 
|  |  | 
|  | return RL78_MAKE_STEPPED (); | 
|  | } |