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 /* FIXME: cycles already calculated, not needed? */
628 ldr r0, [fp, #pcaddr-dynarec_local]
630 ldr r1, [fp, #next_interupt-dynarec_local]
631 ldr r10, [fp, #cycle-dynarec_local]
632 str r1, [fp, #last_count-dynarec_local]
636 .size do_interrupt, .-do_interrupt
639 .type fp_exception, %function
643 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
645 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
648 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
649 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
653 .size fp_exception, .-fp_exception
655 .global fp_exception_ds
656 .type fp_exception_ds, %function
658 mov r2, #0x90000000 /* Set high bit if delay slot */
660 .size fp_exception_ds, .-fp_exception_ds
664 .type jump_syscall, %function
666 ldr r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
668 str r0, [fp, #reg_cop0+56-dynarec_local] /* EPC */
671 str r1, [fp, #reg_cop0+48-dynarec_local] /* Status */
672 str r2, [fp, #reg_cop0+52-dynarec_local] /* Cause */
676 .size jump_syscall, .-jump_syscall
680 .global jump_syscall_hle
681 .type jump_syscall_hle, %function
683 str r0, [fp, #pcaddr-dynarec_local] /* PC must be set to EPC for psxException */
684 ldr r2, [fp, #last_count-dynarec_local]
685 mov r1, #0 /* in delay slot */
687 mov r0, #0x20 /* cause */
688 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
691 /* note: psxException might do recorsive recompiler call from it's HLE code,
692 * so be ready for this */
694 ldr r1, [fp, #next_interupt-dynarec_local]
695 ldr r10, [fp, #cycle-dynarec_local]
696 ldr r0, [fp, #pcaddr-dynarec_local]
698 str r1, [fp, #last_count-dynarec_local]
701 .size jump_syscall_hle, .-jump_syscall_hle
705 .type jump_hlecall, %function
707 ldr r2, [fp, #last_count-dynarec_local]
708 str r0, [fp, #pcaddr-dynarec_local]
710 str r2, [fp, #cycle-dynarec_local] /* PCSX cycle counter */
713 .size jump_hlecall, .-jump_hlecall
717 .global new_dyna_leave
718 .type new_dyna_leave, %function
719 ldr r0, [fp, #last_count-dynarec_local]
722 str r10, [fp, #cycle-dynarec_local]
723 ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
724 .size new_dyna_leave, .-new_dyna_leave
726 /* these are used to call memhandlers */
728 .global indirect_jump_indexed
729 .type indirect_jump_indexed, %function
730 indirect_jump_indexed:
731 ldr r0, [r0, r1, lsl #2]
732 .global indirect_jump
733 .type indirect_jump, %function
735 ldr r12, [fp, #last_count-dynarec_local]
737 str r2, [fp, #cycle-dynarec_local]
739 .size indirect_jump, .-indirect_jump
740 .size indirect_jump_indexed, .-indirect_jump_indexed
743 .global new_dyna_start
744 .type new_dyna_start, %function
746 /* ip is stored to conform EABI alignment */
747 stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr}
749 movw fp, #:lower16:dynarec_local
750 movt fp, #:upper16:dynarec_local
754 ldr r0, [fp, #pcaddr-dynarec_local]
756 ldr r1, [fp, #next_interupt-dynarec_local]
757 ldr r10, [fp, #cycle-dynarec_local]
758 str r1, [fp, #last_count-dynarec_local]
763 .size new_dyna_start, .-new_dyna_start
765 /* --------------------------------------- */
768 .global ari_read_ram8
769 .global ari_read_ram16
770 .global ari_read_ram32
771 .global ari_read_ram_mirror8
772 .global ari_read_ram_mirror16
773 .global ari_read_ram_mirror32
774 .global ari_write_ram8
775 .global ari_write_ram16
776 .global ari_write_ram32
777 .global ari_write_ram_mirror8
778 .global ari_write_ram_mirror16
779 .global ari_write_ram_mirror32
780 .global ari_read_bios8
781 .global ari_read_bios16
782 .global ari_read_bios32
784 .global ari_read_io16
785 .global ari_read_io32
786 .global ari_write_io8
787 .global ari_write_io16
788 .global ari_write_io32
790 .macro ari_read_ram bic_const op
791 ldr r0, [fp, #address-dynarec_local]
793 bic r0, r0, #\bic_const
796 str r0, [fp, #readmem_dword-dynarec_local]
809 .macro ari_read_ram_mirror mvn_const, op
810 ldr r0, [fp, #address-dynarec_local]
815 str r0, [fp, #readmem_dword-dynarec_local]
819 ari_read_ram_mirror8:
820 ari_read_ram_mirror 0, ldrb
822 ari_read_ram_mirror16:
823 ari_read_ram_mirror (1<<11), ldrh
825 ari_read_ram_mirror32:
826 ari_read_ram_mirror (3<<11), ldr
828 /* invalidation is already taken care of by the caller */
829 .macro ari_write_ram bic_const var op
830 ldr r0, [fp, #address-dynarec_local]
831 ldr r1, [fp, #\var-dynarec_local]
833 bic r0, r0, #\bic_const
840 ari_write_ram 0, byte, strb
843 ari_write_ram 1, hword, strh
846 ari_write_ram 3, word, str
848 .macro ari_write_ram_mirror mvn_const var op
849 ldr r0, [fp, #address-dynarec_local]
851 ldr r1, [fp, #\var-dynarec_local]
853 ldr r2, [fp, #invc_ptr-dynarec_local]
855 ldrb r2, [r2, r0, lsr #12]
863 ari_write_ram_mirror8:
864 ari_write_ram_mirror 0, byte, strb
866 ari_write_ram_mirror16:
867 ari_write_ram_mirror (1<<11), hword, strh
869 ari_write_ram_mirror32:
870 ari_write_ram_mirror (3<<11), word, str
873 .macro ari_read_bios_mirror bic_const op
874 ldr r0, [fp, #address-dynarec_local]
875 orr r0, r0, #0x80000000
876 bic r0, r0, #(0x20000000|\bic_const) @ map to 0x9fc...
878 str r0, [fp, #readmem_dword-dynarec_local]
883 ari_read_bios_mirror 0, ldrb
886 ari_read_bios_mirror 1, ldrh
889 ari_read_bios_mirror 3, ldr
893 .macro ari_read_io_old tab_shift
894 str lr, [sp, #-8]! @ EABI alignment..
904 str r0, [fp, #readmem_dword-dynarec_local]
908 .macro ari_read_io readop mem_tab tab_shift
909 ldr r0, [fp, #address-dynarec_local]
910 ldr r1, [fp, #psxH_ptr-dynarec_local]
917 bic r2, r0, #0x1f800000
918 ldr r12,[fp, #\mem_tab-dynarec_local]
921 @ ari_read_io_old \tab_shift
924 ldr r12,[r12, r3, lsl #\tab_shift]
928 str lr, [sp, #-8]! @ EABI alignment..
930 str r0, [fp, #readmem_dword-dynarec_local]
934 .if \tab_shift == 1 @ read16
939 ldr r12,[fp, #spu_readf-dynarec_local]
943 @ no handler, just read psxH
945 str r0, [fp, #readmem_dword-dynarec_local]
950 ari_read_io ldrb, tab_read8, 2
953 ari_read_io ldrh, tab_read16, 1
956 ari_read_io ldr, tab_read32, 0
958 .macro ari_write_io_old tab_shift
970 .macro ari_write_io opvl opst var mem_tab tab_shift
971 ldr r0, [fp, #address-dynarec_local]
972 \opvl r1, [fp, #\var-dynarec_local]
979 bic r2, r0, #0x1f800000
980 ldr r12,[fp, #\mem_tab-dynarec_local]
983 @ ari_write_io_old \tab_shift
986 ldr r12,[r12, r3, lsl #\tab_shift]
991 ldr r3, [fp, #psxH_ptr-dynarec_local]
995 .if \tab_shift == 1 @ write16
999 ldrlo pc, [fp, #spu_writef-dynarec_local]
1006 @ PCSX always writes to psxH, so do we for consistency
1007 ldr r0, [fp, #address-dynarec_local]
1008 ldr r3, [fp, #psxH_ptr-dynarec_local]
1009 ldrb r1, [fp, #byte-dynarec_local]
1010 bic r2, r0, #0x1f800000
1011 ldr r12,[fp, #tab_write8-dynarec_local]
1013 subs r3, r2, #0x1000
1015 @ ari_write_io_old 2
1018 ldr r12,[r12, r3, lsl #2]
1025 ari_write_io ldrh, strh, hword, tab_write16, 1
1028 ari_write_io ldr, str, word, tab_write32, 0
1030 @ vim:filetype=armasm