revert between 56095 -> 55830 in arch
[AROS.git] / workbench / devs / AHI / Device / warpup.c
blob9b88a253710a7bc75f5fd33c7bafd6e0933cd353
1 /*
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,
18 MA 02139, USA.
21 #include <config.h>
23 #include "ahi_def.h"
25 # include <hardware/intbits.h>
27 #ifdef __PPC__
28 # include "mixer.h"
29 # include "sound.h"
30 # include <powerpc/powerpc.h>
31 #else
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>
40 # undef __NOLIBBASE__
41 # undef __NOGLOBALIFACE__
42 #endif
44 #include "misc.h"
45 #include "warpup.h"
47 #ifdef __PPC__
49 /******************************************************************************
50 ** WarpUp/PPC prototypes ******************************************************
51 ******************************************************************************/
53 static int
54 CallMixroutine( struct PowerPCContext* context );
56 void
57 FlushCache( void* address, unsigned long length );
59 void
60 FlushCacheAll( void );
62 void
63 InvalidateCache( void* address, unsigned long length );
66 /******************************************************************************
67 ** WarpUp/PPC function used to call the actual mixing routine *****************
68 ******************************************************************************/
70 static int
71 CallMixroutine( struct PowerPCContext* context )
73 struct AHIPrivAudioCtrl* audioctrl;
74 struct AHISoundData* sd;
75 int i;
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;
94 #if 0
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!)
115 FlushCacheAll();
116 break;
118 else
120 // *Invalidate* block
122 InvalidateCache( sd->sd_Addr,
123 sd->sd_Length * SampleFrameSize( sd->sd_Type, AHIBase ) );
126 sd++;
129 #endif
131 // Wait for m68k interrupt handler to go active
133 while( audioctrl->ahiac_PowerPCContext->Command != PPCC_COM_ACK );
135 // *((ULONG*) 0x100000) = 6;
137 #if 0
139 // Mix
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 );
148 #endif
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;
159 // Wait for it
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;
169 return 0;
173 /******************************************************************************
174 ** WarpUp/PPC cache manipulation routines *************************************
175 ******************************************************************************/
177 asm( "
178 .text
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
187 .align 2
188 .globl FlushCache
189 .type FlushCache,@function
191 FlushCache:
192 addi 4,4,31
193 srwi 4,4,5 /* convert to cache blocks to flush */
194 mtctr 4
195 li 4,0
197 dcbf 3,4 /* flush data cache block to mem */
198 addi 4,4,32
199 bdnz 1b
201 sync /* force mem transactions to complete */
202 blr /* return to calling routine */
205 /* FlushCacheAll *************************************************************/
207 .align 2
208 .globl FlushCacheAll
209 .type FlushCacheAll,@function
211 FlushCacheAll:
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 */
219 bdnz 1b
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 */
227 bdnz 2b
228 sync
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
237 .align 2
238 .globl InvalidateCache
239 .type InvalidateCache,@function
241 InvalidateCache:
242 addi 4,4,31
243 srwi 4,4,5 /* convert to cache blocks to invalidate */
244 mtctr 4
245 li 4,0
247 dcbi 3,4 /* invalidate data cache block */
248 addi 4,4,32
249 bdnz 1b
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, },
271 { TAG_DONE, 0 }
274 asm( "
275 .text
277 _LVOSetExcHandler = -516
278 _LVORemExcHandler = -522
279 _LVOSetExcMMU = -576
280 _LVOClearExcMMU = -582
282 EXCRETURN_ABORT = 1
284 # struct PowerPCContext
286 ppcc_Command = 0*4
287 ppcc_Argument = 1*4
288 ppcc_Active = 2*4
289 ppcc_Hook = 3*4
290 ppcc_Dst = 4*4
291 ppcc_XLock = 5*4
292 ppcc_AudioCtrl = 6*4
293 ppcc_PowerPCBase = 7*4
294 ppcc_MixInterrupt = 8*4
295 ppcc_MixBuffer = 9*4
297 /* WarpUpRegisterExcHandler **************************************************/
299 .align 2
300 .globl WarpUpRegisterExcHandler
301 .type WarpUpRegisterExcHandler,@function
303 /* r3 = struct PowerPCContext*
306 WarpUpRegisterExcHandler:
307 mflr 0
308 stw 0,8(1)
309 mfcr 0
310 stw 0,4(1)
311 stw 13,-4(1)
312 subi 13,1,4
313 stwu 1,-(28+14*4+1*4)(1)
315 stw 14,-4(13)
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
324 addi 5,1,28-4
327 lwzu 6,4(4)
328 stwu 6,4(5)
329 cmpwi 0,6,0
330 lwzu 6,4(4)
331 stwu 6,4(5)
332 bne 1b
334 stw 14,28+3*4(1) # Store PowerPCContext in tag list
336 # Register the exception handler
338 lwz 3,ppcc_PowerPCBase(14)
339 addi 4,1,28
340 lwz 0,_LVOSetExcHandler+2(3)
341 mtlr 0
342 blrl
344 stw 3,ppcc_XLock(14)
346 lwz 14,-4(13)
348 lwz 1,0(1)
349 lwz 13,-4(1)
350 lwz 0,4(1)
351 mtcr 0
352 lwz 0,8(1)
353 mtlr 0
357 /* WarpUpInterrupt ***********************************************************/
359 .align 2
360 .globl WarpUpInterrupt
361 .type WarpUpInterrupt,@function
363 WarpUpInterrupt:
364 mflr 0
365 stw 0,8(1)
366 mfcr 0
367 stw 0,4(1)
368 stw 13,-4(1)
369 subi 13,1,4
370 stwu 1,-(28+1*4)(1)
372 stw 14,-4(13)
374 mr 14,2
376 # Set up MMU
378 lwz 3,ppcc_PowerPCBase(14)
379 lwz 0,_LVOSetExcMMU+2(3)
380 mtlr 0
381 blrl
383 # Test and clear activation flag (is this our interrupt or somebody elses?)
385 addi 3,14,ppcc_Active
386 li 4,0
388 lwarx 5,0,3
389 stwcx. 4,0,3
390 bne- 1b
392 cmpwi 0,5,0
393 beq 2f
395 # Call the CallMixroutine (V.4 ABI)
397 stwu 1,-16(1)
398 stw 2,8(1)
399 stw 13,12(1)
401 mr 3,14
402 bl CallMixroutine
404 lwz 2,8(1)
405 lwz 13,12(1)
406 addi 1,1,16
409 # Restore MMU
411 lwz 3,ppcc_PowerPCBase(14)
412 lwz 0,_LVOClearExcMMU+2(3)
413 mtlr 0
414 blrl
416 li 3,EXCRETURN_ABORT
418 lwz 14,-4(13)
420 lwz 1,0(1)
421 lwz 13,-4(1)
422 lwz 0,4(1)
423 mtcr 0
424 lwz 0,8(1)
425 mtlr 0
428 /* WarpUpRemoveExcHandler ****************************************************/
430 .align 2
431 .globl WarpUpRemoveExcHandler
432 .type WarpUpRemoveExcHandler,@function
434 /* r3 = struct PowerPCContext*
437 WarpUpRemoveExcHandler:
438 mflr 0
439 stw 0,8(1)
440 mfcr 0
441 stw 0,4(1)
442 stw 13,-4(1)
443 subi 13,1,4
444 stwu 1,-28(1)
446 # Unregister the exception handler
448 lwz 4,ppcc_XLock(3)
449 lwz 3,ppcc_PowerPCBase(3)
450 lwz 0,_LVORemExcHandler+2(3)
451 mtlr 0
452 blrl
454 lwz 1,0(1)
455 lwz 13,-4(1)
456 lwz 0,4(1)
457 mtcr 0
458 lwz 0,8(1)
459 mtlr 0
463 #endif /* __PPC__ */
465 /* WarpUpCallSoundHook *******************************************************/
467 void
468 WarpUpCallSoundHook( struct AHIPrivAudioCtrl *audioctrl,
469 void* arg )
472 #ifdef __PPC__
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 );
479 #else
481 // Crash and burn! We shouldn't be here!
482 KPrintF( "WarpUpCallSoundHook() called from non-PPC code!\n" );
484 #endif /* __PPC__ */
488 /******************************************************************************
489 ** m68k client code ***********************************************************
490 ******************************************************************************/
492 #ifndef __PPC__
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 */
503 return 0;
505 else
507 BOOL running = TRUE;
508 //kprintf("I");
509 while( running )
511 //kprintf("0");
512 switch( audioctrl->ahiac_PowerPCContext->Command )
514 case PPCC_COM_INIT:
515 //kprintf("1");
516 // Keep looping
517 audioctrl->ahiac_PowerPCContext->Command = PPCC_COM_ACK;
518 break;
520 case PPCC_COM_ACK:
521 //kprintf("2");
522 // Keep looping, try not to waste to much memory bandwidth...
523 asm( "stop #(1<<13) | (2<<8)" : );
524 break;
526 case PPCC_COM_SOUNDFUNC:
527 //kprintf("3");
528 CallHookPkt( audioctrl->ac.ahiac_SoundFunc,
529 audioctrl,
530 (APTR) audioctrl->ahiac_PowerPCContext->Argument );
531 audioctrl->ahiac_PowerPCContext->Command = PPCC_COM_ACK;
532 break;
534 case PPCC_COM_DEBUG:
535 //kprintf("4");
536 KPrintF( "%lx ", (ULONG) audioctrl->ahiac_PowerPCContext->Argument );
537 audioctrl->ahiac_PowerPCContext->Command = PPCC_COM_ACK;
538 break;
540 case PPCC_COM_QUIT:
541 //kprintf("5");
542 running = FALSE;
543 audioctrl->ahiac_PowerPCContext->Command = PPCC_COM_ACK;
544 break;
546 case PPCC_COM_NONE:
547 default:
548 //kprintf("6");
549 // Error
550 running = FALSE;
551 audioctrl->ahiac_PowerPCContext->Command = PPCC_COM_ACK;
552 break;
555 //kprintf("i");
557 /* End chain! */
558 return 1;
562 #endif /* __PPC__ */
564 /* WarpUpInit ****************************************************************/
566 BOOL
567 WarpUpInit( struct AHIPrivAudioCtrl* audioctrl )
570 #ifdef __PPC__
572 // Crash and burn! We shouldn't be here!
573 Req( "Internal error: WarpUpInit() called from PPC code!" );
574 return FALSE;
576 #else
578 BOOL rc = FALSE;
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." );
590 else
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." );
606 else
608 // Initialize the WarpUp side
610 struct PPCArgs args =
612 NULL,
615 NULL,
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'." );
627 else
629 if( RunPPC( &args ) != PPERR_SUCCESS )
631 Req( "Call to WarpUpRegisterExcHandler() failed." );
633 else
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." );
643 else
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 );
655 rc = TRUE;
662 //KPrintF( "=> %ld\n", rc );
663 return rc;
665 #endif /* __PPC__ */
670 /* WarpUpCallMixer ***********************************************************/
672 void
673 WarpUpCallMixer( struct AHIPrivAudioCtrl* audioctrl,
674 void* dst )
677 #ifdef __PPC__
679 // Crash and burn! We shouldn't be here!
680 KPrintF( "WarpUpCallMixer() called from PPC code!\n" );
682 #else
684 // Calls the PPC mixing code to fill a buffer with mixed samples
686 struct AHISoundData *sd;
687 int i;
688 BOOL flushed = FALSE;
690 //kprintf("M");
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 )
701 //kprintf("a");
702 // Flush all and exit
703 CacheClearU();
704 flushed = TRUE;
705 break;
707 else
709 //kprintf("b");
710 SetCache68K( CACHE_DCACHEFLUSH,
711 sd->sd_Addr,
712 sd->sd_Length * AHI_SampleFrameSize( sd->sd_Type ) );
714 //kprintf("c");
716 sd++;
719 //kprintf("d");
720 if( ! flushed )
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 );
729 //kprintf("e");
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;
736 CausePPCInterrupt();
738 //kprintf("h");
739 while( audioctrl->ahiac_PowerPCContext->Command != PPCC_COM_FINISHED );
740 //kprintf("i");
742 #endif /* __PPC__ */
747 /* WarpUpCleanUp *************************************************************/
749 void
750 WarpUpCleanUp( struct AHIPrivAudioCtrl* audioctrl )
753 #ifdef __PPC__
755 // Crash and burn! We shouldn't be here!
756 Req( "Internal error: WarpUpCleanUp() called from PPC code!" );
758 #else
760 if( audioctrl->ahiac_PowerPCContext != NULL )
762 struct PPCArgs args =
764 NULL,
767 NULL,
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'." );
786 else
788 RunPPC( &args );
791 FreeVec( audioctrl->ahiac_PowerPCContext->MixInterrupt );
793 AHIFreeVec( audioctrl->ahiac_PowerPCContext->MixBuffer );
796 AHIFreeVec( audioctrl->ahiac_PowerPCContext );
797 audioctrl->ahiac_PowerPCContext = NULL;
799 #endif /* __PPC__ */