924 lines
19 KiB
C
924 lines
19 KiB
C
/* rl78.c --- opcode semantics for stand-alone RL78 simulator.
|
|
|
|
Copyright (C) 2008-2022 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 */
|
|
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 ();
|
|
}
|