1 |
/* |
2 |
* Copyright (C) 2005-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: cpu_avr_instr.c,v 1.16 2006/12/30 13:30:53 debug Exp $ |
29 |
* |
30 |
* Atmel AVR (8-bit) instructions. |
31 |
* |
32 |
* Individual functions should keep track of cpu->n_translated_instrs. |
33 |
* (n_translated_instrs is automatically increased by 1 for each function |
34 |
* call. If no instruction was executed, then it should be decreased. If, say, |
35 |
* 4 instructions were combined into one function and executed, then it should |
36 |
* be increased by 3.) |
37 |
*/ |
38 |
|
39 |
|
40 |
/*****************************************************************************/ |
41 |
|
42 |
|
43 |
void push_value(struct cpu *cpu, uint32_t value, int len) |
44 |
{ |
45 |
unsigned char data[4]; |
46 |
data[0] = value; data[1] = value >> 8; |
47 |
data[2] = value >> 16; data[3] = value >> 24; |
48 |
cpu->memory_rw(cpu, cpu->mem, cpu->cd.avr.sp + AVR_SRAM_BASE, |
49 |
data, len, MEM_WRITE, CACHE_DATA); |
50 |
cpu->cd.avr.sp -= len; |
51 |
cpu->cd.avr.sp &= cpu->cd.avr.sram_mask; |
52 |
} |
53 |
|
54 |
|
55 |
void pop_value(struct cpu *cpu, uint32_t *value, int len) |
56 |
{ |
57 |
unsigned char data[4]; |
58 |
cpu->cd.avr.sp += len; |
59 |
cpu->cd.avr.sp &= cpu->cd.avr.sram_mask; |
60 |
cpu->memory_rw(cpu, cpu->mem, cpu->cd.avr.sp + AVR_SRAM_BASE, |
61 |
data, len, MEM_READ, CACHE_DATA); |
62 |
*value = data[0]; |
63 |
if (len > 1) |
64 |
(*value) += (data[1] << 8); |
65 |
if (len > 2) |
66 |
(*value) += (data[2] << 16); |
67 |
if (len > 3) |
68 |
(*value) += (data[3] << 24); |
69 |
} |
70 |
|
71 |
|
72 |
/*****************************************************************************/ |
73 |
|
74 |
|
75 |
/* |
76 |
* nop: Do nothing. |
77 |
*/ |
78 |
X(nop) |
79 |
{ |
80 |
} |
81 |
|
82 |
|
83 |
/* |
84 |
* breq: Conditional relative jump. |
85 |
* |
86 |
* arg[1]: relative offset |
87 |
*/ |
88 |
X(breq) |
89 |
{ |
90 |
uint32_t low_pc; |
91 |
|
92 |
if (!(cpu->cd.avr.sreg & AVR_SREG_Z)) |
93 |
return; |
94 |
|
95 |
cpu->cd.avr.extra_cycles ++; |
96 |
|
97 |
/* Calculate new PC from the next instruction + arg[1] */ |
98 |
low_pc = ((size_t)ic - (size_t)cpu->cd.avr.cur_ic_page) / |
99 |
sizeof(struct avr_instr_call); |
100 |
cpu->pc &= ~((AVR_IC_ENTRIES_PER_PAGE-1) |
101 |
<< AVR_INSTR_ALIGNMENT_SHIFT); |
102 |
cpu->pc += (low_pc << AVR_INSTR_ALIGNMENT_SHIFT); |
103 |
cpu->pc += (int32_t)ic->arg[1]; |
104 |
|
105 |
/* Find the new physical page and update the translation pointers: */ |
106 |
avr_pc_to_pointers(cpu); |
107 |
} |
108 |
|
109 |
|
110 |
/* |
111 |
* breq_samepage: Continional relative jump (to within the same page). |
112 |
* |
113 |
* arg[1] = pointer to new avr_instr_call |
114 |
*/ |
115 |
X(breq_samepage) |
116 |
{ |
117 |
if (!(cpu->cd.avr.sreg & AVR_SREG_Z)) |
118 |
return; |
119 |
|
120 |
cpu->cd.avr.extra_cycles ++; |
121 |
cpu->cd.avr.next_ic = (struct avr_instr_call *) ic->arg[1]; |
122 |
} |
123 |
|
124 |
|
125 |
/* |
126 |
* brne: Conditional relative jump. |
127 |
* |
128 |
* arg[1]: relative offset |
129 |
*/ |
130 |
X(brne) |
131 |
{ |
132 |
uint32_t low_pc; |
133 |
|
134 |
if (cpu->cd.avr.sreg & AVR_SREG_Z) |
135 |
return; |
136 |
|
137 |
cpu->cd.avr.extra_cycles ++; |
138 |
|
139 |
/* Calculate new PC from the next instruction + arg[1] */ |
140 |
low_pc = ((size_t)ic - (size_t)cpu->cd.avr.cur_ic_page) / |
141 |
sizeof(struct avr_instr_call); |
142 |
cpu->pc &= ~((AVR_IC_ENTRIES_PER_PAGE-1) |
143 |
<< AVR_INSTR_ALIGNMENT_SHIFT); |
144 |
cpu->pc += (low_pc << AVR_INSTR_ALIGNMENT_SHIFT); |
145 |
cpu->pc += (int32_t)ic->arg[1]; |
146 |
|
147 |
/* Find the new physical page and update the translation pointers: */ |
148 |
avr_pc_to_pointers(cpu); |
149 |
} |
150 |
|
151 |
|
152 |
/* |
153 |
* brne_samepage: Continional relative jump (to within the same page). |
154 |
* |
155 |
* arg[1] = pointer to new avr_instr_call |
156 |
*/ |
157 |
X(brne_samepage) |
158 |
{ |
159 |
if (cpu->cd.avr.sreg & AVR_SREG_Z) |
160 |
return; |
161 |
|
162 |
cpu->cd.avr.extra_cycles ++; |
163 |
cpu->cd.avr.next_ic = (struct avr_instr_call *) ic->arg[1]; |
164 |
} |
165 |
|
166 |
|
167 |
/* |
168 |
* clX: Clear an sreg bit. |
169 |
*/ |
170 |
X(clc) { cpu->cd.avr.sreg &= ~AVR_SREG_C; } |
171 |
X(clz) { cpu->cd.avr.sreg &= ~AVR_SREG_Z; } |
172 |
X(cln) { cpu->cd.avr.sreg &= ~AVR_SREG_N; } |
173 |
X(clv) { cpu->cd.avr.sreg &= ~AVR_SREG_V; } |
174 |
X(cls) { cpu->cd.avr.sreg &= ~AVR_SREG_S; } |
175 |
X(clh) { cpu->cd.avr.sreg &= ~AVR_SREG_H; } |
176 |
X(clt) { cpu->cd.avr.sreg &= ~AVR_SREG_T; } |
177 |
X(cli) { cpu->cd.avr.sreg &= ~AVR_SREG_I; } |
178 |
|
179 |
|
180 |
/* |
181 |
* ldi: Load immediate. |
182 |
* |
183 |
* arg[1]: ptr to register |
184 |
* arg[2]: byte value |
185 |
*/ |
186 |
X(ldi) |
187 |
{ |
188 |
*(uint8_t *)(ic->arg[1]) = ic->arg[2]; |
189 |
} |
190 |
|
191 |
|
192 |
/* |
193 |
* ld_y: Load byte pointed to by register Y into a register. |
194 |
* |
195 |
* arg[1]: ptr to rd |
196 |
*/ |
197 |
X(ld_y) |
198 |
{ |
199 |
cpu->memory_rw(cpu, cpu->mem, AVR_SRAM_BASE + cpu->cd.avr.r[28] |
200 |
+ 256*cpu->cd.avr.r[29], (uint8_t *)(ic->arg[1]), 1, MEM_READ, |
201 |
CACHE_DATA); |
202 |
cpu->cd.avr.extra_cycles ++; |
203 |
} |
204 |
|
205 |
|
206 |
/* |
207 |
* out: Write a byte from a register to I/O space. |
208 |
* (out is the generic function, out_* are special cases.) |
209 |
* |
210 |
* arg[1]: ptr to rr |
211 |
* arg[2]: I/O port nr |
212 |
*/ |
213 |
X(out) |
214 |
{ |
215 |
cpu->memory_rw(cpu, cpu->mem, AVR_SRAM_BASE + ic->arg[2], |
216 |
(uint8_t *)(ic->arg[1]), 1, MEM_WRITE, CACHE_DATA); |
217 |
} |
218 |
X(out_ddra) { cpu->cd.avr.ddra = *(uint8_t *)(ic->arg[1]); } |
219 |
X(out_ddrb) { cpu->cd.avr.ddrb = *(uint8_t *)(ic->arg[1]); } |
220 |
X(out_ddrc) { cpu->cd.avr.ddrc = *(uint8_t *)(ic->arg[1]); } |
221 |
X(out_ddrd) { cpu->cd.avr.ddrd = *(uint8_t *)(ic->arg[1]); } |
222 |
X(out_porta) { cpu->cd.avr.portd_write = *(uint8_t *)(ic->arg[1]); } |
223 |
X(out_portb) { cpu->cd.avr.portd_write = *(uint8_t *)(ic->arg[1]); } |
224 |
X(out_portc) { cpu->cd.avr.portd_write = *(uint8_t *)(ic->arg[1]); } |
225 |
X(out_portd) { cpu->cd.avr.portd_write = *(uint8_t *)(ic->arg[1]); } |
226 |
|
227 |
|
228 |
/* |
229 |
* adiw: rd+1:rd += constant |
230 |
* |
231 |
* arg[1]: ptr to rd |
232 |
* arg[2]: k |
233 |
*/ |
234 |
X(adiw) |
235 |
{ |
236 |
uint32_t value = *(uint8_t *)(ic->arg[1]) + |
237 |
(*(uint8_t *)(ic->arg[1] + 1) << 8); |
238 |
value += ic->arg[2]; |
239 |
|
240 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
241 |
| AVR_SREG_Z | AVR_SREG_C); |
242 |
|
243 |
/* TODO: is this V bit calculated correctly? */ |
244 |
if (value > 0xffff) |
245 |
cpu->cd.avr.sreg |= AVR_SREG_C | AVR_SREG_V; |
246 |
if (value & 0x8000) |
247 |
cpu->cd.avr.sreg |= AVR_SREG_N; |
248 |
if (value == 0) |
249 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
250 |
|
251 |
if ((cpu->cd.avr.sreg & AVR_SREG_N? 1 : 0) ^ |
252 |
(cpu->cd.avr.sreg & AVR_SREG_V? 1 : 0)) |
253 |
cpu->cd.avr.sreg |= AVR_SREG_S; |
254 |
|
255 |
*(uint8_t *)(ic->arg[1]) = value; |
256 |
*(uint8_t *)(ic->arg[1] + 1) = value >> 8; |
257 |
|
258 |
cpu->cd.avr.extra_cycles ++; |
259 |
} |
260 |
|
261 |
|
262 |
/* |
263 |
* and: rd = rd & rr |
264 |
* |
265 |
* arg[1]: ptr to rr |
266 |
* arg[2]: ptr to rd |
267 |
*/ |
268 |
X(and) |
269 |
{ |
270 |
*(uint8_t *)(ic->arg[2]) &= *(uint8_t *)(ic->arg[1]); |
271 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
272 |
| AVR_SREG_Z); |
273 |
if (*(uint8_t *)(ic->arg[2]) == 0) |
274 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
275 |
if (*(uint8_t *)(ic->arg[2]) & 0x80) |
276 |
cpu->cd.avr.sreg |= AVR_SREG_S | AVR_SREG_N; |
277 |
} |
278 |
|
279 |
|
280 |
/* |
281 |
* eor: rd = rd ^ rr |
282 |
* |
283 |
* arg[1]: ptr to rr |
284 |
* arg[2]: ptr to rd |
285 |
*/ |
286 |
X(eor) |
287 |
{ |
288 |
*(uint8_t *)(ic->arg[2]) ^= *(uint8_t *)(ic->arg[1]); |
289 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
290 |
| AVR_SREG_Z); |
291 |
if (*(uint8_t *)(ic->arg[2]) == 0) |
292 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
293 |
if (*(uint8_t *)(ic->arg[2]) & 0x80) |
294 |
cpu->cd.avr.sreg |= AVR_SREG_S | AVR_SREG_N; |
295 |
} |
296 |
|
297 |
|
298 |
/* |
299 |
* andi: rd = rd & imm |
300 |
* |
301 |
* arg[1]: ptr to rd |
302 |
* arg[2]: imm |
303 |
*/ |
304 |
X(andi) |
305 |
{ |
306 |
uint8_t x = *(uint8_t *)(ic->arg[1]) & ic->arg[2]; |
307 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
308 |
| AVR_SREG_Z); |
309 |
if (x == 0) |
310 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
311 |
if (x & 0x80) |
312 |
cpu->cd.avr.sreg |= AVR_SREG_S | AVR_SREG_N; |
313 |
*(uint8_t *)(ic->arg[1]) = x; |
314 |
} |
315 |
|
316 |
|
317 |
/* |
318 |
* cpi: Compare rd with immediate |
319 |
* |
320 |
* arg[1]: ptr to rd |
321 |
* arg[2]: imm |
322 |
*/ |
323 |
X(cpi) |
324 |
{ |
325 |
uint8_t x = *(uint8_t *)(ic->arg[1]), k = ic->arg[2], z = x - k; |
326 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
327 |
| AVR_SREG_Z | AVR_SREG_H | AVR_SREG_C); |
328 |
if (z == 0) |
329 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
330 |
if (z & 0x80) |
331 |
cpu->cd.avr.sreg |= AVR_SREG_N; |
332 |
/* TODO: h and v bits! */ |
333 |
if (abs((int)k) > abs((int)x)) |
334 |
cpu->cd.avr.sreg |= AVR_SREG_C; |
335 |
if ((cpu->cd.avr.sreg & AVR_SREG_N? 1 : 0) ^ |
336 |
(cpu->cd.avr.sreg & AVR_SREG_V? 1 : 0)) |
337 |
cpu->cd.avr.sreg |= AVR_SREG_S; |
338 |
} |
339 |
|
340 |
|
341 |
/* |
342 |
* mov: Copy register. |
343 |
* |
344 |
* arg[1]: ptr to rr |
345 |
* arg[2]: ptr to rd |
346 |
*/ |
347 |
X(mov) |
348 |
{ |
349 |
*(uint8_t *)(ic->arg[2]) = *(uint8_t *)(ic->arg[1]); |
350 |
} |
351 |
|
352 |
|
353 |
/* |
354 |
* sts: Store a register into memory. |
355 |
* |
356 |
* arg[1]: pointer to the register |
357 |
* arg[2]: absolute address (16 bits) |
358 |
*/ |
359 |
X(sts) |
360 |
{ |
361 |
uint8_t r = *(uint8_t *)(ic->arg[1]); |
362 |
if (!cpu->memory_rw(cpu, cpu->mem, ic->arg[2] + AVR_SRAM_BASE, |
363 |
&r, sizeof(uint8_t), MEM_WRITE, CACHE_DATA)) { |
364 |
fatal("sts: write failed: TODO\n"); |
365 |
exit(1); |
366 |
} |
367 |
cpu->cd.avr.extra_cycles ++; |
368 |
} |
369 |
|
370 |
|
371 |
/* |
372 |
* st_y: Store a register into memory at address Y. |
373 |
* st_y_plus: Store a register into memory at address Y, and update Y. |
374 |
* st_minus_y: Same as above, but with pre-decrement instead of post-increment. |
375 |
* |
376 |
* arg[1]: pointer to the register |
377 |
*/ |
378 |
X(st_y) |
379 |
{ |
380 |
uint16_t y = (cpu->cd.avr.r[29] << 8) + cpu->cd.avr.r[28]; |
381 |
cpu->memory_rw(cpu, cpu->mem, AVR_SRAM_BASE + y, |
382 |
(uint8_t *)ic->arg[1], sizeof(uint8_t), MEM_WRITE, CACHE_DATA); |
383 |
cpu->cd.avr.extra_cycles ++; |
384 |
} |
385 |
X(st_y_plus) |
386 |
{ |
387 |
uint16_t y = (cpu->cd.avr.r[29] << 8) + cpu->cd.avr.r[28]; |
388 |
cpu->memory_rw(cpu, cpu->mem, AVR_SRAM_BASE + y, |
389 |
(uint8_t *)ic->arg[1], sizeof(uint8_t), MEM_WRITE, CACHE_DATA); |
390 |
cpu->cd.avr.extra_cycles ++; |
391 |
y ++; |
392 |
cpu->cd.avr.r[29] = y >> 8; |
393 |
cpu->cd.avr.r[28] = y; |
394 |
} |
395 |
X(st_minus_y) |
396 |
{ |
397 |
uint16_t y = (cpu->cd.avr.r[29] << 8) + cpu->cd.avr.r[28]; |
398 |
y --; |
399 |
cpu->cd.avr.r[29] = y >> 8; |
400 |
cpu->cd.avr.r[28] = y; |
401 |
cpu->memory_rw(cpu, cpu->mem, AVR_SRAM_BASE + y, |
402 |
(uint8_t *)ic->arg[1], sizeof(uint8_t), MEM_WRITE, CACHE_DATA); |
403 |
cpu->cd.avr.extra_cycles ++; |
404 |
} |
405 |
|
406 |
|
407 |
/* |
408 |
* cbi,sbi: Clear/Set bit in I/O register. |
409 |
* |
410 |
* arg[1]: I/O register number (0..31) |
411 |
* arg[2]: byte mask to and/or into the old value (1, 2, ..., 0x40, or 0x80) |
412 |
*/ |
413 |
X(cbi) |
414 |
{ |
415 |
uint8_t r; |
416 |
cpu->memory_rw(cpu, cpu->mem, ic->arg[1] + AVR_SRAM_BASE, |
417 |
&r, sizeof(uint8_t), MEM_READ, CACHE_DATA); |
418 |
r &= ic->arg[2]; |
419 |
cpu->memory_rw(cpu, cpu->mem, ic->arg[1] + AVR_SRAM_BASE, |
420 |
&r, sizeof(uint8_t), MEM_WRITE, CACHE_DATA); |
421 |
cpu->cd.avr.extra_cycles ++; |
422 |
} |
423 |
X(sbi) |
424 |
{ |
425 |
uint8_t r; |
426 |
cpu->memory_rw(cpu, cpu->mem, ic->arg[1] + AVR_SRAM_BASE, |
427 |
&r, sizeof(uint8_t), MEM_READ, CACHE_DATA); |
428 |
r |= ic->arg[2]; |
429 |
cpu->memory_rw(cpu, cpu->mem, ic->arg[1] + AVR_SRAM_BASE, |
430 |
&r, sizeof(uint8_t), MEM_WRITE, CACHE_DATA); |
431 |
cpu->cd.avr.extra_cycles ++; |
432 |
} |
433 |
|
434 |
|
435 |
/* |
436 |
* lpm: Load program memory at addess Z into r0. |
437 |
*/ |
438 |
X(lpm) |
439 |
{ |
440 |
uint16_t z = (cpu->cd.avr.r[31] << 8) + cpu->cd.avr.r[30]; |
441 |
cpu->memory_rw(cpu, cpu->mem, z, &cpu->cd.avr.r[0], |
442 |
sizeof(uint8_t), MEM_READ, CACHE_DATA); |
443 |
cpu->cd.avr.extra_cycles += 2; |
444 |
} |
445 |
|
446 |
|
447 |
/* |
448 |
* ret: Return from subroutine call. |
449 |
*/ |
450 |
X(ret) |
451 |
{ |
452 |
uint32_t new_pc; |
453 |
|
454 |
cpu->cd.avr.extra_cycles += 3 + cpu->cd.avr.is_22bit; |
455 |
|
456 |
/* Pop the address of the following instruction: */ |
457 |
pop_value(cpu, &new_pc, 2 + cpu->cd.avr.is_22bit); |
458 |
cpu->pc = new_pc << 1; |
459 |
|
460 |
/* Find the new physical page and update the translation pointers: */ |
461 |
avr_pc_to_pointers(cpu); |
462 |
} |
463 |
|
464 |
|
465 |
/* |
466 |
* rcall: Relative call. |
467 |
* |
468 |
* arg[1]: relative offset |
469 |
*/ |
470 |
X(rcall) |
471 |
{ |
472 |
uint32_t low_pc; |
473 |
|
474 |
cpu->cd.avr.extra_cycles += 2 + cpu->cd.avr.is_22bit; |
475 |
|
476 |
/* Push the address of the following instruction: */ |
477 |
low_pc = ((size_t)ic - (size_t)cpu->cd.avr.cur_ic_page) / |
478 |
sizeof(struct avr_instr_call); |
479 |
cpu->pc &= ~((AVR_IC_ENTRIES_PER_PAGE-1) |
480 |
<< AVR_INSTR_ALIGNMENT_SHIFT); |
481 |
cpu->pc += (low_pc << AVR_INSTR_ALIGNMENT_SHIFT); |
482 |
push_value(cpu, (cpu->pc >> 1) + 1, 2 + cpu->cd.avr.is_22bit); |
483 |
|
484 |
/* Calculate new PC from the next instruction + arg[1] */ |
485 |
cpu->pc += (int32_t)ic->arg[1]; |
486 |
|
487 |
/* Find the new physical page and update the translation pointers: */ |
488 |
avr_pc_to_pointers(cpu); |
489 |
} |
490 |
|
491 |
|
492 |
/* |
493 |
* rjmp: Relative jump. |
494 |
* |
495 |
* arg[1]: relative offset |
496 |
*/ |
497 |
X(rjmp) |
498 |
{ |
499 |
uint32_t low_pc; |
500 |
|
501 |
cpu->cd.avr.extra_cycles ++; |
502 |
|
503 |
/* Calculate new PC from the next instruction + arg[1] */ |
504 |
low_pc = ((size_t)ic - (size_t)cpu->cd.avr.cur_ic_page) / |
505 |
sizeof(struct avr_instr_call); |
506 |
cpu->pc &= ~((AVR_IC_ENTRIES_PER_PAGE-1) |
507 |
<< AVR_INSTR_ALIGNMENT_SHIFT); |
508 |
cpu->pc += (low_pc << AVR_INSTR_ALIGNMENT_SHIFT); |
509 |
cpu->pc += (int32_t)ic->arg[1]; |
510 |
|
511 |
/* Find the new physical page and update the translation pointers: */ |
512 |
avr_pc_to_pointers(cpu); |
513 |
} |
514 |
|
515 |
|
516 |
/* |
517 |
* rjmp_samepage: Relative jump (to within the same translated page). |
518 |
* |
519 |
* arg[1] = pointer to new avr_instr_call |
520 |
*/ |
521 |
X(rjmp_samepage) |
522 |
{ |
523 |
cpu->cd.avr.extra_cycles ++; |
524 |
cpu->cd.avr.next_ic = (struct avr_instr_call *) ic->arg[1]; |
525 |
} |
526 |
|
527 |
|
528 |
/* |
529 |
* seX: Set an sreg bit. |
530 |
*/ |
531 |
X(sec) { cpu->cd.avr.sreg |= AVR_SREG_C; } |
532 |
X(sez) { cpu->cd.avr.sreg |= AVR_SREG_Z; } |
533 |
X(sen) { cpu->cd.avr.sreg |= AVR_SREG_N; } |
534 |
X(sev) { cpu->cd.avr.sreg |= AVR_SREG_V; } |
535 |
X(ses) { cpu->cd.avr.sreg |= AVR_SREG_S; } |
536 |
X(seh) { cpu->cd.avr.sreg |= AVR_SREG_H; } |
537 |
X(set) { cpu->cd.avr.sreg |= AVR_SREG_T; } |
538 |
X(sei) { cpu->cd.avr.sreg |= AVR_SREG_I; } |
539 |
|
540 |
|
541 |
/* |
542 |
* push, pop: Push/pop a register onto/from the stack. |
543 |
* |
544 |
* arg[1]: ptr to rd |
545 |
*/ |
546 |
X(push) { push_value(cpu, *(uint8_t *)(ic->arg[1]), 1); |
547 |
cpu->cd.avr.extra_cycles ++; } |
548 |
X(pop) { uint32_t t; pop_value(cpu, &t, 1); *(uint8_t *)(ic->arg[1]) = t; |
549 |
cpu->cd.avr.extra_cycles ++; } |
550 |
|
551 |
|
552 |
/* |
553 |
* inc, dec: Increment/decrement a register. |
554 |
* |
555 |
* arg[1]: ptr to rd |
556 |
*/ |
557 |
X(inc) |
558 |
{ |
559 |
uint8_t x = *(uint8_t *)(ic->arg[1]) + 1; |
560 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
561 |
| AVR_SREG_Z); |
562 |
if (x == 0) |
563 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
564 |
if (x == 0x80) |
565 |
cpu->cd.avr.sreg |= AVR_SREG_V; |
566 |
if (x & 0x80) |
567 |
cpu->cd.avr.sreg |= AVR_SREG_N; |
568 |
if ((cpu->cd.avr.sreg & AVR_SREG_N? 1 : 0) ^ |
569 |
(cpu->cd.avr.sreg & AVR_SREG_V? 1 : 0)) |
570 |
cpu->cd.avr.sreg |= AVR_SREG_S; |
571 |
*(uint8_t *)(ic->arg[1]) = x; |
572 |
} |
573 |
X(dec) |
574 |
{ |
575 |
uint8_t x = *(uint8_t *)(ic->arg[1]) - 1; |
576 |
cpu->cd.avr.sreg &= ~(AVR_SREG_S | AVR_SREG_V | AVR_SREG_N |
577 |
| AVR_SREG_Z); |
578 |
if (x == 0) |
579 |
cpu->cd.avr.sreg |= AVR_SREG_Z; |
580 |
if (x == 0x7f) |
581 |
cpu->cd.avr.sreg |= AVR_SREG_V; |
582 |
if (x & 0x80) |
583 |
cpu->cd.avr.sreg |= AVR_SREG_N; |
584 |
if ((cpu->cd.avr.sreg & AVR_SREG_N? 1 : 0) ^ |
585 |
(cpu->cd.avr.sreg & AVR_SREG_V? 1 : 0)) |
586 |
cpu->cd.avr.sreg |= AVR_SREG_S; |
587 |
*(uint8_t *)(ic->arg[1]) = x; |
588 |
} |
589 |
|
590 |
|
591 |
/* |
592 |
* swap: Swap nibbles in a register. |
593 |
* |
594 |
* arg[1]: ptr to rd |
595 |
*/ |
596 |
X(swap) |
597 |
{ |
598 |
uint8_t x = *(uint8_t *)(ic->arg[1]); |
599 |
*(uint8_t *)(ic->arg[1]) = (x >> 4) | (x << 4); |
600 |
} |
601 |
|
602 |
|
603 |
/*****************************************************************************/ |
604 |
|
605 |
|
606 |
X(end_of_page) |
607 |
{ |
608 |
/* Update the PC: (offset 0, but on the next page) */ |
609 |
cpu->pc &= ~((AVR_IC_ENTRIES_PER_PAGE-1) << 1); |
610 |
cpu->pc += (AVR_IC_ENTRIES_PER_PAGE << 1); |
611 |
|
612 |
/* Find the new physical page and update the translation pointers: */ |
613 |
avr_pc_to_pointers(cpu); |
614 |
|
615 |
/* end_of_page doesn't count as an executed instruction: */ |
616 |
cpu->n_translated_instrs --; |
617 |
} |
618 |
|
619 |
|
620 |
/*****************************************************************************/ |
621 |
|
622 |
|
623 |
/* |
624 |
* avr_combine_instructions(): |
625 |
* |
626 |
* Combine two or more instructions, if possible, into a single function call. |
627 |
*/ |
628 |
void avr_combine_instructions(struct cpu *cpu, struct avr_instr_call *ic, |
629 |
uint32_t addr) |
630 |
{ |
631 |
int n_back; |
632 |
n_back = (addr >> 1) & (AVR_IC_ENTRIES_PER_PAGE-1); |
633 |
|
634 |
if (n_back >= 1) { |
635 |
/* TODO */ |
636 |
} |
637 |
|
638 |
/* TODO: Combine forward as well */ |
639 |
} |
640 |
|
641 |
|
642 |
/*****************************************************************************/ |
643 |
|
644 |
|
645 |
static uint16_t read_word(struct cpu *cpu, unsigned char *ib, int addr) |
646 |
{ |
647 |
uint16_t iword; |
648 |
unsigned char *page = cpu->cd.avr.host_load[addr >> 12]; |
649 |
|
650 |
if (page != NULL) { |
651 |
/* fatal("TRANSLATION HIT!\n"); */ |
652 |
memcpy(ib, page + (addr & 0xfff), sizeof(uint16_t)); |
653 |
} else { |
654 |
/* fatal("TRANSLATION MISS!\n"); */ |
655 |
if (!cpu->memory_rw(cpu, cpu->mem, addr, ib, |
656 |
sizeof(uint16_t), MEM_READ, CACHE_INSTRUCTION)) { |
657 |
fatal("to_be_translated(): " |
658 |
"read failed: TODO\n"); |
659 |
exit(1); |
660 |
} |
661 |
} |
662 |
|
663 |
iword = *((uint16_t *)&ib[0]); |
664 |
|
665 |
#ifdef HOST_BIG_ENDIAN |
666 |
iword = ((iword & 0xff) << 8) | |
667 |
((iword & 0xff00) >> 8); |
668 |
#endif |
669 |
return iword; |
670 |
} |
671 |
|
672 |
|
673 |
/* |
674 |
* avr_instr_to_be_translated(): |
675 |
* |
676 |
* Translate an instruction word into an avr_instr_call. ic is filled in with |
677 |
* valid data for the translated instruction, or a "nothing" instruction if |
678 |
* there was a translation failure. The newly translated instruction is then |
679 |
* executed. |
680 |
*/ |
681 |
X(to_be_translated) |
682 |
{ |
683 |
int addr, low_pc, rd, rr, tmp, main_opcode, a; |
684 |
uint16_t iword; |
685 |
unsigned char ib[2]; |
686 |
void (*samepage_function)(struct cpu *, struct avr_instr_call *); |
687 |
|
688 |
/* Figure out the (virtual) address of the instruction: */ |
689 |
low_pc = ((size_t)ic - (size_t)cpu->cd.avr.cur_ic_page) |
690 |
/ sizeof(struct avr_instr_call); |
691 |
addr = cpu->pc & ~((AVR_IC_ENTRIES_PER_PAGE-1) << |
692 |
AVR_INSTR_ALIGNMENT_SHIFT); |
693 |
addr += (low_pc << AVR_INSTR_ALIGNMENT_SHIFT); |
694 |
cpu->pc = addr; |
695 |
addr &= ~((1 << AVR_INSTR_ALIGNMENT_SHIFT) - 1); |
696 |
|
697 |
addr &= cpu->cd.avr.pc_mask; |
698 |
|
699 |
/* Read the instruction word from memory: */ |
700 |
iword = read_word(cpu, ib, addr); |
701 |
|
702 |
|
703 |
#define DYNTRANS_TO_BE_TRANSLATED_HEAD |
704 |
#include "cpu_dyntrans.c" |
705 |
#undef DYNTRANS_TO_BE_TRANSLATED_HEAD |
706 |
|
707 |
|
708 |
/* Default instruction length: */ |
709 |
ic->arg[0] = 1; |
710 |
|
711 |
|
712 |
/* |
713 |
* Translate the instruction: |
714 |
*/ |
715 |
main_opcode = iword >> 12; |
716 |
|
717 |
switch (main_opcode) { |
718 |
|
719 |
case 0x0: |
720 |
if (iword == 0x0000) { |
721 |
ic->f = instr(nop); |
722 |
break; |
723 |
} |
724 |
goto bad; |
725 |
|
726 |
case 0x2: |
727 |
if ((iword & 0xfc00) == 0x2000) { |
728 |
rd = (iword & 0x1f0) >> 4; |
729 |
rr = ((iword & 0x200) >> 5) | (iword & 0xf); |
730 |
ic->f = instr(and); |
731 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rr]); |
732 |
ic->arg[2] = (size_t)(&cpu->cd.avr.r[rd]); |
733 |
break; |
734 |
} |
735 |
if ((iword & 0xfc00) == 0x2400) { |
736 |
rd = (iword & 0x1f0) >> 4; |
737 |
rr = ((iword & 0x200) >> 5) | (iword & 0xf); |
738 |
ic->f = instr(eor); |
739 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rr]); |
740 |
ic->arg[2] = (size_t)(&cpu->cd.avr.r[rd]); |
741 |
break; |
742 |
} |
743 |
if ((iword & 0xfc00) == 0x2c00) { |
744 |
rd = (iword & 0x1f0) >> 4; |
745 |
rr = ((iword & 0x200) >> 5) | (iword & 0xf); |
746 |
ic->f = instr(mov); |
747 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rr]); |
748 |
ic->arg[2] = (size_t)(&cpu->cd.avr.r[rd]); |
749 |
break; |
750 |
} |
751 |
goto bad; |
752 |
|
753 |
case 0x3: |
754 |
rd = ((iword >> 4) & 15) + 16; |
755 |
ic->f = instr(cpi); |
756 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
757 |
ic->arg[2] = ((iword >> 4) & 0xf0) + (iword & 0xf); |
758 |
break; |
759 |
|
760 |
case 0x7: |
761 |
rd = ((iword >> 4) & 15) + 16; |
762 |
ic->f = instr(andi); |
763 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
764 |
ic->arg[2] = ((iword >> 4) & 0xf0) + (iword & 0xf); |
765 |
break; |
766 |
|
767 |
case 0x8: |
768 |
if ((iword & 0xfe0f) == 0x8008) { |
769 |
rd = (iword >> 4) & 31; |
770 |
ic->f = instr(ld_y); |
771 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
772 |
break; |
773 |
} |
774 |
if ((iword & 0xfe0f) == 0x8208) { |
775 |
rd = (iword >> 4) & 31; |
776 |
ic->f = instr(st_y); |
777 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
778 |
break; |
779 |
} |
780 |
goto bad; |
781 |
|
782 |
case 0x9: |
783 |
if ((iword & 0xfe0f) == 0x900f) { |
784 |
rd = (iword >> 4) & 31; |
785 |
ic->f = instr(pop); |
786 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
787 |
break; |
788 |
} |
789 |
if ((iword & 0xfe0f) == 0x9200) { |
790 |
uint8_t tmpbytes[2]; |
791 |
ic->arg[0] = 2; /* Note: 2 words! */ |
792 |
ic->f = instr(sts); |
793 |
rd = (iword >> 4) & 31; |
794 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
795 |
ic->arg[2] = read_word(cpu, tmpbytes, addr + 2); |
796 |
break; |
797 |
} |
798 |
if ((iword & 0xfe0f) == 0x9209) { |
799 |
rd = (iword >> 4) & 31; |
800 |
ic->f = instr(st_y_plus); |
801 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
802 |
break; |
803 |
} |
804 |
if ((iword & 0xfe0f) == 0x920a) { |
805 |
rd = (iword >> 4) & 31; |
806 |
ic->f = instr(st_minus_y); |
807 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
808 |
break; |
809 |
} |
810 |
if ((iword & 0xfe0f) == 0x920f) { |
811 |
rd = (iword >> 4) & 31; |
812 |
ic->f = instr(push); |
813 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
814 |
break; |
815 |
} |
816 |
if ((iword & 0xfe0f) == 0x9402) { |
817 |
rd = (iword >> 4) & 31; |
818 |
ic->f = instr(swap); |
819 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
820 |
break; |
821 |
} |
822 |
if ((iword & 0xfe0f) == 0x9403) { |
823 |
rd = (iword >> 4) & 31; |
824 |
ic->f = instr(inc); |
825 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
826 |
break; |
827 |
} |
828 |
if ((iword & 0xff8f) == 0x9408) { |
829 |
switch ((iword >> 4) & 7) { |
830 |
case 0: ic->f = instr(sec); break; |
831 |
case 1: ic->f = instr(sez); break; |
832 |
case 2: ic->f = instr(sen); break; |
833 |
case 3: ic->f = instr(sev); break; |
834 |
case 4: ic->f = instr(ses); break; |
835 |
case 5: ic->f = instr(seh); break; |
836 |
case 6: ic->f = instr(set); break; |
837 |
case 7: ic->f = instr(sei); break; |
838 |
} |
839 |
break; |
840 |
} |
841 |
if ((iword & 0xfe0f) == 0x940a) { |
842 |
rd = (iword >> 4) & 31; |
843 |
ic->f = instr(dec); |
844 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
845 |
break; |
846 |
} |
847 |
if ((iword & 0xff8f) == 0x9488) { |
848 |
switch ((iword >> 4) & 7) { |
849 |
case 0: ic->f = instr(clc); break; |
850 |
case 1: ic->f = instr(clz); break; |
851 |
case 2: ic->f = instr(cln); break; |
852 |
case 3: ic->f = instr(clv); break; |
853 |
case 4: ic->f = instr(cls); break; |
854 |
case 5: ic->f = instr(clh); break; |
855 |
case 6: ic->f = instr(clt); break; |
856 |
case 7: ic->f = instr(cli); break; |
857 |
} |
858 |
break; |
859 |
} |
860 |
if ((iword & 0xffff) == 0x9508) { |
861 |
ic->f = instr(ret); |
862 |
break; |
863 |
} |
864 |
if ((iword & 0xffff) == 0x95c8) { |
865 |
ic->f = instr(lpm); |
866 |
break; |
867 |
} |
868 |
if ((iword & 0xff00) == 0x9600) { |
869 |
ic->f = instr(adiw); |
870 |
rd = ((iword >> 3) & 6) + 24; |
871 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
872 |
ic->arg[2] = (iword & 15) + ((iword & 0xc0) >> 2); |
873 |
break; |
874 |
} |
875 |
if ((iword & 0xfd00) == 0x9800) { |
876 |
if (iword & 0x200) |
877 |
ic->f = instr(sbi); |
878 |
else |
879 |
ic->f = instr(cbi); |
880 |
ic->arg[1] = (iword >> 3) & 31; |
881 |
ic->arg[2] = 1 << (iword & 7); |
882 |
if (!(iword & 0x200)) |
883 |
ic->arg[2] = ~ic->arg[2]; |
884 |
break; |
885 |
} |
886 |
goto bad; |
887 |
|
888 |
case 0xb: |
889 |
if ((iword & 0xf800) == 0xb800) { |
890 |
a = ((iword & 0x600) >> 5) | (iword & 0xf); |
891 |
rr = (iword >> 4) & 31; |
892 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rr]); |
893 |
ic->arg[2] = a; |
894 |
switch (a) { |
895 |
case 0x1a: ic->f = instr(out_ddra); break; |
896 |
case 0x17: ic->f = instr(out_ddrb); break; |
897 |
case 0x14: ic->f = instr(out_ddrc); break; |
898 |
case 0x11: ic->f = instr(out_ddrd); break; |
899 |
case 0x1b: ic->f = instr(out_porta); break; |
900 |
case 0x18: ic->f = instr(out_portb); break; |
901 |
case 0x15: ic->f = instr(out_portc); break; |
902 |
case 0x12: ic->f = instr(out_portd); break; |
903 |
default:ic->f = instr(out); |
904 |
} |
905 |
break; |
906 |
} |
907 |
goto bad; |
908 |
|
909 |
case 0xc: /* rjmp */ |
910 |
case 0xd: /* rcall */ |
911 |
samepage_function = NULL; |
912 |
switch (main_opcode) { |
913 |
case 0xc: |
914 |
ic->f = instr(rjmp); |
915 |
samepage_function = instr(rjmp_samepage); |
916 |
break; |
917 |
case 0xd: |
918 |
ic->f = instr(rcall); |
919 |
break; |
920 |
} |
921 |
ic->arg[1] = (((int16_t)((iword & 0x0fff) << 4)) >> 3) + 2; |
922 |
/* Special case: branch within the same page: */ |
923 |
if (samepage_function != NULL) { |
924 |
uint32_t mask_within_page = |
925 |
((AVR_IC_ENTRIES_PER_PAGE-1) << |
926 |
AVR_INSTR_ALIGNMENT_SHIFT) | |
927 |
((1 << AVR_INSTR_ALIGNMENT_SHIFT) - 1); |
928 |
uint32_t old_pc = addr; |
929 |
uint32_t new_pc = old_pc + (int32_t)ic->arg[1]; |
930 |
if ((old_pc & ~mask_within_page) == |
931 |
(new_pc & ~mask_within_page)) { |
932 |
ic->f = samepage_function; |
933 |
ic->arg[1] = (size_t) ( |
934 |
cpu->cd.avr.cur_ic_page + |
935 |
((new_pc & mask_within_page) >> |
936 |
AVR_INSTR_ALIGNMENT_SHIFT)); |
937 |
} |
938 |
} |
939 |
break; |
940 |
|
941 |
case 0xe: |
942 |
rd = ((iword >> 4) & 0xf) + 16; |
943 |
ic->f = instr(ldi); |
944 |
ic->arg[1] = (size_t)(&cpu->cd.avr.r[rd]); |
945 |
ic->arg[2] = ((iword >> 4) & 0xf0) | (iword & 0xf); |
946 |
break; |
947 |
|
948 |
case 0xf: |
949 |
if ((iword & 0xfc07) == 0xf001) { |
950 |
ic->f = instr(breq); |
951 |
samepage_function = instr(breq_samepage); |
952 |
tmp = (iword >> 3) & 0x7f; |
953 |
if (tmp >= 64) |
954 |
tmp -= 128; |
955 |
ic->arg[1] = (tmp + 1) * 2; |
956 |
/* Special case: branch within the same page: */ |
957 |
if (samepage_function != NULL) { |
958 |
uint32_t mask_within_page = |
959 |
((AVR_IC_ENTRIES_PER_PAGE-1) << |
960 |
AVR_INSTR_ALIGNMENT_SHIFT) | |
961 |
((1 << AVR_INSTR_ALIGNMENT_SHIFT) - 1); |
962 |
uint32_t old_pc = addr; |
963 |
uint32_t new_pc = old_pc + (int32_t)ic->arg[1]; |
964 |
if ((old_pc & ~mask_within_page) == |
965 |
(new_pc & ~mask_within_page)) { |
966 |
ic->f = samepage_function; |
967 |
ic->arg[1] = (size_t) ( |
968 |
cpu->cd.avr.cur_ic_page + |
969 |
((new_pc & mask_within_page) >> |
970 |
AVR_INSTR_ALIGNMENT_SHIFT)); |
971 |
} |
972 |
} |
973 |
break; |
974 |
} |
975 |
/* TODO: refactor */ |
976 |
if ((iword & 0xfc07) == 0xf401) { |
977 |
ic->f = instr(brne); |
978 |
samepage_function = instr(brne_samepage); |
979 |
tmp = (iword >> 3) & 0x7f; |
980 |
if (tmp >= 64) |
981 |
tmp -= 128; |
982 |
ic->arg[1] = (tmp + 1) * 2; |
983 |
/* Special case: branch within the same page: */ |
984 |
if (samepage_function != NULL) { |
985 |
uint32_t mask_within_page = |
986 |
((AVR_IC_ENTRIES_PER_PAGE-1) << |
987 |
AVR_INSTR_ALIGNMENT_SHIFT) | |
988 |
((1 << AVR_INSTR_ALIGNMENT_SHIFT) - 1); |
989 |
uint32_t old_pc = addr; |
990 |
uint32_t new_pc = old_pc + (int32_t)ic->arg[1]; |
991 |
if ((old_pc & ~mask_within_page) == |
992 |
(new_pc & ~mask_within_page)) { |
993 |
ic->f = samepage_function; |
994 |
ic->arg[1] = (size_t) ( |
995 |
cpu->cd.avr.cur_ic_page + |
996 |
((new_pc & mask_within_page) >> |
997 |
AVR_INSTR_ALIGNMENT_SHIFT)); |
998 |
} |
999 |
} |
1000 |
break; |
1001 |
} |
1002 |
goto bad; |
1003 |
|
1004 |
default:goto bad; |
1005 |
} |
1006 |
|
1007 |
|
1008 |
#define DYNTRANS_TO_BE_TRANSLATED_TAIL |
1009 |
#include "cpu_dyntrans.c" |
1010 |
#undef DYNTRANS_TO_BE_TRANSLATED_TAIL |
1011 |
} |
1012 |
|