472 lines
11 KiB
ArmAsm
472 lines
11 KiB
ArmAsm
/* Overlay manager for SPU.
|
|
|
|
Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU Binutils.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
/* MFC DMA defn's. */
|
|
#define MFC_GET_CMD 0x40
|
|
#define MFC_MAX_DMA_SIZE 0x4000
|
|
#define MFC_TAG_UPDATE_ALL 2
|
|
#define MFC_TAG_ID 0
|
|
|
|
/* Register usage. */
|
|
#define reserved1 $75
|
|
#define parm $75
|
|
#define tab1 reserved1
|
|
#define tab2 reserved1
|
|
#define vma reserved1
|
|
#define oldvma reserved1
|
|
#define newmask reserved1
|
|
#define map reserved1
|
|
|
|
#define reserved2 $76
|
|
#define off1 reserved2
|
|
#define off2 reserved2
|
|
#define present1 reserved2
|
|
#define present2 reserved2
|
|
#define sz reserved2
|
|
#define cmp reserved2
|
|
#define add64 reserved2
|
|
#define cgbits reserved2
|
|
#define off3 reserved2
|
|
#define off4 reserved2
|
|
#define addr4 reserved2
|
|
#define off5 reserved2
|
|
#define tagstat reserved2
|
|
|
|
#define reserved3 $77
|
|
#define size1 reserved3
|
|
#define size2 reserved3
|
|
#define rv3 reserved3
|
|
#define ealo reserved3
|
|
#define cmd reserved3
|
|
#define off64 reserved3
|
|
#define tab3 reserved3
|
|
#define tab4 reserved3
|
|
#define tab5 reserved3
|
|
|
|
#define reserved4 $78
|
|
#define ovl reserved4
|
|
#define rv2 reserved4
|
|
#define rv5 reserved4
|
|
#define cgshuf reserved4
|
|
#define newovl reserved4
|
|
#define irqtmp1 reserved4
|
|
#define irqtmp2 reserved4
|
|
|
|
#define reserved5 $79
|
|
#define target reserved5
|
|
|
|
#define save1 $74
|
|
#define rv4 save1
|
|
#define rv7 save1
|
|
#define tagid save1
|
|
#define maxsize save1
|
|
#define pbyte save1
|
|
#define pbit save1
|
|
|
|
#define save2 $73
|
|
#define cur save2
|
|
#define rv6 save2
|
|
#define osize save2
|
|
#define zovl save2
|
|
#define oldovl save2
|
|
#define newvma save2
|
|
|
|
#define save3 $72
|
|
#define rv1 save3
|
|
#define ea64 save3
|
|
#define buf3 save3
|
|
#define genwi save3
|
|
#define newmap save3
|
|
#define oldmask save3
|
|
|
|
#define save4 $71
|
|
#define irq_stat save4
|
|
|
|
.text
|
|
.align 4
|
|
.type __rv_pattern, @object
|
|
.size __rv_pattern, 16
|
|
__rv_pattern:
|
|
.word 0x00010203, 0x10111213, 0x80808080, 0x80808080
|
|
|
|
.type __cg_pattern, @object
|
|
.size __cg_pattern, 16
|
|
__cg_pattern:
|
|
.word 0x04050607, 0x80808080, 0x80808080, 0x80808080
|
|
|
|
.type __ovly_current, @object
|
|
.size __ovly_current, 16
|
|
__ovly_current:
|
|
.space 16
|
|
|
|
/*
|
|
* __ovly_return - stub for returning from overlay functions.
|
|
*
|
|
* On entry the four slots of $lr are:
|
|
* __ovly_return, prev ovl index, caller return addr, undefined.
|
|
*
|
|
* Load the previous overlay and jump to the caller return address.
|
|
* Updates __ovly_current.
|
|
*/
|
|
.align 4
|
|
.global __ovly_return
|
|
.type __ovly_return, @function
|
|
__ovly_return:
|
|
ila tab1, _ovly_table - 16 # 0,2 0
|
|
shlqbyi ovl, $lr, 4 # 1,4 0
|
|
#nop
|
|
shlqbyi target, $lr, 8 # 1,4 1
|
|
#nop; lnop
|
|
#nop; lnop
|
|
shli off1, ovl, 4 # 0,4 4
|
|
#lnop
|
|
#nop
|
|
hbr ovly_ret9, target # 1,15 5
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
lqx vma, tab1, off1 # 1,6 8
|
|
#ifdef OVLY_IRQ_SAVE
|
|
nop
|
|
stqd save4, -64($sp) # 1,6 9
|
|
#else
|
|
#nop; lnop
|
|
#endif
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
rotqbyi size1, vma, 4 # 1,4 14
|
|
#nop
|
|
stqd save3, -48($sp) # 1,6 15
|
|
#nop
|
|
stqd save2, -32($sp) # 1,6 16
|
|
#nop
|
|
stqd save1, -16($sp) # 1,6 17
|
|
andi present1, size1, 1 # 0,2 18
|
|
stqr ovl, __ovly_current # 1,6 18
|
|
#nop; lnop
|
|
#nop
|
|
brz present1, do_load # 1,4 20
|
|
ovly_ret9:
|
|
#nop
|
|
bi target # 1,4 21
|
|
|
|
/*
|
|
* __ovly_load - copy an overlay partion to local store.
|
|
*
|
|
* On entry $75 points to a word consisting of the overlay index in
|
|
* the top 14 bits, and the target address in the bottom 18 bits.
|
|
*
|
|
* Sets up $lr to return via __ovly_return. If $lr is already set
|
|
* to return via __ovly_return, don't change it. In that case we
|
|
* have a tail call from one overlay function to another.
|
|
* Updates __ovly_current.
|
|
*/
|
|
.align 3
|
|
.global __ovly_load
|
|
.type __ovly_load, @function
|
|
__ovly_load:
|
|
#if OVL_STUB_SIZE == 8
|
|
########
|
|
#nop
|
|
lqd target, 0(parm) # 1,6 -11
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
rotqby target, target, parm # 1,4 -5
|
|
ila tab2, _ovly_table - 16 # 0,2 -4
|
|
stqd save3, -48($sp) # 1,6 -4
|
|
#nop
|
|
stqd save2, -32($sp) # 1,6 -3
|
|
#nop
|
|
stqd save1, -16($sp) # 1,6 -2
|
|
rotmi ovl, target, -18 # 0,4 -1
|
|
hbr ovly_load9, target # 1,15 -1
|
|
ila rv1, __ovly_return # 0,2 0
|
|
#lnop
|
|
#nop; lnop
|
|
#nop
|
|
lqr cur, __ovly_current # 1,6 2
|
|
shli off2, ovl, 4 # 0,4 3
|
|
stqr ovl, __ovly_current # 1,6 3
|
|
ceq rv2, $lr, rv1 # 0,2 4
|
|
lqr rv3, __rv_pattern # 1,6 4
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
lqx vma, tab2, off2 # 1,6 7
|
|
########
|
|
#else /* OVL_STUB_SIZE == 16 */
|
|
########
|
|
ila tab2, _ovly_table - 16 # 0,2 0
|
|
stqd save3, -48($sp) # 1,6 0
|
|
ila rv1, __ovly_return # 0,2 1
|
|
stqd save2, -32($sp) # 1,6 1
|
|
shli off2, ovl, 4 # 0,4 2
|
|
lqr cur, __ovly_current # 1,6 2
|
|
nop
|
|
stqr ovl, __ovly_current # 1,6 3
|
|
ceq rv2, $lr, rv1 # 0,2 4
|
|
lqr rv3, __rv_pattern # 1,6 4
|
|
#nop
|
|
hbr ovly_load9, target # 1,15 5
|
|
#nop
|
|
lqx vma, tab2, off2 # 1,6 6
|
|
#nop
|
|
stqd save1, -16($sp) # 1,6 7
|
|
########
|
|
#endif
|
|
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
shufb rv4, rv1, cur, rv3 # 1,4 10
|
|
#nop
|
|
fsmb rv5, rv2 # 1,4 11
|
|
#nop
|
|
rotqmbyi rv6, $lr, -8 # 1,4 12
|
|
#nop
|
|
rotqbyi size2, vma, 4 # 1,4 13
|
|
#nop
|
|
lqd save3, -48($sp) # 1,6 14
|
|
#nop; lnop
|
|
or rv7, rv4, rv6 # 0,2 16
|
|
lqd save2, -32($sp) # 1,6 16
|
|
andi present2, size2, 1 # 0,2 17
|
|
#ifdef OVLY_IRQ_SAVE
|
|
stqd save4, -64($sp) # 1,6 17
|
|
#else
|
|
lnop # 1,0 17
|
|
#endif
|
|
selb $lr, rv7, $lr, rv5 # 0,2 18
|
|
lqd save1, -16($sp) # 1,6 18
|
|
#nop
|
|
brz present2, do_load # 1,4 19
|
|
ovly_load9:
|
|
#nop
|
|
bi target # 1,4 20
|
|
|
|
/* If we get here, we are about to load a new overlay.
|
|
* "vma" contains the relevant entry from _ovly_table[].
|
|
* extern struct {
|
|
* u32 vma;
|
|
* u32 size;
|
|
* u32 file_offset;
|
|
* u32 buf;
|
|
* } _ovly_table[];
|
|
*/
|
|
.align 3
|
|
.global __ovly_load_event
|
|
.type __ovly_load_event, @function
|
|
__ovly_load_event:
|
|
do_load:
|
|
#ifdef OVLY_IRQ_SAVE
|
|
ila irqtmp1, do_load10 # 0,2 -5
|
|
rotqbyi sz, vma, 8 # 1,4 -5
|
|
#nop
|
|
rdch irq_stat, $SPU_RdMachStat # 1,6 -4
|
|
#nop
|
|
bid irqtmp1 # 1,4 -3
|
|
do_load10:
|
|
nop
|
|
#else
|
|
#nop
|
|
rotqbyi sz, vma, 8 # 1,4 0
|
|
#endif
|
|
rotqbyi osize, vma, 4 # 1,4 1
|
|
#nop
|
|
lqa ea64, _EAR_ # 1,6 2
|
|
#nop
|
|
lqr cgshuf, __cg_pattern # 1,6 3
|
|
|
|
/* We could predict the branch at the end of this loop by adding a few
|
|
instructions, and there are plenty of free cycles to do so without
|
|
impacting loop execution time. However, it doesn't make a great
|
|
deal of sense since we need to wait for the dma to complete anyway. */
|
|
__ovly_xfer_loop:
|
|
#nop
|
|
rotqmbyi off64, sz, -4 # 1,4 4
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
cg cgbits, ea64, off64 # 0,2 8
|
|
#lnop
|
|
#nop; lnop
|
|
#nop
|
|
shufb add64, cgbits, cgbits, cgshuf # 1,4 10
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
addx add64, ea64, off64 # 0,2 14
|
|
#lnop
|
|
ila maxsize, MFC_MAX_DMA_SIZE # 0,2 15
|
|
lnop
|
|
ori ea64, add64, 0 # 0,2 16
|
|
rotqbyi ealo, add64, 4 # 1,4 16
|
|
cgt cmp, osize, maxsize # 0,2 17
|
|
wrch $MFC_LSA, vma # 1,6 17
|
|
#nop; lnop
|
|
selb sz, osize, maxsize, cmp # 0,2 19
|
|
wrch $MFC_EAH, ea64 # 1,6 19
|
|
ila tagid, MFC_TAG_ID # 0,2 20
|
|
wrch $MFC_EAL, ealo # 1,6 20
|
|
ila cmd, MFC_GET_CMD # 0,2 21
|
|
wrch $MFC_Size, sz # 1,6 21
|
|
sf osize, sz, osize # 0,2 22
|
|
wrch $MFC_TagId, tagid # 1,6 22
|
|
a vma, vma, sz # 0,2 23
|
|
wrch $MFC_Cmd, cmd # 1,6 23
|
|
#nop
|
|
brnz osize, __ovly_xfer_loop # 1,4 24
|
|
|
|
/* Now update our data structions while waiting for DMA to complete.
|
|
Low bit of .size needs to be cleared on the _ovly_table entry
|
|
corresponding to the evicted overlay, and set on the entry for the
|
|
newly loaded overlay. Note that no overlay may in fact be evicted
|
|
as _ovly_buf_table[] starts with all zeros. Don't zap .size entry
|
|
for zero index! Also of course update the _ovly_buf_table entry. */
|
|
#nop
|
|
lqr newovl, __ovly_current # 1,6 25
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
shli off3, newovl, 4 # 0,4 31
|
|
#lnop
|
|
ila tab3, _ovly_table - 16 # 0,2 32
|
|
#lnop
|
|
#nop
|
|
fsmbi pbyte, 0x100 # 1,4 33
|
|
#nop; lnop
|
|
#nop
|
|
lqx vma, tab3, off3 # 1,6 35
|
|
#nop; lnop
|
|
andi pbit, pbyte, 1 # 0,2 37
|
|
lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
or newvma, vma, pbit # 0,2 41
|
|
rotqbyi buf3, vma, 12 # 1,4 41
|
|
#nop; lnop
|
|
#nop
|
|
stqx newvma, tab3, off3 # 1,6 43
|
|
#nop; lnop
|
|
shli off4, buf3, 2 # 1,4 45
|
|
#lnop
|
|
ila tab4, _ovly_buf_table - 4 # 0,2 46
|
|
#lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
lqx map, tab4, off4 # 1,6 49
|
|
#nop
|
|
cwx genwi, tab4, off4 # 1,4 50
|
|
a addr4, tab4, off4 # 0,2 51
|
|
#lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#nop
|
|
rotqby oldovl, map, addr4 # 1,4 55
|
|
#nop
|
|
shufb newmap, newovl, map, genwi # 0,4 56
|
|
#if MFC_TAG_ID < 16
|
|
ila newmask, 1 << MFC_TAG_ID # 0,2 57
|
|
#else
|
|
ilhu newmask, 1 << (MFC_TAG_ID - 16) # 0,2 57
|
|
#endif
|
|
#lnop
|
|
#nop; lnop
|
|
#nop; lnop
|
|
stqd newmap, 0(addr4) # 1,6 60
|
|
|
|
/* Save app's tagmask, wait for DMA complete, restore mask. */
|
|
ila tagstat, MFC_TAG_UPDATE_ALL # 0,2 61
|
|
rdch oldmask, $MFC_RdTagMask # 1,6 61
|
|
#nop
|
|
wrch $MFC_WrTagMask, newmask # 1,6 62
|
|
#nop
|
|
wrch $MFC_WrTagUpdate, tagstat # 1,6 63
|
|
#nop
|
|
rdch tagstat, $MFC_RdTagStat # 1,6 64
|
|
#nop
|
|
sync # 1,4 65
|
|
/* Any hint prior to the sync is lost. A hint here allows the branch
|
|
to complete 15 cycles after the hint. With no hint the branch will
|
|
take 18 or 19 cycles. */
|
|
ila tab5, _ovly_table - 16 # 0,2 66
|
|
hbr do_load99, target # 1,15 66
|
|
shli off5, oldovl, 4 # 0,4 67
|
|
wrch $MFC_WrTagMask, oldmask # 1,6 67
|
|
ceqi zovl, oldovl, 0 # 0,2 68
|
|
#lnop
|
|
#nop; lnop
|
|
#nop
|
|
fsm zovl, zovl # 1,4 70
|
|
#nop
|
|
lqx oldvma, tab5, off5 # 1,6 71
|
|
#nop
|
|
lqd save3, -48($sp) # 1,6 72
|
|
#nop; lnop
|
|
andc pbit, pbit, zovl # 0,2 74
|
|
lqd save2, -32($sp) # 1,6 74
|
|
#ifdef OVLY_IRQ_SAVE
|
|
ila irqtmp2, do_load90 # 0,2 75
|
|
#lnop
|
|
andi irq_stat, irq_stat, 1 # 0,2 76
|
|
#lnop
|
|
#else
|
|
#nop; lnop
|
|
#nop; lnop
|
|
#endif
|
|
andc oldvma, oldvma, pbit # 0,2 77
|
|
lqd save1, -16($sp) # 1,6 77
|
|
nop # 0,0 78
|
|
#lnop
|
|
#nop
|
|
stqx oldvma, tab5, off5 # 1,6 79
|
|
#nop
|
|
#ifdef OVLY_IRQ_SAVE
|
|
binze irq_stat, irqtmp2 # 1,4 80
|
|
do_load90:
|
|
#nop
|
|
lqd save4, -64($sp) # 1,6 84
|
|
#else
|
|
#nop; lnop
|
|
#endif
|
|
|
|
.global _ovly_debug_event
|
|
.type _ovly_debug_event, @function
|
|
_ovly_debug_event:
|
|
nop
|
|
/* Branch to target address. */
|
|
do_load99:
|
|
bi target # 1,4 81/85
|
|
|
|
.size __ovly_load, . - __ovly_load
|