1 |
/* |
2 |
* Copyright (C) 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_m88k_instr_loadstore.c,v 1.8 2007/05/27 03:01:28 debug Exp $ |
29 |
* |
30 |
* M88K load/store instructions; the following args are used: |
31 |
* |
32 |
* arg[0] = pointer to the register to load to or store from (d) |
33 |
* arg[1] = pointer to the base register (s1) |
34 |
* arg[2] = pointer to the offset register (s2), or an uint32_t offset |
35 |
* |
36 |
* The GENERIC function always checks for alignment, and supports both big |
37 |
* and little endian byte order. |
38 |
* |
39 |
* The quick function is included twice (big/little endian) for each |
40 |
* GENERIC function. |
41 |
* |
42 |
* |
43 |
* Defines: |
44 |
* LS_LOAD or LS_STORE (only one) |
45 |
* LS_INCLUDE_GENERIC (to generate the generic function) |
46 |
* LS_GENERIC_N is defined as the name of the generic function |
47 |
* LS_N is defined as the name of the fast function |
48 |
* LS_1, LS_2, LS_4, or LS_8 (only one) |
49 |
* LS_SIZE is defined to 1, 2, 4, or 8 |
50 |
* LS_SIGNED is defined for signed loads |
51 |
* LS_LE or LS_BE (only one) |
52 |
* LS_SCALED for scaled accesses |
53 |
* LS_USR for usr accesses |
54 |
* LS_REGOFS is defined when arg[2] is a register pointer |
55 |
*/ |
56 |
|
57 |
|
58 |
#ifdef LS_INCLUDE_GENERIC |
59 |
void LS_GENERIC_N(struct cpu *cpu, struct m88k_instr_call *ic) |
60 |
{ |
61 |
#ifdef LS_USR |
62 |
const int memory_rw_flags = CACHE_DATA | MEMORY_USER_ACCESS; |
63 |
#else |
64 |
const int memory_rw_flags = CACHE_DATA; |
65 |
#endif |
66 |
uint32_t addr = reg(ic->arg[1]) + |
67 |
#ifdef LS_REGOFS |
68 |
#ifdef LS_SCALED |
69 |
LS_SIZE * |
70 |
#endif |
71 |
reg(ic->arg[2]); |
72 |
#else |
73 |
ic->arg[2]; |
74 |
#endif |
75 |
uint8_t data[LS_SIZE]; |
76 |
uint64_t x; |
77 |
|
78 |
/* Synchronize the PC: */ |
79 |
int low_pc = ((size_t)ic - (size_t)cpu->cd.m88k.cur_ic_page) |
80 |
/ sizeof(struct m88k_instr_call); |
81 |
cpu->pc &= ~((M88K_IC_ENTRIES_PER_PAGE-1)<<M88K_INSTR_ALIGNMENT_SHIFT); |
82 |
cpu->pc += (low_pc << M88K_INSTR_ALIGNMENT_SHIFT); |
83 |
|
84 |
/* |
85 |
* Update the memory transaction registers: |
86 |
*/ |
87 |
cpu->cd.m88k.dmt[1] = 0; |
88 |
|
89 |
cpu->cd.m88k.dmt[0] = DMT_VALID; |
90 |
#ifdef LS_STORE |
91 |
cpu->cd.m88k.dmt[0] |= DMT_WRITE; |
92 |
#else |
93 |
{ |
94 |
int dreg = (((uint32_t *)ic->arg[0]) - &cpu->cd.m88k.r[0]); |
95 |
if (dreg < 1 || dreg > 31) { |
96 |
fatal("HUH? dreg = %i in cpu_m88k_instr_loadstore.c." |
97 |
" Internal error.\n", dreg); |
98 |
exit(1); |
99 |
} |
100 |
cpu->cd.m88k.dmt[0] |= dreg << DMT_DREGSHIFT; |
101 |
} |
102 |
#ifdef LS_SIGNED |
103 |
cpu->cd.m88k.dmt[0] |= DMT_SIGNED; |
104 |
#endif |
105 |
#endif |
106 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
107 |
cpu->cd.m88k.dmt[0] |= DMT_BO; |
108 |
|
109 |
#ifndef LS_USR |
110 |
if (cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE) |
111 |
cpu->cd.m88k.dmt[0] |= DMT_DAS; /* supervisor */ |
112 |
#endif |
113 |
|
114 |
/* EN bits: */ |
115 |
#ifdef LS_1 |
116 |
/* TODO: Is the EN offset only valid for Big-Endian? */ |
117 |
cpu->cd.m88k.dmt[0] |= 1 << DMT_ENSHIFT << (3 - (addr & 3)); |
118 |
#else |
119 |
#ifdef LS_2 |
120 |
cpu->cd.m88k.dmt[0] |= 3 << DMT_ENSHIFT << (2 - (addr & 2)); |
121 |
#else |
122 |
cpu->cd.m88k.dmt[0] |= 0xf << DMT_ENSHIFT; |
123 |
#endif |
124 |
#endif |
125 |
|
126 |
cpu->cd.m88k.dma[0] = addr & ~0x3; |
127 |
cpu->cd.m88k.dmd[0] = 0; |
128 |
|
129 |
#ifdef LS_8 |
130 |
cpu->cd.m88k.dmt[1] = cpu->cd.m88k.dmt[0]; |
131 |
cpu->cd.m88k.dmt[0] |= DMT_DOUB1; |
132 |
cpu->cd.m88k.dma[1] = cpu->cd.m88k.dma[0] + sizeof(uint32_t); |
133 |
cpu->cd.m88k.dmd[0] = 0; |
134 |
#ifdef LS_LOAD |
135 |
{ |
136 |
int dreg = (((uint32_t *)ic->arg[0]) - &cpu->cd.m88k.r[0]); |
137 |
dreg ++; |
138 |
if (dreg < 1 || dreg > 31) { |
139 |
fatal("HUH? dreg = %i in cpu_m88k_instr_loadstore.c." |
140 |
" Internal error.\n", dreg); |
141 |
exit(1); |
142 |
} |
143 |
cpu->cd.m88k.dmt[1] &= ~((0x1f) << DMT_DREGSHIFT); |
144 |
cpu->cd.m88k.dmt[1] |= dreg << DMT_DREGSHIFT; |
145 |
} |
146 |
#endif |
147 |
#endif |
148 |
|
149 |
|
150 |
#ifdef LS_USR |
151 |
if (!(cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE)) { |
152 |
/* Cause a privilege violation exception: */ |
153 |
m88k_exception(cpu, M88K_EXCEPTION_PRIVILEGE_VIOLATION, 0); |
154 |
return; |
155 |
} |
156 |
#endif |
157 |
|
158 |
#ifndef LS_1 |
159 |
/* Check alignment: */ |
160 |
if (addr & (LS_SIZE - 1)) { |
161 |
#if 0 |
162 |
/* Cause an address alignment exception: */ |
163 |
m88k_exception(cpu, M88K_EXCEPTION_MISALIGNED_ACCESS, 0); |
164 |
#else |
165 |
fatal("{ m88k dyntrans alignment exception, size = %i," |
166 |
" addr = %08"PRIx32", pc = %08"PRIx32" }\n", LS_SIZE, |
167 |
(uint32_t) addr, (uint32_t) cpu->pc); |
168 |
|
169 |
/* TODO: Generalize this into a abort_call, or similar: */ |
170 |
cpu->running = 0; |
171 |
debugger_n_steps_left_before_interaction = 0; |
172 |
cpu->cd.m88k.next_ic = ¬hing_call; |
173 |
|
174 |
if (cpu->delay_slot) |
175 |
cpu->delay_slot |= EXCEPTION_IN_DELAY_SLOT; |
176 |
#endif |
177 |
return; |
178 |
} |
179 |
#endif |
180 |
|
181 |
#ifdef LS_LOAD |
182 |
if (!cpu->memory_rw(cpu, cpu->mem, addr, data, sizeof(data), |
183 |
MEM_READ, memory_rw_flags)) { |
184 |
/* Exception. */ |
185 |
return; |
186 |
} |
187 |
x = memory_readmax64(cpu, data, LS_SIZE); |
188 |
#ifdef LS_8 |
189 |
if (cpu->byte_order == EMUL_BIG_ENDIAN) { |
190 |
reg(ic->arg[0]) = x >> 32; |
191 |
reg(ic->arg[0] + 4) = x; |
192 |
} else { |
193 |
reg(ic->arg[0]) = x; |
194 |
reg(ic->arg[0] + 4) = x >> 32; |
195 |
} |
196 |
#else |
197 |
#ifdef LS_SIGNED |
198 |
#ifdef LS_1 |
199 |
x = (int8_t)x; |
200 |
#endif |
201 |
#ifdef LS_2 |
202 |
x = (int16_t)x; |
203 |
#endif |
204 |
#ifdef LS_4 |
205 |
x = (int32_t)x; |
206 |
#endif |
207 |
#endif |
208 |
reg(ic->arg[0]) = x; |
209 |
#endif |
210 |
|
211 |
#else /* LS_STORE: */ |
212 |
|
213 |
#ifdef LS_8 |
214 |
if (cpu->byte_order == EMUL_BIG_ENDIAN) |
215 |
x = ((uint64_t)reg(ic->arg[0]) << 32) + reg(ic->arg[0] + 4); |
216 |
else |
217 |
x = ((uint64_t)reg(ic->arg[0] + 4) << 32) + reg(ic->arg[0]); |
218 |
cpu->cd.m88k.dmd[0] = reg(ic->arg[0]); |
219 |
cpu->cd.m88k.dmd[1] = reg(ic->arg[0] + 4); |
220 |
#else |
221 |
x = reg(ic->arg[0]); |
222 |
cpu->cd.m88k.dmd[0] = x; |
223 |
#endif |
224 |
memory_writemax64(cpu, data, LS_SIZE, x); |
225 |
if (!cpu->memory_rw(cpu, cpu->mem, addr, data, sizeof(data), |
226 |
MEM_WRITE, memory_rw_flags)) { |
227 |
/* Exception. */ |
228 |
return; |
229 |
} |
230 |
#endif |
231 |
} |
232 |
#endif /* LS_INCLUDE_GENERIC */ |
233 |
|
234 |
|
235 |
void LS_N(struct cpu *cpu, struct m88k_instr_call *ic) |
236 |
{ |
237 |
uint32_t addr = reg(ic->arg[1]) + |
238 |
#ifdef LS_REGOFS |
239 |
#ifdef LS_SCALED |
240 |
LS_SIZE * |
241 |
#endif |
242 |
reg(ic->arg[2]); |
243 |
#else |
244 |
ic->arg[2]; |
245 |
#endif |
246 |
|
247 |
#ifdef LS_USR |
248 |
#ifdef LS_LOAD |
249 |
uint8_t *p = cpu->cd.m88k.host_load_usr[addr >> 12]; |
250 |
#else |
251 |
uint8_t *p = cpu->cd.m88k.host_store_usr[addr >> 12]; |
252 |
#endif |
253 |
#else |
254 |
#ifdef LS_LOAD |
255 |
uint8_t *p = cpu->cd.m88k.host_load[addr >> 12]; |
256 |
#else |
257 |
uint8_t *p = cpu->cd.m88k.host_store[addr >> 12]; |
258 |
#endif |
259 |
#endif |
260 |
|
261 |
/* |
262 |
* Call the generic function, if things become too complicated: |
263 |
* |
264 |
* 1) .usr used in non-supervisor mode |
265 |
* 2) the page pointer is NULL |
266 |
* 3) unaligned access |
267 |
*/ |
268 |
if ( |
269 |
#ifdef LS_USR |
270 |
!(cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE) || |
271 |
#endif |
272 |
|
273 |
p == NULL |
274 |
#ifndef LS_1 |
275 |
|| addr & (LS_SIZE - 1) |
276 |
#endif |
277 |
) { |
278 |
LS_GENERIC_N(cpu, ic); |
279 |
return; |
280 |
} |
281 |
|
282 |
addr &= 0xfff; |
283 |
|
284 |
#ifdef LS_LOAD |
285 |
/* Load: */ |
286 |
|
287 |
#ifdef LS_1 |
288 |
reg(ic->arg[0]) = |
289 |
#ifdef LS_SIGNED |
290 |
(int8_t) |
291 |
#endif |
292 |
p[addr]; |
293 |
#endif /* LS_1 */ |
294 |
|
295 |
#ifdef LS_2 |
296 |
reg(ic->arg[0]) = |
297 |
#ifdef LS_SIGNED |
298 |
(int16_t) |
299 |
#endif |
300 |
#ifdef LS_BE |
301 |
#ifdef HOST_BIG_ENDIAN |
302 |
( *(uint16_t *)(p + addr) ); |
303 |
#else |
304 |
((p[addr]<<8) + p[addr+1]); |
305 |
#endif |
306 |
#else |
307 |
#ifdef HOST_LITTLE_ENDIAN |
308 |
( *(uint16_t *)(p + addr) ); |
309 |
#else |
310 |
(p[addr] + (p[addr+1]<<8)); |
311 |
#endif |
312 |
#endif |
313 |
#endif /* LS_2 */ |
314 |
|
315 |
#ifdef LS_4 |
316 |
reg(ic->arg[0]) = |
317 |
#ifdef LS_SIGNED |
318 |
(int32_t) |
319 |
#else |
320 |
(uint32_t) |
321 |
#endif |
322 |
#ifdef LS_BE |
323 |
#ifdef HOST_BIG_ENDIAN |
324 |
( *(uint32_t *)(p + addr) ); |
325 |
#else |
326 |
((p[addr]<<24) + (p[addr+1]<<16) + (p[addr+2]<<8) + p[addr+3]); |
327 |
#endif |
328 |
#else |
329 |
#ifdef HOST_LITTLE_ENDIAN |
330 |
( *(uint32_t *)(p + addr) ); |
331 |
#else |
332 |
(p[addr] + (p[addr+1]<<8) + (p[addr+2]<<16) + (p[addr+3]<<24)); |
333 |
#endif |
334 |
#endif |
335 |
#endif /* LS_4 */ |
336 |
|
337 |
#ifdef LS_8 |
338 |
|
339 |
/* Load first word in pair: */ |
340 |
reg(ic->arg[0]) = |
341 |
#ifdef LS_BE |
342 |
#ifdef HOST_BIG_ENDIAN |
343 |
( *(uint32_t *)(p + addr) ); |
344 |
#else |
345 |
((p[addr]<<24) + (p[addr+1]<<16) + (p[addr+2]<<8) + p[addr+3]); |
346 |
#endif |
347 |
#else |
348 |
#ifdef HOST_LITTLE_ENDIAN |
349 |
( *(uint32_t *)(p + addr + 4) ); |
350 |
#else |
351 |
(p[addr+4] + (p[addr+5]<<8) + (p[addr+6]<<16) + (p[addr+7]<<24)); |
352 |
#endif |
353 |
#endif |
354 |
|
355 |
/* Load second word in pair: */ |
356 |
reg(ic->arg[0] + 4) = |
357 |
#ifdef LS_BE |
358 |
#ifdef HOST_BIG_ENDIAN |
359 |
( *(uint32_t *)(p + addr + 4) ); |
360 |
#else |
361 |
((p[addr+4]<<24) + (p[addr+5]<<16) + (p[addr+6]<<8) + p[addr+7]); |
362 |
#endif |
363 |
#else |
364 |
#ifdef HOST_LITTLE_ENDIAN |
365 |
( *(uint32_t *)(p + addr) ); |
366 |
#else |
367 |
(p[addr] + (p[addr+1]<<8) + (p[addr+2]<<16) + (p[addr+3]<<24)); |
368 |
#endif |
369 |
#endif |
370 |
|
371 |
#endif /* LS_8 */ |
372 |
|
373 |
#else |
374 |
/* Store: */ |
375 |
|
376 |
#ifdef LS_1 |
377 |
p[addr] = reg(ic->arg[0]); |
378 |
#endif |
379 |
#ifdef LS_2 |
380 |
{ uint32_t x = reg(ic->arg[0]); |
381 |
#ifdef LS_BE |
382 |
#ifdef HOST_BIG_ENDIAN |
383 |
*((uint16_t *)(p+addr)) = x; } |
384 |
#else |
385 |
p[addr] = x >> 8; p[addr+1] = x; } |
386 |
#endif |
387 |
#else |
388 |
#ifdef HOST_LITTLE_ENDIAN |
389 |
*((uint16_t *)(p+addr)) = x; } |
390 |
#else |
391 |
p[addr] = x; p[addr+1] = x >> 8; } |
392 |
#endif |
393 |
#endif |
394 |
#endif /* LS_2 */ |
395 |
#ifdef LS_4 |
396 |
{ uint32_t x = reg(ic->arg[0]); |
397 |
#ifdef LS_BE |
398 |
#ifdef HOST_BIG_ENDIAN |
399 |
*((uint32_t *)(p+addr)) = x; } |
400 |
#else |
401 |
p[addr] = x >> 24; p[addr+1] = x >> 16; |
402 |
p[addr+2] = x >> 8; p[addr+3] = x; } |
403 |
#endif |
404 |
#else |
405 |
#ifdef HOST_LITTLE_ENDIAN |
406 |
*((uint32_t *)(p+addr)) = x; } |
407 |
#else |
408 |
p[addr] = x; p[addr+1] = x >> 8; |
409 |
p[addr+2] = x >> 16; p[addr+3] = x >> 24; } |
410 |
#endif |
411 |
#endif |
412 |
#endif /* LS_4 */ |
413 |
#ifdef LS_8 |
414 |
|
415 |
/* First word in pair: */ |
416 |
{ uint32_t x = reg(ic->arg[0]); |
417 |
#ifdef LS_BE |
418 |
#ifdef HOST_BIG_ENDIAN |
419 |
*((uint32_t *)(p+addr)) = x; } |
420 |
#else |
421 |
p[addr] = x >> 24; p[addr+1] = x >> 16; |
422 |
p[addr+2] = x >> 8; p[addr+3] = x; } |
423 |
#endif |
424 |
#else |
425 |
#ifdef HOST_LITTLE_ENDIAN |
426 |
*((uint32_t *)(p+addr+4)) = x; } |
427 |
#else |
428 |
p[addr +4] = x; p[addr+1+4] = x >> 8; |
429 |
p[addr+2+4] = x >> 16; p[addr+3+4] = x >> 24; } |
430 |
#endif |
431 |
#endif |
432 |
|
433 |
/* Second word in pair: */ |
434 |
{ uint32_t x = reg(ic->arg[0] + 4); |
435 |
#ifdef LS_BE |
436 |
#ifdef HOST_BIG_ENDIAN |
437 |
*((uint32_t *)(p+addr+4)) = x; } |
438 |
#else |
439 |
p[addr +4] = x >> 24; p[addr+1+4] = x >> 16; |
440 |
p[addr+2+4] = x >> 8; p[addr+3+4] = x; } |
441 |
#endif |
442 |
#else |
443 |
#ifdef HOST_LITTLE_ENDIAN |
444 |
*((uint32_t *)(p+addr)) = x; } |
445 |
#else |
446 |
p[addr ] = x; p[addr+1] = x >> 8; |
447 |
p[addr+2] = x >> 16; p[addr+3] = x >> 24; } |
448 |
#endif |
449 |
#endif |
450 |
|
451 |
#endif /* LS_8 */ |
452 |
|
453 |
#endif /* store */ |
454 |
} |
455 |
|