Commit | Line | Data |
---|---|---|
87326c78 DD |
1 | /* mem.c --- memory for RL78 simulator. |
2 | ||
32d0add0 | 3 | Copyright (C) 2011-2015 Free Software Foundation, Inc. |
87326c78 DD |
4 | Contributed by Red Hat, Inc. |
5 | ||
6 | This file is part of the GNU simulators. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include "config.h" | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | ||
27 | #include "opcode/rl78.h" | |
28 | #include "mem.h" | |
29 | #include "cpu.h" | |
30 | ||
31 | #define ILLEGAL_OPCODE 0xff | |
32 | ||
33 | int rom_limit = 0x100000; | |
34 | int ram_base = 0xf8000; | |
35 | unsigned char memory[MEM_SIZE]; | |
36 | #define MASK 0xfffff | |
37 | ||
38 | unsigned char initted[MEM_SIZE]; | |
39 | int skip_init = 0; | |
40 | ||
41 | #define tprintf if (trace) printf | |
42 | ||
43 | void | |
44 | init_mem (void) | |
45 | { | |
46 | memset (memory, ILLEGAL_OPCODE, sizeof (memory)); | |
47 | memset (memory + 0xf0000, 0x33, 0x10000); | |
48 | ||
49 | memset (initted, 0, sizeof (initted)); | |
50 | memset (initted + 0xffee0, 1, 0x00120); | |
51 | memset (initted + 0xf0000, 1, 0x01000); | |
52 | } | |
53 | ||
54 | void | |
55 | mem_ram_size (int ram_bytes) | |
56 | { | |
57 | ram_base = 0x100000 - ram_bytes; | |
58 | } | |
59 | ||
60 | void | |
61 | mem_rom_size (int rom_bytes) | |
62 | { | |
63 | rom_limit = rom_bytes; | |
64 | } | |
65 | ||
0952813b DD |
66 | int mirror_rom_base = 0x01000; |
67 | int mirror_ram_base = 0xf1000; | |
68 | int mirror_length = 0x7000; | |
69 | ||
70 | void | |
71 | mem_set_mirror (int rom_base, int ram_base, int length) | |
72 | { | |
73 | mirror_rom_base = rom_base; | |
74 | mirror_ram_base = ram_base; | |
75 | mirror_length = length; | |
76 | } | |
77 | ||
87326c78 DD |
78 | /* ---------------------------------------------------------------------- */ |
79 | /* Note: the RL78 memory map has a few surprises. For starters, part | |
80 | of the first 64k is mapped to the last 64k, depending on an SFR bit | |
81 | and how much RAM the chip has. This is simulated here, as are a | |
82 | few peripherals. */ | |
83 | ||
84 | /* This is stdout. We only care about the data byte, not the upper byte. */ | |
85 | #define SDR00 0xfff10 | |
86 | #define SSR00 0xf0100 | |
87 | #define TS0 0xf01b2 | |
88 | ||
89 | /* RL78/G13 multiply/divide peripheral. */ | |
90 | #define MDUC 0xf00e8 | |
91 | #define MDAL 0xffff0 | |
92 | #define MDAH 0xffff2 | |
69276d02 NC |
93 | #define MDBL 0xffff6 |
94 | #define MDBH 0xffff4 | |
87326c78 DD |
95 | #define MDCL 0xf00e0 |
96 | #define MDCH 0xf00e2 | |
97 | static long long mduc_clock = 0; | |
98 | static int mda_set = 0; | |
99 | #define MDA_SET 15 | |
100 | ||
101 | static int last_addr_was_mirror; | |
102 | ||
103 | static int | |
104 | address_mapping (int address) | |
105 | { | |
106 | address &= MASK; | |
0952813b | 107 | if (address >= mirror_ram_base && address < mirror_ram_base + mirror_length) |
87326c78 | 108 | { |
0952813b | 109 | address = address - mirror_ram_base + mirror_rom_base; |
87326c78 DD |
110 | if (memory[RL78_SFR_PMC] & 1) |
111 | { | |
87326c78 DD |
112 | address |= 0x10000; |
113 | } | |
114 | last_addr_was_mirror = 1; | |
115 | } | |
116 | else | |
117 | last_addr_was_mirror = 0; | |
118 | ||
119 | return address; | |
120 | } | |
121 | ||
122 | static void | |
123 | mem_put_byte (int address, unsigned char value) | |
124 | { | |
125 | address = address_mapping (address); | |
126 | memory [address] = value; | |
127 | initted [address] = 1; | |
128 | if (address == SDR00) | |
129 | { | |
130 | putchar (value); | |
131 | fflush (stdout); | |
132 | } | |
133 | if (address == TS0) | |
134 | { | |
135 | if (timer_enabled == 2) | |
136 | { | |
137 | total_clocks = 0; | |
138 | pending_clocks = 0; | |
139 | memset (counts_per_insn, 0, sizeof (counts_per_insn)); | |
140 | memory[0xf0180] = 0xff; | |
141 | memory[0xf0181] = 0xff; | |
142 | } | |
143 | if (value & 1) | |
144 | timer_enabled = 1; | |
145 | else | |
146 | timer_enabled = 0; | |
147 | } | |
148 | if (address == RL78_SFR_SP && value & 1) | |
149 | { | |
150 | printf ("Warning: SP value 0x%04x truncated at pc=0x%05x\n", value, pc); | |
151 | value &= ~1; | |
152 | } | |
4819f490 NC |
153 | |
154 | if (! g13_multiply) | |
155 | return; | |
156 | ||
87326c78 DD |
157 | if (address == MDUC) |
158 | { | |
159 | if ((value & 0x81) == 0x81) | |
160 | { | |
161 | /* division */ | |
162 | mduc_clock = total_clocks; | |
163 | } | |
164 | } | |
165 | if ((address & ~3) == MDAL) | |
166 | { | |
167 | mda_set |= (1 << (address & 3)); | |
168 | if (mda_set == MDA_SET) | |
169 | { | |
170 | long als, ahs; | |
171 | unsigned long alu, ahu; | |
172 | long rvs; | |
173 | long mdc; | |
174 | unsigned long rvu; | |
175 | mda_set = 0; | |
176 | switch (memory [MDUC] & 0xc8) | |
177 | { | |
178 | case 0x00: | |
179 | alu = mem_get_hi (MDAL); | |
180 | ahu = mem_get_hi (MDAH); | |
181 | rvu = alu * ahu; | |
182 | tprintf ("MDUC: %lu * %lu = %lu\n", alu, ahu, rvu); | |
4819f490 NC |
183 | mem_put_hi (MDBL, rvu & 0xffff); |
184 | mem_put_hi (MDBH, rvu >> 16); | |
87326c78 DD |
185 | break; |
186 | case 0x08: | |
187 | als = sign_ext (mem_get_hi (MDAL), 16); | |
188 | ahs = sign_ext (mem_get_hi (MDAH), 16); | |
189 | rvs = als * ahs; | |
190 | tprintf ("MDUC: %ld * %ld = %ld\n", als, ahs, rvs); | |
4819f490 NC |
191 | mem_put_hi (MDBL, rvs & 0xffff); |
192 | mem_put_hi (MDBH, rvs >> 16); | |
87326c78 DD |
193 | break; |
194 | case 0x40: | |
195 | alu = mem_get_hi (MDAL); | |
196 | ahu = mem_get_hi (MDAH); | |
197 | rvu = alu * ahu; | |
4819f490 NC |
198 | mem_put_hi (MDBL, rvu & 0xffff); |
199 | mem_put_hi (MDBH, rvu >> 16); | |
87326c78 DD |
200 | mdc = mem_get_si (MDCL); |
201 | tprintf ("MDUC: %lu * %lu + %lu = ", alu, ahu, mdc); | |
202 | mdc += (long) rvu; | |
203 | tprintf ("%lu\n", mdc); | |
204 | mem_put_si (MDCL, mdc); | |
205 | break; | |
206 | case 0x48: | |
207 | als = sign_ext (mem_get_hi (MDAL), 16); | |
208 | ahs = sign_ext (mem_get_hi (MDAH), 16); | |
209 | rvs = als * ahs; | |
4819f490 NC |
210 | mem_put_hi (MDBL, rvs & 0xffff); |
211 | mem_put_hi (MDBH, rvs >> 16); | |
87326c78 DD |
212 | mdc = mem_get_si (MDCL); |
213 | tprintf ("MDUC: %ld * %ld + %ld = ", als, ahs, mdc); | |
214 | tprintf ("%ld\n", mdc); | |
215 | mdc += rvs; | |
216 | mem_put_si (MDCL, mdc); | |
217 | break; | |
218 | } | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
223 | extern long long total_clocks; | |
224 | ||
225 | static unsigned char | |
226 | mem_get_byte (int address) | |
227 | { | |
228 | address = address_mapping (address); | |
229 | switch (address) | |
230 | { | |
231 | case SSR00: | |
232 | case SSR00 + 1: | |
233 | return 0x00; | |
234 | case 0xf00f0: | |
235 | return 0; | |
236 | case 0xf0180: | |
237 | case 0xf0181: | |
238 | return memory[address]; | |
239 | ||
240 | case MDUC: | |
241 | { | |
242 | unsigned char mduc = memory [MDUC]; | |
243 | if ((mduc & 0x81) == 0x81 | |
244 | && total_clocks > mduc_clock + 16) | |
245 | { | |
246 | unsigned long a, b, q, r; | |
247 | memory [MDUC] &= 0xfe; | |
248 | a = mem_get_si (MDAL); | |
4819f490 | 249 | b = mem_get_hi (MDBL) | (mem_get_hi (MDBH) << 16); |
87326c78 DD |
250 | if (b == 0) |
251 | { | |
252 | q = ~0; | |
253 | r = ~0; | |
254 | } | |
255 | else | |
256 | { | |
257 | q = a / b; | |
258 | r = a % b; | |
259 | } | |
260 | tprintf ("MDUC: %lu / %lu = q %lu, r %lu\n", a, b, q, r); | |
261 | mem_put_si (MDAL, q); | |
262 | mem_put_si (MDCL, r); | |
263 | } | |
264 | return memory[address]; | |
265 | } | |
266 | case MDCL: | |
267 | case MDCL + 1: | |
268 | case MDCH: | |
269 | case MDCH + 1: | |
270 | return memory[address]; | |
271 | } | |
272 | if (address < 0xf1000 && address >= 0xf0000) | |
273 | { | |
274 | #if 1 | |
275 | /* Note: comment out this return to trap the invalid access | |
276 | instead of returning an "undefined" value. */ | |
277 | return 0x11; | |
278 | #else | |
279 | fprintf (stderr, "SFR access error: addr 0x%05x pc 0x%05x\n", address, pc); | |
280 | exit (1); | |
281 | #endif | |
282 | } | |
283 | #if 0 | |
284 | /* Uncomment this block if you want to trap on reads from unwritten memory. */ | |
285 | if (!skip_init && !initted [address]) | |
286 | { | |
287 | static int uninit_count = 0; | |
288 | fprintf (stderr, "\033[31mwarning :read from uninit addr %05x pc %05x\033[0m\n", address, pc); | |
289 | uninit_count ++; | |
290 | if (uninit_count > 5) | |
291 | exit (1); | |
292 | } | |
293 | #endif | |
294 | return memory [address]; | |
295 | } | |
296 | ||
297 | extern jmp_buf decode_jmp_buf; | |
298 | #define DO_RETURN(x) longjmp (decode_jmp_buf, x) | |
299 | ||
300 | #define CHECK_ALIGNMENT(a,v,m) \ | |
301 | if (a & m) { printf ("Misalignment addr 0x%05x val 0x%04x pc %05x\n", (int)a, (int)v, (int)pc); \ | |
302 | DO_RETURN (RL78_MAKE_HIT_BREAK ()); } | |
303 | ||
304 | /* ---------------------------------------------------------------------- */ | |
305 | #define SPECIAL_ADDR(a) (0xffff0 <= a || (0xffee0 <= a && a < 0xfff00)) | |
306 | ||
307 | void | |
308 | mem_put_qi (int address, unsigned char value) | |
309 | { | |
310 | if (!SPECIAL_ADDR (address)) | |
311 | tprintf ("\033[34m([%05X]<-%02X)\033[0m", address, value); | |
312 | mem_put_byte (address, value); | |
313 | } | |
314 | ||
315 | void | |
316 | mem_put_hi (int address, unsigned short value) | |
317 | { | |
318 | if (!SPECIAL_ADDR (address)) | |
319 | tprintf ("\033[34m([%05X]<-%04X)\033[0m", address, value); | |
320 | CHECK_ALIGNMENT (address, value, 1); | |
321 | if (address > 0xffff8 && address != RL78_SFR_SP) | |
322 | { | |
323 | tprintf ("Word access to 0x%05x!!\n", address); | |
324 | DO_RETURN (RL78_MAKE_HIT_BREAK ()); | |
325 | } | |
326 | mem_put_byte (address, value); | |
327 | mem_put_byte (address + 1, value >> 8); | |
328 | } | |
329 | ||
330 | void | |
331 | mem_put_psi (int address, unsigned long value) | |
332 | { | |
333 | tprintf ("\033[34m([%05X]<-%06lX)\033[0m", address, value); | |
334 | mem_put_byte (address, value); | |
335 | mem_put_byte (address + 1, value >> 8); | |
336 | mem_put_byte (address + 2, value >> 16); | |
337 | } | |
338 | ||
339 | void | |
340 | mem_put_si (int address, unsigned long value) | |
341 | { | |
342 | tprintf ("\033[34m([%05X]<-%08lX)\033[0m", address, value); | |
343 | CHECK_ALIGNMENT (address, value, 3); | |
344 | mem_put_byte (address, value); | |
345 | mem_put_byte (address + 1, value >> 8); | |
346 | mem_put_byte (address + 2, value >> 16); | |
347 | mem_put_byte (address + 3, value >> 24); | |
348 | } | |
349 | ||
350 | void | |
351 | mem_put_blk (int address, const void *bufptr, int nbytes) | |
352 | { | |
353 | const unsigned char *bp = (unsigned char *)bufptr; | |
354 | while (nbytes --) | |
355 | mem_put_byte (address ++, *bp ++); | |
356 | } | |
357 | ||
358 | unsigned char | |
359 | mem_get_pc (int address) | |
360 | { | |
361 | /* Catch obvious problems. */ | |
362 | if (address >= rom_limit && address < 0xf0000) | |
363 | return 0xff; | |
364 | /* This does NOT go through the flash mirror area; you cannot | |
365 | execute out of the mirror. */ | |
366 | return memory [address & MASK]; | |
367 | } | |
368 | ||
369 | unsigned char | |
370 | mem_get_qi (int address) | |
371 | { | |
372 | int v; | |
373 | v = mem_get_byte (address); | |
374 | if (!SPECIAL_ADDR (address)) | |
375 | tprintf ("\033[35m([%05X]->%04X)\033[0m", address, v); | |
376 | if (last_addr_was_mirror) | |
377 | { | |
378 | pending_clocks += 3; | |
379 | tprintf ("ROM read\n"); | |
380 | } | |
381 | return v; | |
382 | } | |
383 | ||
384 | unsigned short | |
385 | mem_get_hi (int address) | |
386 | { | |
387 | int v; | |
388 | v = mem_get_byte (address) | |
389 | | mem_get_byte (address + 1) * 256; | |
390 | CHECK_ALIGNMENT (address, v, 1); | |
391 | if (!SPECIAL_ADDR (address)) | |
392 | tprintf ("\033[35m([%05X]->%04X)\033[0m", address, v); | |
393 | if (last_addr_was_mirror) | |
394 | { | |
395 | pending_clocks += 3; | |
396 | tprintf ("ROM read\n"); | |
397 | } | |
398 | return v; | |
399 | } | |
400 | ||
401 | unsigned long | |
402 | mem_get_psi (int address) | |
403 | { | |
404 | int v; | |
405 | v = mem_get_byte (address) | |
406 | | mem_get_byte (address + 1) * 256 | |
407 | | mem_get_byte (address + 2) * 65536; | |
408 | tprintf ("\033[35m([%05X]->%04X)\033[0m", address, v); | |
409 | return v; | |
410 | } | |
411 | ||
412 | unsigned long | |
413 | mem_get_si (int address) | |
414 | { | |
415 | int v; | |
416 | v = mem_get_byte (address) | |
417 | | mem_get_byte (address + 1) * 256 | |
418 | | mem_get_byte (address + 2) * 65536 | |
419 | | mem_get_byte (address + 2) * 16777216; | |
420 | CHECK_ALIGNMENT (address, v, 3); | |
421 | tprintf ("(\033[35m[%05X]->%04X)\033[0m", address, v); | |
422 | return v; | |
423 | } | |
424 | ||
425 | void | |
426 | mem_get_blk (int address, void *bufptr, int nbytes) | |
427 | { | |
428 | unsigned char *bp = (unsigned char *)bufptr; | |
429 | while (nbytes --) | |
430 | *bp ++ = mem_get_byte (address ++); | |
431 | } | |
432 | ||
433 | int | |
434 | sign_ext (int v, int bits) | |
435 | { | |
436 | if (bits < 8 * sizeof (int)) | |
437 | { | |
438 | v &= (1 << bits) - 1; | |
439 | if (v & (1 << (bits - 1))) | |
440 | v -= (1 << bits); | |
441 | } | |
442 | return v; | |
443 | } |