2 AHI - Hardware independent audio subsystem
3 Copyright (C) 1996-2005 Martin Blom <martin@blom.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, Cambridge,
25 # include <hardware/intbits.h>
30 # include <powerpc/powerpc.h>
32 # include <exec/memory.h>
33 # include <powerpc/powerpc.h>
34 # include <proto/exec.h>
35 # include <proto/powerpc.h>
36 # include <proto/utility.h>
37 # define __NOLIBBASE__
38 # define __NOGLOBALIFACE__
39 # include <proto/ahi.h>
41 # undef __NOGLOBALIFACE__
49 /******************************************************************************
50 ** WarpUp/PPC prototypes ******************************************************
51 ******************************************************************************/
54 CallMixroutine( struct PowerPCContext
* context
);
57 FlushCache( void* address
, unsigned long length
);
60 FlushCacheAll( void );
63 InvalidateCache( void* address
, unsigned long length
);
66 /******************************************************************************
67 ** WarpUp/PPC function used to call the actual mixing routine *****************
68 ******************************************************************************/
71 CallMixroutine( struct PowerPCContext
* context
)
73 struct AHIPrivAudioCtrl
* audioctrl
;
74 struct AHISoundData
* sd
;
77 audioctrl
= context
->AudioCtrl
;
79 // *((ULONG*) 0x100000) = 1;
81 // Wait for start signal...
83 while( audioctrl
->ahiac_PowerPCContext
->Command
!= PPCC_COM_START
);
85 // *((ULONG*) 0x100000) = 4;
87 // Start m68k interrupt handler
89 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_INIT
;
90 *((WORD
*) 0xdff09C) = INTF_SETCLR
| INTF_PORTS
;
92 // *((ULONG*) 0x100000) = 5;
96 // Invalidate dynamic sample sounds (which is faster than flushing).
97 // Currently, the PPC is assumed not to modify dynamic samples.
98 // It makes sense as long as no PPC hooks can be called from AHI.
99 // Anyway, each dynamic sample is flushed on the m68k side before
100 // this routine is called, and invalidated here on the PPC side.
101 // However, should a dynamic sample start at address 0, which
102 // probably means that the whole address space is used for that
103 // sample, all of the data caches are instead flushed.
105 sd
= audioctrl
->ahiac_SoundDatas
;
107 for( i
= 0; i
< audioctrl
->ac
.ahiac_Sounds
; i
++)
109 if( sd
->sd_Type
== AHIST_DYNAMICSAMPLE
)
111 if( sd
->sd_Addr
== NULL
)
113 // *Flush* all and exit (add an L2 cache and watch this code break!)
120 // *Invalidate* block
122 InvalidateCache( sd
->sd_Addr
,
123 sd
->sd_Length
* SampleFrameSize( sd
->sd_Type
, AHIBase
) );
131 // Wait for m68k interrupt handler to go active
133 while( audioctrl
->ahiac_PowerPCContext
->Command
!= PPCC_COM_ACK
);
135 // *((ULONG*) 0x100000) = 6;
141 Mix( context
->Hook
, context
->Dst
, audioctrl
);
142 DoMasterVolume( context
->Dst
, audioctrl
);
144 // Flush mixed samples to memory
146 FlushCache( context
->Dst
, audioctrl
->ahiac_BuffSizeNow
);
150 // *((ULONG*) 0x100000) = 7;
152 // Kill the m68k interrupt handler
154 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_QUIT
;
155 *((WORD
*) 0xdff09C) = INTF_SETCLR
| INTF_PORTS
;
157 // *((ULONG*) 0x100000) = 8;
161 while( audioctrl
->ahiac_PowerPCContext
->Command
!= PPCC_COM_ACK
);
163 // *((ULONG*) 0x100000) = 9;
165 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_FINISHED
;
167 // *((ULONG*) 0x100000) = 10;
173 /******************************************************************************
174 ** WarpUp/PPC cache manipulation routines *************************************
175 ******************************************************************************/
180 /* FlushCache ****************************************************************/
182 /* r3 = beginning address of data block to flush
183 * r4 = size of data block to flush (in bytes)
184 * assumes cache block granule is 32 bytes
189 .type FlushCache,@function
193 srwi 4,4,5 /* convert to cache blocks to flush */
197 dcbf 3,4 /* flush data cache block to mem */
201 sync /* force mem transactions to complete */
202 blr /* return to calling routine */
205 /* FlushCacheAll *************************************************************/
209 .type FlushCacheAll,@function
213 /* Load the entire data cache with known contents. */
214 li 3,-16 /* Start at address 0 */
215 li 4,2*256 /* 2 ways, 256 sets per way */
216 mtctr 4 /* (use CTR register to save an instruction) */
218 lwzu 5,16(3) /* load a cache line if it's not already present */
221 /* Flush those known contents from the cache. */
222 li 3,0 /* Read 2*128*16 bytes at address 0 */
223 mtctr 4 /* (use CTR register to save an instruction) */
225 dcbf 0,3 /* flush a cache line */
226 addi 3,3,16 /* next line: assumes cache lines are 16 bytes */
231 /* InvalidateCache ***********************************************************/
233 /* r3 = beginning address of data block to flush
234 * r4 = size of data block to flush (in bytes)
235 * assumes cache block granule is 32 bytes
238 .globl InvalidateCache
239 .type InvalidateCache,@function
243 srwi 4,4,5 /* convert to cache blocks to invalidate */
247 dcbi 3,4 /* invalidate data cache block */
251 sync /* force mem transactions to complete */
252 blr /* return to calling routine */
257 /******************************************************************************
258 ** WarpUp/PPC handling ********************************************************
259 ******************************************************************************/
261 void WarpUpInterrupt( void );
263 struct TagItem InitTags
[] =
265 { EXCATTR_CODE
, (ULONG
) &WarpUpInterrupt
, },
266 { EXCATTR_DATA
, 0, },
267 { EXCATTR_NAME
, (ULONG
) AHINAME
" Exception Handler" },
268 { EXCATTR_PRI
, 32, },
269 { EXCATTR_EXCID
, EXCF_INTERRUPT
, },
270 { EXCATTR_FLAGS
, EXCF_GLOBAL
| EXCF_LARGECONTEXT
, },
277 _LVOSetExcHandler = -516
278 _LVORemExcHandler = -522
280 _LVOClearExcMMU = -582
284 # struct PowerPCContext
293 ppcc_PowerPCBase = 7*4
294 ppcc_MixInterrupt = 8*4
297 /* WarpUpRegisterExcHandler **************************************************/
300 .globl WarpUpRegisterExcHandler
301 .type WarpUpRegisterExcHandler,@function
303 /* r3 = struct PowerPCContext*
306 WarpUpRegisterExcHandler:
313 stwu 1,-(28+14*4+1*4)(1)
317 mr 14,3 # Save PowerPCContext in r14
319 # Build the tag list on the stack
321 lis 4,(InitTags-4)@ha
322 addi 4,4,InitTags-4@l
334 stw 14,28+3*4(1) # Store PowerPCContext in tag list
336 # Register the exception handler
338 lwz 3,ppcc_PowerPCBase(14)
340 lwz 0,_LVOSetExcHandler+2(3)
357 /* WarpUpInterrupt ***********************************************************/
360 .globl WarpUpInterrupt
361 .type WarpUpInterrupt,@function
378 lwz 3,ppcc_PowerPCBase(14)
379 lwz 0,_LVOSetExcMMU+2(3)
383 # Test and clear activation flag (is this our interrupt or somebody elses?)
385 addi 3,14,ppcc_Active
395 # Call the CallMixroutine (V.4 ABI)
411 lwz 3,ppcc_PowerPCBase(14)
412 lwz 0,_LVOClearExcMMU+2(3)
428 /* WarpUpRemoveExcHandler ****************************************************/
431 .globl WarpUpRemoveExcHandler
432 .type WarpUpRemoveExcHandler,@function
434 /* r3 = struct PowerPCContext*
437 WarpUpRemoveExcHandler:
446 # Unregister the exception handler
449 lwz 3,ppcc_PowerPCBase(3)
450 lwz 0,_LVORemExcHandler+2(3)
465 /* WarpUpCallSoundHook *******************************************************/
468 WarpUpCallSoundHook( struct AHIPrivAudioCtrl
*audioctrl
,
474 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_SOUNDFUNC
;
475 audioctrl
->ahiac_PowerPCContext
->Argument
= arg
;
476 *((WORD
*) 0xdff09C) = INTF_SETCLR
| INTF_PORTS
;
477 while( audioctrl
->ahiac_PowerPCContext
->Command
!= PPCC_COM_ACK
);
481 // Crash and burn! We shouldn't be here!
482 KPrintF( "WarpUpCallSoundHook() called from non-PPC code!\n" );
488 /******************************************************************************
489 ** m68k client code ***********************************************************
490 ******************************************************************************/
494 /* Interrupt *****************************************************************/
496 static INTERRUPT SAVEDS
int
497 Interrupt( struct AHIPrivAudioCtrl
* audioctrl
__asm( "a1" ) )
500 if( audioctrl
->ahiac_PowerPCContext
->Command
!= PPCC_COM_INIT
)
502 /* Not for us, continue */
512 switch( audioctrl
->ahiac_PowerPCContext
->Command
)
517 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_ACK
;
522 // Keep looping, try not to waste to much memory bandwidth...
523 asm( "stop #(1<<13) | (2<<8)" : );
526 case PPCC_COM_SOUNDFUNC
:
528 CallHookPkt( audioctrl
->ac
.ahiac_SoundFunc
,
530 (APTR
) audioctrl
->ahiac_PowerPCContext
->Argument
);
531 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_ACK
;
536 KPrintF( "%lx ", (ULONG
) audioctrl
->ahiac_PowerPCContext
->Argument
);
537 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_ACK
;
543 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_ACK
;
551 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_ACK
;
564 /* WarpUpInit ****************************************************************/
567 WarpUpInit( struct AHIPrivAudioCtrl
* audioctrl
)
572 // Crash and burn! We shouldn't be here!
573 Req( "Internal error: WarpUpInit() called from PPC code!" );
580 //KPrintF( "WarpUpInit( 0x%08lx )", audioctrl );
582 audioctrl
->ahiac_PowerPCContext
=
583 AllocVec32( sizeof( struct PowerPCContext
),
584 MEMF_PUBLIC
| MEMF_CHIP
| MEMF_CLEAR
);
586 if( audioctrl
->ahiac_PowerPCContext
== NULL
)
588 Req( "Out of memory." );
592 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_NONE
;
593 audioctrl
->ahiac_PowerPCContext
->Active
= FALSE
;
595 audioctrl
->ahiac_PowerPCContext
->AudioCtrl
= audioctrl
;
596 audioctrl
->ahiac_PowerPCContext
->PowerPCBase
= PowerPCBase
;
598 audioctrl
->ahiac_PowerPCContext
->MixBuffer
=
599 AllocVec32( audioctrl
->ac
.ahiac_BuffSize
,
600 MEMF_PUBLIC
| MEMF_CLEAR
);
602 if( audioctrl
->ahiac_PowerPCContext
->MixBuffer
== NULL
)
604 Req( "Out of memory." );
608 // Initialize the WarpUp side
610 struct PPCArgs args
=
617 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
618 { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }
621 args
.PP_Regs
[ 0 ] = (ULONG
) audioctrl
->ahiac_PowerPCContext
;
623 if( ! AHIGetELFSymbol( "WarpUpRegisterExcHandler", &args
.PP_Code
) )
625 Req( "Unable to fetch symbol 'WarpUpRegisterExcHandler'." );
629 if( RunPPC( &args
) != PPERR_SUCCESS
)
631 Req( "Call to WarpUpRegisterExcHandler() failed." );
635 audioctrl
->ahiac_PowerPCContext
->MixInterrupt
=
636 AllocVec( sizeof( struct Interrupt
),
637 MEMF_PUBLIC
| MEMF_CLEAR
);
639 if( audioctrl
->ahiac_PowerPCContext
->MixInterrupt
== NULL
)
641 Req( "Out of memory." );
645 struct Interrupt
* mi
= audioctrl
->ahiac_PowerPCContext
->MixInterrupt
;
647 mi
->is_Node
.ln_Type
= NT_INTERRUPT
;
648 mi
->is_Node
.ln_Pri
= 127;
649 mi
->is_Node
.ln_Name
= AHINAME
" PPC Handler Interrupt";
650 mi
->is_Data
= audioctrl
;
651 mi
->is_Code
= (void(*)(void)) Interrupt
;
653 AddIntServer( INTB_PORTS
, mi
);
662 //KPrintF( "=> %ld\n", rc );
670 /* WarpUpCallMixer ***********************************************************/
673 WarpUpCallMixer( struct AHIPrivAudioCtrl
* audioctrl
,
679 // Crash and burn! We shouldn't be here!
680 KPrintF( "WarpUpCallMixer() called from PPC code!\n" );
684 // Calls the PPC mixing code to fill a buffer with mixed samples
686 struct AHISoundData
*sd
;
688 BOOL flushed
= FALSE
;
691 // Flush all DYNAMICSAMPLE's
693 sd
= audioctrl
->ahiac_SoundDatas
;
695 for( i
= 0; i
< audioctrl
->ac
.ahiac_Sounds
; i
++)
697 if( sd
->sd_Type
== AHIST_DYNAMICSAMPLE
)
699 if( sd
->sd_Addr
== NULL
)
702 // Flush all and exit
710 SetCache68K( CACHE_DCACHEFLUSH
,
712 sd
->sd_Length
* AHI_SampleFrameSize( sd
->sd_Type
) );
722 /* Since the PPC mix buffer is m68k cacheable in WarpUp, we have to
723 flush, or better, *invalidate* the cache before mixing starts. */
725 SetCache68K( CACHE_DCACHEFLUSH
,
726 audioctrl
->ahiac_PowerPCContext
->MixBuffer
,
727 audioctrl
->ahiac_BuffSizeNow
);
731 audioctrl
->ahiac_PowerPCContext
->Hook
= audioctrl
->ac
.ahiac_MixerFunc
;
732 audioctrl
->ahiac_PowerPCContext
->Dst
= audioctrl
->ahiac_PowerPCContext
->MixBuffer
;
733 audioctrl
->ahiac_PowerPCContext
->Active
= TRUE
;
734 audioctrl
->ahiac_PowerPCContext
->Command
= PPCC_COM_START
;
739 while( audioctrl
->ahiac_PowerPCContext
->Command
!= PPCC_COM_FINISHED
);
747 /* WarpUpCleanUp *************************************************************/
750 WarpUpCleanUp( struct AHIPrivAudioCtrl
* audioctrl
)
755 // Crash and burn! We shouldn't be here!
756 Req( "Internal error: WarpUpCleanUp() called from PPC code!" );
760 if( audioctrl
->ahiac_PowerPCContext
!= NULL
)
762 struct PPCArgs args
=
769 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
770 { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }
773 if( audioctrl
->ahiac_PowerPCContext
->MixInterrupt
!= NULL
)
775 RemIntServer( INTB_PORTS
, audioctrl
->ahiac_PowerPCContext
->MixInterrupt
);
778 // Clean up the WarpUp side
780 args
.PP_Regs
[ 0 ] = (ULONG
) audioctrl
->ahiac_PowerPCContext
;
782 if( ! AHIGetELFSymbol( "WarpUpRemoveExcHandler", &args
.PP_Code
) )
784 Req( "Unable to fetch symbol 'WarpUpRemoveExcHandler'." );
791 FreeVec( audioctrl
->ahiac_PowerPCContext
->MixInterrupt
);
793 AHIFreeVec( audioctrl
->ahiac_PowerPCContext
->MixBuffer
);
796 AHIFreeVec( audioctrl
->ahiac_PowerPCContext
);
797 audioctrl
->ahiac_PowerPCContext
= NULL
;