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