6a9fb6d08131e429428a00a660157920dae59c45
[gpsp.git] / arm / arm_stub.S
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 #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
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
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
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)                                            ;\
194   stmdb sp!, { call_c_saved_regs }                                           ;\
195   bl function                                                                ;\
196   ldmia sp!, { call_c_saved_regs }                                           ;\
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()                                                      ;\
547   and r1, r1, #0xff                                                          ;\
548
549 #define store_align_16()                                                     ;\
550   bic r0, r0, #0x01                                                          ;\
551   extract_u16(r1, r1)                                                        ;\
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
1017
1018 .comm memory_map_read 0x8000
1019 .comm memory_map_write 0x8000
1020
1021
1022