1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * linkage_arm.s for PCSX *
3 * Copyright (C) 2009-2010 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
209 .type dyna_linker, %function
211 /* r0 = virtual target address */
212 /* r1 = instruction to patch */
219 eor r2, r2, r12, lsr #12
220 and r6, r6, r12, lsr #12
224 ldr r5, [r3, r2, lsl #2]
240 add r1, r1, r12, asr #6
242 moveq pc, r4 /* Stale i-cache */
245 and r1, r7, #0xff000000
248 add r1, r1, r2, lsr #8
252 /* hash_table lookup */
255 eor r4, r0, r0, lsl #16
261 ldr r5, [r3, r2, lsl #2]
268 /* jump_dirty lookup */
278 /* hash_table insert */
289 bl new_recompile_block
297 .size dyna_linker, .-dyna_linker
298 .global exec_pagefault
299 .type exec_pagefault, %function
301 /* r0 = instruction pointer */
302 /* r1 = fault address */
304 ldr r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
306 ldr r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
307 bic r6, r6, #0x0F800000
308 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
310 str r1, [fp, #reg_cop0+32-dynarec_local] /* BadVAddr */
312 str r3, [fp, #reg_cop0+48-dynarec_local] /* Status */
313 and r5, r6, r1, lsr #9
314 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
315 and r1, r1, r6, lsl #9
316 str r1, [fp, #reg_cop0+40-dynarec_local] /* EntryHi */
318 str r4, [fp, #reg_cop0+16-dynarec_local] /* Context */
322 .size exec_pagefault, .-exec_pagefault
324 /* Special dynamic linker for the case where a page fault
325 may occur in a branch delay slot */
326 .global dyna_linker_ds
327 .type dyna_linker_ds, %function
329 /* r0 = virtual target address */
330 /* r1 = instruction to patch */
337 eor r2, r2, r12, lsr #12
338 and r6, r6, r12, lsr #12
342 ldr r5, [r3, r2, lsl #2]
358 add r1, r1, r12, asr #6
360 moveq pc, r4 /* Stale i-cache */
363 and r1, r7, #0xff000000
366 add r1, r1, r2, lsr #8
370 /* hash_table lookup */
373 eor r4, r0, r0, lsl #16
379 ldr r5, [r3, r2, lsl #2]
386 /* jump_dirty lookup */
396 /* hash_table insert */
409 bl new_recompile_block
416 mov r2, #0x80000008 /* High bit set indicates pagefault in delay slot */
419 .size dyna_linker_ds, .-dyna_linker_ds
428 .global jump_vaddr_r0
429 .type jump_vaddr_r0, %function
431 eor r2, r0, r0, lsl #16
433 .size jump_vaddr_r0, .-jump_vaddr_r0
434 .global jump_vaddr_r1
435 .type jump_vaddr_r1, %function
437 eor r2, r1, r1, lsl #16
440 .size jump_vaddr_r1, .-jump_vaddr_r1
441 .global jump_vaddr_r2
442 .type jump_vaddr_r2, %function
445 eor r2, r2, r2, lsl #16
447 .size jump_vaddr_r2, .-jump_vaddr_r2
448 .global jump_vaddr_r3
449 .type jump_vaddr_r3, %function
451 eor r2, r3, r3, lsl #16
454 .size jump_vaddr_r3, .-jump_vaddr_r3
455 .global jump_vaddr_r4
456 .type jump_vaddr_r4, %function
458 eor r2, r4, r4, lsl #16
461 .size jump_vaddr_r4, .-jump_vaddr_r4
462 .global jump_vaddr_r5
463 .type jump_vaddr_r5, %function
465 eor r2, r5, r5, lsl #16
468 .size jump_vaddr_r5, .-jump_vaddr_r5
469 .global jump_vaddr_r6
470 .type jump_vaddr_r6, %function
472 eor r2, r6, r6, lsl #16
475 .size jump_vaddr_r6, .-jump_vaddr_r6
476 .global jump_vaddr_r8
477 .type jump_vaddr_r8, %function
479 eor r2, r8, r8, lsl #16
482 .size jump_vaddr_r8, .-jump_vaddr_r8
483 .global jump_vaddr_r9
484 .type jump_vaddr_r9, %function
486 eor r2, r9, r9, lsl #16
489 .size jump_vaddr_r9, .-jump_vaddr_r9
490 .global jump_vaddr_r10
491 .type jump_vaddr_r10, %function
493 eor r2, r10, r10, lsl #16
496 .size jump_vaddr_r10, .-jump_vaddr_r10
497 .global jump_vaddr_r12
498 .type jump_vaddr_r12, %function
500 eor r2, r12, r12, lsl #16
503 .size jump_vaddr_r12, .-jump_vaddr_r12
504 .global jump_vaddr_r7
505 .type jump_vaddr_r7, %function
507 eor r2, r7, r7, lsl #16
509 .size jump_vaddr_r7, .-jump_vaddr_r7
511 .type jump_vaddr, %function
515 and r2, r3, r2, lsr #12
522 str r10, [fp, #cycle_count-dynarec_local]
524 ldr r10, [fp, #cycle_count-dynarec_local]
526 .size jump_vaddr, .-jump_vaddr
529 .global verify_code_ds
530 .type verify_code_ds, %function
532 str r8, [fp, #branch_target-dynarec_local]
533 .size verify_code_ds, .-verify_code_ds
534 .global verify_code_vm
535 .type verify_code_vm, %function
538 .type verify_code, %function
567 ldr r8, [fp, #branch_target-dynarec_local]
572 .size verify_code, .-verify_code
573 .size verify_code_vm, .-verify_code_vm
577 .type cc_interrupt, %function
579 ldr r0, [fp, #last_count-dynarec_local]
583 str r1, [fp, #pending_exception-dynarec_local]
584 and r2, r2, r10, lsr #17
585 add r3, fp, #restore_candidate-dynarec_local
586 str r10, [fp, #cycle-dynarec_local] /* PCSX cycles */
587 @@ str r10, [fp, #reg_cop0+36-dynarec_local] /* Count */
595 ldr r10, [fp, #cycle-dynarec_local]
596 ldr r0, [fp, #next_interupt-dynarec_local]
597 ldr r1, [fp, #pending_exception-dynarec_local]
598 ldr r2, [fp, #stop-dynarec_local]
599 str r0, [fp, #last_count-dynarec_local]
602 ldmnefd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
606 ldr r0, [fp, #pcaddr-dynarec_local]
610 /* Move 'dirty' blocks to the 'clean' list */
621 .size cc_interrupt, .-cc_interrupt
625 .type do_interrupt, %function
627 ldr r0, [fp, #pcaddr-dynarec_local]
631 .size do_interrupt, .-do_interrupt
635 .type fp_exception, %function
639 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
641 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
644 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
645 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
649 .size fp_exception, .-fp_exception
651 .global fp_exception_ds
652 .type fp_exception_ds, %function
654 mov r2, #0x90000000 /* Set high bit if delay slot */
656 .size fp_exception_ds, .-fp_exception_ds
660 .type jump_syscall, %function
662 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
664 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
667 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
668 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
672 .size jump_syscall, .-jump_syscall
676 .global jump_syscall_hle
677 .type jump_syscall_hle, %function
679 str r0, [fp, #pcaddr-dynarec_local] /* PC must be set to EPC for psxException */
680 ldr r2, [fp, #last_count-dynarec_local]
681 mov r1, #0 /* in delay slot */
683 mov r0, #0x20 /* cause */
684 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
687 /* note: psxException might do recorsive recompiler call from it's HLE code,
688 * so be ready for this */
690 ldr r1, [fp, #next_interupt-dynarec_local]
691 ldr r10, [fp, #cycle-dynarec_local]
692 ldr r0, [fp, #pcaddr-dynarec_local]
694 str r1, [fp, #last_count-dynarec_local]
697 .size jump_syscall_hle, .-jump_syscall_hle
701 .type jump_hlecall, %function
703 ldr r2, [fp, #last_count-dynarec_local]
704 str r0, [fp, #pcaddr-dynarec_local]
707 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
709 .size jump_hlecall, .-jump_hlecall
713 .type jump_intcall, %function
715 ldr r2, [fp, #last_count-dynarec_local]
716 str r0, [fp, #pcaddr-dynarec_local]
719 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
721 .size jump_hlecall, .-jump_hlecall
725 .global new_dyna_leave
726 .type new_dyna_leave, %function
727 ldr r0, [fp, #last_count-dynarec_local]
730 str r10, [fp, #cycle-dynarec_local]
731 ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
732 .size new_dyna_leave, .-new_dyna_leave
734 /* these are used to call memhandlers */
736 .global indirect_jump_indexed
737 .type indirect_jump_indexed, %function
738 indirect_jump_indexed:
739 ldr r0, [r0, r1, lsl #2]
740 .global indirect_jump
741 .type indirect_jump, %function
743 ldr r12, [fp, #last_count-dynarec_local]
745 str r2, [fp, #cycle-dynarec_local]
747 .size indirect_jump, .-indirect_jump
748 .size indirect_jump_indexed, .-indirect_jump_indexed
751 .global new_dyna_start
752 .type new_dyna_start, %function
754 /* ip is stored to conform EABI alignment */
755 stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr}
757 movw fp, #:lower16:dynarec_local
758 movt fp, #:upper16:dynarec_local
762 ldr r0, [fp, #pcaddr-dynarec_local]
764 ldr r1, [fp, #next_interupt-dynarec_local]
765 ldr r10, [fp, #cycle-dynarec_local]
766 str r1, [fp, #last_count-dynarec_local]
771 .size new_dyna_start, .-new_dyna_start
773 /* --------------------------------------- */
776 .global ari_read_ram8
777 .global ari_read_ram16
778 .global ari_read_ram32
779 .global ari_read_ram_mirror8
780 .global ari_read_ram_mirror16
781 .global ari_read_ram_mirror32
782 .global ari_write_ram8
783 .global ari_write_ram16
784 .global ari_write_ram32
785 .global ari_write_ram_mirror8
786 .global ari_write_ram_mirror16
787 .global ari_write_ram_mirror32
788 .global ari_read_bios8
789 .global ari_read_bios16
790 .global ari_read_bios32
792 .global ari_read_io16
793 .global ari_read_io32
794 .global ari_write_io8
795 .global ari_write_io16
796 .global ari_write_io32
798 .macro ari_read_ram bic_const op
799 ldr r0, [fp, #address-dynarec_local]
801 bic r0, r0, #\bic_const
804 str r0, [fp, #readmem_dword-dynarec_local]
817 .macro ari_read_ram_mirror mvn_const, op
818 ldr r0, [fp, #address-dynarec_local]
823 str r0, [fp, #readmem_dword-dynarec_local]
827 ari_read_ram_mirror8:
828 ari_read_ram_mirror 0, ldrb
830 ari_read_ram_mirror16:
831 ari_read_ram_mirror (1<<11), ldrh
833 ari_read_ram_mirror32:
834 ari_read_ram_mirror (3<<11), ldr
836 /* invalidation is already taken care of by the caller */
837 .macro ari_write_ram bic_const var pf
838 ldr r0, [fp, #address-dynarec_local]
839 ldr\pf r1, [fp, #\var-dynarec_local]
841 bic r0, r0, #\bic_const
848 ari_write_ram 0, byte, b
851 ari_write_ram 1, hword, h
854 ari_write_ram 3, word,
856 .macro ari_write_ram_mirror mvn_const var pf
857 ldr r0, [fp, #address-dynarec_local]
859 ldr\pf r1, [fp, #\var-dynarec_local]
861 ldr r2, [fp, #invc_ptr-dynarec_local]
863 ldrb r2, [r2, r0, lsr #12]
871 ari_write_ram_mirror8:
872 ari_write_ram_mirror 0, byte, b
874 ari_write_ram_mirror16:
875 ari_write_ram_mirror (1<<11), hword, h
877 ari_write_ram_mirror32:
878 ari_write_ram_mirror (3<<11), word,
881 .macro ari_read_bios_mirror bic_const op
882 ldr r0, [fp, #address-dynarec_local]
883 orr r0, r0, #0x80000000
884 bic r0, r0, #(0x20000000|\bic_const) @ map to 0x9fc...
886 str r0, [fp, #readmem_dword-dynarec_local]
891 ari_read_bios_mirror 0, ldrb
894 ari_read_bios_mirror 1, ldrh
897 ari_read_bios_mirror 3, ldr
901 .macro ari_read_io_old tab_shift
902 str lr, [sp, #-8]! @ EABI alignment..
912 str r0, [fp, #readmem_dword-dynarec_local]
916 .macro ari_read_io readop mem_tab tab_shift
917 ldr r0, [fp, #address-dynarec_local]
918 ldr r1, [fp, #psxH_ptr-dynarec_local]
925 bic r2, r0, #0x1f800000
926 ldr r12,[fp, #\mem_tab-dynarec_local]
929 @ ari_read_io_old \tab_shift
932 ldr r12,[r12, r3, lsl #\tab_shift]
936 str lr, [sp, #-8]! @ EABI alignment..
938 str r0, [fp, #readmem_dword-dynarec_local]
942 .if \tab_shift == 1 @ read16
947 ldr r12,[fp, #spu_readf-dynarec_local]
951 @ no handler, just read psxH
953 str r0, [fp, #readmem_dword-dynarec_local]
958 ari_read_io ldrb, tab_read8, 2
961 ari_read_io ldrh, tab_read16, 1
964 ari_read_io ldr, tab_read32, 0
966 .macro ari_write_io_old tab_shift
978 .macro ari_write_io pf var mem_tab tab_shift
979 ldr r0, [fp, #address-dynarec_local]
980 ldr\pf r1, [fp, #\var-dynarec_local]
987 bic r2, r0, #0x1f800000
988 ldr r12,[fp, #\mem_tab-dynarec_local]
991 @ ari_write_io_old \tab_shift
994 ldr r12,[r12, r3, lsl #\tab_shift]
999 ldr r3, [fp, #psxH_ptr-dynarec_local]
1003 .if \tab_shift == 1 @ write16
1007 ldrlo pc, [fp, #spu_writef-dynarec_local]
1014 @ PCSX always writes to psxH, so do we for consistency
1015 ldr r0, [fp, #address-dynarec_local]
1016 ldr r3, [fp, #psxH_ptr-dynarec_local]
1017 ldrb r1, [fp, #byte-dynarec_local]
1018 bic r2, r0, #0x1f800000
1019 ldr r12,[fp, #tab_write8-dynarec_local]
1021 subs r3, r2, #0x1000
1023 @ ari_write_io_old 2
1026 ldr r12,[r12, r3, lsl #2]
1033 ari_write_io h, hword, tab_write16, 1
1036 ari_write_io , word, tab_write32, 0
1038 @ vim:filetype=armasm