;------------------------------------------------------------------------------
;
;   File    :	AudioPort.a
;
;   Author  :	Stéphane TAVENARD
;
;   $VER:   AudioPort.a  1.3  (22/07/1995)
;
;   (C) Copyright 1995-1995 Stéphane TAVENARD
;	All Rights Reserved
;
;   #Rev|   Date   |			  Comment
;   ----|----------|--------------------------------------------------------
;   0	|05/06/1995| Initial revision					  ST
;   1	|06/06/1995| First release (no more C code)                       ST
;   2	|19/06/1995| Added FreeSignal in DeletePort			  ST
;   3	|23/06/1995| Aminet release					  ST
;   4	|09/07/1995| Use now ex_EClockFrequency for period calculation	  ST
;   5	|22/07/1995| Added volume control for 16-bit samples		  ST
;   5	|24/07/1995| Added Mixing frequency				  ST
;
;   ------------------------------------------------------------------------
;
;   An Audio Port 8 or 16 bits, mono or stereo
;
;------------------------------------------------------------------------------


		     IFND     INCLUDE_SOURCE
		     incdir   "include:"
		     include  "exec/funcdef.i"
		     include  "exec/exec.i"
		     include  "exec/exec_lib.i"
		     include  "devices/audio.i"
		     include  "hardware/custom.i"
		     ENDC

		     include  "AudioPort.i"

		     IFND     INCLUDE_SOURCE
		     XREF     _custom

CALLEXEC	     MACRO
		     move.l  (_SysBase).w,a6
		     jsr     _LVO\1(a6)
		     ENDM
_SysBase	     EQU     4

		     ENDC


		     XDEF     @AU_close
		     XDEF     @AU_open
		     XDEF     @AU_write

; Audio registers

AU_PTR		     equ   ac_ptr
AU_LEN		     equ   ac_len
AU_PER		     equ   ac_per
AU_VOL		     equ   ac_vol
AU_DAT		     equ   ac_dat

AUDIO_PRI	     equ   ADALLOC_MAXPREC

		     section  ASMDATA,data

		     even
AU_AllChannels	     dc.b  15
		     even
AU_LRChannels	     dc.b  3, 5, 10, 12
		     even
AU_DEVICE_NAME	     AUDIONAME
		     even
AU_PORT_NAME	     dc.b  'AudioPort', 0

		     section  ASMCODE,code

;
;		     Calculate audio period with help of frequency
;
;		     d0.l = frequency
;		     -> d0.l = period
;		     a0, d1 is affected
;
calculate_period
		     tst.l    d0
		     beq      calculate_period9
		     move.l   (_SysBase).w,a0
		     move.l   ex_EClockFrequency(a0),d1
		     mulu.l   #5,d1		; d1 = Audio DMA base freq.
		     divu.l   d0,d1
;		      move.l   #100000000,d1
;		      mulu.w   #28,d0
;		      divu.l   d0,d1
		     move.l   d1,d0
calculate_period9    rts


;
;		     Delete a port
;
;		     a0:   MsgPort *
;
DeletePort	     movem.l  a2,-(sp)
		     move.l   a0,a2
		     tst.l    MP+LN_NAME(a2)
		     beq      DeletePort1
		     move.l   a2,a1
		     CALLEXEC RemPort
DeletePort1	     clr.l    d0
		     move.b   MP_SIGBIT(a2),d0
		     CALLEXEC FreeSignal
		     move.b   #-1,MP+LN_TYPE(a2)
		     move.l   #-1,MP_MSGLIST+LH_HEAD(a2)
		     move.l   a2,a1
		     move.l   #MP_SIZE,d0
		     CALLEXEC FreeMem
		     movem.l  (sp)+,a2
		     rts
;
;		     Create a port
;
;		     a0:   name
;		     d0.b: pri
;		     -> d0 = MsgPort * (=0 if fail)
;
CreatePort	     movem.l  a2-a3/d2-d3,-(sp)
		     move.l   d0,d2
		     move.l   a0,a2
		     moveq.b  #-1,d0
		     CALLEXEC AllocSignal
		     cmp.b    #-1,d0
		     beq      CreatePort8    ; Signal error
		     move.l   d0,d3
		     move.l   #MP_SIZE,d0
		     move.l   #MEMF_PUBLIC|MEMF_CLEAR,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      CreatePort7    ; Memory error
		     move.l   d0,a3
		     move.l   a2,MP+LN_NAME(a3)
		     move.b   d2,MP+LN_PRI(a3)
		     move.b   #NT_MSGPORT,MP+LN_TYPE(a3)
		     move.b   #PA_SIGNAL,MP_FLAGS(a3)
		     move.b   d3,MP_SIGBIT(a3)
		     clr.l    d0
		     move.l   d0,a1
		     CALLEXEC FindTask
		     move.l   d0,MP_SIGTASK(a3)
		     tst.l    a2
		     beq      CreatePort1
		     move.l   a3,a1
		     CALLEXEC AddPort
		     move.l   a3,d0
		     bra      CreatePort9
CreatePort1	     lea      MP_MSGLIST(a3),a1
		     NEWLIST  a1
		     move.l   a3,d0
		     bra      CreatePort9
CreatePort7	     move.l   d3,d0
		     CALLEXEC FreeSignal
CreatePort8	     clr.l    d0
CreatePort9	     movem.l  (sp)+,a2-a3/d2-d3
		     rts

;
;		     Delete an Ext IORequest
;
;		     a0:   IORequest *
;
DeleteExtIO
		     tst.l    a0
		     beq      DeleteExtIO9
		     move.b   #-1,IO+MN+LN_TYPE(a0)
		     move.l   #-1,IO_DEVICE(a0)
		     move.l   #-1,IO_UNIT(a0)
		     move.l   a0,a1
		     clr.l    d0
		     move.w   IO+MN_LENGTH(a0),d0
		     CALLEXEC FreeMem
DeleteExtIO9	     rts

;
;		     Create an Ext IORequest
;
;		     a0:   MsgPort *
;		     d0.w: Size
;		     -> d0 = IORequest * (=0 if fail)
;
CreateExtIO	     movem.l  a2/d2,-(sp)
		     tst.l    a0
		     beq      CreateExtIO9
		     and.l    #$0000FFFF,d0
		     move.l   d0,d2
		     move.l   a0,a2
		     move.l   #MEMF_PUBLIC|MEMF_CLEAR,d1
		     CALLEXEC AllocMem
		     move.l   d0,a0
		     tst.l    a0
		     beq      CreateExtIO9
		     move.b   #NT_MESSAGE,IO+MN+LN_TYPE(a0)
		     move.w   d2,IO+MN_LENGTH(a0)
		     move.l   a2,IO+MN_REPLYPORT(a0)
CreateExtIO9	     move.l   a0,d0
		     movem.l  (sp)+,a2/d2
		     rts

;
;		     Free a DeviceBlock
;
;		     a0: DeviceBlock ptr
;
FreeDeviceBlock      movem.l  a2,-(sp)
		     move.l   a0,a2
		     tst.l    a0
		     beq      FreeDeviceBlock9
		     move.l   IO+MN_REPLYPORT(a2),d0
		     beq      FreeDeviceBlock8
		     move.l   d0,a0
		     bsr      DeletePort
FreeDeviceBlock8     move.l   a2,a0
		     bsr      DeleteExtIO
FreeDeviceBlock9     movem.l  (sp)+,a2
		     rts

;
;		     Get a DeviceBlock
;
;		     d0.w: Size
;		     -> d0 = DeviceBlock ptr (=0 if fail)
;
GetDeviceBlock	     movem.l  d2-d3,-(sp)
		     move.l   d0,d2
		     clr.l    d0
		     move.l   d0,a0
		     bsr      CreatePort
		     move.l   d0,d3
		     beq      GetDeviceBlock9
		     move.l   d3,a0
		     move.l   d2,d0
		     bsr      CreateExtIO
		     tst.l    d0
		     bne      GetDeviceBlock9
		     move.l   d3,a0
		     bsr      DeletePort
		     clr.l    d0
GetDeviceBlock9      movem.l  (sp)+,d2-d3
		     rts


;
;		     Close Audio channels
;
;		     a0: hard_port
;
close_audio_channels
   XDEF close_audio_channels
		     movem.l  a2-a3,-(sp)
		     move.l   a0,a2
		     move.l   hp_port(a2),a3
		     move.l   hp_intnum(a2),d0
		     move.l   hp_old_interrupt(a2),a1
		     CALLEXEC SetIntVector
		     move.l   IOAudio+IO+MN_REPLYPORT(a3),d0
		     beq      close_audio_channel1
		     move.l   d0,a0
		     bsr      DeletePort
close_audio_channel1 tst.l    IOAudio+IO_DEVICE(a3)
		     beq      close_audio_channel2
		     move.l   a3,a1
		     CALLEXEC CloseDevice
close_audio_channel2 move.l   a3,a0
		     bsr      DeleteExtIO
		     movem.l  (sp)+,a2-a3
		     rts

;
;		     Open Audio channels
;
;		     a0: hard_port
;		     -> d0.l = 0 if fail, 1 if ok
;
open_audio_channels
   XDEF  open_audio_channels
		     movem.l  a2-a3,-(sp)
		     move.l   a0,a2
		     clr.l    d0
		     move.l   d0,a1
		     CALLEXEC FindTask
		     move.l   d0,hp_task(a2)
		     move.l   #ioa_SIZEOF,d0
		     bsr      GetDeviceBlock
		     move.l   d0,hp_port(a2)
		     beq      open_audio_channels9
		     move.l   d0,a3
		     clr.l    d0
		     move.l   IOAudio+IO+MN_REPLYPORT(a3),a0
		     move.b   MP_SIGBIT(a0),d0
		     move.l   d0,hp_signal(a2)
		     btst.b   #AUFB_16BITS,hp_flags(a2)
		     beq      open_audio_channels1
		     lea      AU_AllChannels,a1
		     moveq.l  #1,d1
		     bra      open_audio_channels2
open_audio_channels1 lea      AU_LRChannels,a1
		     moveq.l  #4,d1
open_audio_channels2 move.l   a1,ioa_Data(a3)
		     move.l   d1,ioa_Length(a3)
		     move.b   #AUDIO_PRI,IOAudio+IO+MN+LN_PRI(a3)
		     move.l   a3,a1
		     lea      AU_DEVICE_NAME,a0
		     moveq.l  #0,d0
		     moveq.l  #0,d1
		     CALLEXEC OpenDevice
		     tst.b    d0
		     bne      open_audio_channels8    ; Open error
		     move.l   IOAudio+IO_UNIT(a3),hp_channels_bitmap(a2)
		     move.l   a2,a1
		     bsr      @ASM_audio_init
		     lea      hp_audio_interrupt(a2),a1
		     move.b   #NT_INTERRUPT,IS+LN_TYPE(a1)
		     move.b   #0,IS+LN_PRI(a1)
		     lea      AU_PORT_NAME,a0
		     move.l   a0,IS+LN_NAME(a1)
		     move.l   a2,IS_DATA(a1)
		     lea      @ASM_audio_handler,a0
		     move.l   a0,IS_CODE(a1)
		     move.l   hp_intnum(a2),d0
		     CALLEXEC SetIntVector
		     move.l   d0,hp_old_interrupt(a2)
		     moveq.l  #1,d0
		     bra      open_audio_channels9
open_audio_channels8 move.l   a3,a0
		     bsr      FreeDeviceBlock
		     clr.l    hp_port(a2)
		     clr.l    d0
open_audio_channels9 movem.l  (sp)+,a2-a3
		     rts

;
;		     Delete Audio port
;
;		     a0: audio_port
;
delete_audio_port
   XDEF  delete_audio_port
		     movem.l  d2-d3/a2-a4,-(sp)
		     tst.l    a0
		     beq      delete_audio_port9
		     move.l   a0,a2
		     move.l   au_hard_port(a2),a3
		     tst.l    a3
		     beq      delete_audio_port8
		     ; free USER input buffers
		     move.l   a3,a0
		     bsr      free_input_buffers
		     ; free mixing buffer
		     move.l   a3,a0
		     bsr      free_mixing_buffer
		     ; free audio buffers
		     clr.l    d4
delete_audio_port3   move.w   d4,d0
		     move.l   a3,a0
		     bsr      free_audio_buffer
		     addq.w   #1,d4
		     cmp.w    #2,d4
		     blt.s    delete_audio_port3
		     ; free hard port
		     move.l   a3,a1
		     move.l   #hp_sizeof,d0
		     CALLEXEC FreeMem
delete_audio_port8   ; free audio port
		     move.l   a2,a1
		     move.l   #au_sizeof,d0
		     CALLEXEC FreeMem
delete_audio_port9   movem.l  (sp)+,d2-d3/a2-a4
		     rts

;		     Free the USER input buffers
;
;		     a0.l  = hard_port *
;
free_input_buffers
   XDEF  free_input_buffers
		     movem.l  d2/a2/a6,-(sp)
		     move.l   a0,a2
		     move.l   hp_wave_max_length(a2),d2
		     beq.s    free_input_buffers9
		     btst.b   #AUFB_16BITS,hp_flags(a2)
		     beq.s    free_input_buffers0
		     add.l    d2,d2			 ; 16-bit buffers
free_input_buffers0  move.l   hp_input_l_wave(a2),a1
		     tst.l    a1
		     beq.s    free_input_buffers1
		     move.l   d2,d0
		     CALLEXEC FreeMem
		     clr.l    hp_input_l_wave(a2)
free_input_buffers1  move.l   hp_input_r_wave(a2),a1
		     tst.l    a1
		     beq.s    free_input_buffers9
		     move.l   d2,d0
		     CALLEXEC FreeMem
		     clr.l    hp_input_r_wave(a2)
free_input_buffers9  clr.l    hp_wave_max_length(a2)
		     movem.l  (sp)+,d2/a2/a6
		     rts

;		     Allocate the input buffers
;
;		     d0.l  = input sample max length
;		     a0.l  = hard_port *
;		     -> d0.l = 0 if failure
;
allocate_input_buffers
   XDEF  allocate_input_buffers
		     movem.l  d2-d3/a2/a6,-(sp)
		     move.l   a0,a2		      ; a2 = hard_port *
		     move.l   d0,d2
		     move.l   d0,hp_wave_max_length(a2)
		     btst.b   #AUFB_16BITS,hp_flags(a2)
		     beq.s    allocate_input_buff1    ; 8-bit sample
		     add.l    d2,d2
allocate_input_buff1 move.l   #MEMF_PUBLIC|MEMF_CLEAR,d3
		     move.l   d2,d0
		     move.l   d3,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      allocate_input_buff6    ; memory error
		     move.l   d0,hp_input_l_wave(a2)
		     btst.b   #AUFB_STEREO,hp_flags(a2)
		     beq      allocate_input_buff2
		     move.l   d2,d0
		     move.l   d3,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      allocate_input_buff6    ; memory error
		     move.l   d0,hp_input_r_wave(a2)
allocate_input_buff2 moveq.l  #-1,d0
		     bra.s    allocate_input_buff9
allocate_input_buff6 move.l   a2,a0
		     bsr      free_input_buffers
		     clr.l    d0
allocate_input_buff9 movem.l  (sp)+,d2-d3/a2/a6
		     rts



;		     Free the mixing buffer
;
;		     a0.l  = hard_port *
;
free_mixing_buffer
   XDEF  free_mixing_buffer
		     movem.l  a2/a6,-(sp)
		     move.l   a0,a2
		     move.l   hp_mixing_buffer(a2),a1
		     tst.l    a1
		     beq.s    free_mixing_buffer9
		     move.l   hp_mixing_max_size(a2),d0
		     CALLEXEC FreeMem
		     clr.l    hp_mixing_buffer(a2)
free_mixing_buffer9  clr.l    hp_mixing_max_size(a2)
		     movem.l  (sp)+,a2/a6
		     rts

;		     (Re)Allocate the mixing buffer if necessary
;
;		     d0.l  = mixing sample length
;		     a0.l  = hard_port *
;		     -> d0.l = 0 if failure
;
allocate_mixing_buffer
   XDEF  allocate_mixing_buffer
		     movem.l  d2/d5/a3/a6,-(sp)
		     move.l   a0,a3		   ; a3 = hard_port *
		     move.b   hp_flags(a3),d5
		     btst     #AUFB_16BITS,d5
		     beq.s    allocate_mixing_buf1 ; 8-bit sample
		     add.l    d0,d0
allocate_mixing_buf1 cmp.l    hp_mixing_max_size(a3),d0
		     ble      allocate_mixing_buf8 ; Actual mixing buffer is ok
		     move.l   d0,d2		   ; d2 = New mixing size
		     move.l   a3,a0
		     bsr      free_mixing_buffer
		     move.l   d2,d0
		     move.l   #MEMF_PUBLIC,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq.s    allocate_mixing_buf6 ; memory error
		     move.l   d0,hp_mixing_buffer(a3)
		     move.l   d2,hp_mixing_max_size(a3)
		     bra.s    allocate_mixing_buf8
allocate_mixing_buf6 clr.l    d0
		     bra.s	allocate_mixing_buf9
allocate_mixing_buf8 moveq.l  #-1,d0
allocate_mixing_buf9 movem.l  (sp)+,d2/d5/a3/a6
		     rts



;		     Free one audio buffer
;
;		     d0.w  = buffer number
;		     a0.l  = hard_port *
;
free_audio_buffer
   XDEF  free_audio_buffer
		     movem.l  d2-d3/a2/a6,-(sp)
		     move.l   (hp_wave_max_size,a0,d0.w*4),d2  ; Actual size
;		      tst.l    d2
;		      beq      free_audio_buffer9
		     clr.l    (hp_wave_max_size,a0,d0.w*4)  ; New size is 0
		     lea      (hp_l_wave_h,a0,d0.w*4),a2 ; first buffer ptr
		     moveq.w  #4-1,d3			 ; 4 buffer to free
free_audio_buffer1   move.l   (a2),a1
		     tst.l    a1
		     beq.s    free_audio_buffer2   ; This buffer is free
		     move.l   d2,d0
		     CALLEXEC FreeMem
		     clr.l    (a2)
free_audio_buffer2   addq.l   #2*4,a2
		     dbra     d3,free_audio_buffer1
free_audio_buffer9
		     movem.l  (sp)+,d2-d3/a2/a6
		     rts

;		     (Re)Allocate one audio buffer if necessary
;
;		     d0.l  = sample size
;		     d1.w  = buffer number
;		     a0.l  = hard_port *
;		     -> d0.l = 0 if failure
;
allocate_audio_buffer
   XDEF  allocate_audio_buffer
		     movem.l  d2-d5/a2-a3/a6,-(sp)
		     move.l   d0,d2		   ; d2 = sample size
		     move.l   d1,d3		   ; d3 = # buffer
		     move.l   #MEMF_CHIP,d4	   ; d4 = memory type
		     move.l   a0,a3		   ; a3 = hard_port *
		     move.l   (hp_wave_max_size,a3,d3.w*4),d0  ; Actual size
		     cmp.l    d0,d2
		     ble      allocate_audio_buff8 ; Actual size is ok
		     move.l   d3,d0
		     move.l   a3,a0
		     bsr      free_audio_buffer    ; Free this audio buffer
		     move.l   d2,(hp_wave_max_size,a3,d3.w*4)  ; New size
		     move.b   hp_flags(a3),d5
		     move.l   d2,d0
		     move.l   d4,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      allocate_audio_buff6	 ; memory error
		     move.l   d0,(hp_l_wave_h,a3,d3.w*4)
		     btst     #AUFB_16BITS,d5
		     beq      allocate_audio_buff2	 ; 8-bit only !
		     move.l   d2,d0
		     move.l   d4,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      allocate_audio_buff6	 ; memory error
		     move.l   d0,(hp_l_wave_l,a3,d3.w*4)
allocate_audio_buff2 btst     #AUFB_STEREO,d5		 ; mono samples !
		     beq      allocate_audio_buff3
		     move.l   d2,d0
		     move.l   d4,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      allocate_audio_buff6	 ; memory error
		     move.l   d0,(hp_r_wave_h,a3,d3.w*4)
		     btst     #AUFB_16BITS,d5		 ; 8-bit only !
		     beq      allocate_audio_buff3
		     move.l   d2,d0
		     move.l   d4,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      allocate_audio_buff6	 ; memory error
		     move.l   d0,(hp_r_wave_l,a3,d3.w*4)
allocate_audio_buff3 bra.s    allocate_audio_buff8
allocate_audio_buff6			  ; memory error -> free allocated buffers
		     move.l   d3,d0
		     move.l   a3,a0
		     bsr      free_audio_buffer    ; Free this audio buffer
		     clr.l    d0
		     bra.s	allocate_audio_buff9
allocate_audio_buff8 moveq.l  #-1,d0
allocate_audio_buff9 movem.l  (sp)+,d2-d5/a2-a3/a6
		     rts

;
;		     New Audio port
;
;		     d0.b  : flags
;		     d1.l  : buffer_size
;		     -> d0.l = audio_port (=NULL if fail)
;
new_audio_port
   XDEF  new_audio_port
		     movem.l  d2-d5/a2-a4,-(sp)
		     move.l   d0,d2
		     move.l   d1,d3
		     tst.l    d1
		     ble      new_audio_port8	; <= 0
		     cmp.l    #$20000,d1
		     bge      new_audio_port8	; >= $20000
		     btst     #0,d1
		     bne      new_audio_port8	; odd buffer_size !
		     move.l   #au_sizeof,d0
		     move.l   #MEMF_PUBLIC|MEMF_CLEAR,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      new_audio_port8	; memory error
		     move.l   d0,a2
		     move.l   #hp_sizeof,d0
		     move.l   #MEMF_PUBLIC|MEMF_CLEAR,d1
		     CALLEXEC AllocMem
		     tst.l    d0
		     beq      new_audio_port6	; memory error
		     move.l   d0,a3
		     move.l   a3,au_hard_port(a2)
;		      move.l   d3,hp_wave_max_length(a3)  (#5 set by allocate_input_buffers)
		     move.b   d2,hp_flags(a3)
		     ; default values
		     moveq.l  #23,d0
		     move.l   d0,hp_current_period(a3)
		     moveq.w  #64,d0
		     move.w   d0,hp_l_vol(a3)
		     move.w   d0,hp_r_vol(a3)
		     ;
		     ; allocate USER input buffers	 #5
		     ;
		     move.l   a3,a0
		     move.l   d3,d0
		     bsr      allocate_input_buffers
		     tst.l    d0
		     beq      new_audio_port6	   ; memory error
		     move.l   hp_input_l_wave(a3),au_l_wave(a2)
		     move.l   hp_input_r_wave(a3),au_r_wave(a2)
		     ;
		     ; allocate audio buffers		 #5
		     ;
		     clr.l    d4
new_audio_port1      move.l   d3,d0		   ; sample size
		     move.w   d4,d1		   ; buffer number
		     move.l   a3,a0		   ; hard_port *
		     bsr      allocate_audio_buffer
		     tst.l    d0
		     beq      new_audio_port6	   ; memory error
		     addq.w   #1,d4
		     cmp.w    #2,d4		   ; double buffering
		     blt.s    new_audio_port1
		     clr.l    hp_active_wave(a3)

		     move.l   a2,d0
		     bra      new_audio_port9
new_audio_port6      move.l   a2,a0
		     bsr      delete_audio_port
new_audio_port8      clr.l    d0
new_audio_port9      movem.l  (sp)+,d2-d5/a2-a4
		     rts


;
;		     Process an input wave
;
;		     a0: audio_port
;		     -> d0.l = 0 if failure
;
process_wave
		     movem.l  d2-d5/a2-a4,-(sp)
		     move.l   a0,a3		      ; a3 = audio_port
		     move.l   au_hard_port(a3),a4     ; a4 = hard_port
		     move.l   hp_active_wave(a4),d3
		     move.l   au_wave_length(a3),d5   ; = sample length
		     beq      process_wave85	      ; No sample -> no process
		     move.l   hp_current_period(a4),d0
		     move.l   d0,(hp_wave_period,a4,d3.w*4)
		     btst.b   #AUFB_MIXING,au_flags(a3)
		     beq      process_wave0		 ; no mixing
		     move.l   d5,d2			 ; d2.l = wave_length
		     move.l   hp_input_frequency(a4),d0
		     move.l   hp_output_frequency(a4),d1
		     bsr      audio_mix_size
		     move.l   d0,hp_mixing_length(a4)
		     move.l   d0,d5			 ; = new sample length
		     beq      process_wave8		 ; failure
		     move.l   a4,a0			 ; and d0.l = mixing length
		     bsr      allocate_mixing_buffer
		     tst.l    d0
		     beq      process_wave8		 ; failure
process_wave0	     move.l   d5,(hp_wave_length,a4,d3.w*4) ; = sample length
		     ; Allocate audio buffers if necessary
		     move.l   d5,d0		      ; out sample length
		     move.l   d3,d1		      ; buffer number
		     move.l   a4,a0		      ; hard_port *
		     bsr      allocate_audio_buffer
		     tst.l    d0
		     beq      process_wave8	      ; allocate failure
		     btst.b   #AUFB_16BITS,hp_flags(a4)
		     bne      process_wave5	      ; 16-bit process
		     ;
		     ; 8-bit samples processing
		     ;
		     ; 1) Mix Mono or Left sample ?
		     move.l   au_l_wave(a3),a0           ; input sample
		     tst.l    a0
		     beq      process_wave2
		     btst.b   #AUFB_MIXING,au_flags(a3)
		     beq      process_wave1		 ; no mixing
		     move.l   hp_mixing_buffer(a4),a1    ; BYTE *output_sample
		     move.l   hp_input_frequency(a4),d0
		     move.l   hp_output_frequency(a4),d1
		     move.l   d5,d2
		     bsr      @ASM_audio_8_mix
		     move.l   hp_mixing_buffer(a4),a0
process_wave1	     ; 2) Copy processed wave to audio buffers
		     move.l   (hp_l_wave_h,a4,d3.w*4),a1 ; audio buffer
		     tst.l    a1
		     beq      process_wave2
		     move.l   d5,d0			 ; #bytes to copy
		     CALLEXEC CopyMem
process_wave2	     ; 3) Mix Right or Stereo sample ?
		     btst.b   #AUFB_STEREO,hp_flags(a4)
		     beq      process_wave3		 ; Mono sample
		     move.l   au_r_wave(a3),a0           ; input sample
		     tst.l    a0
		     beq      process_wave4
		     btst.b   #AUFB_MIXING,au_flags(a3)
		     beq      process_wave3		 ; no mixing
		     move.l   hp_mixing_buffer(a4),a1    ; BYTE *output_sample
		     move.l   hp_input_frequency(a4),d0
		     move.l   hp_output_frequency(a4),d1
		     move.l   d5,d2
		     bsr      @ASM_audio_8_mix
		     move.l   hp_mixing_buffer(a4),a0
process_wave3	     ; 4) Copy processed wave to audio buffers
		     move.l   (hp_r_wave_h,a4,d3.w*4),a1 ; audio buffer
		     tst.l    a1
		     beq      process_wave4
		     move.l   d5,d0			 ; #bytes to copy
		     CALLEXEC CopyMem
process_wave4	     moveq.l  #-1,d0
		     bra      process_wave9
		     ;
process_wave5	     ; 16-bit samples processing
		     ;
		     ; 1) Mix Mono or Left sample ?
		     move.l   au_l_wave(a3),a0           ; input sample
		     tst.l    a0
		     beq      process_wave65
		     btst.b   #AUFB_MIXING,au_flags(a3)
		     beq      process_wave61		 ; no mixing
		     move.l   hp_mixing_buffer(a4),a1    ; BYTE *output_sample
		     move.l   hp_input_frequency(a4),d0
		     move.l   hp_output_frequency(a4),d1
		     move.l   d5,d2
		     bsr      @ASM_audio_16_mix
		     move.l   hp_mixing_buffer(a4),a0
process_wave61	     ; 2) Split 16-bit sample to 8-bit audio buffers
		     move.l   d5,d0
		     move.l   (hp_l_wave_h,a4,d3.w*4),a1
		     move.l   (hp_l_wave_l,a4,d3.w*4),a2
		     move.w   hp_l_vol(a4),d1
		     bsr      @ASM_audio_split_vol
process_wave65	     ; 3) Mix Right or Stereo sample ?
		     btst.b   #AUFB_STEREO,hp_flags(a4)
		     beq      process_wave75		 ; Mono sample
		     move.l   au_r_wave(a3),a0           ; input sample
		     tst.l    a0
		     beq      process_wave75
		     btst.b   #AUFB_MIXING,au_flags(a3)
		     beq      process_wave71		 ; no mixing
		     move.l   hp_mixing_buffer(a4),a1    ; BYTE *output_sample
		     move.l   hp_input_frequency(a4),d0
		     move.l   hp_output_frequency(a4),d1
		     move.l   d5,d2
		     bsr      @ASM_audio_16_mix
		     move.l   hp_mixing_buffer(a4),a0
process_wave71	     ; 4) Split 16-bit sample to 8-bit audio buffers
		     move.l   d5,d0
		     move.l   (hp_r_wave_h,a4,d3.w*4),a1
		     move.l   (hp_r_wave_l,a4,d3.w*4),a2
		     move.w   hp_r_vol(a4),d1
		     bsr      @ASM_audio_split_vol
process_wave75	     moveq.l  #-1,d0
		     bra      process_wave9
process_wave8	     clr.l    d0
		     bra      process_wave9
process_wave85	     clr.l    (hp_wave_length,a4,d3.w*4) ; sample length = 0
		     moveq.l  #-1,d0
process_wave9	     movem.l  (sp)+,d2-d5/a2-a4
		     rts

;
;		     Write to Audio port
;
;		     a0: audio_port
;
@AU_write
AU_write	     movem.l  d2-d5/a2-a4,-(sp)
		     tst.l    a0
		     beq      AU_write9
		     move.l   a0,a3
		     move.l   au_hard_port(a3),a4     ; a4 = hard_port
		     clr.l    d2
		     move.b   au_flags(a3),d2
		     btst     #AUFB_FREQ,d2
		     beq      AU_write1
		     move.l   au_frequency(a3),d0
		     move.l   d0,hp_input_frequency(a4)  ; #5
		     btst     #AUFB_MIXING,d2		 ; #5
		     beq      AU_write0 		 ; #5
		     move.l   au_mixing_frequency(a3),d0 ; #5
AU_write0	     move.l   d0,hp_output_frequency(a4) ; #5
		     bsr      calculate_period
		     move.l   d0,hp_current_period(a4)
AU_write1	     btst     #AUFB_VOL,d2
		     beq      AU_write2
		     move.w   au_l_vol(a3),hp_l_vol(a4)
		     move.w   au_r_vol(a3),hp_r_vol(a4)
AU_write2	     btst     #AUFB_FILTER,d2
		     beq      AU_write3
		     bclr     #AUFB_FILTER,hp_flags(a4)  ; ???
		     tst.b    au_filter_on(a3)
		     beq      AU_write3
		     bset     #AUFB_FILTER,hp_flags(a4)  ; ???
AU_write3	     move.b   au_command(a3),d3
		     cmp.b    #AUC_CONTROL,d3
		     bne      AU_write4
;		      btst     #AUFB_FREQ,d2
;		      move.l   a4,a1
;		      bsr      @ASM_audio_per
AU_write31	     btst     #AUFB_VOL,d2
		     beq      AU_write32
		     move.l   a4,a1
		     bsr      @ASM_audio_vol
AU_write32	     btst     #AUFB_FILTER,d2
		     beq      AU_write33
		     move.l   a4,a1
		     bsr      @ASM_audio_filter
AU_write33	     bra      AU_write9
AU_write4	     cmp.b    #AUC_WRITE,d3
		     bne      AU_write5
		     move.l   a3,a0		      ; a0 = audio_port *
		     bsr      process_wave	      ; #5
		     tst.l    d0
		     beq      AU_write9 	      ; process_wave error
		     move.l   a4,a1
		     bsr      @ASM_audio_write
		     move.l   hp_active_wave(a4),d3
		     bchg     #0,d3
		     move.l   d3,hp_active_wave(a4)   ; active_wave ^= 1
		     btst.b   #AUFB_NOWAIT,au_flags(a3)
		     bne      AU_write42
		     clr.l    d0
		     move.l   hp_signal(a4),d1
		     bset     d1,d0
		     CALLEXEC Wait
AU_write42
		     ; #5: if no input buffers, use audio buffers for USER
;		      btst     #AUFB_16BITS,d2
;		      tst.l    hp_input_l_wave(a4)
;		      bne      AU_write43	    ; Ok, left input buffer exist
;		      move.l   (hp_l_wave_h,a4,d3.w*4),au_l_wave(a3)
;AU_write43	      tst.l    hp_input_r_wave(a4)
;		      bne      AU_write44	    ; Ok, Right input buffer exist
;		      move.l   (hp_r_wave_h,a4,d3.w*4),au_r_wave(a3)
AU_write44	     bra      AU_write9
AU_write5	     cmp.b    #AUC_STOP,d3
		     bne      AU_write6
		     move.l   a4,a1
		     bsr      @ASM_audio_stop
		     bra      AU_write9
AU_write6	     cmp.b    #AUC_CONTINUE,d3
		     bne      AU_write9

AU_write9	     movem.l  (sp)+,d2-d5/a2-a4
		     rts

;
;		     Close an Audio port
;
;		     a0 = audio_port
;
@AU_close
AU_close	     movem.l  a2-a3,-(sp)
		     tst.l    a0
		     beq      AU_close9
		     move.l   a0,a2
		     move.l   au_hard_port(a2),a3
		     move.l   a3,a1
		     bsr      @ASM_audio_stop
		     move.l   a3,a0
		     bsr      close_audio_channels
		     move.l   a2,a0
		     bsr      delete_audio_port
AU_close9	     movem.l  (sp)+,a2-a3
		     rts

;
;		     Open an Audio port
;
;		     d0.b  : flags
;		     d1.l  : buffer_size
;		     -> d0.l = audio_port or NULL if fail
;
@AU_open
AU_open 	     movem.l  a2-a3,-(sp)
		     bsr      new_audio_port
		     tst.l    d0
		     beq      AU_open9
		     move.l   d0,a2
		     move.l   au_hard_port(a2),a3
		     move.l   a3,a0
		     bsr      open_audio_channels
		     tst.l    d0
		     bne      AU_open1
		     move.l   a2,a0
		     bsr      delete_audio_port
		     clr.l    d0
		     bra      AU_open9
AU_open1	     move.l   hp_signal(a3),d0
		     move.b   d0,au_signal(a2)
		     move.l   a2,d0
AU_open9	     movem.l  (sp)+,a2-a3
		     rts


;		     Audio Split 16bits -> 2x8bits for 14bit audio
;
;		     a0 = WORD	*Wave16bits
;		     a1 = BYTE	*WaveH8bits
;		     a2 = BYTE	*WaveL8bits
;		     d0 = ULONG  Wave16Length
;
@ASM_audio_split
		     movem.l  d0-d7/a0-a2,-(sp)
		     move.w   #128,d7
		     move.w   #$FF00,d6
		     move.w   #$FF,d5
		     lsr.l    #1,d0
		     subq.l   #1,d0
ASM_audio_split1
		     move.w   (a0)+,d1
		     move.w   d1,d2
		     add.w    d7,d1
		     bvs.s    ASM_audio_split2
		     and.w    d6,d1
		     asr.b    #2,d2
		     lsl.w    #8,d2
ASM_audio_split3
		     move.w   (a0)+,d3
		     move.w   d3,d4
		     add.w    d7,d3
		     bvs.s    ASM_audio_split4
		     lsr.w    #8,d3
		     asr.b    #2,d4
		     and.w    d5,d4
ASM_audio_split5
		     or.w     d3,d1
		     move.w   d1,(a1)+
		     or.w     d4,d2
		     move.w   d2,(a2)+
		     dbra     d0,ASM_audio_split1
		     movem.l  (sp)+,d0-d7/a0-a2
		     rts
ASM_audio_split2
		     move.w   #$7F00,d1
		     move.w   d1,d2
		     bra.s    ASM_audio_split3
ASM_audio_split4
		     moveq.w #$7F,d3
		     move.w  d1,d4
		     bra.s   ASM_audio_split5



; #5		     Audio Split 16bits + Volume set -> 2x8bits for 14bit audio
;
;		     a0 = WORD	*Wave16bits
;		     a1 = BYTE	*WaveH8bits
;		     a2 = BYTE	*WaveL8bits
;		     d0 = ULONG  Wave16Length
;		     d1.w = Volume (64 = Normal)
;
@ASM_audio_split_vol
		     cmp.w    #64,d1		   ; If volume is normal, use
		     beq      @ASM_audio_split	   ; ASM_audio_split instead
		     movem.l  d0-d7/a0-a3,-(sp)
		     move.w   d1,d7
		     lea.l    128,a3
		     move.w   #$FF00,d6
		     move.w   #$FF,d5
		     lsr.l    #1,d0
		     subq.l   #1,d0
ASM_audio_split_vol1
		     move.w   (a0)+,d1
		     muls.w   d7,d1
		     asr.l    #6,d1
		     move.w   d1,d2
		     add.w    a3,d1
		     bvs.s    ASM_audio_split_vol2
		     and.w    d6,d1
		     asr.b    #2,d2
		     lsl.w    #8,d2
ASM_audio_split_vol3
		     move.w   (a0)+,d3
		     muls.w   d7,d3
		     asr.l    #6,d3
		     move.w   d3,d4
		     add.w    a3,d3
		     bvs.s    ASM_audio_split_vol4
		     lsr.w    #8,d3
		     asr.b    #2,d4
		     and.w    d5,d4
ASM_audio_split_vol5
		     or.w     d3,d1
		     move.w   d1,(a1)+
		     or.w     d4,d2
		     move.w   d2,(a2)+
		     dbra     d0,ASM_audio_split_vol1
		     movem.l  (sp)+,d0-d7/a0-a3
		     rts
ASM_audio_split_vol2
		     move.w   #$7F00,d1
		     move.w   d1,d2
		     bra.s    ASM_audio_split_vol3
ASM_audio_split_vol4
		     moveq.w #$7F,d3
		     move.w  d1,d4
		     bra.s   ASM_audio_split_vol5


AUDIO_16_MIX_SHIFT   equ      14

; #5		     Audio mix 8-bit sample at freq f1 -> 8-bit sample freq f2
;
;		     a0 = BYTE *input_sample
;		     a1 = BYTE *output_sample
;		     d0.l = input freq
;		     d1.l = output freq
;		     d2.l = output sample size
;

@ASM_audio_8_mix
   XDEF  @ASM_audio_8_mix
		     movem.l  d2-d7,-(sp)

		     move.l   d2,d7		   ; d7 = output sample size
		     tst.w    d1
		     beq      ASM_audio_8_mix9
		     move.l   #1<<AUDIO_16_MIX_SHIFT,d6  ; d6 = t_max
		     mulu.w   d6,d0
		     divu.l   d1,d0
		     move.l   d0,d5		   ; d5 = t_inc
		     move.l   d6,d4
		     add.l    d4,d4		   ; d4 = t_out = 2*t_max
		     moveq.l  #AUDIO_16_MIX_SHIFT,d1
ASM_audio_8_mix1
		     cmp.l    d6,d4
		     blt.s    ASM_audio_8_mix2
ASM_audio_8_mix10    move.l   d3,d2		   ; d2 = e0, d3 = e1
		     move.b   (a0)+,d3
		     ext.w    d3
		     sub.l    d6,d4		   ; t_out -= t_max
		     cmp.l    d6,d4
		     bge.s    ASM_audio_8_mix10
ASM_audio_8_mix2     move.w   d3,d0
		     sub.w    d2,d0		   ; e1-e0
		     muls.w   d4,d0		   ; (e1-e0) * t_out
		     asr.l    d1,d0
		     add.w    d2,d0		   ; e0 + ((e1-e0) * t_out)>>14
		     move.b   d0,(a1)+
		     add.l    d5,d4		   ; t_out += t_inc
		     subq.l   #1,d7
		     bne.s    ASM_audio_8_mix1
ASM_audio_8_mix9     movem.l  (sp)+,d2-d7
		     rts




; #6		     Audio mix 16-bit sample at freq f1 -> 16-bit sample freq f2
;
;		     a0 = WORD *input_sample
;		     a1 = WORD *output_sample
;		     d0.l = input freq
;		     d1.l = output freq
;		     d2.l = output sample size
;
@ASM_audio_16_mix
   XDEF  @ASM_audio_16_mix
		     movem.l  d2-d7,-(sp)

		     move.l   d2,d7		   ; d7 = output sample size
		     tst.w    d1
		     beq      ASM_audio_16_mix9
		     move.l   #1<<AUDIO_16_MIX_SHIFT,d6  ; d6 = t_max
		     mulu.w   d6,d0
		     divu.l   d1,d0
		     move.l   d0,d5		   ; d5 = t_inc
		     move.l   d6,d4
		     add.l    d4,d4		   ; d4 = t_out = 2*t_max
		     moveq.l  #AUDIO_16_MIX_SHIFT,d1
ASM_audio_16_mix1
		     cmp.l    d6,d4
		     blt.s    ASM_audio_16_mix2
ASM_audio_16_mix10   move.l   d3,d2		   ; d2 = e0, d3 = e1
		     move.w   (a0)+,d3
		     ext.l    d3
		     sub.l    d6,d4		   ; t_out -= t_max
		     cmp.l    d6,d4
		     bge.s    ASM_audio_16_mix10
ASM_audio_16_mix2    move.l   d3,d0
		     sub.l    d2,d0		   ; e1-e0
		     muls.l   d4,d0		   ; (e1-e0) * t_out
		     asr.l    d1,d0
		     add.l    d2,d0		   ; e0 + ((e1-e0) * t_out)>>13
		     move.w   d0,(a1)+
		     add.l    d5,d4		   ; t_out += t_inc
		     subq.l   #1,d7
		     bne.s    ASM_audio_16_mix1
ASM_audio_16_mix9    movem.l  (sp)+,d2-d7
		     rts



; #5		     Audio mix sample size calculation
;
;		     d0.l = input_freq
;		     d1.l = mixing_freq
;		     d2.l = wave_length: input sample size (0x20000 max)
;		     -> d0.l = mixing_length: output mixing sample size
;
;		     Formula is :
;		     mixing_length = (wave_length * t_max) / t_inc
;		     t_max = 1<<AUDIO_16_MIX_SHIFT
;		     t_inc = (t_max * input_freq) / mixing_freq
;
audio_mix_size
   XDEF audio_mix_size
		     movem.l  d2-d3,-(sp)
		     tst.l    d1
		     beq      audio_mix_size9
		     move.l   #1<<AUDIO_16_MIX_SHIFT,d3
		     mulu.l   d3,d0
		     divu.l   d1,d0	     ; d0 = t_inc
		     mulu.l   d3,d2
;		      add.l    d0,d2	     ; Suppressed 24/07/1995
		     divu.l   d0,d2
audio_mix_size9      move.l   d2,d0	     ; d0 = mixing sample size
		     and.b    #$FE,d0	     ; Assume size to be even
		     cmp.l    #$20000,d0     ; max size for audio dma's
		     ble      audio_mix_size10
		     move.l   #$20000,d0
audio_mix_size10     movem.l  (sp)+,d2-d3
		     rts

;		     Audio init
;
;		     a1 = struct hard_port *hport
;
@ASM_audio_init
		     movem.l  d2-d4,-(sp)
		     lea      _custom,a0
		     move.l   hp_channels_bitmap(a1),d1
		     btst.b   #AUFB_16BITS,hp_flags(a1)
		     bne.s    ASM_audio_ini5	   ; 16 bits
		     move.w   #aud0,d2
		     btst     #0,d1
		     bne.s    ASM_audio_ini1
		     move.w   #aud3,d2
ASM_audio_ini1	     move.w   d2,hp_l_channel_h(a1)
		     move.w   #aud1,d2
		     btst     #1,d1
		     bne.s    ASM_audio_ini2
		     move.w   #aud2,d2
ASM_audio_ini2	     move.w   d2,hp_r_channel_h(a1)
		     bra.s    ASM_audio_ini6
ASM_audio_ini5
		     move.w   #aud0,hp_l_channel_h(a1)
		     move.w   #aud1,hp_r_channel_h(a1)
		     move.w   #aud2,hp_r_channel_l(a1)
		     move.w   #aud3,hp_l_channel_l(a1)
ASM_audio_ini6
		     move.w   #$00FF,hp_adkcon(a1) ; adkcon
		     move.w   d1,d2		   ; audena
		     and.w    #$000F,d2
		     move.w   d2,hp_audena(a1)
		     move.w   d1,d2		   ; intdis
		     lsl.w    #7,d2
		     and.w    #$0780,d2
		     move.w   d2,hp_intdis(a1)
		     clr.l    d2		   ; intena (enable only 1 channel int)
		     moveq.l  #7,d3		   ; start int level
		     moveq.w  #3-1,d4
ASM_audio_ini7
		     lsr.l    #1,d1
		     bcs.s    ASM_audio_ini8
		     addq.l   #1,d3
		     dbra     d4,ASM_audio_ini7
ASM_audio_ini8
		     move.l   d3,hp_intnum(a1)
		     bset     d3,d2
		     bset     #15,d2
		     move.w   d2,hp_intena(a1)

		     movem.l  (sp)+,d2-d4
		     bsr.w    @ASM_audio_stop
		     rts

;		     Audio stop wave
;
;		     a1 = struct hard_port *hport
;
@ASM_audio_stop
		     lea      _custom,a0
		     clr.b    hp_playing(a1)    ; Audio stopped
		     moveq.l  #0,d0
		     move.w   hp_l_channel_h(a1),d1
		     move.w   d0,(AU_VOL,a0,d1.w)      ; Volume -> 0
		     move.w   hp_r_channel_h(a1),d1
		     move.w   d0,(AU_VOL,a0,d1.w)      ; Volume -> 0
		     btst.b   #AUFB_16BITS,hp_flags(a1)
		     beq.s    ASM_audio_stop2	; 8 bits
		     move.w   hp_l_channel_l(a1),d1
		     move.w   d0,(AU_VOL,a0,d1.w)      ; Volume -> 0
		     move.w   hp_r_channel_l(a1),d1
		     move.w   d0,(AU_VOL,a0,d1.w)      ; Volume -> 0
ASM_audio_stop2
		     move.w   hp_audena(a1),$96(a0)    ; DMACON AUDxEN -> 0
		     move.w   hp_adkcon(a1),$9E(a0)    ; ADKCON clr all audio bits
		     move.w   hp_intdis(a1),$9C(a0)    ; Clr audio x interrupts
		     move.w   hp_intdis(a1),$9A(a0)    ; INTENA audio 0 -> 0
		     rts

;		     Audio set filter
;
;		     a1 = struct hard_port *hport
;
@ASM_audio_filter
		     lea      $BFE001,a0
		     btst.b   #AUFB_FILTER,hp_flags(a1)
		     bne.s    ASM_audio_filter5
		     bset     #1,(a0)
		     bra.s    ASM_audio_filter9
ASM_audio_filter5    bclr     #1,(a0)
ASM_audio_filter9    rts


;		     Audio set volume
;
;		     a1 = struct hard_port *hport
;
@ASM_audio_vol
		     lea      _custom,a0
		     move.w   hp_l_channel_h(a1),d0
		     move.w   hp_r_channel_h(a1),d1
		     btst.b   #AUFB_16BITS,hp_flags(a1)
		     bne.w    ASM_audio_vol5		 ; 16 bits
		     move.w   hp_l_vol(a1),(AU_VOL,a0,d0.w)
		     move.w   hp_r_vol(a1),(AU_VOL,a0,d1.w)
ASM_audio_vol5	     rts

;		     Audio set period
;
;		     a1 = struct hard_port *hport
;		     d0.l = period
;
@ASM_audio_per
		     movem.l  d2-d7,-(sp)
		     lea      _custom,a0
;		      move.l   hp_active_wave(a1),d1
;		      bchg     #0,d1
;		      move.l   (hp_current_period,a1,d1.w*4),d0
		     move.w   hp_l_channel_h(a1),d4
		     move.w   hp_r_channel_h(a1),d5
		     CALLEXEC Disable
		     btst.b   #AUFB_16BITS,hp_flags(a1)
		     bne.w    ASM_audio_per5		; 16 bits
		     move.w   d0,(AU_PER,a0,d4.w)
		     move.w   d0,(AU_PER,a0,d5.w)
		     bra.s    ASM_audio_per9
ASM_audio_per5
		     move.w   hp_l_channel_l(a1),d6
		     move.w   hp_r_channel_l(a1),d7
		     move.w   d0,(AU_PER,a0,d4.w)
		     move.w   d0,(AU_PER,a0,d6.w)
		     move.w   d0,(AU_PER,a0,d5.w)
		     move.w   d0,(AU_PER,a0,d7.w)
ASM_audio_per9
		     CALLEXEC Enable
		     movem.l  (sp)+,d2-d7
		     rts

;		     Audio write wave
;
;		     a1 = struct hard_port *hport
;
@ASM_audio_write
		     movem.l  d2-d7,-(sp)
		     lea      _custom,a0
		     move.l   hp_active_wave(a1),d1
		     asl.l    #2,d1		      ; d1*4
		     move.l   hp_wave_period(a1,d1.l),d0
		     move.l   d0,hp_next_period(a1)
		     move.w   hp_l_channel_h(a1),d4
		     move.w   hp_r_channel_h(a1),d5
		     CALLEXEC Disable
		     btst.b   #AUFB_16BITS,hp_flags(a1)
		     bne.w    ASM_audio_wr5	      ; 16 bits
		     tst.b    hp_playing(a1)          ;#5
		     bne      ASM_audio_wr0
		     move.w   d0,(AU_PER,a0,d4.w)
		     move.w   d0,(AU_PER,a0,d5.w)
ASM_audio_wr0
		     move.w   hp_l_vol(a1),(AU_VOL,a0,d4.w)
		     move.w   hp_r_vol(a1),(AU_VOL,a0,d5.w)
		     move.l   hp_l_wave_h(a1,d1.l),d0
		     move.l   d0,(AU_PTR,a0,d4.w)
		     btst     #AUFB_STEREO,hp_flags(a1)
		     beq.s    ASM_audio_wr1		 ; Mono output
		     move.l   hp_r_wave_h(a1,d1.l),d0
ASM_audio_wr1	     move.l   d0,(AU_PTR,a0,d5.w)

		     clr.b    hp_end_of_play(a1)         ; Not end of play ?
		     move.l   hp_wave_length(a1,d1.l),d0
		     asr.l    #1,d0			 ; Count in word
		     move.w   d0,(AU_LEN,a0,d4.w)
		     move.w   d0,(AU_LEN,a0,d5.w)

		     bne.w    ASM_audio_wr7	      ; Ok wave_length > 0 -> play audio
		     st.b     hp_end_of_play(a1)      ; wave_length = 0 -> end of play
		     bra.w    ASM_audio_wr7

ASM_audio_wr5
		     move.w   hp_l_channel_l(a1),d6
		     move.w   hp_r_channel_l(a1),d7
		     tst.b    hp_playing(a1)          ;#5
		     bne      ASM_audio_wr50
		     move.w   d0,(AU_PER,a0,d4.w)
		     move.w   d0,(AU_PER,a0,d6.w)
		     move.w   d0,(AU_PER,a0,d5.w)
		     move.w   d0,(AU_PER,a0,d7.w)
ASM_audio_wr50
		     moveq.w  #$40,d0
		     move.w   d0,(AU_VOL,a0,d4.w)
		     move.w   d0,(AU_VOL,a0,d5.w)
		     moveq.w  #$01,d0
		     move.w   d0,(AU_VOL,a0,d6.w)
		     move.w   d0,(AU_VOL,a0,d7.w)
		     move.l   hp_l_wave_h(a1,d1.l),d0
		     move.l   hp_l_wave_l(a1,d1.l),d2
		     move.l   d0,(AU_PTR,a0,d4.w)
		     move.l   d2,(AU_PTR,a0,d6.w)
		     btst     #AUFB_STEREO,hp_flags(a1)
		     beq.s    ASM_audio_wr6		 ; Mono output
		     move.l   hp_r_wave_h(a1,d1.l),d0
		     move.l   hp_r_wave_l(a1,d1.l),d2
ASM_audio_wr6	     move.l   d0,(AU_PTR,a0,d5.w)
		     move.l   d2,(AU_PTR,a0,d7.w)

		     clr.b    hp_end_of_play(a1)         ; Not end of play ?
		     move.l   hp_wave_length(a1,d1.l),d0
		     asr.l    #1,d0			 ; Count in word
		     move.w   d0,(AU_LEN,a0,d4.w)
		     move.w   d0,(AU_LEN,a0,d5.w)
		     move.w   d0,(AU_LEN,a0,d6.w)
		     move.w   d0,(AU_LEN,a0,d7.w)

		     bne.s    ASM_audio_wr7	      ; Ok wave_length > 0 -> play audio
		     st.b     hp_end_of_play(a1)      ; wave_length = 0 -> end of play
ASM_audio_wr7
		     tst.b    hp_playing(a1)
		     bne.s    ASM_audio_wr9	      ; Always playing (started)
		     st.b     hp_playing(a1)          ; Started
		     move.w   hp_audena(a1),d0
		     bset     #15,d0
		     move.w   d0,$96(a0)              ; DMACON AUDxEN -> 1
		     move.w   hp_adkcon(a1),$9E(a0)   ; ADKCON clr all audio bits
		     move.w   hp_intdis(a1),$9C(a0)   ; Clr audio x interrupts
		     move.w   hp_intena(a1),$9A(a0)   ; INTENA audio 0 -> 1
ASM_audio_wr9
		     st.b     hp_dma_loaded(a1)       ; Dma is loaded
		     CALLEXEC Enable
		     movem.l  (sp)+,d2-d7
		     rts

;		     Audio interrupt handler
;
;		     a1 = DATA = struct hard_port *hport
;
@ASM_audio_handler
		     lea      _custom,a0
		     move.w   hp_intdis(a1),$9C(a0)   ; Clr audio x interrupts
		     move.l   hp_next_period(a1),d0
		     bsr      @ASM_audio_per	      ; #5
		     tst.b    hp_dma_loaded(a1)
		     bne.s    ASM_audio_handler1      ; Ok DMA was loaded -> signal
		     bsr      @ASM_audio_stop	      ; DMA is empty !!! -> stop audio, no signal
		     bra.s    ASM_audio_handler9
ASM_audio_handler1
		     clr.b    hp_dma_loaded(a1)       ; DMA become free
		     tst.b    hp_end_of_play(a1)      ; end of play ?
		     beq.s    ASM_audio_handler2
		     bsr      @ASM_audio_stop
ASM_audio_handler2
		     move.l   hp_signal(a1),d1
		     clr.l    d0
		     bset     d1,d0
		     move.l   hp_task(a1),a1
		     CALLEXEC Signal
ASM_audio_handler9
		     clr.l    d0
		     rts

