/* rl78.c --- opcode semantics for stand-alone RL78 simulator. Copyright (C) 2008-2015 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 . */ #include "config.h" #include #include #include #include #include #include #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; if (size == RL78_Word) { cmask = 0x10000; vmask = 0xffff; amask = 0x100; avmask = 0x0ff; } else { cmask = 0x100; vmask = 0xff; amask = 0x10; avmask = 0x0f; } int 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_data.dpc = pc; opcode_size = rl78_decode_opcode (pc, &opcode, rl78_get_byte, &rl78_data); 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 */ 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); 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 (); }