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
68 .type dynarec_local, %object
69 .size dynarec_local, dynarec_local_end-dynarec_local
71 .space dynarec_local_end-dynarec_local /*0x400630*/
72 next_interupt = dynarec_local + 64
73 .type next_interupt, %object
74 .size next_interupt, 4
75 cycle_count = next_interupt + 4
76 .type cycle_count, %object
78 last_count = cycle_count + 4
79 .type last_count, %object
81 pending_exception = last_count + 4
82 .type pending_exception, %object
83 .size pending_exception, 4
84 stop = pending_exception + 4
88 .type invc_ptr, %object
90 address = invc_ptr + 4
91 .type address, %object
93 readmem_dword = address + 4
94 readmem_word = readmem_dword
95 .type readmem_dword, %object
96 .size readmem_dword, 8
97 dword = readmem_dword + 8
108 .size byte, 1 /* 1 byte free */
118 .type psxRegs, %object
119 .size psxRegs, psxRegs_end-psxRegs
130 .type reg_cop0, %object
132 reg_cop2d = reg_cop0 + 128
133 .type reg_cop2d, %object
135 reg_cop2c = reg_cop2d + 128
136 .type reg_cop2c, %object
148 interrupt = cycle + 4
149 .type interrupt, %object
151 intCycle = interrupt + 4
152 .type intCycle, %object
154 psxRegs_end = intCycle + 256
157 nd_pcsx_io = psxRegs_end
158 .type nd_pcsx_io, %object
159 .size nd_pcsx_io, nd_pcsx_io_end-nd_pcsx_io
160 tab_read8 = nd_pcsx_io
161 .type tab_read8, %object
163 tab_read16 = tab_read8 + 4
164 .type tab_read16, %object
166 tab_read32 = tab_read16 + 4
167 .type tab_read32, %object
169 tab_write8 = tab_read32 + 4
170 .type tab_write8, %object
172 tab_write16 = tab_write8 + 4
173 .type tab_write16, %object
175 tab_write32 = tab_write16 + 4
176 .type tab_write32, %object
178 spu_readf = tab_write32 + 4
179 .type spu_readf, %object
181 spu_writef = spu_readf + 4
182 .type spu_writef, %object
184 nd_pcsx_io_end = spu_writef + 4
186 psxH_ptr = nd_pcsx_io_end
187 .type psxH_ptr, %object
189 align0 = psxH_ptr + 4 /* just for alignment */
190 .type align0, %object
192 branch_target = align0 + 4
193 .type branch_target, %object
194 .size branch_target, 4
195 mini_ht = branch_target + 4
196 .type mini_ht, %object
198 restore_candidate = mini_ht + 256
199 .type restore_candidate, %object
200 .size restore_candidate, 512
201 memory_map = restore_candidate + 512
202 .type memory_map, %object
203 .size memory_map, 4194304
204 dynarec_local_end = memory_map + 4194304
206 .macro load_var_adr reg var
208 movw \reg, #:lower16:\var
209 movt \reg, #:upper16:\var
215 .macro dyna_linker_main
216 /* r0 = virtual target address */
217 /* r1 = instruction to patch */
231 ldr r5, [r3, r2, lsl #2]
233 add r6, r1, r12, asr #6
248 moveq pc, r4 /* Stale i-cache */
250 b 1b /* jump_in may have dupes, continue search */
253 beq 3f /* r0 not in jump_in */
259 and r1, r7, #0xff000000
262 add r1, r1, r2, lsr #8
266 /* hash_table lookup */
269 eor r4, r0, r0, lsl #16
275 ldr r5, [r3, r2, lsl #2]
282 /* jump_dirty lookup */
292 /* hash_table insert */
306 .type dyna_linker, %function
308 /* r0 = virtual target address */
309 /* r1 = instruction to patch */
314 bl new_recompile_block
322 .size dyna_linker, .-dyna_linker
323 .global exec_pagefault
324 .type exec_pagefault, %function
326 /* r0 = instruction pointer */
327 /* r1 = fault address */
329 ldr r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
331 ldr r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
332 bic r6, r6, #0x0F800000
333 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
335 str r1, [fp, #reg_cop0+32-dynarec_local] /* BadVAddr */
337 str r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
338 and r5, r6, r1, lsr #9
339 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
340 and r1, r1, r6, lsl #9
341 str r1, [fp, #reg_cop0+40-dynarec_local] /* EntryHi */
343 str r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
347 .size exec_pagefault, .-exec_pagefault
349 /* Special dynamic linker for the case where a page fault
350 may occur in a branch delay slot */
351 .global dyna_linker_ds
352 .type dyna_linker_ds, %function
354 /* r0 = virtual target address */
355 /* r1 = instruction to patch */
362 bl new_recompile_block
369 mov r2, #0x80000008 /* High bit set indicates pagefault in delay slot */
372 .size dyna_linker_ds, .-dyna_linker_ds
381 .global jump_vaddr_r0
382 .type jump_vaddr_r0, %function
384 eor r2, r0, r0, lsl #16
386 .size jump_vaddr_r0, .-jump_vaddr_r0
387 .global jump_vaddr_r1
388 .type jump_vaddr_r1, %function
390 eor r2, r1, r1, lsl #16
393 .size jump_vaddr_r1, .-jump_vaddr_r1
394 .global jump_vaddr_r2
395 .type jump_vaddr_r2, %function
398 eor r2, r2, r2, lsl #16
400 .size jump_vaddr_r2, .-jump_vaddr_r2
401 .global jump_vaddr_r3
402 .type jump_vaddr_r3, %function
404 eor r2, r3, r3, lsl #16
407 .size jump_vaddr_r3, .-jump_vaddr_r3
408 .global jump_vaddr_r4
409 .type jump_vaddr_r4, %function
411 eor r2, r4, r4, lsl #16
414 .size jump_vaddr_r4, .-jump_vaddr_r4
415 .global jump_vaddr_r5
416 .type jump_vaddr_r5, %function
418 eor r2, r5, r5, lsl #16
421 .size jump_vaddr_r5, .-jump_vaddr_r5
422 .global jump_vaddr_r6
423 .type jump_vaddr_r6, %function
425 eor r2, r6, r6, lsl #16
428 .size jump_vaddr_r6, .-jump_vaddr_r6
429 .global jump_vaddr_r8
430 .type jump_vaddr_r8, %function
432 eor r2, r8, r8, lsl #16
435 .size jump_vaddr_r8, .-jump_vaddr_r8
436 .global jump_vaddr_r9
437 .type jump_vaddr_r9, %function
439 eor r2, r9, r9, lsl #16
442 .size jump_vaddr_r9, .-jump_vaddr_r9
443 .global jump_vaddr_r10
444 .type jump_vaddr_r10, %function
446 eor r2, r10, r10, lsl #16
449 .size jump_vaddr_r10, .-jump_vaddr_r10
450 .global jump_vaddr_r12
451 .type jump_vaddr_r12, %function
453 eor r2, r12, r12, lsl #16
456 .size jump_vaddr_r12, .-jump_vaddr_r12
457 .global jump_vaddr_r7
458 .type jump_vaddr_r7, %function
460 eor r2, r7, r7, lsl #16
462 .size jump_vaddr_r7, .-jump_vaddr_r7
464 .type jump_vaddr, %function
468 and r2, r3, r2, lsr #12
475 str r10, [fp, #cycle_count-dynarec_local]
477 ldr r10, [fp, #cycle_count-dynarec_local]
479 .size jump_vaddr, .-jump_vaddr
482 .global verify_code_ds
483 .type verify_code_ds, %function
485 str r8, [fp, #branch_target-dynarec_local]
486 .size verify_code_ds, .-verify_code_ds
487 .global verify_code_vm
488 .type verify_code_vm, %function
491 .type verify_code, %function
520 ldr r8, [fp, #branch_target-dynarec_local]
525 .size verify_code, .-verify_code
526 .size verify_code_vm, .-verify_code_vm
530 .type cc_interrupt, %function
532 ldr r0, [fp, #last_count-dynarec_local]
536 str r1, [fp, #pending_exception-dynarec_local]
537 and r2, r2, r10, lsr #17
538 add r3, fp, #restore_candidate-dynarec_local
539 str r10, [fp, #cycle-dynarec_local] /* PCSX cycles */
540 @@ str r10, [fp, #reg_cop0+36-dynarec_local] /* Count */
548 ldr r10, [fp, #cycle-dynarec_local]
549 ldr r0, [fp, #next_interupt-dynarec_local]
550 ldr r1, [fp, #pending_exception-dynarec_local]
551 ldr r2, [fp, #stop-dynarec_local]
552 str r0, [fp, #last_count-dynarec_local]
555 ldmnefd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
559 ldr r0, [fp, #pcaddr-dynarec_local]
563 /* Move 'dirty' blocks to the 'clean' list */
574 .size cc_interrupt, .-cc_interrupt
578 .type do_interrupt, %function
580 ldr r0, [fp, #pcaddr-dynarec_local]
584 .size do_interrupt, .-do_interrupt
588 .type fp_exception, %function
592 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
594 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
597 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
598 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
602 .size fp_exception, .-fp_exception
604 .global fp_exception_ds
605 .type fp_exception_ds, %function
607 mov r2, #0x90000000 /* Set high bit if delay slot */
609 .size fp_exception_ds, .-fp_exception_ds
613 .type jump_syscall, %function
615 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
617 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
620 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
621 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
625 .size jump_syscall, .-jump_syscall
629 .global jump_syscall_hle
630 .type jump_syscall_hle, %function
632 str r0, [fp, #pcaddr-dynarec_local] /* PC must be set to EPC for psxException */
633 ldr r2, [fp, #last_count-dynarec_local]
634 mov r1, #0 /* in delay slot */
636 mov r0, #0x20 /* cause */
637 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
640 /* note: psxException might do recorsive recompiler call from it's HLE code,
641 * so be ready for this */
643 ldr r1, [fp, #next_interupt-dynarec_local]
644 ldr r10, [fp, #cycle-dynarec_local]
645 ldr r0, [fp, #pcaddr-dynarec_local]
647 str r1, [fp, #last_count-dynarec_local]
650 .size jump_syscall_hle, .-jump_syscall_hle
654 .type jump_hlecall, %function
656 ldr r2, [fp, #last_count-dynarec_local]
657 str r0, [fp, #pcaddr-dynarec_local]
660 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
662 .size jump_hlecall, .-jump_hlecall
666 .type jump_intcall, %function
668 ldr r2, [fp, #last_count-dynarec_local]
669 str r0, [fp, #pcaddr-dynarec_local]
672 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
674 .size jump_hlecall, .-jump_hlecall
678 .global new_dyna_leave
679 .type new_dyna_leave, %function
680 ldr r0, [fp, #last_count-dynarec_local]
683 str r10, [fp, #cycle-dynarec_local]
684 ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
685 .size new_dyna_leave, .-new_dyna_leave
687 /* these are used to call memhandlers */
689 .global indirect_jump_indexed
690 .type indirect_jump_indexed, %function
691 indirect_jump_indexed:
692 ldr r0, [r0, r1, lsl #2]
693 .global indirect_jump
694 .type indirect_jump, %function
696 ldr r12, [fp, #last_count-dynarec_local]
698 str r2, [fp, #cycle-dynarec_local]
700 .size indirect_jump, .-indirect_jump
701 .size indirect_jump_indexed, .-indirect_jump_indexed
704 .global invalidate_addr_r0
705 .type invalidate_addr_r0, %function
707 stmia fp, {r0, r1, r2, r3, r12, lr}
709 b invalidate_addr_call
710 .size invalidate_addr_r0, .-invalidate_addr_r0
712 .global invalidate_addr_r1
713 .type invalidate_addr_r1, %function
715 stmia fp, {r0, r1, r2, r3, r12, lr}
717 b invalidate_addr_call
718 .size invalidate_addr_r1, .-invalidate_addr_r1
720 .global invalidate_addr_r2
721 .type invalidate_addr_r2, %function
723 stmia fp, {r0, r1, r2, r3, r12, lr}
725 b invalidate_addr_call
726 .size invalidate_addr_r2, .-invalidate_addr_r2
728 .global invalidate_addr_r3
729 .type invalidate_addr_r3, %function
731 stmia fp, {r0, r1, r2, r3, r12, lr}
733 b invalidate_addr_call
734 .size invalidate_addr_r3, .-invalidate_addr_r3
736 .global invalidate_addr_r4
737 .type invalidate_addr_r4, %function
739 stmia fp, {r0, r1, r2, r3, r12, lr}
741 b invalidate_addr_call
742 .size invalidate_addr_r4, .-invalidate_addr_r4
744 .global invalidate_addr_r5
745 .type invalidate_addr_r5, %function
747 stmia fp, {r0, r1, r2, r3, r12, lr}
749 b invalidate_addr_call
750 .size invalidate_addr_r5, .-invalidate_addr_r5
752 .global invalidate_addr_r6
753 .type invalidate_addr_r6, %function
755 stmia fp, {r0, r1, r2, r3, r12, lr}
757 b invalidate_addr_call
758 .size invalidate_addr_r6, .-invalidate_addr_r6
760 .global invalidate_addr_r7
761 .type invalidate_addr_r7, %function
763 stmia fp, {r0, r1, r2, r3, r12, lr}
765 b invalidate_addr_call
766 .size invalidate_addr_r7, .-invalidate_addr_r7
768 .global invalidate_addr_r8
769 .type invalidate_addr_r8, %function
771 stmia fp, {r0, r1, r2, r3, r12, lr}
773 b invalidate_addr_call
774 .size invalidate_addr_r8, .-invalidate_addr_r8
776 .global invalidate_addr_r9
777 .type invalidate_addr_r9, %function
779 stmia fp, {r0, r1, r2, r3, r12, lr}
781 b invalidate_addr_call
782 .size invalidate_addr_r9, .-invalidate_addr_r9
784 .global invalidate_addr_r10
785 .type invalidate_addr_r10, %function
787 stmia fp, {r0, r1, r2, r3, r12, lr}
789 b invalidate_addr_call
790 .size invalidate_addr_r10, .-invalidate_addr_r10
792 .global invalidate_addr_r12
793 .type invalidate_addr_r12, %function
795 stmia fp, {r0, r1, r2, r3, r12, lr}
797 .size invalidate_addr_r12, .-invalidate_addr_r12
799 .global invalidate_addr_call
800 .type invalidate_addr_call, %function
801 invalidate_addr_call:
803 ldmia fp, {r0, r1, r2, r3, r12, pc}
804 .size invalidate_addr_call, .-invalidate_addr_call
807 .global new_dyna_start
808 .type new_dyna_start, %function
810 /* ip is stored to conform EABI alignment */
811 stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr}
812 load_var_adr fp, dynarec_local
813 ldr r0, [fp, #pcaddr-dynarec_local]
815 ldr r1, [fp, #next_interupt-dynarec_local]
816 ldr r10, [fp, #cycle-dynarec_local]
817 str r1, [fp, #last_count-dynarec_local]
820 .size new_dyna_start, .-new_dyna_start
822 /* --------------------------------------- */
825 .global ari_read_ram8
826 .global ari_read_ram16
827 .global ari_read_ram32
828 .global ari_read_ram_mirror8
829 .global ari_read_ram_mirror16
830 .global ari_read_ram_mirror32
831 .global ari_write_ram8
832 .global ari_write_ram16
833 .global ari_write_ram32
834 .global ari_write_ram_mirror8
835 .global ari_write_ram_mirror16
836 .global ari_write_ram_mirror32
837 .global ari_write_ram_mirror_ro32
838 .global ari_read_bios8
839 .global ari_read_bios16
840 .global ari_read_bios32
842 .global ari_read_io16
843 .global ari_read_io32
844 .global ari_write_io8
845 .global ari_write_io16
846 .global ari_write_io32
848 .macro ari_read_ram bic_const op
849 ldr r0, [fp, #address-dynarec_local]
851 bic r0, r0, #\bic_const
854 str r0, [fp, #readmem_dword-dynarec_local]
867 .macro ari_read_ram_mirror mvn_const, op
868 ldr r0, [fp, #address-dynarec_local]
873 str r0, [fp, #readmem_dword-dynarec_local]
877 ari_read_ram_mirror8:
878 ari_read_ram_mirror 0, ldrb
880 ari_read_ram_mirror16:
881 ari_read_ram_mirror (1<<11), ldrh
883 ari_read_ram_mirror32:
884 ari_read_ram_mirror (3<<11), ldr
886 /* invalidation is already taken care of by the caller */
887 .macro ari_write_ram bic_const var pf
888 ldr r0, [fp, #address-dynarec_local]
889 ldr\pf r1, [fp, #\var-dynarec_local]
891 bic r0, r0, #\bic_const
898 ari_write_ram 0, byte, b
901 ari_write_ram 1, hword, h
904 ari_write_ram 3, word,
906 .macro ari_write_ram_mirror mvn_const var pf
907 ldr r0, [fp, #address-dynarec_local]
909 ldr\pf r1, [fp, #\var-dynarec_local]
911 ldr r2, [fp, #invc_ptr-dynarec_local]
913 ldrb r2, [r2, r0, lsr #12]
921 ari_write_ram_mirror8:
922 ari_write_ram_mirror 0, byte, b
924 ari_write_ram_mirror16:
925 ari_write_ram_mirror (1<<11), hword, h
927 ari_write_ram_mirror32:
928 ari_write_ram_mirror (3<<11), word,
930 ari_write_ram_mirror_ro32:
931 load_var_adr r0, pcsx_ram_is_ro
936 b ari_write_ram_mirror32
939 .macro ari_read_bios_mirror bic_const op
940 ldr r0, [fp, #address-dynarec_local]
941 orr r0, r0, #0x80000000
942 bic r0, r0, #(0x20000000|\bic_const) @ map to 0x9fc...
944 str r0, [fp, #readmem_dword-dynarec_local]
949 ari_read_bios_mirror 0, ldrb
952 ari_read_bios_mirror 1, ldrh
955 ari_read_bios_mirror 3, ldr
959 .macro ari_read_io_old tab_shift
960 str lr, [sp, #-8]! @ EABI alignment..
970 str r0, [fp, #readmem_dword-dynarec_local]
974 .macro ari_read_io readop mem_tab tab_shift
975 ldr r0, [fp, #address-dynarec_local]
976 ldr r1, [fp, #psxH_ptr-dynarec_local]
983 bic r2, r0, #0x1f800000
984 ldr r12,[fp, #\mem_tab-dynarec_local]
987 @ ari_read_io_old \tab_shift
990 ldr r12,[r12, r3, lsl #\tab_shift]
994 str lr, [sp, #-8]! @ EABI alignment..
996 str r0, [fp, #readmem_dword-dynarec_local]
1000 .if \tab_shift == 1 @ read16
1005 ldr r12,[fp, #spu_readf-dynarec_local]
1009 @ no handler, just read psxH
1010 \readop r0, [r1, r2]
1011 str r0, [fp, #readmem_dword-dynarec_local]
1016 ari_read_io ldrb, tab_read8, 2
1019 ari_read_io ldrh, tab_read16, 1
1022 ari_read_io ldr, tab_read32, 0
1024 .macro ari_write_io_old tab_shift
1036 .macro ari_write_io pf var mem_tab tab_shift
1037 ldr r0, [fp, #address-dynarec_local]
1038 ldr\pf r1, [fp, #\var-dynarec_local]
1045 bic r2, r0, #0x1f800000
1046 ldr r12,[fp, #\mem_tab-dynarec_local]
1047 subs r3, r2, #0x1000
1049 @ ari_write_io_old \tab_shift
1052 ldr r12,[r12, r3, lsl #\tab_shift]
1057 ldr r3, [fp, #psxH_ptr-dynarec_local]
1065 ldrlo pc, [fp, #spu_writef-dynarec_local]
1067 @ write32 to SPU - very rare case (is this correct?)
1073 ldr pc, [fp, #spu_writef-dynarec_local]
1075 ldr pc, [fp, #spu_writef-dynarec_local]
1082 @ PCSX always writes to psxH, so do we for consistency
1083 ldr r0, [fp, #address-dynarec_local]
1084 ldr r3, [fp, #psxH_ptr-dynarec_local]
1085 ldrb r1, [fp, #byte-dynarec_local]
1086 bic r2, r0, #0x1f800000
1087 ldr r12,[fp, #tab_write8-dynarec_local]
1089 subs r3, r2, #0x1000
1091 @ ari_write_io_old 2
1094 ldr r12,[r12, r3, lsl #2]
1101 ari_write_io h, hword, tab_write16, 1
1104 ari_write_io , word, tab_write32, 0
1106 @ vim:filetype=armasm