1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * linkage_arm.s for PCSX *
3 * Copyright (C) 2009-2011 Ari64 *
4 * Copyright (C) 2010-2011 GraÅžvydas "notaz" Ignotas *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
34 .global pending_exception
42 .global restore_candidate
49 .global inv_code_start
55 .type dynarec_local, %object
56 .size dynarec_local, dynarec_local_end-dynarec_local
58 .space dynarec_local_end-dynarec_local
59 next_interupt = dynarec_local + 64
60 .type next_interupt, %object
61 .size next_interupt, 4
62 cycle_count = next_interupt + 4
63 .type cycle_count, %object
65 last_count = cycle_count + 4
66 .type last_count, %object
68 pending_exception = last_count + 4
69 .type pending_exception, %object
70 .size pending_exception, 4
71 stop = pending_exception + 4
75 .type invc_ptr, %object
77 address = invc_ptr + 4
78 .type address, %object
83 .type psxRegs, %object
84 .size psxRegs, psxRegs_end-psxRegs
95 .type reg_cop0, %object
97 reg_cop2d = reg_cop0 + 128
98 .type reg_cop2d, %object
100 reg_cop2c = reg_cop2d + 128
101 .type reg_cop2c, %object
113 interrupt = cycle + 4
114 .type interrupt, %object
116 intCycle = interrupt + 4
117 .type intCycle, %object
119 psxRegs_end = intCycle + 256
124 rcnts_end = rcnts + 7*4*4
127 .type mem_rtab, %object
129 mem_wtab = mem_rtab + 4
130 .type mem_wtab, %object
132 psxH_ptr = mem_wtab + 4
133 .type psxH_ptr, %object
135 zeromem_ptr = psxH_ptr + 4
136 .type zeromem_ptr, %object
138 inv_code_start = zeromem_ptr + 4
139 .type inv_code_start, %object
140 .size inv_code_start, 4
141 inv_code_end = inv_code_start + 4
142 .type inv_code_end, %object
143 .size inv_code_end, 4
144 branch_target = inv_code_end + 4
145 .type branch_target, %object
146 .size branch_target, 4
147 align0 = branch_target + 4 /* unused/alignment */
148 .type align0, %object
150 mini_ht = align0 + 16
151 .type mini_ht, %object
153 restore_candidate = mini_ht + 256
154 .type restore_candidate, %object
155 .size restore_candidate, 512
156 dynarec_local_end = restore_candidate + 512
166 .macro load_varadr reg var
167 #if defined(__ARM_ARCH_7A__) && !defined(__PIC__)
168 movw \reg, #:lower16:\var
169 movt \reg, #:upper16:\var
175 .macro mov_16 reg imm
176 #ifdef __ARM_ARCH_7A__
179 mov \reg, #(\imm & 0x00ff)
180 orr \reg, #(\imm & 0xff00)
184 .macro mov_24 reg imm
185 #ifdef __ARM_ARCH_7A__
186 movw \reg, #(\imm & 0xffff)
187 movt \reg, #(\imm >> 16)
189 mov \reg, #(\imm & 0x0000ff)
190 orr \reg, #(\imm & 0x00ff00)
191 orr \reg, #(\imm & 0xff0000)
195 .macro dyna_linker_main
196 /* r0 = virtual target address */
197 /* r1 = instruction to patch */
211 ldr r5, [r3, r2, lsl #2]
213 add r6, r1, r12, asr #6
228 moveq pc, r4 /* Stale i-cache */
230 b 1b /* jump_in may have dupes, continue search */
233 beq 3f /* r0 not in jump_in */
239 and r1, r7, #0xff000000
242 add r1, r1, r2, lsr #8
246 /* hash_table lookup */
249 eor r4, r0, r0, lsl #16
255 ldr r5, [r3, r2, lsl #2]
262 /* jump_dirty lookup */
272 /* hash_table insert */
286 .type dyna_linker, %function
288 /* r0 = virtual target address */
289 /* r1 = instruction to patch */
294 bl new_recompile_block
302 .size dyna_linker, .-dyna_linker
303 .global exec_pagefault
304 .type exec_pagefault, %function
306 /* r0 = instruction pointer */
307 /* r1 = fault address */
309 ldr r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
311 ldr r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
312 bic r6, r6, #0x0F800000
313 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
315 str r1, [fp, #reg_cop0+32-dynarec_local] /* BadVAddr */
317 str r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
318 and r5, r6, r1, lsr #9
319 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
320 and r1, r1, r6, lsl #9
321 str r1, [fp, #reg_cop0+40-dynarec_local] /* EntryHi */
323 str r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
327 .size exec_pagefault, .-exec_pagefault
329 /* Special dynamic linker for the case where a page fault
330 may occur in a branch delay slot */
331 .global dyna_linker_ds
332 .type dyna_linker_ds, %function
334 /* r0 = virtual target address */
335 /* r1 = instruction to patch */
342 bl new_recompile_block
349 mov r2, #0x80000008 /* High bit set indicates pagefault in delay slot */
352 .size dyna_linker_ds, .-dyna_linker_ds
361 .global jump_vaddr_r0
362 .type jump_vaddr_r0, %function
364 eor r2, r0, r0, lsl #16
366 .size jump_vaddr_r0, .-jump_vaddr_r0
367 .global jump_vaddr_r1
368 .type jump_vaddr_r1, %function
370 eor r2, r1, r1, lsl #16
373 .size jump_vaddr_r1, .-jump_vaddr_r1
374 .global jump_vaddr_r2
375 .type jump_vaddr_r2, %function
378 eor r2, r2, r2, lsl #16
380 .size jump_vaddr_r2, .-jump_vaddr_r2
381 .global jump_vaddr_r3
382 .type jump_vaddr_r3, %function
384 eor r2, r3, r3, lsl #16
387 .size jump_vaddr_r3, .-jump_vaddr_r3
388 .global jump_vaddr_r4
389 .type jump_vaddr_r4, %function
391 eor r2, r4, r4, lsl #16
394 .size jump_vaddr_r4, .-jump_vaddr_r4
395 .global jump_vaddr_r5
396 .type jump_vaddr_r5, %function
398 eor r2, r5, r5, lsl #16
401 .size jump_vaddr_r5, .-jump_vaddr_r5
402 .global jump_vaddr_r6
403 .type jump_vaddr_r6, %function
405 eor r2, r6, r6, lsl #16
408 .size jump_vaddr_r6, .-jump_vaddr_r6
409 .global jump_vaddr_r8
410 .type jump_vaddr_r8, %function
412 eor r2, r8, r8, lsl #16
415 .size jump_vaddr_r8, .-jump_vaddr_r8
416 .global jump_vaddr_r9
417 .type jump_vaddr_r9, %function
419 eor r2, r9, r9, lsl #16
422 .size jump_vaddr_r9, .-jump_vaddr_r9
423 .global jump_vaddr_r10
424 .type jump_vaddr_r10, %function
426 eor r2, r10, r10, lsl #16
429 .size jump_vaddr_r10, .-jump_vaddr_r10
430 .global jump_vaddr_r12
431 .type jump_vaddr_r12, %function
433 eor r2, r12, r12, lsl #16
436 .size jump_vaddr_r12, .-jump_vaddr_r12
437 .global jump_vaddr_r7
438 .type jump_vaddr_r7, %function
440 eor r2, r7, r7, lsl #16
442 .size jump_vaddr_r7, .-jump_vaddr_r7
444 .type jump_vaddr, %function
448 and r2, r3, r2, lsr #12
455 str r10, [fp, #cycle_count-dynarec_local]
457 ldr r10, [fp, #cycle_count-dynarec_local]
459 .size jump_vaddr, .-jump_vaddr
462 .global verify_code_ds
463 .type verify_code_ds, %function
465 str r8, [fp, #branch_target-dynarec_local]
466 .size verify_code_ds, .-verify_code_ds
467 .global verify_code_vm
468 .type verify_code_vm, %function
471 .type verify_code, %function
500 ldr r8, [fp, #branch_target-dynarec_local]
505 .size verify_code, .-verify_code
506 .size verify_code_vm, .-verify_code_vm
510 .type cc_interrupt, %function
512 ldr r0, [fp, #last_count-dynarec_local]
516 str r1, [fp, #pending_exception-dynarec_local]
517 and r2, r2, r10, lsr #17
518 add r3, fp, #restore_candidate-dynarec_local
519 str r10, [fp, #cycle-dynarec_local] /* PCSX cycles */
520 @@ str r10, [fp, #reg_cop0+36-dynarec_local] /* Count */
528 ldr r10, [fp, #cycle-dynarec_local]
529 ldr r0, [fp, #next_interupt-dynarec_local]
530 ldr r1, [fp, #pending_exception-dynarec_local]
531 ldr r2, [fp, #stop-dynarec_local]
532 str r0, [fp, #last_count-dynarec_local]
535 ldmnefd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
539 ldr r0, [fp, #pcaddr-dynarec_local]
543 /* Move 'dirty' blocks to the 'clean' list */
554 .size cc_interrupt, .-cc_interrupt
558 .type do_interrupt, %function
560 ldr r0, [fp, #pcaddr-dynarec_local]
564 .size do_interrupt, .-do_interrupt
568 .type fp_exception, %function
572 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
574 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
577 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
578 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
582 .size fp_exception, .-fp_exception
584 .global fp_exception_ds
585 .type fp_exception_ds, %function
587 mov r2, #0x90000000 /* Set high bit if delay slot */
589 .size fp_exception_ds, .-fp_exception_ds
593 .type jump_syscall, %function
595 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
597 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
600 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
601 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
605 .size jump_syscall, .-jump_syscall
609 .global jump_syscall_hle
610 .type jump_syscall_hle, %function
612 str r0, [fp, #pcaddr-dynarec_local] /* PC must be set to EPC for psxException */
613 ldr r2, [fp, #last_count-dynarec_local]
614 mov r1, #0 /* in delay slot */
616 mov r0, #0x20 /* cause */
617 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
620 /* note: psxException might do recorsive recompiler call from it's HLE code,
621 * so be ready for this */
623 ldr r1, [fp, #next_interupt-dynarec_local]
624 ldr r10, [fp, #cycle-dynarec_local]
625 ldr r0, [fp, #pcaddr-dynarec_local]
627 str r1, [fp, #last_count-dynarec_local]
630 .size jump_syscall_hle, .-jump_syscall_hle
634 .type jump_hlecall, %function
636 ldr r2, [fp, #last_count-dynarec_local]
637 str r0, [fp, #pcaddr-dynarec_local]
640 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
642 .size jump_hlecall, .-jump_hlecall
646 .type jump_intcall, %function
648 ldr r2, [fp, #last_count-dynarec_local]
649 str r0, [fp, #pcaddr-dynarec_local]
652 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
654 .size jump_hlecall, .-jump_hlecall
658 .global new_dyna_leave
659 .type new_dyna_leave, %function
660 ldr r0, [fp, #last_count-dynarec_local]
663 str r10, [fp, #cycle-dynarec_local]
664 ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
665 .size new_dyna_leave, .-new_dyna_leave
668 .global invalidate_addr_r0
669 .type invalidate_addr_r0, %function
671 stmia fp, {r0, r1, r2, r3, r12, lr}
672 b invalidate_addr_call
673 .size invalidate_addr_r0, .-invalidate_addr_r0
675 .global invalidate_addr_r1
676 .type invalidate_addr_r1, %function
678 stmia fp, {r0, r1, r2, r3, r12, lr}
680 b invalidate_addr_call
681 .size invalidate_addr_r1, .-invalidate_addr_r1
683 .global invalidate_addr_r2
684 .type invalidate_addr_r2, %function
686 stmia fp, {r0, r1, r2, r3, r12, lr}
688 b invalidate_addr_call
689 .size invalidate_addr_r2, .-invalidate_addr_r2
691 .global invalidate_addr_r3
692 .type invalidate_addr_r3, %function
694 stmia fp, {r0, r1, r2, r3, r12, lr}
696 b invalidate_addr_call
697 .size invalidate_addr_r3, .-invalidate_addr_r3
699 .global invalidate_addr_r4
700 .type invalidate_addr_r4, %function
702 stmia fp, {r0, r1, r2, r3, r12, lr}
704 b invalidate_addr_call
705 .size invalidate_addr_r4, .-invalidate_addr_r4
707 .global invalidate_addr_r5
708 .type invalidate_addr_r5, %function
710 stmia fp, {r0, r1, r2, r3, r12, lr}
712 b invalidate_addr_call
713 .size invalidate_addr_r5, .-invalidate_addr_r5
715 .global invalidate_addr_r6
716 .type invalidate_addr_r6, %function
718 stmia fp, {r0, r1, r2, r3, r12, lr}
720 b invalidate_addr_call
721 .size invalidate_addr_r6, .-invalidate_addr_r6
723 .global invalidate_addr_r7
724 .type invalidate_addr_r7, %function
726 stmia fp, {r0, r1, r2, r3, r12, lr}
728 b invalidate_addr_call
729 .size invalidate_addr_r7, .-invalidate_addr_r7
731 .global invalidate_addr_r8
732 .type invalidate_addr_r8, %function
734 stmia fp, {r0, r1, r2, r3, r12, lr}
736 b invalidate_addr_call
737 .size invalidate_addr_r8, .-invalidate_addr_r8
739 .global invalidate_addr_r9
740 .type invalidate_addr_r9, %function
742 stmia fp, {r0, r1, r2, r3, r12, lr}
744 b invalidate_addr_call
745 .size invalidate_addr_r9, .-invalidate_addr_r9
747 .global invalidate_addr_r10
748 .type invalidate_addr_r10, %function
750 stmia fp, {r0, r1, r2, r3, r12, lr}
752 b invalidate_addr_call
753 .size invalidate_addr_r10, .-invalidate_addr_r10
755 .global invalidate_addr_r12
756 .type invalidate_addr_r12, %function
758 stmia fp, {r0, r1, r2, r3, r12, lr}
760 .size invalidate_addr_r12, .-invalidate_addr_r12
762 .global invalidate_addr_call
763 .type invalidate_addr_call, %function
764 invalidate_addr_call:
765 ldr r12, [fp, #inv_code_start-dynarec_local]
766 ldr lr, [fp, #inv_code_end-dynarec_local]
770 ldmia fp, {r0, r1, r2, r3, r12, pc}
771 .size invalidate_addr_call, .-invalidate_addr_call
774 .global new_dyna_start
775 .type new_dyna_start, %function
777 /* ip is stored to conform EABI alignment */
778 stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr}
779 load_varadr fp, dynarec_local
780 ldr r0, [fp, #pcaddr-dynarec_local]
782 ldr r1, [fp, #next_interupt-dynarec_local]
783 ldr r10, [fp, #cycle-dynarec_local]
784 str r1, [fp, #last_count-dynarec_local]
787 .size new_dyna_start, .-new_dyna_start
789 /* --------------------------------------- */
792 .global jump_handler_read8
793 .global jump_handler_read16
794 .global jump_handler_read32
795 .global jump_handler_write8
796 .global jump_handler_write16
797 .global jump_handler_write32
798 .global jump_handler_write_h
799 .global jump_handle_swl
800 .global jump_handle_swr
801 .global rcnt0_read_count_m0
802 .global rcnt0_read_count_m1
803 .global rcnt1_read_count_m0
804 .global rcnt1_read_count_m1
805 .global rcnt2_read_count_m0
806 .global rcnt2_read_count_m1
809 .macro pcsx_read_mem readop tab_shift
810 /* r0 = address, r1 = handler_tab, r2 = cycles */
812 lsr r3, #(20+\tab_shift)
813 ldr r12, [fp, #last_count-dynarec_local]
814 ldr r1, [r1, r3, lsl #2]
821 \readop r0, [r1, r3, lsl #\tab_shift]
824 str r2, [fp, #cycle-dynarec_local]
829 add r1, #0x1000/4*4 + 0x1000/2*4 @ shift to r8 part
830 pcsx_read_mem ldrccb, 0
833 add r1, #0x1000/4*4 @ shift to r16 part
834 pcsx_read_mem ldrcch, 1
837 pcsx_read_mem ldrcc, 2
840 .macro pcsx_write_mem wrtop tab_shift
841 /* r0 = address, r1 = data, r2 = cycles, r3 = handler_tab */
843 lsr r12, #(20+\tab_shift)
844 ldr r3, [r3, r12, lsl #2]
845 str r0, [fp, #address-dynarec_local] @ some handlers still need it..
847 mov r0, r2 @ cycle return in case of direct store
852 \wrtop r1, [r3, r12, lsl #\tab_shift]
855 ldr r12, [fp, #last_count-dynarec_local]
859 str r2, [fp, #cycle-dynarec_local]
862 ldr r0, [fp, #next_interupt-dynarec_local]
864 str r0, [fp, #last_count-dynarec_local]
870 add r3, #0x1000/4*4 + 0x1000/2*4 @ shift to r8 part
871 pcsx_write_mem strccb, 0
873 jump_handler_write16:
874 add r3, #0x1000/4*4 @ shift to r16 part
875 pcsx_write_mem strcch, 1
877 jump_handler_write32:
878 pcsx_write_mem strcc, 2
880 jump_handler_write_h:
881 /* r0 = address, r1 = data, r2 = cycles, r3 = handler */
882 ldr r12, [fp, #last_count-dynarec_local]
883 str r0, [fp, #address-dynarec_local] @ some handlers still need it..
887 str r2, [fp, #cycle-dynarec_local]
890 ldr r0, [fp, #next_interupt-dynarec_local]
892 str r0, [fp, #last_count-dynarec_local]
897 /* r0 = address, r1 = data, r2 = cycles */
898 ldr r3, [fp, #mem_wtab-dynarec_local]
900 ldr r3, [r3, r12, lsl #2]
921 lsreq r12, r1, #24 @ 0
932 /* r0 = address, r1 = data, r2 = cycles */
933 ldr r3, [fp, #mem_wtab-dynarec_local]
935 ldr r3, [r3, r12, lsl #2]
957 .macro rcntx_read_mode0 num
958 /* r0 = address, r2 = cycles */
959 ldr r3, [fp, #rcnts-dynarec_local+6*4+7*4*\num] @ cycleStart
976 /* r0 = address, r2 = cycles */
977 ldr r3, [fp, #rcnts-dynarec_local+6*4+7*4*0] @ cycleStart
980 mul r0, r1, r2 @ /= 5
985 /* r0 = address, r2 = cycles */
986 ldr r3, [fp, #rcnts-dynarec_local+6*4+7*4*1]
989 umull r3, r0, r1, r2 @ ~ /= hsync_cycles, max ~0x1e6cdd
993 /* r0 = address, r2 = cycles */
994 ldr r3, [fp, #rcnts-dynarec_local+6*4+7*4*2]
995 mov r0, r2, lsl #16-3
996 sub r0, r3, lsl #16-3
1000 @ vim:filetype=armasm