aboutsummaryrefslogtreecommitdiff
path: root/sim/rl78/mem.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/rl78/mem.c')
-rw-r--r--sim/rl78/mem.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/sim/rl78/mem.c b/sim/rl78/mem.c
new file mode 100644
index 0000000..380a297
--- /dev/null
+++ b/sim/rl78/mem.c
@@ -0,0 +1,426 @@
+/* mem.c --- memory for RL78 simulator.
+
+ Copyright (C) 2011
+ 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/>.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "opcode/rl78.h"
+#include "mem.h"
+#include "cpu.h"
+
+#define ILLEGAL_OPCODE 0xff
+
+int rom_limit = 0x100000;
+int ram_base = 0xf8000;
+unsigned char memory[MEM_SIZE];
+#define MASK 0xfffff
+
+unsigned char initted[MEM_SIZE];
+int skip_init = 0;
+
+#define tprintf if (trace) printf
+
+void
+init_mem (void)
+{
+ memset (memory, ILLEGAL_OPCODE, sizeof (memory));
+ memset (memory + 0xf0000, 0x33, 0x10000);
+
+ memset (initted, 0, sizeof (initted));
+ memset (initted + 0xffee0, 1, 0x00120);
+ memset (initted + 0xf0000, 1, 0x01000);
+}
+
+void
+mem_ram_size (int ram_bytes)
+{
+ ram_base = 0x100000 - ram_bytes;
+}
+
+void
+mem_rom_size (int rom_bytes)
+{
+ rom_limit = rom_bytes;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Note: the RL78 memory map has a few surprises. For starters, part
+ of the first 64k is mapped to the last 64k, depending on an SFR bit
+ and how much RAM the chip has. This is simulated here, as are a
+ few peripherals. */
+
+/* This is stdout. We only care about the data byte, not the upper byte. */
+#define SDR00 0xfff10
+#define SSR00 0xf0100
+#define TS0 0xf01b2
+
+/* RL78/G13 multiply/divide peripheral. */
+#define MDUC 0xf00e8
+#define MDAL 0xffff0
+#define MDAH 0xffff2
+#define MDBL 0xffff4
+#define MDBH 0xffff6
+#define MDCL 0xf00e0
+#define MDCH 0xf00e2
+static long long mduc_clock = 0;
+static int mda_set = 0;
+#define MDA_SET 15
+
+static int last_addr_was_mirror;
+
+static int
+address_mapping (int address)
+{
+ address &= MASK;
+ if (address >= 0xf1000 && address < ram_base)
+ {
+ address &= 0xffff;
+ tprintf ("&");
+ if (memory[RL78_SFR_PMC] & 1)
+ {
+ tprintf ("|");
+ address |= 0x10000;
+ }
+ last_addr_was_mirror = 1;
+ }
+ else
+ last_addr_was_mirror = 0;
+
+ return address;
+}
+
+static void
+mem_put_byte (int address, unsigned char value)
+{
+ address = address_mapping (address);
+ memory [address] = value;
+ initted [address] = 1;
+ if (address == SDR00)
+ {
+ putchar (value);
+ fflush (stdout);
+ }
+ if (address == TS0)
+ {
+ if (timer_enabled == 2)
+ {
+ total_clocks = 0;
+ pending_clocks = 0;
+ memset (counts_per_insn, 0, sizeof (counts_per_insn));
+ memory[0xf0180] = 0xff;
+ memory[0xf0181] = 0xff;
+ }
+ if (value & 1)
+ timer_enabled = 1;
+ else
+ timer_enabled = 0;
+ }
+ if (address == RL78_SFR_SP && value & 1)
+ {
+ printf ("Warning: SP value 0x%04x truncated at pc=0x%05x\n", value, pc);
+ value &= ~1;
+ }
+ if (address == MDUC)
+ {
+ if ((value & 0x81) == 0x81)
+ {
+ /* division */
+ mduc_clock = total_clocks;
+ }
+ }
+ if ((address & ~3) == MDAL)
+ {
+ mda_set |= (1 << (address & 3));
+ if (mda_set == MDA_SET)
+ {
+ long als, ahs;
+ unsigned long alu, ahu;
+ long rvs;
+ long mdc;
+ unsigned long rvu;
+ mda_set = 0;
+ switch (memory [MDUC] & 0xc8)
+ {
+ case 0x00:
+ alu = mem_get_hi (MDAL);
+ ahu = mem_get_hi (MDAH);
+ rvu = alu * ahu;
+ tprintf ("MDUC: %lu * %lu = %lu\n", alu, ahu, rvu);
+ mem_put_si (MDBL, rvu);
+ break;
+ case 0x08:
+ als = sign_ext (mem_get_hi (MDAL), 16);
+ ahs = sign_ext (mem_get_hi (MDAH), 16);
+ rvs = als * ahs;
+ tprintf ("MDUC: %ld * %ld = %ld\n", als, ahs, rvs);
+ mem_put_si (MDBL, rvs);
+ break;
+ case 0x40:
+ alu = mem_get_hi (MDAL);
+ ahu = mem_get_hi (MDAH);
+ rvu = alu * ahu;
+ mem_put_si (MDBL, rvu);
+ mdc = mem_get_si (MDCL);
+ tprintf ("MDUC: %lu * %lu + %lu = ", alu, ahu, mdc);
+ mdc += (long) rvu;
+ tprintf ("%lu\n", mdc);
+ mem_put_si (MDCL, mdc);
+ break;
+ case 0x48:
+ als = sign_ext (mem_get_hi (MDAL), 16);
+ ahs = sign_ext (mem_get_hi (MDAH), 16);
+ rvs = als * ahs;
+ mem_put_si (MDBL, rvs);
+ mdc = mem_get_si (MDCL);
+ tprintf ("MDUC: %ld * %ld + %ld = ", als, ahs, mdc);
+ tprintf ("%ld\n", mdc);
+ mdc += rvs;
+ mem_put_si (MDCL, mdc);
+ break;
+ }
+ }
+ }
+}
+
+extern long long total_clocks;
+
+static unsigned char
+mem_get_byte (int address)
+{
+ address = address_mapping (address);
+ switch (address)
+ {
+ case SSR00:
+ case SSR00 + 1:
+ return 0x00;
+ case 0xf00f0:
+ return 0;
+ case 0xf0180:
+ case 0xf0181:
+ return memory[address];
+
+ case MDUC:
+ {
+ unsigned char mduc = memory [MDUC];
+ if ((mduc & 0x81) == 0x81
+ && total_clocks > mduc_clock + 16)
+ {
+ unsigned long a, b, q, r;
+ memory [MDUC] &= 0xfe;
+ a = mem_get_si (MDAL);
+ b = mem_get_si (MDAL);
+ if (b == 0)
+ {
+ q = ~0;
+ r = ~0;
+ }
+ else
+ {
+ q = a / b;
+ r = a % b;
+ }
+ tprintf ("MDUC: %lu / %lu = q %lu, r %lu\n", a, b, q, r);
+ mem_put_si (MDAL, q);
+ mem_put_si (MDCL, r);
+ }
+ return memory[address];
+ }
+ case MDCL:
+ case MDCL + 1:
+ case MDCH:
+ case MDCH + 1:
+ return memory[address];
+ }
+ if (address < 0xf1000 && address >= 0xf0000)
+ {
+#if 1
+ /* Note: comment out this return to trap the invalid access
+ instead of returning an "undefined" value. */
+ return 0x11;
+#else
+ fprintf (stderr, "SFR access error: addr 0x%05x pc 0x%05x\n", address, pc);
+ exit (1);
+#endif
+ }
+#if 0
+ /* Uncomment this block if you want to trap on reads from unwritten memory. */
+ if (!skip_init && !initted [address])
+ {
+ static int uninit_count = 0;
+ fprintf (stderr, "\033[31mwarning :read from uninit addr %05x pc %05x\033[0m\n", address, pc);
+ uninit_count ++;
+ if (uninit_count > 5)
+ exit (1);
+ }
+#endif
+ return memory [address];
+}
+
+extern jmp_buf decode_jmp_buf;
+#define DO_RETURN(x) longjmp (decode_jmp_buf, x)
+
+#define CHECK_ALIGNMENT(a,v,m) \
+ if (a & m) { printf ("Misalignment addr 0x%05x val 0x%04x pc %05x\n", (int)a, (int)v, (int)pc); \
+ DO_RETURN (RL78_MAKE_HIT_BREAK ()); }
+
+/* ---------------------------------------------------------------------- */
+#define SPECIAL_ADDR(a) (0xffff0 <= a || (0xffee0 <= a && a < 0xfff00))
+
+void
+mem_put_qi (int address, unsigned char value)
+{
+ if (!SPECIAL_ADDR (address))
+ tprintf ("\033[34m([%05X]<-%02X)\033[0m", address, value);
+ mem_put_byte (address, value);
+}
+
+void
+mem_put_hi (int address, unsigned short value)
+{
+ if (!SPECIAL_ADDR (address))
+ tprintf ("\033[34m([%05X]<-%04X)\033[0m", address, value);
+ CHECK_ALIGNMENT (address, value, 1);
+ if (address > 0xffff8 && address != RL78_SFR_SP)
+ {
+ tprintf ("Word access to 0x%05x!!\n", address);
+ DO_RETURN (RL78_MAKE_HIT_BREAK ());
+ }
+ mem_put_byte (address, value);
+ mem_put_byte (address + 1, value >> 8);
+}
+
+void
+mem_put_psi (int address, unsigned long value)
+{
+ tprintf ("\033[34m([%05X]<-%06lX)\033[0m", address, value);
+ mem_put_byte (address, value);
+ mem_put_byte (address + 1, value >> 8);
+ mem_put_byte (address + 2, value >> 16);
+}
+
+void
+mem_put_si (int address, unsigned long value)
+{
+ tprintf ("\033[34m([%05X]<-%08lX)\033[0m", address, value);
+ CHECK_ALIGNMENT (address, value, 3);
+ mem_put_byte (address, value);
+ mem_put_byte (address + 1, value >> 8);
+ mem_put_byte (address + 2, value >> 16);
+ mem_put_byte (address + 3, value >> 24);
+}
+
+void
+mem_put_blk (int address, const void *bufptr, int nbytes)
+{
+ const unsigned char *bp = (unsigned char *)bufptr;
+ while (nbytes --)
+ mem_put_byte (address ++, *bp ++);
+}
+
+unsigned char
+mem_get_pc (int address)
+{
+ /* Catch obvious problems. */
+ if (address >= rom_limit && address < 0xf0000)
+ return 0xff;
+ /* This does NOT go through the flash mirror area; you cannot
+ execute out of the mirror. */
+ return memory [address & MASK];
+}
+
+unsigned char
+mem_get_qi (int address)
+{
+ int v;
+ v = mem_get_byte (address);
+ if (!SPECIAL_ADDR (address))
+ tprintf ("\033[35m([%05X]->%04X)\033[0m", address, v);
+ if (last_addr_was_mirror)
+ {
+ pending_clocks += 3;
+ tprintf ("ROM read\n");
+ }
+ return v;
+}
+
+unsigned short
+mem_get_hi (int address)
+{
+ int v;
+ v = mem_get_byte (address)
+ | mem_get_byte (address + 1) * 256;
+ CHECK_ALIGNMENT (address, v, 1);
+ if (!SPECIAL_ADDR (address))
+ tprintf ("\033[35m([%05X]->%04X)\033[0m", address, v);
+ if (last_addr_was_mirror)
+ {
+ pending_clocks += 3;
+ tprintf ("ROM read\n");
+ }
+ return v;
+}
+
+unsigned long
+mem_get_psi (int address)
+{
+ int v;
+ v = mem_get_byte (address)
+ | mem_get_byte (address + 1) * 256
+ | mem_get_byte (address + 2) * 65536;
+ tprintf ("\033[35m([%05X]->%04X)\033[0m", address, v);
+ return v;
+}
+
+unsigned long
+mem_get_si (int address)
+{
+ int v;
+ v = mem_get_byte (address)
+ | mem_get_byte (address + 1) * 256
+ | mem_get_byte (address + 2) * 65536
+ | mem_get_byte (address + 2) * 16777216;
+ CHECK_ALIGNMENT (address, v, 3);
+ tprintf ("(\033[35m[%05X]->%04X)\033[0m", address, v);
+ return v;
+}
+
+void
+mem_get_blk (int address, void *bufptr, int nbytes)
+{
+ unsigned char *bp = (unsigned char *)bufptr;
+ while (nbytes --)
+ *bp ++ = mem_get_byte (address ++);
+}
+
+int
+sign_ext (int v, int bits)
+{
+ if (bits < 8 * sizeof (int))
+ {
+ v &= (1 << bits) - 1;
+ if (v & (1 << (bits - 1)))
+ v -= (1 << bits);
+ }
+ return v;
+}