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 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
45 .global pending_exception
59 .global restore_candidate
65 .global inv_code_start
70 .type dynarec_local, %object
71 .size dynarec_local, dynarec_local_end-dynarec_local
73 .space dynarec_local_end-dynarec_local /*0x400630*/
74 next_interupt = dynarec_local + 64
75 .type next_interupt, %object
76 .size next_interupt, 4
77 cycle_count = next_interupt + 4
78 .type cycle_count, %object
80 last_count = cycle_count + 4
81 .type last_count, %object
83 pending_exception = last_count + 4
84 .type pending_exception, %object
85 .size pending_exception, 4
86 stop = pending_exception + 4
90 .type invc_ptr, %object
92 address = invc_ptr + 4
93 .type address, %object
95 readmem_dword = address + 4
96 readmem_word = readmem_dword
97 .type readmem_dword, %object
98 .size readmem_dword, 8
99 dword = readmem_dword + 8
110 .size byte, 1 /* 1 byte free */
120 .type psxRegs, %object
121 .size psxRegs, psxRegs_end-psxRegs
132 .type reg_cop0, %object
134 reg_cop2d = reg_cop0 + 128
135 .type reg_cop2d, %object
137 reg_cop2c = reg_cop2d + 128
138 .type reg_cop2c, %object
150 interrupt = cycle + 4
151 .type interrupt, %object
153 intCycle = interrupt + 4
154 .type intCycle, %object
156 psxRegs_end = intCycle + 256
159 nd_pcsx_io = psxRegs_end
160 .type nd_pcsx_io, %object
161 .size nd_pcsx_io, nd_pcsx_io_end-nd_pcsx_io
162 tab_read8 = nd_pcsx_io
163 .type tab_read8, %object
165 tab_read16 = tab_read8 + 4
166 .type tab_read16, %object
168 tab_read32 = tab_read16 + 4
169 .type tab_read32, %object
171 tab_write8 = tab_read32 + 4
172 .type tab_write8, %object
174 tab_write16 = tab_write8 + 4
175 .type tab_write16, %object
177 tab_write32 = tab_write16 + 4
178 .type tab_write32, %object
180 spu_readf = tab_write32 + 4
181 .type spu_readf, %object
183 spu_writef = spu_readf + 4
184 .type spu_writef, %object
186 nd_pcsx_io_end = spu_writef + 4
188 psxH_ptr = nd_pcsx_io_end
189 .type psxH_ptr, %object
191 inv_code_start = psxH_ptr + 4
192 .type inv_code_start, %object
193 .size inv_code_start, 4
194 inv_code_end = inv_code_start + 4
195 .type inv_code_end, %object
196 .size inv_code_end, 4
197 align0 = inv_code_end + 4 /* just for alignment */
198 .type align0, %object
200 branch_target = align0 + 12
201 .type branch_target, %object
202 .size branch_target, 4
203 mini_ht = branch_target + 4
204 .type mini_ht, %object
206 restore_candidate = mini_ht + 256
207 .type restore_candidate, %object
208 .size restore_candidate, 512
209 memory_map = restore_candidate + 512
210 .type memory_map, %object
211 .size memory_map, 4194304
212 dynarec_local_end = memory_map + 4194304
214 .macro load_var_adr reg var
216 movw \reg, #:lower16:\var
217 movt \reg, #:upper16:\var
223 .macro dyna_linker_main
224 /* r0 = virtual target address */
225 /* r1 = instruction to patch */
239 ldr r5, [r3, r2, lsl #2]
241 add r6, r1, r12, asr #6
256 moveq pc, r4 /* Stale i-cache */
258 b 1b /* jump_in may have dupes, continue search */
261 beq 3f /* r0 not in jump_in */
267 and r1, r7, #0xff000000
270 add r1, r1, r2, lsr #8
274 /* hash_table lookup */
277 eor r4, r0, r0, lsl #16
283 ldr r5, [r3, r2, lsl #2]
290 /* jump_dirty lookup */
300 /* hash_table insert */
314 .type dyna_linker, %function
316 /* r0 = virtual target address */
317 /* r1 = instruction to patch */
322 bl new_recompile_block
330 .size dyna_linker, .-dyna_linker
331 .global exec_pagefault
332 .type exec_pagefault, %function
334 /* r0 = instruction pointer */
335 /* r1 = fault address */
337 ldr r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
339 ldr r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
340 bic r6, r6, #0x0F800000
341 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
343 str r1, [fp, #reg_cop0+32-dynarec_local] /* BadVAddr */
345 str r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
346 and r5, r6, r1, lsr #9
347 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
348 and r1, r1, r6, lsl #9
349 str r1, [fp, #reg_cop0+40-dynarec_local] /* EntryHi */
351 str r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
355 .size exec_pagefault, .-exec_pagefault
357 /* Special dynamic linker for the case where a page fault
358 may occur in a branch delay slot */
359 .global dyna_linker_ds
360 .type dyna_linker_ds, %function
362 /* r0 = virtual target address */
363 /* r1 = instruction to patch */
370 bl new_recompile_block
377 mov r2, #0x80000008 /* High bit set indicates pagefault in delay slot */
380 .size dyna_linker_ds, .-dyna_linker_ds
389 .global jump_vaddr_r0
390 .type jump_vaddr_r0, %function
392 eor r2, r0, r0, lsl #16
394 .size jump_vaddr_r0, .-jump_vaddr_r0
395 .global jump_vaddr_r1
396 .type jump_vaddr_r1, %function
398 eor r2, r1, r1, lsl #16
401 .size jump_vaddr_r1, .-jump_vaddr_r1
402 .global jump_vaddr_r2
403 .type jump_vaddr_r2, %function
406 eor r2, r2, r2, lsl #16
408 .size jump_vaddr_r2, .-jump_vaddr_r2
409 .global jump_vaddr_r3
410 .type jump_vaddr_r3, %function
412 eor r2, r3, r3, lsl #16
415 .size jump_vaddr_r3, .-jump_vaddr_r3
416 .global jump_vaddr_r4
417 .type jump_vaddr_r4, %function
419 eor r2, r4, r4, lsl #16
422 .size jump_vaddr_r4, .-jump_vaddr_r4
423 .global jump_vaddr_r5
424 .type jump_vaddr_r5, %function
426 eor r2, r5, r5, lsl #16
429 .size jump_vaddr_r5, .-jump_vaddr_r5
430 .global jump_vaddr_r6
431 .type jump_vaddr_r6, %function
433 eor r2, r6, r6, lsl #16
436 .size jump_vaddr_r6, .-jump_vaddr_r6
437 .global jump_vaddr_r8
438 .type jump_vaddr_r8, %function
440 eor r2, r8, r8, lsl #16
443 .size jump_vaddr_r8, .-jump_vaddr_r8
444 .global jump_vaddr_r9
445 .type jump_vaddr_r9, %function
447 eor r2, r9, r9, lsl #16
450 .size jump_vaddr_r9, .-jump_vaddr_r9
451 .global jump_vaddr_r10
452 .type jump_vaddr_r10, %function
454 eor r2, r10, r10, lsl #16
457 .size jump_vaddr_r10, .-jump_vaddr_r10
458 .global jump_vaddr_r12
459 .type jump_vaddr_r12, %function
461 eor r2, r12, r12, lsl #16
464 .size jump_vaddr_r12, .-jump_vaddr_r12
465 .global jump_vaddr_r7
466 .type jump_vaddr_r7, %function
468 eor r2, r7, r7, lsl #16
470 .size jump_vaddr_r7, .-jump_vaddr_r7
472 .type jump_vaddr, %function
476 and r2, r3, r2, lsr #12
483 str r10, [fp, #cycle_count-dynarec_local]
485 ldr r10, [fp, #cycle_count-dynarec_local]
487 .size jump_vaddr, .-jump_vaddr
490 .global verify_code_ds
491 .type verify_code_ds, %function
493 str r8, [fp, #branch_target-dynarec_local]
494 .size verify_code_ds, .-verify_code_ds
495 .global verify_code_vm
496 .type verify_code_vm, %function
499 .type verify_code, %function
528 ldr r8, [fp, #branch_target-dynarec_local]
533 .size verify_code, .-verify_code
534 .size verify_code_vm, .-verify_code_vm
538 .type cc_interrupt, %function
540 ldr r0, [fp, #last_count-dynarec_local]
544 str r1, [fp, #pending_exception-dynarec_local]
545 and r2, r2, r10, lsr #17
546 add r3, fp, #restore_candidate-dynarec_local
547 str r10, [fp, #cycle-dynarec_local] /* PCSX cycles */
548 @@ str r10, [fp, #reg_cop0+36-dynarec_local] /* Count */
556 ldr r10, [fp, #cycle-dynarec_local]
557 ldr r0, [fp, #next_interupt-dynarec_local]
558 ldr r1, [fp, #pending_exception-dynarec_local]
559 ldr r2, [fp, #stop-dynarec_local]
560 str r0, [fp, #last_count-dynarec_local]
563 ldmnefd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
567 ldr r0, [fp, #pcaddr-dynarec_local]
571 /* Move 'dirty' blocks to the 'clean' list */
582 .size cc_interrupt, .-cc_interrupt
586 .type do_interrupt, %function
588 ldr r0, [fp, #pcaddr-dynarec_local]
592 .size do_interrupt, .-do_interrupt
596 .type fp_exception, %function
600 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
602 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
605 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
606 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
610 .size fp_exception, .-fp_exception
612 .global fp_exception_ds
613 .type fp_exception_ds, %function
615 mov r2, #0x90000000 /* Set high bit if delay slot */
617 .size fp_exception_ds, .-fp_exception_ds
621 .type jump_syscall, %function
623 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
625 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
628 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
629 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
633 .size jump_syscall, .-jump_syscall
637 .global jump_syscall_hle
638 .type jump_syscall_hle, %function
640 str r0, [fp, #pcaddr-dynarec_local] /* PC must be set to EPC for psxException */
641 ldr r2, [fp, #last_count-dynarec_local]
642 mov r1, #0 /* in delay slot */
644 mov r0, #0x20 /* cause */
645 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
648 /* note: psxException might do recorsive recompiler call from it's HLE code,
649 * so be ready for this */
651 ldr r1, [fp, #next_interupt-dynarec_local]
652 ldr r10, [fp, #cycle-dynarec_local]
653 ldr r0, [fp, #pcaddr-dynarec_local]
655 str r1, [fp, #last_count-dynarec_local]
658 .size jump_syscall_hle, .-jump_syscall_hle
662 .type jump_hlecall, %function
664 ldr r2, [fp, #last_count-dynarec_local]
665 str r0, [fp, #pcaddr-dynarec_local]
668 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
670 .size jump_hlecall, .-jump_hlecall
674 .type jump_intcall, %function
676 ldr r2, [fp, #last_count-dynarec_local]
677 str r0, [fp, #pcaddr-dynarec_local]
680 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
682 .size jump_hlecall, .-jump_hlecall
686 .global new_dyna_leave
687 .type new_dyna_leave, %function
688 ldr r0, [fp, #last_count-dynarec_local]
691 str r10, [fp, #cycle-dynarec_local]
692 ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
693 .size new_dyna_leave, .-new_dyna_leave
695 /* these are used to call memhandlers */
697 .global indirect_jump_indexed
698 .type indirect_jump_indexed, %function
699 indirect_jump_indexed:
700 ldr r0, [r0, r1, lsl #2]
701 .global indirect_jump
702 .type indirect_jump, %function
704 ldr r12, [fp, #last_count-dynarec_local]
706 str r2, [fp, #cycle-dynarec_local]
708 .size indirect_jump, .-indirect_jump
709 .size indirect_jump_indexed, .-indirect_jump_indexed
712 .global invalidate_addr_r0
713 .type invalidate_addr_r0, %function
715 stmia fp, {r0, r1, r2, r3, r12, lr}
716 b invalidate_addr_call
717 .size invalidate_addr_r0, .-invalidate_addr_r0
719 .global invalidate_addr_r1
720 .type invalidate_addr_r1, %function
722 stmia fp, {r0, r1, r2, r3, r12, lr}
724 b invalidate_addr_call
725 .size invalidate_addr_r1, .-invalidate_addr_r1
727 .global invalidate_addr_r2
728 .type invalidate_addr_r2, %function
730 stmia fp, {r0, r1, r2, r3, r12, lr}
732 b invalidate_addr_call
733 .size invalidate_addr_r2, .-invalidate_addr_r2
735 .global invalidate_addr_r3
736 .type invalidate_addr_r3, %function
738 stmia fp, {r0, r1, r2, r3, r12, lr}
740 b invalidate_addr_call
741 .size invalidate_addr_r3, .-invalidate_addr_r3
743 .global invalidate_addr_r4
744 .type invalidate_addr_r4, %function
746 stmia fp, {r0, r1, r2, r3, r12, lr}
748 b invalidate_addr_call
749 .size invalidate_addr_r4, .-invalidate_addr_r4
751 .global invalidate_addr_r5
752 .type invalidate_addr_r5, %function
754 stmia fp, {r0, r1, r2, r3, r12, lr}
756 b invalidate_addr_call
757 .size invalidate_addr_r5, .-invalidate_addr_r5
759 .global invalidate_addr_r6
760 .type invalidate_addr_r6, %function
762 stmia fp, {r0, r1, r2, r3, r12, lr}
764 b invalidate_addr_call
765 .size invalidate_addr_r6, .-invalidate_addr_r6
767 .global invalidate_addr_r7
768 .type invalidate_addr_r7, %function
770 stmia fp, {r0, r1, r2, r3, r12, lr}
772 b invalidate_addr_call
773 .size invalidate_addr_r7, .-invalidate_addr_r7
775 .global invalidate_addr_r8
776 .type invalidate_addr_r8, %function
778 stmia fp, {r0, r1, r2, r3, r12, lr}
780 b invalidate_addr_call
781 .size invalidate_addr_r8, .-invalidate_addr_r8
783 .global invalidate_addr_r9
784 .type invalidate_addr_r9, %function
786 stmia fp, {r0, r1, r2, r3, r12, lr}
788 b invalidate_addr_call
789 .size invalidate_addr_r9, .-invalidate_addr_r9
791 .global invalidate_addr_r10
792 .type invalidate_addr_r10, %function
794 stmia fp, {r0, r1, r2, r3, r12, lr}
796 b invalidate_addr_call
797 .size invalidate_addr_r10, .-invalidate_addr_r10
799 .global invalidate_addr_r12
800 .type invalidate_addr_r12, %function
802 stmia fp, {r0, r1, r2, r3, r12, lr}
804 .size invalidate_addr_r12, .-invalidate_addr_r12
806 .global invalidate_addr_call
807 .type invalidate_addr_call, %function
808 invalidate_addr_call:
809 ldr r12, [fp, #inv_code_start-dynarec_local]
810 ldr lr, [fp, #inv_code_end-dynarec_local]
814 ldmia fp, {r0, r1, r2, r3, r12, pc}
815 .size invalidate_addr_call, .-invalidate_addr_call
818 .global new_dyna_start
819 .type new_dyna_start, %function
821 /* ip is stored to conform EABI alignment */
822 stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr}
823 load_var_adr fp, dynarec_local
824 ldr r0, [fp, #pcaddr-dynarec_local]
826 ldr r1, [fp, #next_interupt-dynarec_local]
827 ldr r10, [fp, #cycle-dynarec_local]
828 str r1, [fp, #last_count-dynarec_local]
831 .size new_dyna_start, .-new_dyna_start
833 /* --------------------------------------- */
836 .global ari_read_ram8
837 .global ari_read_ram16
838 .global ari_read_ram32
839 .global ari_read_ram_mirror8
840 .global ari_read_ram_mirror16
841 .global ari_read_ram_mirror32
842 .global ari_write_ram8
843 .global ari_write_ram16
844 .global ari_write_ram32
845 .global ari_write_ram_mirror8
846 .global ari_write_ram_mirror16
847 .global ari_write_ram_mirror32
848 .global ari_write_ram_mirror_ro32
849 .global ari_read_bios8
850 .global ari_read_bios16
851 .global ari_read_bios32
853 .global ari_read_io16
854 .global ari_read_io32
855 .global ari_write_io8
856 .global ari_write_io16
857 .global ari_write_io32
859 .macro ari_read_ram bic_const op
860 ldr r0, [fp, #address-dynarec_local]
862 bic r0, r0, #\bic_const
865 str r0, [fp, #readmem_dword-dynarec_local]
878 .macro ari_read_ram_mirror mvn_const, op
879 ldr r0, [fp, #address-dynarec_local]
884 str r0, [fp, #readmem_dword-dynarec_local]
888 ari_read_ram_mirror8:
889 ari_read_ram_mirror 0, ldrb
891 ari_read_ram_mirror16:
892 ari_read_ram_mirror (1<<11), ldrh
894 ari_read_ram_mirror32:
895 ari_read_ram_mirror (3<<11), ldr
897 /* invalidation is already taken care of by the caller */
898 .macro ari_write_ram bic_const var pf
899 ldr r0, [fp, #address-dynarec_local]
900 ldr\pf r1, [fp, #\var-dynarec_local]
902 bic r0, r0, #\bic_const
909 ari_write_ram 0, byte, b
912 ari_write_ram 1, hword, h
915 ari_write_ram 3, word,
917 .macro ari_write_ram_mirror mvn_const var pf
918 ldr r0, [fp, #address-dynarec_local]
920 ldr\pf r1, [fp, #\var-dynarec_local]
922 ldr r2, [fp, #invc_ptr-dynarec_local]
924 ldrb r2, [r2, r0, lsr #12]
928 ldr r1, [fp, #inv_code_start-dynarec_local]
929 ldr r2, [fp, #inv_code_end-dynarec_local]
937 ari_write_ram_mirror8:
938 ari_write_ram_mirror 0, byte, b
940 ari_write_ram_mirror16:
941 ari_write_ram_mirror (1<<11), hword, h
943 ari_write_ram_mirror32:
944 ari_write_ram_mirror (3<<11), word,
946 ari_write_ram_mirror_ro32:
947 load_var_adr r0, pcsx_ram_is_ro
952 b ari_write_ram_mirror32
955 .macro ari_read_bios_mirror bic_const op
956 ldr r0, [fp, #address-dynarec_local]
957 orr r0, r0, #0x80000000
958 bic r0, r0, #(0x20000000|\bic_const) @ map to 0x9fc...
960 str r0, [fp, #readmem_dword-dynarec_local]
965 ari_read_bios_mirror 0, ldrb
968 ari_read_bios_mirror 1, ldrh
971 ari_read_bios_mirror 3, ldr
975 .macro ari_read_io_old tab_shift
976 str lr, [sp, #-8]! @ EABI alignment..
986 str r0, [fp, #readmem_dword-dynarec_local]
990 .macro ari_read_io readop mem_tab tab_shift
991 ldr r0, [fp, #address-dynarec_local]
992 ldr r1, [fp, #psxH_ptr-dynarec_local]
999 bic r2, r0, #0x1f800000
1000 ldr r12,[fp, #\mem_tab-dynarec_local]
1001 subs r3, r2, #0x1000
1003 @ ari_read_io_old \tab_shift
1006 ldr r12,[r12, r3, lsl #\tab_shift]
1010 str lr, [sp, #-8]! @ EABI alignment..
1012 str r0, [fp, #readmem_dword-dynarec_local]
1016 .if \tab_shift == 1 @ read16
1021 ldr r12,[fp, #spu_readf-dynarec_local]
1025 @ no handler, just read psxH
1026 \readop r0, [r1, r2]
1027 str r0, [fp, #readmem_dword-dynarec_local]
1032 ari_read_io ldrb, tab_read8, 2
1035 ari_read_io ldrh, tab_read16, 1
1038 ari_read_io ldr, tab_read32, 0
1040 .macro ari_write_io_old tab_shift
1052 .macro ari_write_io pf var mem_tab tab_shift
1053 ldr r0, [fp, #address-dynarec_local]
1054 ldr\pf r1, [fp, #\var-dynarec_local]
1061 bic r2, r0, #0x1f800000
1062 ldr r12,[fp, #\mem_tab-dynarec_local]
1063 subs r3, r2, #0x1000
1065 @ ari_write_io_old \tab_shift
1068 ldr r12,[r12, r3, lsl #\tab_shift]
1073 ldr r3, [fp, #psxH_ptr-dynarec_local]
1081 ldrlo pc, [fp, #spu_writef-dynarec_local]
1083 @ write32 to SPU - very rare case (is this correct?)
1089 ldr pc, [fp, #spu_writef-dynarec_local]
1091 ldr pc, [fp, #spu_writef-dynarec_local]
1098 @ PCSX always writes to psxH, so do we for consistency
1099 ldr r0, [fp, #address-dynarec_local]
1100 ldr r3, [fp, #psxH_ptr-dynarec_local]
1101 ldrb r1, [fp, #byte-dynarec_local]
1102 bic r2, r0, #0x1f800000
1103 ldr r12,[fp, #tab_write8-dynarec_local]
1105 subs r3, r2, #0x1000
1107 @ ari_write_io_old 2
1110 ldr r12,[r12, r3, lsl #2]
1117 ari_write_io h, hword, tab_write16, 1
1120 ari_write_io , word, tab_write32, 0
1122 @ vim:filetype=armasm