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