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