2823a4c8 |
1 | .align 2 |
2 | |
3 | .global arm_update_gba_arm |
4 | .global arm_update_gba_thumb |
5 | .global arm_update_gba_idle_arm |
6 | .global arm_update_gba_idle_thumb |
7 | |
8 | .global arm_indirect_branch_arm |
9 | .global arm_indirect_branch_thumb |
10 | .global arm_indirect_branch_dual_arm |
11 | .global arm_indirect_branch_dual_thumb |
12 | |
13 | .global execute_arm_translate |
14 | |
15 | .global execute_store_u8 |
16 | .global execute_store_u16 |
17 | .global execute_store_u32 |
18 | .global execute_store_u32_safe |
19 | |
20 | .global execute_load_u8 |
21 | .global execute_load_s8 |
22 | .global execute_load_u16 |
23 | .global execute_load_s16 |
24 | .global execute_load_u32 |
25 | |
26 | .global execute_store_cpsr |
27 | .global execute_read_spsr |
28 | .global execute_store_spsr |
29 | .global execute_spsr_restore |
30 | |
31 | .global execute_swi_arm |
32 | .global execute_swi_thumb |
33 | |
34 | .global execute_patch_bios_read |
35 | .global execute_patch_bios_protect |
36 | |
37 | .global execute_bios_ptr_protected |
38 | .global execute_bios_rom_ptr |
39 | |
40 | |
41 | .global step_debug_arm |
42 | |
43 | .global invalidate_icache_region |
44 | .global invalidate_cache_region |
45 | |
46 | .global memory_map_read |
47 | .global memory_map_write |
48 | .global reg |
49 | |
50 | #define REG_BASE_OFFSET 1024 |
51 | |
52 | #define REG_R0 (REG_BASE_OFFSET + (0 * 4)) |
53 | #define REG_R1 (REG_BASE_OFFSET + (1 * 4)) |
54 | #define REG_R2 (REG_BASE_OFFSET + (2 * 4)) |
55 | #define REG_R3 (REG_BASE_OFFSET + (3 * 4)) |
56 | #define REG_R4 (REG_BASE_OFFSET + (4 * 4)) |
57 | #define REG_R5 (REG_BASE_OFFSET + (5 * 4)) |
58 | #define REG_R6 (REG_BASE_OFFSET + (6 * 4)) |
59 | #define REG_R7 (REG_BASE_OFFSET + (7 * 4)) |
60 | #define REG_R8 (REG_BASE_OFFSET + (8 * 4)) |
61 | #define REG_R9 (REG_BASE_OFFSET + (9 * 4)) |
62 | #define REG_R10 (REG_BASE_OFFSET + (10 * 4)) |
63 | #define REG_R11 (REG_BASE_OFFSET + (11 * 4)) |
64 | #define REG_R12 (REG_BASE_OFFSET + (12 * 4)) |
65 | #define REG_R13 (REG_BASE_OFFSET + (13 * 4)) |
66 | #define REG_R14 (REG_BASE_OFFSET + (14 * 4)) |
67 | #define REG_SP (REG_BASE_OFFSET + (13 * 4)) |
68 | #define REG_LR (REG_BASE_OFFSET + (14 * 4)) |
69 | #define REG_PC (REG_BASE_OFFSET + (15 * 4)) |
70 | |
71 | #define REG_N_FLAG (REG_BASE_OFFSET + (16 * 4)) |
72 | #define REG_Z_FLAG (REG_BASE_OFFSET + (17 * 4)) |
73 | #define REG_C_FLAG (REG_BASE_OFFSET + (18 * 4)) |
74 | #define REG_V_FLAG (REG_BASE_OFFSET + (19 * 4)) |
75 | #define REG_CPSR (REG_BASE_OFFSET + (20 * 4)) |
76 | |
77 | #define REG_SAVE (REG_BASE_OFFSET + (21 * 4)) |
78 | #define REG_SAVE2 (REG_BASE_OFFSET + (22 * 4)) |
79 | #define REG_SAVE3 (REG_BASE_OFFSET + (23 * 4)) |
80 | |
81 | #define CPU_MODE (REG_BASE_OFFSET + (29 * 4)) |
82 | #define CPU_HALT_STATE (REG_BASE_OFFSET + (30 * 4)) |
83 | #define CHANGED_PC_STATUS (REG_BASE_OFFSET + (31 * 4)) |
84 | |
85 | |
86 | #define reg_a0 r0 |
87 | #define reg_a1 r1 |
88 | #define reg_a2 r2 |
89 | |
90 | #define reg_s0 r9 |
91 | #define reg_base sp |
92 | #define reg_flags r11 |
93 | |
94 | #define reg_cycles r12 |
95 | |
96 | #define reg_x0 r3 |
97 | #define reg_x1 r4 |
98 | #define reg_x2 r5 |
99 | #define reg_x3 r6 |
100 | #define reg_x4 r7 |
101 | #define reg_x5 r8 |
102 | |
103 | |
104 | #define MODE_SUPERVISOR 3 |
105 | |
106 | |
107 | @ Will load the register set from memory into the appropriate cached registers. |
108 | @ See arm_emit.h for listing explanation. |
109 | |
110 | #define load_registers_arm() ;\ |
111 | ldr reg_x0, [reg_base, #REG_R0] ;\ |
112 | ldr reg_x1, [reg_base, #REG_R1] ;\ |
113 | ldr reg_x2, [reg_base, #REG_R6] ;\ |
114 | ldr reg_x3, [reg_base, #REG_R9] ;\ |
115 | ldr reg_x4, [reg_base, #REG_R12] ;\ |
116 | ldr reg_x5, [reg_base, #REG_R14] ;\ |
117 | |
118 | #define load_registers_thumb() ;\ |
119 | ldr reg_x0, [reg_base, #REG_R0] ;\ |
120 | ldr reg_x1, [reg_base, #REG_R1] ;\ |
121 | ldr reg_x2, [reg_base, #REG_R2] ;\ |
122 | ldr reg_x3, [reg_base, #REG_R3] ;\ |
123 | ldr reg_x4, [reg_base, #REG_R4] ;\ |
124 | ldr reg_x5, [reg_base, #REG_R5] ;\ |
125 | |
126 | |
127 | @ Will store the register set from cached registers back to memory. |
128 | |
129 | #define store_registers_arm() ;\ |
130 | str reg_x0, [reg_base, #REG_R0] ;\ |
131 | str reg_x1, [reg_base, #REG_R1] ;\ |
132 | str reg_x2, [reg_base, #REG_R6] ;\ |
133 | str reg_x3, [reg_base, #REG_R9] ;\ |
134 | str reg_x4, [reg_base, #REG_R12] ;\ |
135 | str reg_x5, [reg_base, #REG_R14] ;\ |
136 | |
137 | #define store_registers_thumb() ;\ |
138 | str reg_x0, [reg_base, #REG_R0] ;\ |
139 | str reg_x1, [reg_base, #REG_R1] ;\ |
140 | str reg_x2, [reg_base, #REG_R2] ;\ |
141 | str reg_x3, [reg_base, #REG_R3] ;\ |
142 | str reg_x4, [reg_base, #REG_R4] ;\ |
143 | str reg_x5, [reg_base, #REG_R5] ;\ |
144 | |
145 | |
146 | @ Returns an updated persistent cpsr with the cached flags register. |
147 | @ Uses reg as a temporary register and returns the CPSR here. |
148 | |
149 | #define collapse_flags_no_update(reg) ;\ |
150 | ldr reg, [reg_base, #REG_CPSR] /* reg = cpsr */;\ |
151 | bic reg, reg, #0xF0000000 /* clear ALU flags in cpsr */;\ |
152 | and reg_flags, reg_flags, #0xF0000000 /* clear non-ALU flags */;\ |
153 | orr reg, reg, reg_flags /* update cpsr with ALU flags */;\ |
154 | |
155 | @ Updates cpsr using the above macro. |
156 | |
157 | #define collapse_flags(reg) ;\ |
158 | collapse_flags_no_update(reg) ;\ |
159 | str reg, [reg_base, #REG_CPSR] ;\ |
160 | |
161 | @ Loads the saved flags register from the persistent cpsr. |
162 | |
163 | #define extract_flags() ;\ |
164 | ldr reg_flags, [reg_base, #REG_CPSR] ;\ |
165 | msr cpsr_f, reg_flags ;\ |
166 | |
167 | |
168 | #define save_flags() ;\ |
169 | mrs reg_flags, cpsr ;\ |
170 | |
171 | #define restore_flags() ;\ |
172 | msr cpsr_f, reg_flags ;\ |
173 | |
0a74ef66 |
174 | #ifdef __ARM_EABI__ |
175 | @ must align stack |
176 | #define call_c_saved_regs r2, r3, r12, lr |
177 | #else |
178 | #define call_c_saved_regs r3, r12, lr |
179 | #endif |
180 | |
2823a4c8 |
181 | @ Calls a C function - all caller save registers which are important to the |
182 | @ dynarec and to returning from this function are saved. |
183 | |
184 | #define call_c_function(function) ;\ |
0a74ef66 |
185 | stmdb sp!, { call_c_saved_regs } ;\ |
2823a4c8 |
186 | bl function ;\ |
0a74ef66 |
187 | ldmia sp!, { call_c_saved_regs } ;\ |
2823a4c8 |
188 | |
189 | |
190 | @ Update the GBA hardware (video, sound, input, etc) |
191 | |
192 | @ Input: |
193 | @ r0: current PC |
194 | |
195 | #define return_straight() ;\ |
196 | bx lr ;\ |
197 | |
198 | #define return_add() ;\ |
199 | add pc, lr, #4 ;\ |
200 | |
201 | #define load_pc_straight() ;\ |
202 | ldr r0, [lr, #-8] ;\ |
203 | |
204 | #define load_pc_add() ;\ |
205 | ldr r0, [lr] ;\ |
206 | |
207 | |
208 | #define arm_update_gba_builder(name, mode, return_op) ;\ |
209 | ;\ |
210 | arm_update_gba_##name: ;\ |
211 | load_pc_##return_op() ;\ |
212 | str r0, [reg_base, #REG_PC] /* write out the PC */;\ |
213 | ;\ |
214 | save_flags() ;\ |
215 | collapse_flags(r0) /* update the flags */;\ |
216 | ;\ |
217 | store_registers_##mode() /* save out registers */;\ |
218 | call_c_function(update_gba) /* update GBA state */;\ |
219 | ;\ |
220 | mvn reg_cycles, r0 /* load new cycle count */;\ |
221 | ;\ |
222 | ldr r0, [reg_base, #CHANGED_PC_STATUS] /* load PC changed status */;\ |
223 | cmp r0, #0 /* see if PC has changed */;\ |
224 | beq 1f /* if not return */;\ |
225 | ;\ |
226 | ldr r0, [reg_base, #REG_PC] /* load new PC */;\ |
227 | ldr r1, [reg_base, #REG_CPSR] /* r1 = flags */;\ |
228 | tst r1, #0x20 /* see if Thumb bit is set */;\ |
229 | bne 2f /* if so load Thumb PC */;\ |
230 | ;\ |
231 | load_registers_arm() /* load ARM regs */;\ |
232 | call_c_function(block_lookup_address_arm) ;\ |
233 | restore_flags() ;\ |
234 | bx r0 /* jump to new ARM block */;\ |
235 | ;\ |
236 | 1: ;\ |
237 | load_registers_##mode() /* reload registers */;\ |
238 | restore_flags() ;\ |
239 | return_##return_op() ;\ |
240 | ;\ |
241 | 2: ;\ |
242 | load_registers_thumb() /* load Thumb regs */;\ |
243 | call_c_function(block_lookup_address_thumb) ;\ |
244 | restore_flags() ;\ |
245 | bx r0 /* jump to new ARM block */;\ |
246 | |
247 | |
248 | arm_update_gba_builder(arm, arm, straight) |
249 | arm_update_gba_builder(thumb, thumb, straight) |
250 | |
251 | arm_update_gba_builder(idle_arm, arm, add) |
252 | arm_update_gba_builder(idle_thumb, thumb, add) |
253 | |
254 | |
255 | |
256 | @ These are b stubs for performing indirect branches. They are not |
257 | @ linked to and don't return, instead they link elsewhere. |
258 | |
259 | @ Input: |
260 | @ r0: PC to branch to |
261 | |
262 | arm_indirect_branch_arm: |
263 | save_flags() |
264 | call_c_function(block_lookup_address_arm) |
265 | restore_flags() |
266 | bx r0 |
267 | |
268 | arm_indirect_branch_thumb: |
269 | save_flags() |
270 | call_c_function(block_lookup_address_thumb) |
271 | restore_flags() |
272 | bx r0 |
273 | |
274 | arm_indirect_branch_dual_arm: |
275 | save_flags() |
276 | tst r0, #0x01 @ check lower bit |
277 | bne 1f @ if set going to Thumb mode |
278 | call_c_function(block_lookup_address_arm) |
279 | restore_flags() |
280 | bx r0 @ return |
281 | |
282 | 1: |
283 | bic r0, r0, #0x01 |
284 | store_registers_arm() @ save out ARM registers |
285 | load_registers_thumb() @ load in Thumb registers |
286 | ldr r1, [reg_base, #REG_CPSR] @ load cpsr |
287 | orr r1, r1, #0x20 @ set Thumb mode |
288 | str r1, [reg_base, #REG_CPSR] @ store flags |
289 | call_c_function(block_lookup_address_thumb) |
290 | restore_flags() |
291 | bx r0 @ return |
292 | |
293 | arm_indirect_branch_dual_thumb: |
294 | save_flags() |
295 | tst r0, #0x01 @ check lower bit |
296 | beq 1f @ if set going to ARM mode |
297 | bic r0, r0, #0x01 |
298 | call_c_function(block_lookup_address_thumb) |
299 | restore_flags() |
300 | bx r0 @ return |
301 | |
302 | 1: |
303 | store_registers_thumb() @ save out Thumb registers |
304 | load_registers_arm() @ load in ARM registers |
305 | ldr r1, [reg_base, #REG_CPSR] @ load cpsr |
306 | bic r1, r1, #0x20 @ clear Thumb mode |
307 | str r1, [reg_base, #REG_CPSR] @ store flags |
308 | call_c_function(block_lookup_address_arm) |
309 | restore_flags() |
310 | bx r0 @ return |
311 | |
312 | |
313 | @ Update the cpsr. |
314 | |
315 | @ Input: |
316 | @ r0: new cpsr value |
317 | @ r1: bitmask of which bits in cpsr to update |
318 | @ r2: current PC |
319 | |
320 | execute_store_cpsr: |
321 | save_flags() |
322 | and reg_flags, r0, r1 @ reg_flags = new_cpsr & store_mask |
323 | ldr r0, [reg_base, #REG_CPSR] @ r0 = cpsr |
324 | bic r0, r0, r1 @ r0 = cpsr & ~store_mask |
325 | orr reg_flags, reg_flags, r0 @ reg_flags = new_cpsr | cpsr |
326 | |
327 | mov r0, reg_flags @ also put new cpsr in r0 |
328 | |
329 | store_registers_arm() @ save ARM registers |
330 | ldr r2, [lr] @ r2 = pc |
331 | call_c_function(execute_store_cpsr_body) |
332 | load_registers_arm() @ restore ARM registers |
333 | |
334 | cmp r0, #0 @ check new PC |
335 | beq 1f @ if it's zero, return |
336 | |
337 | call_c_function(block_lookup_address_arm) |
338 | |
339 | restore_flags() |
340 | bx r0 @ return to new ARM address |
341 | |
342 | 1: |
343 | restore_flags() |
344 | add pc, lr, #4 @ return |
345 | |
346 | |
347 | @ Update the current spsr. |
348 | |
349 | @ Input: |
350 | @ r0: new cpsr value |
351 | @ r1: bitmask of which bits in spsr to update |
352 | |
353 | execute_store_spsr: |
354 | ldr r1, 1f @ r1 = spsr |
355 | ldr r2, [reg_base, #CPU_MODE] @ r2 = CPU_MODE |
356 | str r0, [r1, r2, lsl #2] @ spsr[CPU_MODE] = new_spsr |
357 | bx lr |
358 | |
359 | 1: |
360 | .word spsr |
361 | |
362 | @ Read the current spsr. |
363 | |
364 | @ Output: |
365 | @ r0: spsr |
366 | |
367 | execute_read_spsr: |
368 | ldr r0, 1b @ r0 = spsr |
369 | ldr r1, [reg_base, #CPU_MODE] @ r1 = CPU_MODE |
370 | ldr r0, [r0, r1, lsl #2] @ r0 = spsr[CPU_MODE] |
371 | bx lr @ return |
372 | |
373 | |
374 | @ Restore the cpsr from the mode spsr and mode shift. |
375 | |
376 | @ Input: |
377 | @ r0: current pc |
378 | |
379 | execute_spsr_restore: |
380 | save_flags() |
381 | ldr r1, 1f @ r1 = spsr |
382 | ldr r2, [reg_base, #CPU_MODE] @ r2 = cpu_mode |
383 | ldr r1, [r1, r2, lsl #2] @ r1 = spsr[cpu_mode] (new cpsr) |
384 | str r1, [reg_base, #REG_CPSR] @ update cpsr |
385 | mov reg_flags, r1 @ also, update shadow flags |
386 | |
387 | @ This function call will pass r0 (address) and return it. |
388 | store_registers_arm() @ save ARM registers |
389 | call_c_function(execute_spsr_restore_body) |
390 | |
391 | ldr r1, [reg_base, #REG_CPSR] @ r1 = cpsr |
392 | tst r1, #0x20 @ see if Thumb mode is set |
393 | bne 2f @ if so handle it |
394 | |
395 | load_registers_arm() @ restore ARM registers |
396 | call_c_function(block_lookup_address_arm) |
397 | restore_flags() |
398 | bx r0 |
399 | |
400 | @ This will service execute_spsr_restore and execute_swi |
401 | 1: |
402 | .word spsr |
403 | |
404 | 2: |
405 | load_registers_thumb() @ load Thumb registers |
406 | call_c_function(block_lookup_address_thumb) |
407 | restore_flags() |
408 | bx r0 |
409 | |
410 | |
411 | |
412 | @ Setup the mode transition work for calling an SWI. |
413 | |
414 | @ Input: |
415 | @ r0: current pc |
416 | |
417 | #define execute_swi_builder(mode) ;\ |
418 | ;\ |
419 | execute_swi_##mode: ;\ |
420 | save_flags() ;\ |
421 | ldr r1, 1f /* r1 = reg_mode */;\ |
422 | /* reg_mode[MODE_SUPERVISOR][6] = pc */;\ |
423 | ldr r0, [lr] /* load PC */;\ |
424 | str r0, [r1, #((MODE_SUPERVISOR * (7 * 4)) + (6 * 4))] ;\ |
425 | collapse_flags_no_update(r0) /* r0 = cpsr */;\ |
426 | ldr r1, 2f /* r1 = spsr */;\ |
427 | str r0, [r1, #(MODE_SUPERVISOR * 4)] /* spsr[MODE_SUPERVISOR] = cpsr */;\ |
428 | bic r0, r0, #0x3F /* clear mode flag in r0 */;\ |
429 | orr r0, r0, #0x13 /* set to supervisor mode */;\ |
430 | str r0, [reg_base, #REG_CPSR] /* update cpsr */;\ |
431 | ;\ |
432 | call_c_function(bios_region_read_allow) ;\ |
433 | ;\ |
434 | mov r0, #MODE_SUPERVISOR ;\ |
435 | ;\ |
436 | store_registers_##mode() /* store regs for mode */;\ |
437 | call_c_function(set_cpu_mode) /* set the CPU mode to svsr */;\ |
438 | load_registers_arm() /* load ARM regs */;\ |
439 | ;\ |
440 | restore_flags() ;\ |
441 | add pc, lr, #4 /* return */;\ |
442 | ;\ |
443 | 1: ;\ |
444 | .word reg_mode ;\ |
445 | ;\ |
446 | 2: ;\ |
447 | .word spsr ;\ |
448 | ;\ |
449 | 3: ;\ |
450 | .word execute_bios_rom_ptr ;\ |
451 | |
452 | execute_swi_builder(arm) |
453 | execute_swi_builder(thumb) |
454 | |
455 | |
456 | @ Wrapper for calling SWI functions in C (or can implement some in ASM if |
457 | @ desired) |
458 | |
459 | #define execute_swi_function_builder(swi_function, mode) ;\ |
460 | ;\ |
461 | .global execute_swi_hle_##swi_function##_##mode ;\ |
462 | execute_swi_hle_##swi_function##_##mode: ;\ |
463 | save_flags() ;\ |
464 | store_registers_##mode() ;\ |
465 | call_c_function(execute_swi_hle_##swi_function##_c) ;\ |
466 | load_registers_##mode() ;\ |
467 | restore_flags() ;\ |
468 | bx lr ;\ |
469 | |
470 | execute_swi_function_builder(div, arm) |
471 | execute_swi_function_builder(div, thumb) |
472 | |
473 | |
474 | @ Start program execution. Normally the mode should be Thumb and the |
475 | @ PC should be 0x8000000, however if a save state is preloaded this |
476 | @ will be different. |
477 | |
478 | @ Input: |
479 | @ r0: initial value for cycle counter |
480 | |
481 | @ Uses sp as reg_base; must hold consistently true. |
482 | |
483 | execute_arm_translate: |
484 | sub sp, sp, #0x100 @ allocate room for register data |
485 | |
486 | mvn reg_cycles, r0 @ load cycle counter |
487 | |
488 | mov r0, reg_base @ load reg_base into first param |
489 | call_c_function(move_reg) @ make reg_base the new reg ptr |
490 | |
491 | sub sp, sp, #REG_BASE_OFFSET @ allocate room for ptr table |
492 | bl load_ptr_read_function_table @ load read function ptr table |
493 | |
494 | ldr r0, [reg_base, #REG_PC] @ r0 = current pc |
495 | ldr r1, [reg_base, #REG_CPSR] @ r1 = flags |
496 | tst r1, #0x20 @ see if Thumb bit is set |
497 | |
498 | bne 1f @ if so lookup thumb |
499 | |
500 | load_registers_arm() @ load ARM registers |
501 | call_c_function(block_lookup_address_arm) |
502 | extract_flags() @ load flags |
503 | bx r0 @ jump to first ARM block |
504 | |
505 | 1: |
506 | load_registers_thumb() @ load Thumb registers |
507 | call_c_function(block_lookup_address_thumb) |
508 | extract_flags() @ load flags |
509 | bx r0 @ jump to first Thumb block |
510 | |
511 | |
512 | @ Write out to memory. |
513 | |
514 | @ Input: |
515 | @ r0: address |
516 | @ r1: value |
517 | @ r2: current pc |
518 | |
519 | #define execute_store_body(store_type, store_op) ;\ |
520 | save_flags() ;\ |
521 | stmdb sp!, { lr } /* save lr */;\ |
522 | tst r0, #0xF0000000 /* make sure address is in range */;\ |
523 | bne ext_store_u##store_type /* if not do ext store */;\ |
524 | ;\ |
525 | ldr r2, 1f /* r2 = memory_map_write */;\ |
526 | mov lr, r0, lsr #15 /* lr = page index of address */;\ |
527 | ldr r2, [r2, lr, lsl #2] /* r2 = memory page */;\ |
528 | ;\ |
529 | cmp r2, #0 /* see if map is ext */;\ |
530 | beq ext_store_u##store_type /* if so do ext store */;\ |
531 | ;\ |
532 | mov r0, r0, lsl #17 /* isolate bottom 15 bits in top */;\ |
533 | mov r0, r0, lsr #17 /* like performing and 0x7FFF */;\ |
534 | store_op r1, [r2, r0] /* store result */;\ |
535 | |
536 | |
537 | #define store_align_8() ;\ |
538 | |
539 | #define store_align_16() ;\ |
540 | bic r0, r0, #0x01 ;\ |
541 | |
542 | #define store_align_32() ;\ |
543 | bic r0, r0, #0x03 ;\ |
544 | |
545 | |
546 | #define execute_store_builder(store_type, store_op, load_op) ;\ |
547 | ;\ |
548 | execute_store_u##store_type: ;\ |
549 | execute_store_body(store_type, store_op) ;\ |
550 | sub r2, r2, #0x8000 /* Pointer to code status data */;\ |
551 | load_op r0, [r2, r0] /* check code flag */;\ |
552 | ;\ |
553 | cmp r0, #0 /* see if it's not 0 */;\ |
554 | bne 2f /* if so perform smc write */;\ |
555 | ldmia sp!, { lr } /* restore lr */;\ |
556 | restore_flags() ;\ |
557 | add pc, lr, #4 /* return */;\ |
558 | ;\ |
559 | 2: ;\ |
560 | ldmia sp!, { lr } /* restore lr */;\ |
561 | ldr r0, [lr] /* load PC */;\ |
562 | str r0, [reg_base, #REG_PC] /* write out PC */;\ |
563 | b smc_write /* perform smc write */;\ |
564 | 1: ;\ |
565 | .word memory_map_write ;\ |
566 | ;\ |
567 | ext_store_u##store_type: ;\ |
568 | ldmia sp!, { lr } /* pop lr off of stack */;\ |
569 | ldr r2, [lr] /* load PC */;\ |
570 | str r2, [reg_base, #REG_PC] /* write out PC */;\ |
571 | store_align_##store_type() ;\ |
572 | call_c_function(write_memory##store_type) ;\ |
573 | b write_epilogue /* handle additional write stuff */;\ |
574 | |
575 | execute_store_builder(8, strb, ldrb) |
576 | execute_store_builder(16, strh, ldrh) |
577 | execute_store_builder(32, str, ldr) |
578 | |
579 | |
580 | execute_store_u32_safe: |
581 | execute_store_body(32_safe, str) |
582 | restore_flags() |
583 | ldmia sp!, { pc } @ return |
584 | |
585 | 1: |
586 | .word memory_map_write |
587 | |
588 | ext_store_u32_safe: |
589 | ldmia sp!, { lr } @ Restore lr |
590 | call_c_function(write_memory32) @ Perform 32bit store |
591 | restore_flags() |
592 | bx lr @ Return |
593 | |
594 | |
595 | write_epilogue: |
596 | cmp r0, #0 @ check if the write rose an alert |
597 | beq 4f @ if not we can exit |
598 | |
599 | collapse_flags(r1) @ interrupt needs current flags |
600 | |
601 | cmp r0, #2 @ see if the alert is due to SMC |
602 | beq smc_write @ if so, goto SMC handler |
603 | |
604 | ldr r1, [reg_base, #REG_CPSR] @ r1 = cpsr |
605 | tst r1, #0x20 @ see if Thumb bit is set |
606 | bne 1f @ if so do Thumb update |
607 | |
608 | store_registers_arm() @ save ARM registers |
609 | |
610 | 3: |
611 | bl update_gba @ update GBA until CPU isn't halted |
612 | |
613 | mvn reg_cycles, r0 @ load new cycle count |
614 | ldr r0, [reg_base, #REG_PC] @ load new PC |
615 | ldr r1, [reg_base, #REG_CPSR] @ r1 = flags |
616 | tst r1, #0x20 @ see if Thumb bit is set |
617 | bne 2f |
618 | |
619 | load_registers_arm() |
620 | call_c_function(block_lookup_address_arm) |
621 | restore_flags() |
622 | bx r0 @ jump to new ARM block |
623 | |
624 | 1: |
625 | store_registers_thumb() @ save Thumb registers |
626 | b 3b |
627 | |
628 | 2: |
629 | load_registers_thumb() |
630 | call_c_function(block_lookup_address_thumb) |
631 | restore_flags() |
632 | bx r0 @ jump to new Thumb block |
633 | |
634 | 4: |
635 | restore_flags() |
636 | add pc, lr, #4 @ return |
637 | |
638 | |
639 | smc_write: |
640 | call_c_function(flush_translation_cache_ram) |
641 | |
642 | lookup_pc: |
643 | ldr r0, [reg_base, #REG_PC] @ r0 = new pc |
644 | ldr r1, [reg_base, #REG_CPSR] @ r1 = flags |
645 | tst r1, #0x20 @ see if Thumb bit is set |
646 | beq lookup_pc_arm @ if not lookup ARM |
647 | |
648 | lookup_pc_thumb: |
649 | call_c_function(block_lookup_address_thumb) |
650 | restore_flags() |
651 | bx r0 @ jump to new Thumb block |
652 | |
653 | lookup_pc_arm: |
654 | call_c_function(block_lookup_address_arm) |
655 | restore_flags() |
656 | bx r0 @ jump to new ARM block |
657 | |
658 | |
659 | #define sign_extend_u8(reg) |
660 | #define sign_extend_u16(reg) |
661 | #define sign_extend_u32(reg) |
662 | |
663 | #define sign_extend_s8(reg) ;\ |
664 | mov reg, reg, lsl #24 /* shift reg into upper 8bits */;\ |
665 | mov reg, reg, asr #24 /* shift down, sign extending */;\ |
666 | |
667 | #define sign_extend_s16(reg) ;\ |
668 | mov reg, reg, lsl #16 /* shift reg into upper 16bits */;\ |
669 | mov reg, reg, asr #16 /* shift down, sign extending */;\ |
670 | |
671 | #define execute_load_op_u8(load_op) ;\ |
672 | mov r0, r0, lsl #17 ;\ |
673 | load_op r0, [r2, r0, lsr #17] ;\ |
674 | |
675 | #define execute_load_op_s8(load_op) ;\ |
676 | mov r0, r0, lsl #17 ;\ |
677 | mov r0, r0, lsr #17 ;\ |
678 | load_op r0, [r2, r0] ;\ |
679 | |
680 | #define execute_load_op_u16(load_op) ;\ |
681 | execute_load_op_s8(load_op) ;\ |
682 | |
683 | #define execute_load_op_s16(load_op) ;\ |
684 | execute_load_op_s8(load_op) ;\ |
685 | |
686 | #define execute_load_op_u16(load_op) ;\ |
687 | execute_load_op_s8(load_op) ;\ |
688 | |
689 | #define execute_load_op_u32(load_op) ;\ |
690 | execute_load_op_u8(load_op) ;\ |
691 | |
692 | |
693 | #define execute_load_builder(load_type, load_function, load_op, mask) ;\ |
694 | ;\ |
695 | execute_load_##load_type: ;\ |
696 | save_flags() ;\ |
697 | tst r0, mask /* make sure address is in range */;\ |
698 | bne ext_load_##load_type /* if not do ext load */;\ |
699 | ;\ |
700 | ldr r2, 1f /* r2 = memory_map_read */;\ |
701 | mov r1, r0, lsr #15 /* r1 = page index of address */;\ |
702 | ldr r2, [r2, r1, lsl #2] /* r2 = memory page */;\ |
703 | ;\ |
704 | cmp r2, #0 /* see if map is ext */;\ |
705 | beq ext_load_##load_type /* if so do ext load */;\ |
706 | ;\ |
707 | execute_load_op_##load_type(load_op) ;\ |
708 | restore_flags() ;\ |
709 | add pc, lr, #4 /* return */;\ |
710 | ;\ |
711 | ext_load_##load_type: ;\ |
712 | ldr r1, [lr] /* r1 = PC */;\ |
713 | str r1, [reg_base, #REG_PC] /* update PC */;\ |
714 | call_c_function(read_memory##load_function) ;\ |
715 | sign_extend_##load_type(r0) /* sign extend result */;\ |
716 | restore_flags() ;\ |
717 | add pc, lr, #4 /* return */;\ |
718 | ;\ |
719 | 1: ;\ |
720 | .word memory_map_read ;\ |
721 | |
722 | |
723 | execute_load_builder(u8, 8, ldrneb, #0xF0000000) |
724 | execute_load_builder(s8, 8, ldrnesb, #0xF0000000) |
725 | execute_load_builder(u16, 16, ldrneh, #0xF0000001) |
726 | execute_load_builder(s16, 16_signed, ldrnesh, #0xF0000001) |
727 | execute_load_builder(u32, 32, ldrne, #0xF0000000) |
728 | |
729 | |
730 | #define execute_ptr_builder(region, ptr, bits) ;\ |
731 | ;\ |
732 | execute_##region##_ptr: ;\ |
733 | ldr r1, 1f /* load region ptr */;\ |
734 | mov r0, r0, lsl #(32 - bits) /* isolate bottom bits */;\ |
735 | mov r0, r0, lsr #(32 - bits) ;\ |
736 | bx lr /* return */;\ |
737 | ;\ |
738 | 1: ;\ |
739 | .word (ptr) ;\ |
740 | |
741 | |
742 | execute_bios_ptr_protected: |
743 | ldr r1, 1f @ load bios read ptr |
744 | and r0, r0, #0x03 @ only want bottom 2 bits |
745 | bx lr @ return |
746 | |
747 | 1: |
748 | .word bios_read_protect |
749 | |
750 | |
751 | @ address = (address & 0x7FFF) + ((address & 0x38000) * 2) + 0x8000; |
752 | |
753 | execute_ewram_ptr: |
754 | ldr r1, 1f @ load ewram read ptr |
755 | mov r2, r0, lsl #17 @ isolate bottom 15 bits |
756 | mov r2, r2, lsr #17 |
757 | and r0, r0, #0x38000 @ isolate top 2 bits |
758 | add r0, r2, r0, lsl #1 @ add top 2 bits * 2 to bottom 15 |
759 | bx lr @ return |
760 | |
761 | 1: |
762 | .word (ewram + 0x8000) |
763 | |
764 | |
765 | @ u32 gamepak_index = address >> 15; |
766 | @ u8 *map = memory_map_read[gamepak_index]; |
767 | |
768 | @ if(map == NULL) |
769 | @ map = load_gamepak_page(gamepak_index & 0x3FF); |
770 | |
771 | @ value = address##type(map, address & 0x7FFF) |
772 | |
773 | execute_gamepak_ptr: |
774 | ldr r1, 1f @ load memory_map_read |
775 | mov r2, r0, lsr #15 @ isolate top 17 bits |
776 | ldr r1, [r1, r2, lsl #2] @ load memory map read ptr |
777 | |
778 | save_flags() |
779 | cmp r1, #0 @ see if map entry is NULL |
780 | bne 2f @ if not resume |
781 | |
782 | stmdb sp!, { r0 } @ save r0 on stack |
783 | mov r2, r2, lsl #20 @ isolate page index |
784 | mov r0, r2, lsr #20 |
785 | call_c_function(load_gamepak_page) @ read new page into r0 |
786 | |
787 | mov r1, r0 @ new map = return |
788 | ldmia sp!, { r0 } @ restore r0 |
789 | |
790 | 2: |
791 | mov r0, r0, lsl #17 @ isolate bottom 15 bits |
792 | mov r0, r0, lsr #17 |
793 | restore_flags() |
794 | bx lr @ return |
795 | |
796 | 1: |
797 | .word memory_map_read |
798 | |
799 | |
800 | @ These will store the result in a pointer, then pass that pointer. |
801 | |
802 | execute_eeprom_ptr: |
803 | save_flags() |
804 | |
805 | call_c_function(read_eeprom) @ load EEPROM result |
806 | add r1, reg_base, #(REG_SAVE & 0xFF00) |
807 | add r1, r1, #(REG_SAVE & 0xFF) |
808 | strh r0, [r1] @ write result out |
809 | mov r0, #0 @ zero out address |
810 | |
811 | restore_flags() |
812 | bx lr @ return |
813 | |
814 | |
815 | execute_backup_ptr: |
816 | save_flags() |
817 | |
818 | mov r0, r0, lsl #16 @ only want top 16 bits |
819 | mov r0, r0, lsr #16 |
820 | call_c_function(read_backup) @ load backup result |
821 | add r1, reg_base, #(REG_SAVE & 0xFF00) |
822 | add r1, r1, #(REG_SAVE & 0xFF) |
823 | strb r0, [r1] @ write result out |
824 | mov r0, #0 @ zero out address |
825 | |
826 | restore_flags() |
827 | bx lr @ return |
828 | |
829 | |
830 | execute_open_ptr: |
831 | ldr r1, [reg_base, #REG_CPSR] @ r1 = cpsr |
832 | save_flags() |
833 | |
834 | stmdb sp!, { r0 } @ save r0 |
835 | |
836 | ldr r0, [lr, #-4] @ r0 = current PC |
837 | |
838 | tst r1, #0x20 @ see if Thumb bit is set |
839 | bne 1f @ if so load Thumb op |
840 | |
841 | call_c_function(read_memory32) @ read open address |
842 | |
843 | add r1, reg_base, #((REG_SAVE + 4) & 0xFF00) |
844 | add r1, r1, #((REG_SAVE + 4) & 0xFF) |
845 | add r1, r1, reg_base |
846 | str r0, [r1] @ write out |
847 | |
848 | ldmia sp!, { r0 } @ restore r0 |
849 | and r0, r0, #0x03 @ isolate bottom 2 bits |
850 | |
851 | restore_flags() |
852 | bx lr |
853 | |
854 | 1: |
855 | call_c_function(read_memory16) @ read open address |
856 | |
857 | orr r0, r0, r0, lsl #16 @ duplicate opcode over halves |
858 | add r1, reg_base, #((REG_SAVE + 4) & 0xFF00) |
859 | add r1, r1, #((REG_SAVE + 4) & 0xFF) |
860 | |
861 | add r1, r1, reg_base |
862 | str r0, [r1] @ write out |
863 | |
864 | ldmia sp!, { r0 } @ restore r0 |
865 | and r0, r0, #0x03 @ isolate bottom 2 bits |
866 | |
867 | restore_flags(); |
868 | bx lr |
869 | |
870 | |
871 | execute_ptr_builder(bios_rom, bios_rom, 14) |
872 | execute_ptr_builder(iwram, iwram + 0x8000, 15) |
873 | execute_ptr_builder(vram, vram, 17) |
874 | execute_ptr_builder(oam_ram, oam_ram, 10) |
875 | execute_ptr_builder(io_registers, io_registers, 10) |
876 | execute_ptr_builder(palette_ram, palette_ram, 10) |
877 | |
878 | ptr_read_function_table: |
879 | .word execute_bios_ptr_protected @ 0x00: BIOS |
880 | .word execute_open_ptr @ 0x01: open |
881 | .word execute_ewram_ptr @ 0x02: ewram |
882 | .word execute_iwram_ptr @ 0x03: iwram |
883 | .word execute_io_registers_ptr @ 0x04: I/O registers |
884 | .word execute_palette_ram_ptr @ 0x05: palette RAM |
885 | .word execute_vram_ptr @ 0x06: vram |
886 | .word execute_oam_ram_ptr @ 0x07: oam RAM |
887 | .word execute_gamepak_ptr @ 0x08: gamepak |
888 | .word execute_gamepak_ptr @ 0x09: gamepak |
889 | .word execute_gamepak_ptr @ 0x0A: gamepak |
890 | .word execute_gamepak_ptr @ 0x0B: gamepak |
891 | .word execute_gamepak_ptr @ 0x0C: gamepak |
892 | .word execute_eeprom_ptr @ 0x0D: EEPROM |
893 | .word execute_backup_ptr @ 0x0E: backup |
894 | |
895 | .rept (256 - 15) @ 0x0F - 0xFF: open |
896 | .word execute_open_ptr |
897 | .endr |
898 | |
899 | |
900 | @ Setup the read function table. |
901 | @ Load this onto the the stack; assume we're free to use r3 |
902 | |
903 | load_ptr_read_function_table: |
904 | mov r0, #256 @ 256 elements |
905 | ldr r1, 1f @ r0 = ptr_read_function_table |
906 | mov r2, sp @ load here |
907 | |
908 | 2: |
909 | ldr r3, [r1], #4 @ read pointer |
910 | str r3, [r2], #4 @ write pointer |
911 | |
912 | subs r0, r0, #1 @ goto next iteration |
913 | bne 2b |
914 | |
915 | bx lr |
916 | |
917 | 1: |
918 | .word ptr_read_function_table |
919 | |
920 | |
921 | @ Patch the read function table to allow for BIOS reads. |
922 | |
923 | execute_patch_bios_read: |
924 | ldr r0, 1f @ r0 = patch function |
925 | ldr r1, 2f @ r1 = reg |
926 | ldr r1, [r1] |
927 | str r0, [r1, #-REG_BASE_OFFSET] |
928 | bx lr |
929 | |
930 | 1: |
931 | .word execute_bios_rom_ptr |
932 | |
933 | 2: |
934 | .word reg |
935 | |
936 | |
937 | @ Patch the read function table to allow for BIOS reads. |
938 | |
939 | execute_patch_bios_protect: |
940 | ldr r0, 1f @ r0 = patch function |
941 | ldr r1, 2f @ r1 = reg |
942 | ldr r1, [r1] |
943 | str r0, [r1, #-REG_BASE_OFFSET] |
944 | bx lr |
945 | |
946 | 1: |
947 | .word execute_bios_ptr_protected |
948 | |
949 | 2: |
950 | .word reg |
951 | |
952 | |
953 | #define save_reg_scratch(reg) ;\ |
954 | ldr r2, [reg_base, #(REG_BASE_OFFSET + (reg * 4))] ;\ |
955 | str r2, [reg_base, #(REG_BASE_OFFSET + (reg * 4) + 128)] ;\ |
956 | |
957 | #define restore_reg_scratch(reg) ;\ |
958 | ldr r2, [reg_base, #(REG_BASE_OFFSET + (reg * 4) + 128)] ;\ |
959 | str r2, [reg_base, #(REG_BASE_OFFSET + (reg * 4))] ;\ |
960 | |
961 | #define scratch_regs_thumb(type) ;\ |
962 | type##_reg_scratch(0) ;\ |
963 | type##_reg_scratch(1) ;\ |
964 | type##_reg_scratch(2) ;\ |
965 | type##_reg_scratch(3) ;\ |
966 | type##_reg_scratch(4) ;\ |
967 | type##_reg_scratch(5) ;\ |
968 | |
969 | #define scratch_regs_arm(type) ;\ |
970 | type##_reg_scratch(0) ;\ |
971 | type##_reg_scratch(1) ;\ |
972 | type##_reg_scratch(6) ;\ |
973 | type##_reg_scratch(9) ;\ |
974 | type##_reg_scratch(12) ;\ |
975 | type##_reg_scratch(14) ;\ |
976 | |
977 | |
978 | step_debug_arm: |
979 | save_flags() |
980 | collapse_flags(r0) |
981 | |
982 | ldr r0, [reg_base, #REG_CPSR] @ r1 = cpsr |
983 | tst r0, #0x20 @ see if Thumb bit is set |
984 | |
985 | ldr r0, [lr] @ load PC |
986 | mvn r1, reg_cycles @ load cycle counter |
987 | |
988 | beq 1f @ if not goto ARM mode |
989 | |
990 | scratch_regs_thumb(save) |
991 | |
992 | store_registers_thumb() @ write back Thumb regs |
993 | call_c_function(step_debug) @ call debug step |
994 | scratch_regs_thumb(restore) |
995 | restore_flags() |
996 | add pc, lr, #4 @ return |
997 | |
998 | 1: |
999 | scratch_regs_arm(save) |
1000 | store_registers_arm() @ write back ARM regs |
1001 | call_c_function(step_debug) @ call debug step |
1002 | scratch_regs_arm(restore) |
1003 | restore_flags() |
1004 | add pc, lr, #4 @ return, skipping PC |
1005 | |
2823a4c8 |
1006 | |
2823a4c8 |
1007 | .comm memory_map_read 0x8000 |
1008 | .comm memory_map_write 0x8000 |
1009 | |
1010 | |
1011 | |