Commit | Line | Data |
---|---|---|
3346cfda NC |
1 | /* Simulator for TI MSP430 and MSP430X |
2 | ||
88b9d363 | 3 | Copyright (C) 2013-2022 Free Software Foundation, Inc. |
3346cfda NC |
4 | Contributed by Red Hat. |
5 | Based on sim/bfin/bfin-sim.c which was contributed by Analog Devices, Inc. | |
6 | ||
7 | This file is part of simulators. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation; either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
21 | ||
6df01ab8 MF |
22 | /* This must come before any other includes. */ |
23 | #include "defs.h" | |
24 | ||
3346cfda NC |
25 | #include <stdio.h> |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <inttypes.h> | |
c1e768db | 29 | #include <unistd.h> |
3346cfda | 30 | #include <assert.h> |
3346cfda NC |
31 | #include "opcode/msp430-decode.h" |
32 | #include "sim-main.h" | |
1fef66b0 | 33 | #include "sim-signal.h" |
61a0c964 | 34 | #include "sim-syscall.h" |
3346cfda NC |
35 | #include "targ-vals.h" |
36 | ||
3346cfda NC |
37 | static sim_cia |
38 | msp430_pc_fetch (SIM_CPU *cpu) | |
39 | { | |
40 | return cpu->state.regs[0]; | |
41 | } | |
42 | ||
43 | static void | |
44 | msp430_pc_store (SIM_CPU *cpu, sim_cia newpc) | |
45 | { | |
46 | cpu->state.regs[0] = newpc; | |
47 | } | |
48 | ||
3346cfda NC |
49 | static int |
50 | msp430_reg_fetch (SIM_CPU *cpu, int regno, unsigned char *buf, int len) | |
51 | { | |
52 | if (0 <= regno && regno < 16) | |
53 | { | |
54 | if (len == 2) | |
55 | { | |
56 | int val = cpu->state.regs[regno]; | |
57 | buf[0] = val & 0xff; | |
58 | buf[1] = (val >> 8) & 0xff; | |
59 | return 0; | |
60 | } | |
61 | else if (len == 4) | |
62 | { | |
63 | int val = cpu->state.regs[regno]; | |
64 | buf[0] = val & 0xff; | |
65 | buf[1] = (val >> 8) & 0xff; | |
66 | buf[2] = (val >> 16) & 0x0f; /* Registers are only 20 bits wide. */ | |
67 | buf[3] = 0; | |
68 | return 0; | |
69 | } | |
70 | else | |
71 | return -1; | |
72 | } | |
73 | else | |
74 | return -1; | |
75 | } | |
76 | ||
77 | static int | |
78 | msp430_reg_store (SIM_CPU *cpu, int regno, unsigned char *buf, int len) | |
79 | { | |
80 | if (0 <= regno && regno < 16) | |
81 | { | |
82 | if (len == 2) | |
83 | { | |
84 | cpu->state.regs[regno] = (buf[1] << 8) | buf[0]; | |
85 | return len; | |
86 | } | |
87 | ||
88 | if (len == 4) | |
89 | { | |
90 | cpu->state.regs[regno] = ((buf[2] << 16) & 0xf0000) | |
91 | | (buf[1] << 8) | buf[0]; | |
92 | return len; | |
93 | } | |
94 | } | |
95 | ||
96 | return -1; | |
97 | } | |
98 | ||
99 | static inline void | |
100 | msp430_initialize_cpu (SIM_DESC sd, SIM_CPU *cpu) | |
101 | { | |
102 | memset (&cpu->state, 0, sizeof (cpu->state)); | |
103 | } | |
104 | ||
105 | SIM_DESC | |
106 | sim_open (SIM_OPEN_KIND kind, | |
107 | struct host_callback_struct *callback, | |
108 | struct bfd *abfd, | |
2e3d4f4d | 109 | char * const *argv) |
3346cfda NC |
110 | { |
111 | SIM_DESC sd = sim_state_alloc (kind, callback); | |
112 | char c; | |
3346cfda NC |
113 | |
114 | /* Initialise the simulator. */ | |
115 | ||
f9a4d543 MF |
116 | /* Set default options before parsing user options. */ |
117 | current_target_byte_order = BFD_ENDIAN_LITTLE; | |
118 | ||
d5a71b11 | 119 | if (sim_cpu_alloc_all (sd, 1) != SIM_RC_OK) |
3346cfda NC |
120 | { |
121 | sim_state_free (sd); | |
122 | return 0; | |
123 | } | |
124 | ||
125 | if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) | |
126 | { | |
127 | sim_state_free (sd); | |
128 | return 0; | |
129 | } | |
130 | ||
131 | if (sim_parse_args (sd, argv) != SIM_RC_OK) | |
132 | { | |
133 | sim_state_free (sd); | |
134 | return 0; | |
135 | } | |
136 | ||
137 | CPU_PC_FETCH (MSP430_CPU (sd)) = msp430_pc_fetch; | |
138 | CPU_PC_STORE (MSP430_CPU (sd)) = msp430_pc_store; | |
139 | CPU_REG_FETCH (MSP430_CPU (sd)) = msp430_reg_fetch; | |
140 | CPU_REG_STORE (MSP430_CPU (sd)) = msp430_reg_store; | |
141 | ||
10d602c7 NC |
142 | /* Allocate memory if none specified by user. |
143 | Note - these values match the memory regions in the libgloss/msp430/msp430[xl]-sim.ld scripts. */ | |
144 | if (sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, &c, 0x2, 1) == 0) | |
145 | sim_do_commandf (sd, "memory-region 0,0x20"); /* Needed by the GDB testsuite. */ | |
f7584f05 | 146 | if (sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, &c, 0x500, 1) == 0) |
4b48e6d4 | 147 | sim_do_commandf (sd, "memory-region 0x500,0xfac0"); /* RAM and/or ROM */ |
3346cfda | 148 | if (sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, &c, 0xfffe, 1) == 0) |
10d602c7 | 149 | sim_do_commandf (sd, "memory-region 0xffc0,0x40"); /* VECTORS. */ |
3346cfda | 150 | if (sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, &c, 0x10000, 1) == 0) |
10d602c7 NC |
151 | sim_do_commandf (sd, "memory-region 0x10000,0x80000"); /* HIGH FLASH RAM. */ |
152 | if (sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, &c, 0x90000, 1) == 0) | |
153 | sim_do_commandf (sd, "memory-region 0x90000,0x70000"); /* HIGH ROM. */ | |
3346cfda NC |
154 | |
155 | /* Check for/establish the a reference program image. */ | |
156 | if (sim_analyze_program (sd, | |
157 | (STATE_PROG_ARGV (sd) != NULL | |
158 | ? *STATE_PROG_ARGV (sd) | |
159 | : NULL), abfd) != SIM_RC_OK) | |
160 | { | |
161 | sim_state_free (sd); | |
162 | return 0; | |
163 | } | |
164 | ||
3346cfda NC |
165 | /* Establish any remaining configuration options. */ |
166 | if (sim_config (sd) != SIM_RC_OK) | |
167 | { | |
168 | sim_state_free (sd); | |
169 | return 0; | |
170 | } | |
171 | ||
172 | if (sim_post_argv_init (sd) != SIM_RC_OK) | |
173 | { | |
174 | sim_state_free (sd); | |
175 | return 0; | |
176 | } | |
177 | ||
178 | /* CPU specific initialization. */ | |
179 | assert (MAX_NR_PROCESSORS == 1); | |
180 | msp430_initialize_cpu (sd, MSP430_CPU (sd)); | |
181 | ||
5357150c MF |
182 | MSP430_CPU (sd)->state.cio_breakpoint = trace_sym_value (sd, "C$$IO$$"); |
183 | MSP430_CPU (sd)->state.cio_buffer = trace_sym_value (sd, "__CIOBUF__"); | |
402cf053 | 184 | if (MSP430_CPU (sd)->state.cio_buffer == -1) |
5357150c | 185 | MSP430_CPU (sd)->state.cio_buffer = trace_sym_value (sd, "_CIOBUF_"); |
3346cfda NC |
186 | |
187 | return sd; | |
188 | } | |
189 | ||
3346cfda NC |
190 | SIM_RC |
191 | sim_create_inferior (SIM_DESC sd, | |
192 | struct bfd *abfd, | |
2e3d4f4d MF |
193 | char * const *argv, |
194 | char * const *env) | |
3346cfda NC |
195 | { |
196 | unsigned char resetv[2]; | |
197 | int c; | |
198 | int new_pc; | |
199 | ||
7b0278dc | 200 | /* Set the PC to the default reset vector if available. */ |
3346cfda | 201 | c = sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, resetv, 0xfffe, 2); |
3346cfda | 202 | new_pc = resetv[0] + 256 * resetv[1]; |
7b0278dc MF |
203 | |
204 | /* If the reset vector isn't initialized, then use the ELF entry. */ | |
205 | if (abfd != NULL && !new_pc) | |
206 | new_pc = bfd_get_start_address (abfd); | |
207 | ||
3346cfda NC |
208 | sim_pc_set (MSP430_CPU (sd), new_pc); |
209 | msp430_pc_store (MSP430_CPU (sd), new_pc); | |
210 | ||
211 | return SIM_RC_OK; | |
212 | } | |
213 | ||
214 | typedef struct | |
215 | { | |
216 | SIM_DESC sd; | |
217 | int gb_addr; | |
218 | } Get_Byte_Local_Data; | |
219 | ||
220 | static int | |
221 | msp430_getbyte (void *vld) | |
222 | { | |
223 | Get_Byte_Local_Data *ld = (Get_Byte_Local_Data *)vld; | |
224 | char buf[1]; | |
225 | SIM_DESC sd = ld->sd; | |
226 | ||
227 | sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, buf, ld->gb_addr, 1); | |
228 | ld->gb_addr ++; | |
229 | return buf[0]; | |
230 | } | |
231 | ||
232 | #define REG(N) MSP430_CPU (sd)->state.regs[(N)] | |
233 | #define PC REG(MSR_PC) | |
234 | #define SP REG(MSR_SP) | |
235 | #define SR REG(MSR_SR) | |
236 | ||
237 | static const char * | |
238 | register_names[] = | |
239 | { | |
240 | "PC", "SP", "SR", "CG", "R4", "R5", "R6", "R7", "R8", | |
241 | "R9", "R10", "R11", "R12", "R13", "R14", "R15" | |
242 | }; | |
243 | ||
244 | static void | |
245 | trace_reg_put (SIM_DESC sd, int n, unsigned int v) | |
246 | { | |
fa8f87e5 | 247 | TRACE_REGISTER (MSP430_CPU (sd), "PUT: %#x -> %s", v, register_names[n]); |
3346cfda NC |
248 | REG (n) = v; |
249 | } | |
250 | ||
251 | static unsigned int | |
252 | trace_reg_get (SIM_DESC sd, int n) | |
253 | { | |
fa8f87e5 | 254 | TRACE_REGISTER (MSP430_CPU (sd), "GET: %s -> %#x", register_names[n], REG (n)); |
3346cfda NC |
255 | return REG (n); |
256 | } | |
257 | ||
258 | #define REG_PUT(N,V) trace_reg_put (sd, N, V) | |
259 | #define REG_GET(N) trace_reg_get (sd, N) | |
260 | ||
8969934d | 261 | /* Hardware multiply (and accumulate) support. */ |
8969934d NC |
262 | |
263 | static unsigned int | |
264 | zero_ext (unsigned int v, unsigned int bits) | |
265 | { | |
266 | v &= ((1 << bits) - 1); | |
267 | return v; | |
268 | } | |
269 | ||
a7da346e DD |
270 | static signed long long |
271 | sign_ext (signed long long v, unsigned int bits) | |
8969934d | 272 | { |
a7da346e DD |
273 | signed long long sb = 1LL << (bits-1); /* Sign bit. */ |
274 | signed long long mb = (1LL << (bits-1)) - 1LL; /* Mantissa bits. */ | |
8969934d NC |
275 | |
276 | if (v & sb) | |
277 | v = v | ~mb; | |
278 | else | |
279 | v = v & mb; | |
280 | return v; | |
281 | } | |
282 | ||
3346cfda NC |
283 | static int |
284 | get_op (SIM_DESC sd, MSP430_Opcode_Decoded *opc, int n) | |
285 | { | |
286 | MSP430_Opcode_Operand *op = opc->op + n; | |
3819af13 | 287 | int rv = 0; |
3346cfda NC |
288 | int addr; |
289 | unsigned char buf[4]; | |
290 | int incval = 0; | |
291 | ||
292 | switch (op->type) | |
293 | { | |
294 | case MSP430_Operand_Immediate: | |
295 | rv = op->addend; | |
296 | break; | |
297 | case MSP430_Operand_Register: | |
298 | rv = REG_GET (op->reg); | |
299 | break; | |
300 | case MSP430_Operand_Indirect: | |
301 | case MSP430_Operand_Indirect_Postinc: | |
302 | addr = op->addend; | |
303 | if (op->reg != MSR_None) | |
304 | { | |
10d602c7 NC |
305 | int reg = REG_GET (op->reg); |
306 | int sign = opc->ofs_430x ? 20 : 16; | |
307 | ||
308 | /* Index values are signed. */ | |
309 | if (addr & (1 << (sign - 1))) | |
1d19cae7 | 310 | addr |= -(1 << sign); |
10d602c7 | 311 | |
3346cfda | 312 | addr += reg; |
10d602c7 NC |
313 | |
314 | /* For MSP430 instructions the sum is limited to 16 bits if the | |
315 | address in the index register is less than 64k even if we are | |
316 | running on an MSP430X CPU. This is for MSP430 compatibility. */ | |
3346cfda | 317 | if (reg < 0x10000 && ! opc->ofs_430x) |
10d602c7 NC |
318 | { |
319 | if (addr >= 0x10000) | |
320 | fprintf (stderr, " XXX WRAPPING ADDRESS %x on read\n", addr); | |
321 | ||
322 | addr &= 0xffff; | |
323 | } | |
3346cfda NC |
324 | } |
325 | addr &= 0xfffff; | |
326 | switch (opc->size) | |
327 | { | |
328 | case 8: | |
329 | sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, buf, addr, 1); | |
330 | rv = buf[0]; | |
331 | break; | |
332 | case 16: | |
333 | sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, buf, addr, 2); | |
334 | rv = buf[0] | (buf[1] << 8); | |
335 | break; | |
336 | case 20: | |
337 | case 32: | |
338 | sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, buf, addr, 4); | |
339 | rv = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); | |
340 | break; | |
341 | default: | |
342 | assert (! opc->size); | |
343 | break; | |
344 | } | |
345 | #if 0 | |
346 | /* Hack - MSP430X5438 serial port status register. */ | |
347 | if (addr == 0x5dd) | |
348 | rv = 2; | |
349 | #endif | |
f7584f05 NC |
350 | if ((addr >= 0x130 && addr <= 0x15B) |
351 | || (addr >= 0x4C0 && addr <= 0x4EB)) | |
8969934d NC |
352 | { |
353 | switch (addr) | |
354 | { | |
f7584f05 | 355 | case 0x4CA: |
8969934d | 356 | case 0x13A: |
180eb063 | 357 | switch (HWMULT (sd, hwmult_type)) |
aef392c4 NC |
358 | { |
359 | case UNSIGN_MAC_32: | |
180eb063 NC |
360 | case UNSIGN_32: |
361 | rv = zero_ext (HWMULT (sd, hwmult_result), 16); | |
362 | break; | |
10d602c7 | 363 | case SIGN_MAC_32: |
180eb063 NC |
364 | case SIGN_32: |
365 | rv = sign_ext (HWMULT (sd, hwmult_signed_result), 16); | |
366 | break; | |
aef392c4 | 367 | } |
8969934d NC |
368 | break; |
369 | ||
f7584f05 | 370 | case 0x4CC: |
8969934d | 371 | case 0x13C: |
180eb063 | 372 | switch (HWMULT (sd, hwmult_type)) |
8969934d | 373 | { |
aef392c4 | 374 | case UNSIGN_MAC_32: |
8969934d | 375 | case UNSIGN_32: |
180eb063 | 376 | rv = zero_ext (HWMULT (sd, hwmult_result) >> 16, 16); |
8969934d NC |
377 | break; |
378 | ||
aef392c4 | 379 | case SIGN_MAC_32: |
8969934d | 380 | case SIGN_32: |
180eb063 | 381 | rv = sign_ext (HWMULT (sd, hwmult_signed_result) >> 16, 16); |
8969934d NC |
382 | break; |
383 | } | |
384 | break; | |
385 | ||
f7584f05 | 386 | case 0x4CE: |
8969934d | 387 | case 0x13E: |
180eb063 | 388 | switch (HWMULT (sd, hwmult_type)) |
8969934d NC |
389 | { |
390 | case UNSIGN_32: | |
391 | rv = 0; | |
392 | break; | |
393 | case SIGN_32: | |
180eb063 | 394 | rv = HWMULT (sd, hwmult_signed_result) < 0 ? -1 : 0; |
8969934d NC |
395 | break; |
396 | case UNSIGN_MAC_32: | |
397 | rv = 0; /* FIXME: Should be carry of last accumulate. */ | |
398 | break; | |
399 | case SIGN_MAC_32: | |
180eb063 | 400 | rv = HWMULT (sd, hwmult_signed_accumulator) < 0 ? -1 : 0; |
8969934d NC |
401 | break; |
402 | } | |
403 | break; | |
404 | ||
f7584f05 | 405 | case 0x4E4: |
8969934d | 406 | case 0x154: |
180eb063 | 407 | rv = zero_ext (HWMULT (sd, hw32mult_result), 16); |
8969934d NC |
408 | break; |
409 | ||
f7584f05 | 410 | case 0x4E6: |
8969934d | 411 | case 0x156: |
180eb063 | 412 | rv = zero_ext (HWMULT (sd, hw32mult_result) >> 16, 16); |
8969934d NC |
413 | break; |
414 | ||
f7584f05 | 415 | case 0x4E8: |
8969934d | 416 | case 0x158: |
180eb063 | 417 | rv = zero_ext (HWMULT (sd, hw32mult_result) >> 32, 16); |
8969934d NC |
418 | break; |
419 | ||
f7584f05 | 420 | case 0x4EA: |
8969934d | 421 | case 0x15A: |
180eb063 | 422 | switch (HWMULT (sd, hw32mult_type)) |
8969934d | 423 | { |
180eb063 NC |
424 | case UNSIGN_64: rv = zero_ext (HWMULT (sd, hw32mult_result) >> 48, 16); break; |
425 | case SIGN_64: rv = sign_ext (HWMULT (sd, hw32mult_result) >> 48, 16); break; | |
8969934d NC |
426 | } |
427 | break; | |
428 | ||
429 | default: | |
10d602c7 | 430 | fprintf (stderr, "unimplemented HW MULT read from %x!\n", addr); |
8969934d NC |
431 | break; |
432 | } | |
433 | } | |
434 | ||
5b064994 MF |
435 | TRACE_MEMORY (MSP430_CPU (sd), "GET: [%#x].%d -> %#x", addr, opc->size, |
436 | rv); | |
3346cfda | 437 | break; |
10d602c7 | 438 | |
3346cfda NC |
439 | default: |
440 | fprintf (stderr, "invalid operand %d type %d\n", n, op->type); | |
441 | abort (); | |
442 | } | |
443 | ||
444 | switch (opc->size) | |
445 | { | |
446 | case 8: | |
447 | rv &= 0xff; | |
448 | incval = 1; | |
449 | break; | |
450 | case 16: | |
451 | rv &= 0xffff; | |
452 | incval = 2; | |
453 | break; | |
454 | case 20: | |
455 | rv &= 0xfffff; | |
456 | incval = 4; | |
457 | break; | |
458 | case 32: | |
459 | rv &= 0xffffffff; | |
460 | incval = 4; | |
461 | break; | |
462 | } | |
463 | ||
464 | if (op->type == MSP430_Operand_Indirect_Postinc) | |
465 | REG_PUT (op->reg, REG_GET (op->reg) + incval); | |
466 | ||
467 | return rv; | |
468 | } | |
469 | ||
470 | static int | |
471 | put_op (SIM_DESC sd, MSP430_Opcode_Decoded *opc, int n, int val) | |
472 | { | |
473 | MSP430_Opcode_Operand *op = opc->op + n; | |
3819af13 | 474 | int rv = 0; |
3346cfda NC |
475 | int addr; |
476 | unsigned char buf[4]; | |
477 | int incval = 0; | |
478 | ||
479 | switch (opc->size) | |
480 | { | |
481 | case 8: | |
482 | val &= 0xff; | |
483 | break; | |
484 | case 16: | |
485 | val &= 0xffff; | |
486 | break; | |
487 | case 20: | |
488 | val &= 0xfffff; | |
489 | break; | |
490 | case 32: | |
491 | val &= 0xffffffff; | |
492 | break; | |
493 | } | |
494 | ||
495 | switch (op->type) | |
496 | { | |
497 | case MSP430_Operand_Register: | |
498 | REG (op->reg) = val; | |
499 | REG_PUT (op->reg, val); | |
500 | break; | |
501 | case MSP430_Operand_Indirect: | |
502 | case MSP430_Operand_Indirect_Postinc: | |
503 | addr = op->addend; | |
504 | if (op->reg != MSR_None) | |
505 | { | |
10d602c7 NC |
506 | int reg = REG_GET (op->reg); |
507 | int sign = opc->ofs_430x ? 20 : 16; | |
508 | ||
509 | /* Index values are signed. */ | |
510 | if (addr & (1 << (sign - 1))) | |
1d19cae7 | 511 | addr |= -(1 << sign); |
10d602c7 | 512 | |
3346cfda | 513 | addr += reg; |
10d602c7 NC |
514 | |
515 | /* For MSP430 instructions the sum is limited to 16 bits if the | |
516 | address in the index register is less than 64k even if we are | |
517 | running on an MSP430X CPU. This is for MSP430 compatibility. */ | |
518 | if (reg < 0x10000 && ! opc->ofs_430x) | |
519 | { | |
520 | if (addr >= 0x10000) | |
521 | fprintf (stderr, " XXX WRAPPING ADDRESS %x on write\n", addr); | |
522 | ||
523 | addr &= 0xffff; | |
524 | } | |
3346cfda NC |
525 | } |
526 | addr &= 0xfffff; | |
527 | ||
5b064994 MF |
528 | TRACE_MEMORY (MSP430_CPU (sd), "PUT: [%#x].%d <- %#x", addr, opc->size, |
529 | val); | |
3346cfda NC |
530 | #if 0 |
531 | /* Hack - MSP430X5438 serial port transmit register. */ | |
532 | if (addr == 0x5ce) | |
533 | putchar (val); | |
534 | #endif | |
f7584f05 NC |
535 | if ((addr >= 0x130 && addr <= 0x15B) |
536 | || (addr >= 0x4C0 && addr <= 0x4EB)) | |
8969934d NC |
537 | { |
538 | signed int a,b; | |
539 | ||
540 | /* Hardware Multiply emulation. */ | |
541 | assert (opc->size == 16); | |
542 | ||
543 | switch (addr) | |
544 | { | |
f7584f05 NC |
545 | case 0x4C0: |
546 | case 0x130: | |
547 | HWMULT (sd, hwmult_op1) = val; | |
548 | HWMULT (sd, hwmult_type) = UNSIGN_32; | |
549 | break; | |
550 | ||
551 | case 0x4C2: | |
552 | case 0x132: | |
553 | HWMULT (sd, hwmult_op1) = val; | |
554 | HWMULT (sd, hwmult_type) = SIGN_32; | |
555 | break; | |
556 | ||
557 | case 0x4C4: | |
558 | case 0x134: | |
559 | HWMULT (sd, hwmult_op1) = val; | |
560 | HWMULT (sd, hwmult_type) = UNSIGN_MAC_32; | |
561 | break; | |
562 | ||
563 | case 0x4C6: | |
564 | case 0x136: | |
565 | HWMULT (sd, hwmult_op1) = val; | |
566 | HWMULT (sd, hwmult_type) = SIGN_MAC_32; | |
567 | break; | |
8969934d | 568 | |
f7584f05 NC |
569 | case 0x4C8: |
570 | case 0x138: | |
571 | HWMULT (sd, hwmult_op2) = val; | |
180eb063 | 572 | switch (HWMULT (sd, hwmult_type)) |
8969934d NC |
573 | { |
574 | case UNSIGN_32: | |
e8a387fb JL |
575 | a = HWMULT (sd, hwmult_op1); |
576 | b = HWMULT (sd, hwmult_op2); | |
577 | /* For unsigned 32-bit multiplication of 16-bit operands, an | |
578 | explicit cast is required to prevent any implicit | |
579 | sign-extension. */ | |
580 | HWMULT (sd, hwmult_result) = (unsigned32) a * (unsigned32) b; | |
581 | HWMULT (sd, hwmult_signed_result) = a * b; | |
180eb063 | 582 | HWMULT (sd, hwmult_accumulator) = HWMULT (sd, hwmult_signed_accumulator) = 0; |
8969934d NC |
583 | break; |
584 | ||
585 | case SIGN_32: | |
180eb063 NC |
586 | a = sign_ext (HWMULT (sd, hwmult_op1), 16); |
587 | b = sign_ext (HWMULT (sd, hwmult_op2), 16); | |
588 | HWMULT (sd, hwmult_signed_result) = a * b; | |
e8a387fb | 589 | HWMULT (sd, hwmult_result) = (unsigned32) a * (unsigned32) b; |
180eb063 | 590 | HWMULT (sd, hwmult_accumulator) = HWMULT (sd, hwmult_signed_accumulator) = 0; |
8969934d NC |
591 | break; |
592 | ||
593 | case UNSIGN_MAC_32: | |
e8a387fb JL |
594 | a = HWMULT (sd, hwmult_op1); |
595 | b = HWMULT (sd, hwmult_op2); | |
596 | HWMULT (sd, hwmult_accumulator) | |
597 | += (unsigned32) a * (unsigned32) b; | |
598 | HWMULT (sd, hwmult_signed_accumulator) += a * b; | |
180eb063 NC |
599 | HWMULT (sd, hwmult_result) = HWMULT (sd, hwmult_accumulator); |
600 | HWMULT (sd, hwmult_signed_result) = HWMULT (sd, hwmult_signed_accumulator); | |
8969934d NC |
601 | break; |
602 | ||
603 | case SIGN_MAC_32: | |
180eb063 NC |
604 | a = sign_ext (HWMULT (sd, hwmult_op1), 16); |
605 | b = sign_ext (HWMULT (sd, hwmult_op2), 16); | |
e8a387fb JL |
606 | HWMULT (sd, hwmult_accumulator) |
607 | += (unsigned32) a * (unsigned32) b; | |
180eb063 NC |
608 | HWMULT (sd, hwmult_signed_accumulator) += a * b; |
609 | HWMULT (sd, hwmult_result) = HWMULT (sd, hwmult_accumulator); | |
610 | HWMULT (sd, hwmult_signed_result) = HWMULT (sd, hwmult_signed_accumulator); | |
8969934d NC |
611 | break; |
612 | } | |
613 | break; | |
614 | ||
f7584f05 NC |
615 | case 0x4CA: |
616 | case 0x13A: | |
aef392c4 | 617 | /* Copy into LOW result... */ |
180eb063 | 618 | switch (HWMULT (sd, hwmult_type)) |
aef392c4 NC |
619 | { |
620 | case UNSIGN_MAC_32: | |
621 | case UNSIGN_32: | |
180eb063 NC |
622 | HWMULT (sd, hwmult_accumulator) = HWMULT (sd, hwmult_result) = zero_ext (val, 16); |
623 | HWMULT (sd, hwmult_signed_accumulator) = sign_ext (val, 16); | |
aef392c4 NC |
624 | break; |
625 | case SIGN_MAC_32: | |
626 | case SIGN_32: | |
180eb063 NC |
627 | HWMULT (sd, hwmult_signed_accumulator) = HWMULT (sd, hwmult_result) = sign_ext (val, 16); |
628 | HWMULT (sd, hwmult_accumulator) = zero_ext (val, 16); | |
aef392c4 NC |
629 | break; |
630 | } | |
631 | break; | |
632 | ||
f7584f05 | 633 | case 0x4D0: |
180eb063 NC |
634 | case 0x140: |
635 | HWMULT (sd, hw32mult_op1) = val; | |
636 | HWMULT (sd, hw32mult_type) = UNSIGN_64; | |
637 | break; | |
f7584f05 NC |
638 | |
639 | case 0x4D2: | |
180eb063 NC |
640 | case 0x142: |
641 | HWMULT (sd, hw32mult_op1) = (HWMULT (sd, hw32mult_op1) & 0xFFFF) | (val << 16); | |
642 | break; | |
f7584f05 NC |
643 | |
644 | case 0x4D4: | |
180eb063 NC |
645 | case 0x144: |
646 | HWMULT (sd, hw32mult_op1) = val; | |
647 | HWMULT (sd, hw32mult_type) = SIGN_64; | |
648 | break; | |
f7584f05 NC |
649 | |
650 | case 0x4D6: | |
180eb063 NC |
651 | case 0x146: |
652 | HWMULT (sd, hw32mult_op1) = (HWMULT (sd, hw32mult_op1) & 0xFFFF) | (val << 16); | |
653 | break; | |
f7584f05 NC |
654 | |
655 | case 0x4E0: | |
180eb063 NC |
656 | case 0x150: |
657 | HWMULT (sd, hw32mult_op2) = val; | |
658 | break; | |
659 | ||
f7584f05 | 660 | case 0x4E2: |
180eb063 NC |
661 | case 0x152: |
662 | HWMULT (sd, hw32mult_op2) = (HWMULT (sd, hw32mult_op2) & 0xFFFF) | (val << 16); | |
663 | switch (HWMULT (sd, hw32mult_type)) | |
8969934d NC |
664 | { |
665 | case UNSIGN_64: | |
e8a387fb JL |
666 | HWMULT (sd, hw32mult_result) |
667 | = (unsigned64) HWMULT (sd, hw32mult_op1) | |
668 | * (unsigned64) HWMULT (sd, hw32mult_op2); | |
8969934d NC |
669 | break; |
670 | case SIGN_64: | |
e8a387fb JL |
671 | HWMULT (sd, hw32mult_result) |
672 | = sign_ext (HWMULT (sd, hw32mult_op1), 32) | |
180eb063 | 673 | * sign_ext (HWMULT (sd, hw32mult_op2), 32); |
8969934d NC |
674 | break; |
675 | } | |
676 | break; | |
677 | ||
678 | default: | |
679 | fprintf (stderr, "unimplemented HW MULT write to %x!\n", addr); | |
680 | break; | |
681 | } | |
682 | } | |
683 | ||
3346cfda NC |
684 | switch (opc->size) |
685 | { | |
686 | case 8: | |
687 | buf[0] = val; | |
688 | sim_core_write_buffer (sd, MSP430_CPU (sd), write_map, buf, addr, 1); | |
689 | break; | |
690 | case 16: | |
691 | buf[0] = val; | |
692 | buf[1] = val >> 8; | |
693 | sim_core_write_buffer (sd, MSP430_CPU (sd), write_map, buf, addr, 2); | |
694 | break; | |
695 | case 20: | |
696 | case 32: | |
697 | buf[0] = val; | |
698 | buf[1] = val >> 8; | |
699 | buf[2] = val >> 16; | |
700 | buf[3] = val >> 24; | |
701 | sim_core_write_buffer (sd, MSP430_CPU (sd), write_map, buf, addr, 4); | |
702 | break; | |
703 | default: | |
704 | assert (! opc->size); | |
705 | break; | |
706 | } | |
707 | break; | |
708 | default: | |
709 | fprintf (stderr, "invalid operand %d type %d\n", n, op->type); | |
710 | abort (); | |
711 | } | |
712 | ||
713 | switch (opc->size) | |
714 | { | |
715 | case 8: | |
716 | rv &= 0xff; | |
717 | incval = 1; | |
718 | break; | |
719 | case 16: | |
720 | rv &= 0xffff; | |
721 | incval = 2; | |
722 | break; | |
723 | case 20: | |
724 | rv &= 0xfffff; | |
725 | incval = 4; | |
726 | break; | |
727 | case 32: | |
728 | rv &= 0xffffffff; | |
729 | incval = 4; | |
730 | break; | |
731 | } | |
732 | ||
733 | if (op->type == MSP430_Operand_Indirect_Postinc) | |
734 | { | |
735 | int new_val = REG_GET (op->reg) + incval; | |
736 | /* SP is always word-aligned. */ | |
737 | if (op->reg == MSR_SP && (new_val & 1)) | |
738 | new_val ++; | |
739 | REG_PUT (op->reg, new_val); | |
740 | } | |
741 | ||
742 | return rv; | |
743 | } | |
744 | ||
745 | static void | |
746 | mem_put_val (SIM_DESC sd, int addr, int val, int bits) | |
747 | { | |
748 | MSP430_Opcode_Decoded opc; | |
749 | ||
750 | opc.size = bits; | |
751 | opc.op[0].type = MSP430_Operand_Indirect; | |
752 | opc.op[0].addend = addr; | |
753 | opc.op[0].reg = MSR_None; | |
754 | put_op (sd, &opc, 0, val); | |
755 | } | |
756 | ||
757 | static int | |
758 | mem_get_val (SIM_DESC sd, int addr, int bits) | |
759 | { | |
760 | MSP430_Opcode_Decoded opc; | |
761 | ||
762 | opc.size = bits; | |
763 | opc.op[0].type = MSP430_Operand_Indirect; | |
764 | opc.op[0].addend = addr; | |
765 | opc.op[0].reg = MSR_None; | |
766 | return get_op (sd, &opc, 0); | |
767 | } | |
768 | ||
769 | #define CIO_OPEN (0xF0) | |
770 | #define CIO_CLOSE (0xF1) | |
771 | #define CIO_READ (0xF2) | |
772 | #define CIO_WRITE (0xF3) | |
773 | #define CIO_LSEEK (0xF4) | |
774 | #define CIO_UNLINK (0xF5) | |
775 | #define CIO_GETENV (0xF6) | |
776 | #define CIO_RENAME (0xF7) | |
777 | #define CIO_GETTIME (0xF8) | |
778 | #define CIO_GETCLK (0xF9) | |
779 | #define CIO_SYNC (0xFF) | |
780 | ||
781 | #define CIO_I(n) (parms[(n)] + parms[(n)+1] * 256) | |
782 | #define CIO_L(n) (parms[(n)] + parms[(n)+1] * 256 \ | |
783 | + parms[(n)+2] * 65536 + parms[(n)+3] * 16777216) | |
784 | ||
785 | static void | |
786 | msp430_cio (SIM_DESC sd) | |
787 | { | |
788 | /* A block of data at __CIOBUF__ describes the I/O operation to | |
789 | perform. */ | |
790 | ||
791 | unsigned char raw_parms[13]; | |
792 | unsigned char parms[8]; | |
793 | long length; | |
794 | int command; | |
795 | unsigned char buffer[512]; | |
796 | long ret_buflen = 0; | |
797 | long fd, addr, len, rv; | |
798 | ||
799 | sim_core_read_buffer (sd, MSP430_CPU (sd), 0, parms, | |
800 | MSP430_CPU (sd)->state.cio_buffer, 5); | |
801 | length = CIO_I (0); | |
802 | command = parms[2]; | |
803 | ||
804 | sim_core_read_buffer (sd, MSP430_CPU (sd), 0, parms, | |
805 | MSP430_CPU (sd)->state.cio_buffer + 3, 8); | |
806 | ||
807 | sim_core_read_buffer (sd, MSP430_CPU (sd), 0, buffer, | |
808 | MSP430_CPU (sd)->state.cio_buffer + 11, length); | |
809 | ||
810 | switch (command) | |
811 | { | |
812 | case CIO_WRITE: | |
813 | fd = CIO_I (0); | |
814 | len = CIO_I (2); | |
815 | ||
816 | rv = write (fd, buffer, len); | |
817 | parms[0] = rv & 0xff; | |
818 | parms[1] = rv >> 8; | |
819 | ||
820 | break; | |
821 | } | |
822 | ||
823 | sim_core_write_buffer (sd, MSP430_CPU (sd), 0, parms, | |
824 | MSP430_CPU (sd)->state.cio_buffer + 4, 8); | |
825 | if (ret_buflen) | |
826 | sim_core_write_buffer (sd, MSP430_CPU (sd), 0, buffer, | |
827 | MSP430_CPU (sd)->state.cio_buffer + 12, ret_buflen); | |
828 | } | |
829 | ||
830 | #define SRC get_op (sd, opcode, 1) | |
831 | #define DSRC get_op (sd, opcode, 0) | |
832 | #define DEST(V) put_op (sd, opcode, 0, (V)) | |
833 | ||
3346cfda NC |
834 | #define DO_ALU(OP,SOP,MORE) \ |
835 | { \ | |
836 | int s1 = DSRC; \ | |
837 | int s2 = SRC; \ | |
838 | int result = s1 OP s2 MORE; \ | |
5b064994 MF |
839 | TRACE_ALU (MSP430_CPU (sd), "ALU: %#x %s %#x %s = %#x", s1, SOP, \ |
840 | s2, #MORE, result); \ | |
3346cfda NC |
841 | DEST (result); \ |
842 | } | |
843 | ||
844 | #define SIGN (1 << (opcode->size - 1)) | |
845 | #define POS(x) (((x) & SIGN) ? 0 : 1) | |
846 | #define NEG(x) (((x) & SIGN) ? 1 : 0) | |
847 | ||
3346cfda NC |
848 | #define SX(v) sign_ext (v, opcode->size) |
849 | #define ZX(v) zero_ext (v, opcode->size) | |
850 | ||
851 | static char * | |
852 | flags2string (int f) | |
853 | { | |
854 | static char buf[2][6]; | |
855 | static int bi = 0; | |
856 | char *bp = buf[bi]; | |
857 | ||
858 | bi = (bi + 1) % 2; | |
859 | ||
860 | bp[0] = f & MSP430_FLAG_V ? 'V' : '-'; | |
861 | bp[1] = f & MSP430_FLAG_N ? 'N' : '-'; | |
862 | bp[2] = f & MSP430_FLAG_Z ? 'Z' : '-'; | |
863 | bp[3] = f & MSP430_FLAG_C ? 'C' : '-'; | |
864 | bp[4] = 0; | |
865 | return bp; | |
866 | } | |
867 | ||
868 | /* Random number that won't show up in our usual logic. */ | |
869 | #define MAGIC_OVERFLOW 0x55000F | |
870 | ||
871 | static void | |
872 | do_flags (SIM_DESC sd, | |
873 | MSP430_Opcode_Decoded *opcode, | |
874 | int vnz_val, /* Signed result. */ | |
875 | int carry, | |
876 | int overflow) | |
877 | { | |
878 | int f = SR; | |
879 | int new_f = 0; | |
880 | int signbit = 1 << (opcode->size - 1); | |
881 | ||
882 | f &= ~opcode->flags_0; | |
883 | f &= ~opcode->flags_set; | |
884 | f |= opcode->flags_1; | |
885 | ||
886 | if (vnz_val & signbit) | |
887 | new_f |= MSP430_FLAG_N; | |
888 | if (! (vnz_val & ((signbit << 1) - 1))) | |
889 | new_f |= MSP430_FLAG_Z; | |
890 | if (overflow == MAGIC_OVERFLOW) | |
891 | { | |
892 | if (vnz_val != SX (vnz_val)) | |
893 | new_f |= MSP430_FLAG_V; | |
894 | } | |
895 | else | |
896 | if (overflow) | |
897 | new_f |= MSP430_FLAG_V; | |
898 | if (carry) | |
899 | new_f |= MSP430_FLAG_C; | |
900 | ||
901 | new_f = f | (new_f & opcode->flags_set); | |
5b064994 MF |
902 | if (SR != new_f) |
903 | TRACE_ALU (MSP430_CPU (sd), "FLAGS: %s -> %s", flags2string (SR), | |
904 | flags2string (new_f)); | |
905 | else | |
906 | TRACE_ALU (MSP430_CPU (sd), "FLAGS: %s", flags2string (new_f)); | |
3346cfda NC |
907 | SR = new_f; |
908 | } | |
909 | ||
910 | #define FLAGS(vnz,c) do_flags (sd, opcode, vnz, c, MAGIC_OVERFLOW) | |
911 | #define FLAGSV(vnz,c,v) do_flags (sd, opcode, vnz, c, v) | |
912 | ||
913 | /* These two assume unsigned 16-bit (four digit) words. | |
914 | Mask off unwanted bits for byte operations. */ | |
915 | ||
916 | static int | |
917 | bcd_to_binary (int v) | |
918 | { | |
919 | int r = ( ((v >> 0) & 0xf) * 1 | |
920 | + ((v >> 4) & 0xf) * 10 | |
921 | + ((v >> 8) & 0xf) * 100 | |
922 | + ((v >> 12) & 0xf) * 1000); | |
923 | return r; | |
924 | } | |
925 | ||
926 | static int | |
927 | binary_to_bcd (int v) | |
928 | { | |
929 | int r = ( ((v / 1) % 10) << 0 | |
930 | | ((v / 10) % 10) << 4 | |
931 | | ((v / 100) % 10) << 8 | |
932 | | ((v / 1000) % 10) << 12); | |
933 | return r; | |
934 | } | |
935 | ||
3346cfda NC |
936 | static const char * |
937 | cond_string (int cond) | |
938 | { | |
939 | switch (cond) | |
940 | { | |
941 | case MSC_nz: | |
942 | return "NZ"; | |
943 | case MSC_z: | |
944 | return "Z"; | |
945 | case MSC_nc: | |
946 | return "NC"; | |
947 | case MSC_c: | |
948 | return "C"; | |
949 | case MSC_n: | |
950 | return "N"; | |
951 | case MSC_ge: | |
952 | return "GE"; | |
953 | case MSC_l: | |
954 | return "L"; | |
955 | case MSC_true: | |
956 | return "MP"; | |
957 | default: | |
958 | return "??"; | |
959 | } | |
960 | } | |
961 | ||
962 | /* Checks a CALL to address CALL_ADDR. If this is a special | |
963 | syscall address then the call is simulated and non-zero is | |
964 | returned. Otherwise 0 is returned. */ | |
965 | ||
966 | static int | |
967 | maybe_perform_syscall (SIM_DESC sd, int call_addr) | |
968 | { | |
969 | if (call_addr == 0x00160) | |
970 | { | |
971 | int i; | |
972 | ||
973 | for (i = 0; i < 16; i++) | |
974 | { | |
975 | if (i % 4 == 0) | |
976 | fprintf (stderr, "\t"); | |
977 | fprintf (stderr, "R%-2d %05x ", i, MSP430_CPU (sd)->state.regs[i]); | |
978 | if (i % 4 == 3) | |
979 | { | |
980 | int sp = SP + (3 - (i / 4)) * 2; | |
981 | unsigned char buf[2]; | |
982 | ||
983 | sim_core_read_buffer (sd, MSP430_CPU (sd), read_map, buf, sp, 2); | |
984 | ||
985 | fprintf (stderr, "\tSP%+d: %04x", sp - SP, | |
986 | buf[0] + buf[1] * 256); | |
987 | ||
988 | if (i / 4 == 0) | |
989 | { | |
990 | int flags = SR; | |
991 | ||
992 | fprintf (stderr, flags & 0x100 ? " V" : " -"); | |
993 | fprintf (stderr, flags & 0x004 ? "N" : "-"); | |
994 | fprintf (stderr, flags & 0x002 ? "Z" : "-"); | |
995 | fprintf (stderr, flags & 0x001 ? "C" : "-"); | |
996 | } | |
997 | ||
998 | fprintf (stderr, "\n"); | |
999 | } | |
1000 | } | |
1001 | return 1; | |
1002 | } | |
1003 | ||
1004 | if ((call_addr & ~0x3f) == 0x00180) | |
1005 | { | |
1006 | /* Syscall! */ | |
3819af13 | 1007 | int arg1, arg2, arg3, arg4; |
3346cfda | 1008 | int syscall_num = call_addr & 0x3f; |
3819af13 JL |
1009 | |
1010 | /* syscall_num == 2 is used for the variadic function "open". | |
1011 | The arguments are set up differently for variadic functions. | |
1012 | See slaa534.pdf distributed by TI. */ | |
1013 | if (syscall_num == 2) | |
1014 | { | |
1015 | arg1 = MSP430_CPU (sd)->state.regs[12]; | |
1016 | arg2 = mem_get_val (sd, SP, 16); | |
1017 | arg3 = mem_get_val (sd, SP + 2, 16); | |
1018 | arg4 = mem_get_val (sd, SP + 4, 16); | |
1019 | } | |
1020 | else | |
1021 | { | |
1022 | arg1 = MSP430_CPU (sd)->state.regs[12]; | |
1023 | arg2 = MSP430_CPU (sd)->state.regs[13]; | |
1024 | arg3 = MSP430_CPU (sd)->state.regs[14]; | |
1025 | arg4 = MSP430_CPU (sd)->state.regs[15]; | |
1026 | } | |
7d5c6c43 MF |
1027 | |
1028 | MSP430_CPU (sd)->state.regs[12] = sim_syscall (MSP430_CPU (sd), | |
1029 | syscall_num, arg1, arg2, | |
1030 | arg3, arg4); | |
3346cfda NC |
1031 | return 1; |
1032 | } | |
1033 | ||
1034 | return 0; | |
1035 | } | |
1036 | ||
1037 | static void | |
1038 | msp430_step_once (SIM_DESC sd) | |
1039 | { | |
1040 | Get_Byte_Local_Data ld; | |
1041 | unsigned char buf[100]; | |
1042 | int i; | |
1043 | int opsize; | |
1044 | unsigned int opcode_pc; | |
1045 | MSP430_Opcode_Decoded opcode_buf; | |
1046 | MSP430_Opcode_Decoded *opcode = &opcode_buf; | |
1047 | int s1, s2, result; | |
3819af13 JL |
1048 | int u1 = 0, u2, uresult; |
1049 | int c = 0, reg; | |
3346cfda NC |
1050 | int sp; |
1051 | int carry_to_use; | |
1052 | int n_repeats; | |
1053 | int rept; | |
3819af13 | 1054 | int op_bytes = 0, op_bits; |
3346cfda NC |
1055 | |
1056 | PC &= 0xfffff; | |
1057 | opcode_pc = PC; | |
1058 | ||
1059 | if (opcode_pc < 0x10) | |
1060 | { | |
1061 | fprintf (stderr, "Fault: PC(%#x) is less than 0x10\n", opcode_pc); | |
1062 | sim_engine_halt (sd, MSP430_CPU (sd), NULL, | |
1063 | MSP430_CPU (sd)->state.regs[0], | |
1064 | sim_exited, -1); | |
1065 | return; | |
1066 | } | |
1067 | ||
1068 | if (PC == MSP430_CPU (sd)->state.cio_breakpoint | |
1069 | && STATE_OPEN_KIND (sd) != SIM_OPEN_DEBUG) | |
1070 | msp430_cio (sd); | |
1071 | ||
1072 | ld.sd = sd; | |
1073 | ld.gb_addr = PC; | |
1074 | opsize = msp430_decode_opcode (MSP430_CPU (sd)->state.regs[0], | |
1075 | opcode, msp430_getbyte, &ld); | |
1076 | PC += opsize; | |
1077 | if (opsize <= 0) | |
1078 | { | |
1079 | fprintf (stderr, "Fault: undecodable opcode at %#x\n", opcode_pc); | |
1080 | sim_engine_halt (sd, MSP430_CPU (sd), NULL, | |
1081 | MSP430_CPU (sd)->state.regs[0], | |
1082 | sim_exited, -1); | |
1083 | return; | |
1084 | } | |
1085 | ||
1086 | if (opcode->repeat_reg) | |
1087 | n_repeats = (MSP430_CPU (sd)->state.regs[opcode->repeats] & 0x000f) + 1; | |
1088 | else | |
1089 | n_repeats = opcode->repeats + 1; | |
1090 | ||
1091 | op_bits = opcode->size; | |
1092 | switch (op_bits) | |
1093 | { | |
1094 | case 8: | |
1095 | op_bytes = 1; | |
1096 | break; | |
1097 | case 16: | |
1098 | op_bytes = 2; | |
1099 | break; | |
1100 | case 20: | |
1101 | case 32: | |
1102 | op_bytes = 4; | |
1103 | break; | |
1104 | } | |
1105 | ||
3346cfda NC |
1106 | if (TRACE_ANY_P (MSP430_CPU (sd))) |
1107 | trace_prefix (sd, MSP430_CPU (sd), NULL_CIA, opcode_pc, | |
3819af13 | 1108 | TRACE_LINENUM_P (MSP430_CPU (sd)), NULL, 0, " "); |
3346cfda | 1109 | |
70d39448 MF |
1110 | TRACE_DISASM (MSP430_CPU (sd), opcode_pc); |
1111 | ||
3346cfda NC |
1112 | carry_to_use = 0; |
1113 | switch (opcode->id) | |
1114 | { | |
1115 | case MSO_unknown: | |
1116 | break; | |
1117 | ||
1118 | /* Double-operand instructions. */ | |
1119 | case MSO_mov: | |
1120 | if (opcode->n_bytes == 2 | |
1121 | && opcode->op[0].type == MSP430_Operand_Register | |
1122 | && opcode->op[0].reg == MSR_CG | |
1123 | && opcode->op[1].type == MSP430_Operand_Immediate | |
1124 | && opcode->op[1].addend == 0 | |
1125 | /* A 16-bit write of #0 is a NOP; an 8-bit write is a BRK. */ | |
1126 | && opcode->size == 8) | |
1127 | { | |
1128 | /* This is the designated software breakpoint instruction. */ | |
1129 | PC -= opsize; | |
1130 | sim_engine_halt (sd, MSP430_CPU (sd), NULL, | |
1131 | MSP430_CPU (sd)->state.regs[0], | |
1132 | sim_stopped, SIM_SIGTRAP); | |
1133 | ||
1134 | } | |
1135 | else | |
1136 | { | |
1137 | /* Otherwise, do the move. */ | |
1138 | for (rept = 0; rept < n_repeats; rept ++) | |
1139 | { | |
1140 | DEST (SRC); | |
1141 | } | |
1142 | } | |
1143 | break; | |
1144 | ||
1145 | case MSO_addc: | |
1146 | for (rept = 0; rept < n_repeats; rept ++) | |
1147 | { | |
1148 | carry_to_use = (SR & MSP430_FLAG_C) ? 1 : 0; | |
1149 | u1 = DSRC; | |
1150 | u2 = SRC; | |
1151 | s1 = SX (u1); | |
1152 | s2 = SX (u2); | |
1153 | uresult = u1 + u2 + carry_to_use; | |
1154 | result = s1 + s2 + carry_to_use; | |
5b064994 MF |
1155 | TRACE_ALU (MSP430_CPU (sd), "ADDC: %#x + %#x + %d = %#x", |
1156 | u1, u2, carry_to_use, uresult); | |
3346cfda NC |
1157 | DEST (result); |
1158 | FLAGS (result, uresult != ZX (uresult)); | |
1159 | } | |
1160 | break; | |
1161 | ||
1162 | case MSO_add: | |
1163 | for (rept = 0; rept < n_repeats; rept ++) | |
1164 | { | |
1165 | u1 = DSRC; | |
1166 | u2 = SRC; | |
1167 | s1 = SX (u1); | |
1168 | s2 = SX (u2); | |
1169 | uresult = u1 + u2; | |
1170 | result = s1 + s2; | |
5b064994 MF |
1171 | TRACE_ALU (MSP430_CPU (sd), "ADD: %#x + %#x = %#x", |
1172 | u1, u2, uresult); | |
3346cfda NC |
1173 | DEST (result); |
1174 | FLAGS (result, uresult != ZX (uresult)); | |
1175 | } | |
1176 | break; | |
1177 | ||
1178 | case MSO_subc: | |
1179 | for (rept = 0; rept < n_repeats; rept ++) | |
1180 | { | |
1181 | carry_to_use = (SR & MSP430_FLAG_C) ? 1 : 0; | |
1182 | u1 = DSRC; | |
1183 | u2 = SRC; | |
1184 | s1 = SX (u1); | |
1185 | s2 = SX (u2); | |
1186 | uresult = ZX (~u2) + u1 + carry_to_use; | |
1187 | result = s1 - s2 + (carry_to_use - 1); | |
5b064994 MF |
1188 | TRACE_ALU (MSP430_CPU (sd), "SUBC: %#x - %#x + %d = %#x", |
1189 | u1, u2, carry_to_use, uresult); | |
3346cfda NC |
1190 | DEST (result); |
1191 | FLAGS (result, uresult != ZX (uresult)); | |
1192 | } | |
1193 | break; | |
1194 | ||
1195 | case MSO_sub: | |
1196 | for (rept = 0; rept < n_repeats; rept ++) | |
1197 | { | |
1198 | u1 = DSRC; | |
1199 | u2 = SRC; | |
1200 | s1 = SX (u1); | |
1201 | s2 = SX (u2); | |
1202 | uresult = ZX (~u2) + u1 + 1; | |
1203 | result = SX (uresult); | |
5b064994 MF |
1204 | TRACE_ALU (MSP430_CPU (sd), "SUB: %#x - %#x = %#x", |
1205 | u1, u2, uresult); | |
3346cfda NC |
1206 | DEST (result); |
1207 | FLAGS (result, uresult != ZX (uresult)); | |
1208 | } | |
1209 | break; | |
1210 | ||
1211 | case MSO_cmp: | |
1212 | for (rept = 0; rept < n_repeats; rept ++) | |
1213 | { | |
1214 | u1 = DSRC; | |
1215 | u2 = SRC; | |
1216 | s1 = SX (u1); | |
1217 | s2 = SX (u2); | |
1218 | uresult = ZX (~u2) + u1 + 1; | |
1219 | result = s1 - s2; | |
5b064994 MF |
1220 | TRACE_ALU (MSP430_CPU (sd), "CMP: %#x - %#x = %x", |
1221 | u1, u2, uresult); | |
3346cfda NC |
1222 | FLAGS (result, uresult != ZX (uresult)); |
1223 | } | |
1224 | break; | |
1225 | ||
1226 | case MSO_dadd: | |
1227 | for (rept = 0; rept < n_repeats; rept ++) | |
1228 | { | |
1229 | carry_to_use = (SR & MSP430_FLAG_C) ? 1 : 0; | |
1230 | u1 = DSRC; | |
1231 | u2 = SRC; | |
1232 | uresult = bcd_to_binary (u1) + bcd_to_binary (u2) + carry_to_use; | |
1233 | result = binary_to_bcd (uresult); | |
5b064994 MF |
1234 | TRACE_ALU (MSP430_CPU (sd), "DADD: %#x + %#x + %d = %#x", |
1235 | u1, u2, carry_to_use, result); | |
3346cfda NC |
1236 | DEST (result); |
1237 | FLAGS (result, uresult > ((opcode->size == 8) ? 99 : 9999)); | |
1238 | } | |
1239 | break; | |
1240 | ||
1241 | case MSO_and: | |
1242 | for (rept = 0; rept < n_repeats; rept ++) | |
1243 | { | |
1244 | u1 = DSRC; | |
1245 | u2 = SRC; | |
1246 | uresult = u1 & u2; | |
5b064994 MF |
1247 | TRACE_ALU (MSP430_CPU (sd), "AND: %#x & %#x = %#x", |
1248 | u1, u2, uresult); | |
3346cfda NC |
1249 | DEST (uresult); |
1250 | FLAGS (uresult, uresult != 0); | |
1251 | } | |
1252 | break; | |
1253 | ||
1254 | case MSO_bit: | |
1255 | for (rept = 0; rept < n_repeats; rept ++) | |
1256 | { | |
1257 | u1 = DSRC; | |
1258 | u2 = SRC; | |
1259 | uresult = u1 & u2; | |
5b064994 MF |
1260 | TRACE_ALU (MSP430_CPU (sd), "BIT: %#x & %#x -> %#x", |
1261 | u1, u2, uresult); | |
3346cfda NC |
1262 | FLAGS (uresult, uresult != 0); |
1263 | } | |
1264 | break; | |
1265 | ||
1266 | case MSO_bic: | |
1267 | for (rept = 0; rept < n_repeats; rept ++) | |
1268 | { | |
1269 | u1 = DSRC; | |
1270 | u2 = SRC; | |
1271 | uresult = u1 & ~ u2; | |
5b064994 MF |
1272 | TRACE_ALU (MSP430_CPU (sd), "BIC: %#x & ~ %#x = %#x", |
1273 | u1, u2, uresult); | |
3346cfda NC |
1274 | DEST (uresult); |
1275 | } | |
1276 | break; | |
1277 | ||
1278 | case MSO_bis: | |
1279 | for (rept = 0; rept < n_repeats; rept ++) | |
1280 | { | |
1281 | u1 = DSRC; | |
1282 | u2 = SRC; | |
1283 | uresult = u1 | u2; | |
5b064994 MF |
1284 | TRACE_ALU (MSP430_CPU (sd), "BIS: %#x | %#x = %#x", |
1285 | u1, u2, uresult); | |
3346cfda NC |
1286 | DEST (uresult); |
1287 | } | |
1288 | break; | |
1289 | ||
1290 | case MSO_xor: | |
1291 | for (rept = 0; rept < n_repeats; rept ++) | |
1292 | { | |
1293 | s1 = 1 << (opcode->size - 1); | |
1294 | u1 = DSRC; | |
1295 | u2 = SRC; | |
1296 | uresult = u1 ^ u2; | |
5b064994 MF |
1297 | TRACE_ALU (MSP430_CPU (sd), "XOR: %#x & %#x = %#x", |
1298 | u1, u2, uresult); | |
3346cfda NC |
1299 | DEST (uresult); |
1300 | FLAGSV (uresult, uresult != 0, (u1 & s1) && (u2 & s1)); | |
1301 | } | |
1302 | break; | |
1303 | ||
1304 | /* Single-operand instructions. Note: the decoder puts the same | |
1305 | operand in SRC as in DEST, for our convenience. */ | |
1306 | ||
1307 | case MSO_rrc: | |
1308 | for (rept = 0; rept < n_repeats; rept ++) | |
1309 | { | |
1310 | u1 = SRC; | |
1311 | carry_to_use = u1 & 1; | |
1312 | uresult = u1 >> 1; | |
b7dcc42d JL |
1313 | /* If the ZC bit of the opcode is set, it means we are synthesizing |
1314 | RRUX, so the carry bit must be ignored. */ | |
1315 | if (opcode->zc == 0 && (SR & MSP430_FLAG_C)) | |
1316 | uresult |= (1 << (opcode->size - 1)); | |
5b064994 MF |
1317 | TRACE_ALU (MSP430_CPU (sd), "RRC: %#x >>= %#x", |
1318 | u1, uresult); | |
3346cfda NC |
1319 | DEST (uresult); |
1320 | FLAGS (uresult, carry_to_use); | |
1321 | } | |
1322 | break; | |
1323 | ||
1324 | case MSO_swpb: | |
1325 | for (rept = 0; rept < n_repeats; rept ++) | |
1326 | { | |
1327 | u1 = SRC; | |
1328 | uresult = ((u1 >> 8) & 0x00ff) | ((u1 << 8) & 0xff00); | |
5b064994 MF |
1329 | TRACE_ALU (MSP430_CPU (sd), "SWPB: %#x -> %#x", |
1330 | u1, uresult); | |
3346cfda NC |
1331 | DEST (uresult); |
1332 | } | |
1333 | break; | |
1334 | ||
1335 | case MSO_rra: | |
1336 | for (rept = 0; rept < n_repeats; rept ++) | |
1337 | { | |
1338 | u1 = SRC; | |
1339 | c = u1 & 1; | |
1340 | s1 = 1 << (opcode->size - 1); | |
1341 | uresult = (u1 >> 1) | (u1 & s1); | |
5b064994 MF |
1342 | TRACE_ALU (MSP430_CPU (sd), "RRA: %#x >>= %#x", |
1343 | u1, uresult); | |
3346cfda NC |
1344 | DEST (uresult); |
1345 | FLAGS (uresult, c); | |
1346 | } | |
1347 | break; | |
1348 | ||
1349 | case MSO_rru: | |
1350 | for (rept = 0; rept < n_repeats; rept ++) | |
1351 | { | |
1352 | u1 = SRC; | |
1353 | c = u1 & 1; | |
1354 | uresult = (u1 >> 1); | |
5b064994 MF |
1355 | TRACE_ALU (MSP430_CPU (sd), "RRU: %#x >>= %#x", |
1356 | u1, uresult); | |
3346cfda NC |
1357 | DEST (uresult); |
1358 | FLAGS (uresult, c); | |
1359 | } | |
1360 | break; | |
1361 | ||
1362 | case MSO_sxt: | |
1363 | for (rept = 0; rept < n_repeats; rept ++) | |
1364 | { | |
1365 | u1 = SRC; | |
1366 | if (u1 & 0x80) | |
1367 | uresult = u1 | 0xfff00; | |
1368 | else | |
1369 | uresult = u1 & 0x000ff; | |
5b064994 MF |
1370 | TRACE_ALU (MSP430_CPU (sd), "SXT: %#x -> %#x", |
1371 | u1, uresult); | |
3346cfda NC |
1372 | DEST (uresult); |
1373 | FLAGS (uresult, c); | |
1374 | } | |
1375 | break; | |
1376 | ||
1377 | case MSO_push: | |
1378 | for (rept = 0; rept < n_repeats; rept ++) | |
1379 | { | |
1380 | int new_sp; | |
1381 | ||
1382 | new_sp = REG_GET (MSR_SP) - op_bytes; | |
1383 | /* SP is always word-aligned. */ | |
1384 | if (new_sp & 1) | |
1385 | new_sp --; | |
1386 | REG_PUT (MSR_SP, new_sp); | |
1387 | u1 = SRC; | |
1388 | mem_put_val (sd, SP, u1, op_bits); | |
1389 | if (opcode->op[1].type == MSP430_Operand_Register) | |
1390 | opcode->op[1].reg --; | |
1391 | } | |
1392 | break; | |
1393 | ||
1394 | case MSO_pop: | |
1395 | for (rept = 0; rept < n_repeats; rept ++) | |
1396 | { | |
1397 | int new_sp; | |
1398 | ||
1399 | u1 = mem_get_val (sd, SP, op_bits); | |
1400 | DEST (u1); | |
1401 | if (opcode->op[0].type == MSP430_Operand_Register) | |
1402 | opcode->op[0].reg ++; | |
1403 | new_sp = REG_GET (MSR_SP) + op_bytes; | |
1404 | /* SP is always word-aligned. */ | |
1405 | if (new_sp & 1) | |
1406 | new_sp ++; | |
1407 | REG_PUT (MSR_SP, new_sp); | |
1408 | } | |
1409 | break; | |
1410 | ||
1411 | case MSO_call: | |
1412 | u1 = SRC; | |
1413 | ||
1414 | if (maybe_perform_syscall (sd, u1)) | |
1415 | break; | |
1416 | ||
1417 | REG_PUT (MSR_SP, REG_GET (MSR_SP) - op_bytes); | |
1418 | mem_put_val (sd, SP, PC, op_bits); | |
5b064994 MF |
1419 | TRACE_ALU (MSP430_CPU (sd), "CALL: func %#x ret %#x, sp %#x", |
1420 | u1, PC, SP); | |
3346cfda NC |
1421 | REG_PUT (MSR_PC, u1); |
1422 | break; | |
1423 | ||
1424 | case MSO_reti: | |
8969934d NC |
1425 | u1 = mem_get_val (sd, SP, 16); |
1426 | SR = u1 & 0xFF; | |
3346cfda | 1427 | SP += 2; |
8969934d | 1428 | PC = mem_get_val (sd, SP, 16); |
3346cfda | 1429 | SP += 2; |
8969934d NC |
1430 | /* Emulate the RETI action of the 20-bit CPUX architecure. |
1431 | This is safe for 16-bit CPU architectures as well, since the top | |
1432 | 8-bits of SR will have been written to the stack here, and will | |
1433 | have been read as 0. */ | |
1434 | PC |= (u1 & 0xF000) << 4; | |
5b064994 MF |
1435 | TRACE_ALU (MSP430_CPU (sd), "RETI: pc %#x sr %#x", |
1436 | PC, SR); | |
3346cfda NC |
1437 | break; |
1438 | ||
1439 | /* Jumps. */ | |
1440 | ||
1441 | case MSO_jmp: | |
1442 | i = SRC; | |
1443 | switch (opcode->cond) | |
1444 | { | |
1445 | case MSC_nz: | |
1446 | u1 = (SR & MSP430_FLAG_Z) ? 0 : 1; | |
1447 | break; | |
1448 | case MSC_z: | |
1449 | u1 = (SR & MSP430_FLAG_Z) ? 1 : 0; | |
1450 | break; | |
1451 | case MSC_nc: | |
1452 | u1 = (SR & MSP430_FLAG_C) ? 0 : 1; | |
1453 | break; | |
1454 | case MSC_c: | |
1455 | u1 = (SR & MSP430_FLAG_C) ? 1 : 0; | |
1456 | break; | |
1457 | case MSC_n: | |
1458 | u1 = (SR & MSP430_FLAG_N) ? 1 : 0; | |
1459 | break; | |
1460 | case MSC_ge: | |
1461 | u1 = (!!(SR & MSP430_FLAG_N) == !!(SR & MSP430_FLAG_V)) ? 1 : 0; | |
1462 | break; | |
1463 | case MSC_l: | |
1464 | u1 = (!!(SR & MSP430_FLAG_N) == !!(SR & MSP430_FLAG_V)) ? 0 : 1; | |
1465 | break; | |
1466 | case MSC_true: | |
1467 | u1 = 1; | |
1468 | break; | |
1469 | } | |
1470 | ||
1471 | if (u1) | |
1472 | { | |
5b064994 MF |
1473 | TRACE_BRANCH (MSP430_CPU (sd), "J%s: pc %#x -> %#x sr %#x, taken", |
1474 | cond_string (opcode->cond), PC, i, SR); | |
3346cfda NC |
1475 | PC = i; |
1476 | if (PC == opcode_pc) | |
1477 | exit (0); | |
1478 | } | |
1479 | else | |
5b064994 MF |
1480 | TRACE_BRANCH (MSP430_CPU (sd), "J%s: pc %#x to %#x sr %#x, not taken", |
1481 | cond_string (opcode->cond), PC, i, SR); | |
3346cfda NC |
1482 | break; |
1483 | ||
1484 | default: | |
1485 | fprintf (stderr, "error: unexpected opcode id %d\n", opcode->id); | |
1486 | exit (1); | |
1487 | } | |
1488 | } | |
1489 | ||
1490 | void | |
1491 | sim_engine_run (SIM_DESC sd, | |
1492 | int next_cpu_nr, | |
1493 | int nr_cpus, | |
1494 | int siggnal) | |
1495 | { | |
1496 | while (1) | |
1497 | { | |
1498 | msp430_step_once (sd); | |
1499 | if (sim_events_tick (sd)) | |
1500 | sim_events_process (sd); | |
1501 | } | |
1502 | } |