 ;******************************************************************************
;            (C) Copyright 1992-1995, Radius, Inc. and Atari Corp.
; Secret and confidential                                   All rights reserved
;******************************************************************************
; This source code and any compilation or derivative thereof is the joint
; property of Radius, Inc. and Atari Corp.  This code is provided pursuant
; to a Software License Agreement and/or Software Development Contract.  This
; code is the proprietary information of Radius, Inc. and Atari Corp. and is
; confidential in nature.  Its use and dissemination by any party other than
; Atari Corp. are strictly limited by the confidential information provisions
; of the Agreement or Contract referenced above.
;******************************************************************************
; Revision 1.0  04/25/94  17:18:20  jpe
; Initial revision.
; .
; .
; .
; Revision 1.4  03/15/95  15:24:51  sds
; Handles multiple audio types (only tested m8u and s16u).
; Changed shutdown call to restart call.
; Revision 1.5
; Shutdown is back by popular demand!
;******************************************************************************
; 101 05 Jul 95 JPE     Attenuation of samples by 18 dB when ATTEN_SAMP flag
;                       is set by 68000.
; 102 13 Jul 95 JPE     Attenuation controlled by value of ATTEN_SHIFT.
; 103 08 Aug 95 JPE     Mixing of voice-over track added.
; 104 21 Sep 95 JPE     Audio gain computation added.
; 105 08 Nov 95 JPE     Official legal banner added (no other changes).
; 106 11 Dec 95 JPE     Cleaned up initialization of rAudioDesc/rAudioDrift.
; 107 21 Feb 96 JPE     Modified to incorporate CSX audio decompression.
;******************************************************************************

        .68000
DSP_S:

        .dsp
        .nolist
        .include    "jaguar.inc"
        .list

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   Register utilization:
;
;   r0-r2: Scratch
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

rBufPtr         .regequ        r4       ; Pointer to Sample Buffer
rBytesLeft      .regequ        r7       ; # of samples remaining
rNumBytes       .regequ        r9       ; # of bytes remaining
rLeftDAC        .regequ        r10      ; Address of right DAC
rRightDAC       .regequ        r11      ; Address of left DAC
rVoxCnt         .regequ        r13      ; Voiceover sample count
rAudioDesc      .regequ        r14      ; Audio Data Description
rArgs           .regequ        r15      ; Pointer to CPU arguments
rShutdown       .regequ        r16      ; Address of DSP shutdown
rMainLoop       .regequ        r17      ; Address of main loop
rAudioDrift     .regequ        r23      ; Audio Drift Rate (0.32 fraction)
rFraction       .regequ        r24      ; Cumulative sample difference
rGetS           .regequ        r26      ; Address of GetSample routine
rISR            .regequ        r27      ; ISR Semaphore
rIStack         .regequ        r31      ; Interrupt Stack (r28-r31 reserved)

        .org    D_RAM+$10               ; Interrupt 1: I2S

DSP_LOAD:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Here is the vector for the I2S interrupt.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        movei   #I2Sisr,r29             ; Address  of interrupt handler
        jump    (r29)                   ; Jump to it
        nop
        nop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Here is the entry point and startup code.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DSP_ENTR:
        movei   #R_DAC,rRightDAC        ; Right DAC location
        movei   #L_DAC,rLeftDAC         ; Left DAC location
        movei   #Shutdown,rShutdown     ; Restart routine
        movei   #MainLoop,rMainLoop     ; Main program loop
        movei   #DSP_ARGS,rArgs         ; Arguments passed by CPU
        movei   #$80000000,rFraction    ; Half a sample
        movei   #Stack,rIStack          ; Initialize stack pointer

        moveq   #0,rISR                 ; Clear semaphore register
        moveq   #0,rBytesLeft           ; Clear counter register
        moveq   #0,rVoxCnt              ; Clear voiceover count

        movei   #D_FLAGS,r2
        load    (r2),r0                 ; Read DSP flags register
        bclr    #14,r0
        bset    #5,r0                   ; Enable I2S interrupt
        store   r0,(r2)                 ; Write to flag register
        nop
        nop

;------------------------------------------------------------------------------
;   Plays a chunk of sound data, with parameters specified as follows:
;
;   DSP_ARGS:   Sample count
;   DSP_ARGS+4: Base address of sound buffer
;   DSP_ARGS+8: Audio Data Description (or 0 if none)
;
;   When the number of samples specified by the sample count have been played,
;   locations DSP_ARGS and DSP_ARGS+4 are read again. If the new sample count
;   is non-zero, playing is continued without interruption. Otherwise, zeros
;   are written to the DACs.
;------------------------------------------------------------------------------

MainLoop:
        or      rISR,rISR               ; Test semaphore register
        jr      EQ,MainLoop             ; Wait for an interrupt
        nop

        moveq   #0,rISR                 ; Clear semaphore

        load    (rArgs+2),r0            ; Get stop flag
        or      r0,r0                   ; Is it set?
        jump    NE,(rShutdown)          ; Yes, shut ourselves down
        nop

        cmpq    #0,rBytesLeft           ; Is sample count set?
        jump    NE,(rGetS)              ; Yes, continue playing
        nop

        load    (rArgs),r0              ; Read block sample count
        or      r0,r0                   ; Is it non-zero?
        jr      NE,StartPlay            ; Yes, begin playing
        nop

        store   r0,(rRightDAC)          ; Write a zero to right DAC
        store   r0,(rLeftDAC)           ; Write a zero to left DAC
        jump    (rMainLoop)             ; Loop again
        nop

StartPlay:
        load    (rArgs),rBytesLeft      ; Sample count for block
        load    (rArgs+1),rBufPtr       ; Pointer to audio data
        load    (rArgs+3),rAudioDrift   ; Audio drift rate

        load    (rArgs+4),rAudioDesc    ; Audio data description
        shlq    #2,rAudioDesc           ; Make long index
        movei   #JumpTable,r1           ; Base of jump table
        load    (rAudioDesc+r1),rGetS   ; Indexed handler address

        moveq   #0,r0                   ; Quick zero
        store   r0,(rArgs)              ; Zero out sample count
        jump    (rGetS)                 ; Jump to handler
        nop

m8u:
        loadb   (rBufPtr),r2            ; Read left sample from buffer
        addq    #1,rBufPtr              ; Next sample pointer
        moveq   #1,rNumBytes            ; 1 byte per sample
        subq    #1,rBytesLeft           ; Decrement sample count
        shlq    #8,r2                   ; 16-bit 2's complement
        jr      ApplyAtten              ; Apply attenuation
        move    r2,r0                   ; Duplicate left to right

s16u:
        loadw   (rBufPtr),r0            ; Read right sample from buffer
        addq    #2,rBufPtr              ; Next sample pointer
        loadw   (rBufPtr),r2            ; Read left sample from buffer
        addq    #2,rBufPtr              ; Next sample pointer
        moveq   #4,rNumBytes            ; 4 bytes per sample
        subq    #4,rBytesLeft           ; Decrement sample count

ApplyAtten:
        load    (rArgs+5),r1            ; Get attenuation shift
        shlq    #16,r0                  ; Move sign to bit 31
        shlq    #16,r2                  ; Move sign to bit 31
        sha     r1,r0                   ; Attenuate specified amount
        sha     r1,r2                   ; Attenuate specified amount

CheckVOSamp:
        movei   #VoiceOver,r1           ; Possible jump address
        or      rVoxCnt,rVoxCnt         ; Any voiceover samples?
        jump    NE,(r1)                 ; Yes, proceed
        nop

        movei   #StoreDAC,r1            ; Possible jump address
        movei   #vOverCount,r3          ; Address of sample count
        load    (r3),rVoxCnt            ; Read value from 68K
        or      rVoxCnt,rVoxCnt         ; Any voiceover samples?
        jump    EQ,(r1)                 ; No, skip voiceover
        nop

        moveq   #0,r8                   ; Clear register
        store   r8,(r3)                 ; Zero out sample count
        movei   #vOverData,r3           ; Handle to data
        load    (r3),r8                 ; Start of sample buffer

VoiceOver:
        subq    #1,rVoxCnt              ; Decrement sample counter
        loadb   (r8),r5                 ; Get voiceover sample
        btst    #0,rVoxCnt              ; Odd sample?
        jr      NE,MixVoice             ; Yes, do mixing
        nop

        addq    #1,r8                   ; No, first increment pointer

MixVoice:
        shlq    #24,r5                  ; Move sign to bit 31
        sharq   #16,r5                  ; Sign extended sample
        add     r5,r0                   ; Add to right channel
        add     r5,r2                   ; Add to left channel
        sharq   #1,r0                   ; Renormalize right channel
        sharq   #1,r2                   ; Renormalize left channel

StoreDAC:
        movei   #audioGain,r3
        load    (r3),r1                 ; Get audio gain (Q12)
        imult   r1,r0                   ; Apply gain to right channel
        imult   r1,r2                   ; Apply gain to left channel
        sharq   #12,r0                  ; Normalize right sample
        sharq   #12,r2                  ; Normalize left sample
        sat16s  r0                      ; Saturate right sample
        sat16s  r2                      ; Saturate left sample

        store   r0,(rRightDAC)          ; Store data in right DAC
        store   r2,(rLeftDAC)           ; Store data in left DAC

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Add drift rate to accumulated sample difference. When a carry is
;;; generated, we're off by more than one, and we drop an input sample.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        add     rAudioDrift,rFraction   ; Add difference to accumulated
        jump    CC,(rMainLoop)          ; If nc, still within one sample
        nop

        add     rNumBytes,rBufPtr       ; Increment buffer pointer
        sub     rNumBytes,rBytesLeft    ; Decrement byte count
        jump    PL,(rMainLoop)          ; Continue unless negative
        nop

        load    (rArgs),rBytesLeft      ; Get sample count for block
        load    (rArgs+1),rBufPtr       ; Get pointer to audio data

        moveq   #0,r0                   ; Quick zero
        store   r0,(rArgs)              ; Zero out sample count
        jump    (rMainLoop)             ; Loop
        nop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   Sample rate interrupt service routine.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

I2Sisr:
        movei   #D_FLAGS,r30
        load    (r30),r29               ; Read flags register

        moveq   #1,rISR                 ; Set register semaphore

        bclr    #3,r29                  ; Clear IMASK
        load    (rIStack),r28           ; Get return address
        bset    #10,r29                 ; Clear I2S interrupt
        addq    #2,r28                  ; Fix up return address
        addq    #4,rIStack              ; Balance stack
        jump    (r28)                   ; Return
        store   r29,(r30)               ; Restore flags
        
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Shutdown routine.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Shutdown:
        moveq   #0,r0
        store   r0,(rRightDAC)          ; Write a zero to right DAC
        store   r0,(rLeftDAC)           ; Write a zero to left DAC

        movei   #D_CTRL,r0
        moveq   #0,r1
        store   r1,(r0)
        nop
        nop

        .long

DSP_ARGS:       .dc.l   0,0             ; Argument list
DSP_STOP:       .dc.l   0               ; Stop flag (set by CPU)
AUDIO_DRIFT:    .dc.l   0               ; Audio drift rate
AUDIO_DESC:     .dc.l   0               ; Audio Data Description
ATTEN_SHIFT:    .dc.l   19              ; Attenuation shift value

JumpTable:      .dc.l   m8u             ; 8-bit mono, uncompressed
                .dc.l   0               ; [8-bit stereo, uncompressed]
                .dc.l   0               ; [16-bit mono, uncompressed]
                .dc.l   s16u            ; 16-bit stereo, uncompressed

                .dc.l   0,0             ; Reserve 2 longs for stack
Stack:

        .68000
DSP_E:

        .dsp
        .org    D_RAM+$1000

DSP_BUFF:

;------------------------------------------------------------------------------
;   Declare externals and globals.
;------------------------------------------------------------------------------

        .extern vOverCount
        .extern vOverData
        .extern audioGain

        .globl  DSP_S
        .globl  DSP_E

        .globl  DSP_ARGS
        .globl  DSP_BUFF
        .globl  DSP_ENTR
        .globl  DSP_LOAD
        .globl  DSP_STOP
        .globl  AUDIO_DRIFT
        .globl  AUDIO_DESC
        .globl  ATTEN_SHIFT

        .end
