;
;   This file is part of the Infinity sound library.
;   (C) copyright Serge 2006
;   email: infinity_sound@mail.ru
;
;   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 2 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.

format MS COFF

include 'proc32.inc'
include 'main.inc'

DEBUG		    equ 1

EVENT_NOTIFY	    equ 0x00000200

OS_BASE               equ 0;  0x80400000
new_app_base          equ 0x60400000;   0x01000000
PROC_BASE             equ OS_BASE+0x0080000

public service_proc
public START
public IMPORTS

SND_CREATE_BUFF     equ 2
SND_PLAY            equ 3
SND_STOP            equ 4
SND_SETBUFF         equ 5
SND_DESTROY_BUFF    equ 6

DEV_PLAY            equ 1
DEV_STOP            equ 2
DEV_CALLBACK        equ 3

struc IOCTL
{  .handle           dd ?
   .io_code          dd ?
   .input            dd ?
   .inp_size         dd ?
   .output           dd ?
   .out_size         dd ?
}

virtual at 0
  IOCTL IOCTL
end virtual

section '.flat' align 16

START:
           stdcall [GetService], szSound
           test eax, eax
           jz .fail
           mov [hSound], eax

           stdcall [KernelAlloc], 16*512
           test eax, eax
           jz .out_of_mem
           mov [mix_buff], eax

           mov edi, stream_list
           mov ecx, 17
           xor eax, eax
           cld
           rep stosd

           mov edi, stream
           mov ecx, 4*STREAM_SIZE
           rep stosd

           stdcall set_handler, [hSound], new_mix

	   stdcall [RegService], szInfinity, service_proc
           mov [stream_count],0

	   ret
.fail:
     if DEBUG
	   mov esi, msgFail
	   call   [SysMsgBoardStr]
     end if
	   xor eax, eax
	   ret

.out_of_mem:
     if DEBUG
           mov esi, msgMem
	   call   [SysMsgBoardStr]
     end if
	   xor eax, eax
	   ret

handle     equ  IOCTL.handle
io_code    equ  IOCTL.io_code
input      equ  IOCTL.input
inp_size   equ  IOCTL.inp_size
output     equ  IOCTL.output
out_size   equ  IOCTL.out_size

align 4
proc service_proc stdcall, ioctl:dword

           mov edi, [ioctl]
           mov eax, [edi+io_code]

	   cmp eax, SND_CREATE_BUFF
	   jne @F
           mov ebx, [edi+input]
           stdcall CreateBuffer,[ebx]
	   ret
@@:
	   cmp eax, SND_PLAY
	   jne @F

           mov ebx, [edi+input]
	   stdcall play_buffer, [ebx]
	   ret
@@:
	   cmp eax, SND_STOP
	   jne @F

;       if DEBUG
;	   mov esi, msgStop
;	   call   [SysMsgBoardStr]
;       end if

           mov ebx, [edi+input]
	   stdcall stop_buffer, [ebx]
	   ret
@@:
	   cmp eax, SND_SETBUFF
	   jne @F

           mov ebx, [edi+input]
	   mov eax, [ebx+4]
	   add eax, new_app_base
	   stdcall set_buffer, [ebx],eax,[ebx+8],[ebx+12]
	   ret
@@:
           cmp eax, SND_DESTROY_BUFF
	   jne @F

           mov ebx, [edi+input]
           stdcall DestroyBuffer, [ebx]
	   ret
@@:
	   xor eax, eax
	   ret
endp

restore   handle
restore   io_code
restore   input
restore   inp_size
restore   output
restore   out_size

TASK_COUNT    equ 0x0003004
CURRENT_TASK  equ 0x0003000


align 8
proc CreateBuffer stdcall, format:dword
	   locals
	      str dd ?
	   endl

	   call alloc_stream
	   and eax, eax
	   jz .fail
	   mov [str], eax
           mov edi, eax

	   mov edx, [stream_count]
	   mov [stream_list+edx*4], eax
	   inc [stream_count]

	   mov [edi+STREAM.magic], 'WAVE'
	   mov [edi+STREAM.size], STREAM_SIZE

	   stdcall [KernelAlloc], 180*1024

	   mov edi, [str]
	   mov [edi+STREAM.base], eax
	   mov [edi+STREAM.curr_seg], eax
	   mov [edi+STREAM.notify_off1], eax
	   add eax, 0x8000
	   mov [edi+STREAM.notify_off2], eax
	   add eax, 0x7FFF
	   mov [edi+STREAM.limit], eax

	   inc eax

	   mov [edi+STREAM.work_buff], eax
	   mov [edi+STREAM.work_read], eax
	   mov [edi+STREAM.work_write], eax
	   mov [edi+STREAM.work_count], 0
	   add eax, 0x10000
	   mov [edi+STREAM.work_top], eax
	   add eax, 1024*32
	   mov [edi+STREAM.r_buff], eax

	   mov ebx, [CURRENT_TASK]
           shl ebx, 5
           mov eax, [0x3000+ebx+4]

	   mov [edi+STREAM.notify_task], eax

	   mov eax, [format]
	   mov [edi+STREAM.format], eax
	   mov [edi+STREAM.flags], SND_STOP

           xor ebx, ebx
           cmp eax, 19
           jb @f
           mov ebx, 0x80808080
@@:
           mov [edi+STREAM.r_silence], ebx

	   shl eax, 4
           mov ebx, [resampler_params+eax]
           mov ecx, [resampler_params+eax+4]
           mov edx, [resampler_params+eax+8]

           mov [edi+STREAM.r_size],ebx
           mov [edi+STREAM.r_end], ecx
           mov [edi+STREAM.r_dt],  edx

	   mov ebx, [resampler_params+eax+12]
	   mov [edi+STREAM.resample], ebx

	   mov edi, [edi+STREAM.base]
	   mov ecx, 180*1024/4
	   xor eax, eax
	   rep stosd

	   mov eax, [str]
	   ret

.fail:
	   xor eax, eax
	   ret
endp

align 4
pid_to_slot:

    push   ebx
    push   ecx
    mov    ebx,[TASK_COUNT]
    shl    ebx,5
    mov    ecx,2*32
.loop:
    cmp    byte [CURRENT_TASK+ecx+0xa],9
    jz     .endloop              ;skip empty slots
    cmp    [CURRENT_TASK+ecx+0x4],eax ;check PID
    jz     .pid_found
.endloop:
    add    ecx,32
    cmp    ecx,ebx
    jle    .loop
    pop    ecx
    pop    ebx
    xor    eax,eax
    ret

.pid_found:
    shr    ecx,5
    mov    eax,ecx
    pop    ecx
    pop    ebx
    ret



align 4
proc DestroyBuffer stdcall, str:dword

           mov esi, [str]

           cmp [esi+STREAM.magic], 'WAVE'
	   jne .fail

           cmp [esi+STREAM.size], STREAM_SIZE
	   jne .fail

           stdcall [KernelFree], [esi+STREAM.base]

           mov eax, [str]
	   call free_stream

           mov edi, [str]
           mov ecx, STREAM_SIZE/4
           xor eax, eax
           cld
           rep stosd

           mov eax, [str]
           mov esi, stream_list
           mov ecx, 16
@@:
           cmp [esi], eax
           je .remove
           add esi, 4
           dec ecx
           jnz @B
           xor eax, eax
	   inc eax
	   ret
.remove:
           mov edi, esi
           add esi, 4
           cld
           rep movsd
           dec [stream_count]
           xor eax, eax
	   inc eax
	   ret
.fail:
	   xor eax, eax
	   ret
endp

align 4
proc play_buffer stdcall, str:dword

	   mov ebx, [str]

	   cmp [ebx+STREAM.magic], 'WAVE'
	   jne .fail

	   cmp [ebx+STREAM.size], STREAM_SIZE
	   jne .fail

	   mov [ebx+STREAM.flags], SND_PLAY

	   mov eax,[ebx+STREAM.work_buff]
	   mov [ebx+STREAM.work_read], eax
	   mov [ebx+STREAM.work_write], eax
	   mov [ebx+STREAM.work_count], 0

	   mov eax, [ebx+STREAM.base]
	   mov [ebx+STREAM.curr_seg], eax

	   mov esi, [ebx+STREAM.curr_seg]
	   mov edi, [ebx+STREAM.work_write]
	   mov edx, [ebx+STREAM.r_buff]

	   mov ecx, 32
           mov eax, [ebx+STREAM.r_silence]
@@:
	   mov [edx], eax
	   add edx, 4
	   dec ecx
	   jnz @B

	   mov edx, [ebx+STREAM.r_buff]

	   stdcall [ebx+STREAM.resample], edi, esi, edx,\
	      [ebx+STREAM.r_dt],[ebx+STREAM.r_size],[ebx+STREAM.r_end]

	   mov ebx, [str]

	   add [ebx+STREAM.work_count], eax;
	   add [ebx+STREAM.work_write], eax;

	   mov eax, [ebx+STREAM.r_size]
	   add [ebx+STREAM.curr_seg], eax

;       if DEBUG
;	   mov esi, msgPlay
;	   call   [SysMsgBoardStr]
;       end if

	   stdcall  dev_play, [hSound]

	   xor eax, eax
	   inc eax
	   ret

.fail:
	   xor eax, eax
	   ret

endp


align 4
proc stop_buffer stdcall, str:dword

	   mov edi, [str]

	   cmp [edi+STREAM.magic], 'WAVE'
	   jne .fail

	   cmp [edi+STREAM.size], STREAM_SIZE
	   jne .fail

	   mov [edi+STREAM.flags], SND_STOP

;           stdcall [ServiceHandler], [hSound], dword DEV_STOP, 0

	   xor eax, eax
	   inc eax
	   ret

.fail:
	   xor eax, eax
	   ret

endp

align 4
proc set_buffer stdcall, str:dword,src:dword,offs:dword,size:dword

	   mov edx, [str]
	   test edx, edx
	   jz .fail

	   cmp [edx+STREAM.magic], 'WAVE'
	   jne .fail

	   cmp [edx+STREAM.size], STREAM_SIZE
	   jne .fail

	   mov esi,[src]
	   test esi, esi
	   jz .fail

           cmp esi, new_app_base
           jb .fail

	   mov ecx, [size]
	   test ecx, ecx
	   jz .fail

	   mov eax, [edx+STREAM.base]
	   add eax, [offs]

           cmp eax, [edx+STREAM.base]
           jb .fail

	   mov edi, eax
	   add eax, ecx
	   sub eax, 1

	   cmp eax, [edx+STREAM.limit]
           ja .fail

	   shr ecx, 2
           cld
	   rep movsd

	   xor eax, eax
	   inc eax
	   ret
.fail:
	   xor eax, eax
	   ret
endp

align 4
proc alloc_stream

	   mov esi, stream_map

	   pushf
	   cli

	   bsf eax, [esi]
	   jnz .find
	   popf
	   xor eax, eax
	   ret

.find:	   btr [esi], eax
	   popf
	   mov ebx, STREAM_SIZE
	   mul ebx
	   add eax, stream
	   ret
endp

align 4
proc free_stream
	   sub eax, stream
           mov ebx, STREAM_SIZE
	   xor edx, edx
	   div ebx

	   and edx, edx
           jnz .err

	   bts [stream_map], eax

	   ret
.err:
           xor eax, eax
	   ret
endp

align 4
proc check_stream

	   xor edx, edx
	   mov ecx, [play_count]
.l1:
	   mov esi, [play_list+edx]

	   mov eax, [esi+STR.curr_seg]
	   cmp eax, [esi+STR.limit]
	   jb .next

.m1:	   mov eax,[esi+STR.base]
	   mov [esi+STR.curr_seg], eax
.next:
	   add edx, 4
	   loop .l1
	   ret
endp


align 4
proc prepare_playlist

.restart:
           xor ebx, ebx
	   xor edx, edx
	   mov [play_count], 0
	   mov ecx, [stream_count]
           jcxz .exit
.l1:
           mov esi, [stream_list+ebx]
           test esi, esi
           jz .next

           cmp [esi+STREAM.magic], 'WAVE'
           jne .next

           cmp [esi+STREAM.size], STREAM_SIZE
           jne .next

           mov eax,[esi+STREAM.notify_task]
           cmp eax, -1
           je .fail

           call pid_to_slot
           test eax, eax
           jz .fail

           cmp [esi+STREAM.flags], SND_PLAY;
	   jne .next
           cmp [esi+STREAM.work_count], 16384
           jb .next

           mov [play_list+edx], esi
	   inc [play_count]
	   add edx, 4
.next:
	   add ebx, 4
	   loop .l1
.exit:
	   ret

.fail:
           stdcall DestroyBuffer, esi
           jmp .restart
endp

align 4
proc prepare_updatelist

	   xor ebx, ebx
	   xor edx, edx
	   mov [play_count], 0
	   mov ecx, [stream_count]
           jcxz .exit
.l1:
	   mov eax, [stream_list+ebx]
           test eax, eax
           jz .next
	   cmp [eax+STREAM.flags], SND_PLAY
	   jne .next

	   mov [play_list+edx], eax
	   inc [play_count]
	   add edx, 4
.next:
	   add ebx, 4
	   loop .l1
.exit:
	   ret
endp


align 4
proc set_handler stdcall, hsrv:dword, handler_proc:dword
           locals
             handler    dd ?
             io_code    dd ?
             input      dd ?
             inp_size   dd ?
             output     dd ?
             out_size   dd ?
             val        dd ?
           endl

           mov eax, [hsrv]
           lea ecx, [handler_proc]
           xor ebx, ebx

           mov [handler], eax
           mov [io_code], DEV_CALLBACK
           mov [input], ecx
           mov [inp_size], 4
           mov [output], ebx
           mov [out_size], 0

           lea eax, [handler]
           stdcall [ServiceHandler], eax
           ret
endp

align 4
proc dev_play stdcall, hsrv:dword
           locals
             handle     dd ?
             io_code    dd ?
             input      dd ?
             inp_size   dd ?
             output     dd ?
             out_size   dd ?
             val        dd ?
           endl

           mov eax, [hsrv]
           xor ebx, ebx

           mov [handle], eax
           mov [io_code], DEV_PLAY
           mov [input], ebx
           mov [inp_size], ebx
           mov [output], ebx
           mov [out_size], ebx

           lea eax, [handle]
           stdcall [ServiceHandler], eax
           ret
endp

include 'mixer.asm'

align 16
play_list    dd 16 dup(0)
stream_list  dd 17 dup(0)

align 16
resampler_params:
     ;r_size    r_end   r_dt   resampler_func
     dd 0,0,0,0                                  ; 0  PCM_ALL
     dd 16384,          0,     0, copy_stream    ; 1  PCM_2_16_48
     dd 16384,          0,     0, m16_stereo     ; 2  PCM_1_16_48

     dd 16384, 0x08000000, 30109, resample_2     ; 3  PCM_2_16_44
     dd  8192, 0x08000000, 30109, resample_1     ; 4  PCM_1_16_44

     dd 16384, 0x08000000, 21846, resample_2     ; 5  PCM_2_16_32
     dd  8192, 0x08000000, 21846, resample_1     ; 6  PCM_1_16_32

     dd 16384, 0x08000000, 16384, resample_2     ; 7  PCM_2_16_24
     dd  8192, 0x08000000, 16384, resample_1     ; 8  PCM_1_16_24

     dd  8192, 0x04000000, 15052, resample_2     ; 9  PCM_2_16_22
     dd  4096, 0x04000000, 15052, resample_1     ;10  PCM_1_16_22

     dd  8192, 0x04000000, 10923, resample_2     ;11  PCM_2_16_16
     dd  4096, 0x04000000, 10923, resample_1     ;12  PCM_1_16_16

     dd  8192, 0x04000000,  8192, resample_2     ;13  PCM_2_16_12
     dd  4096, 0x04000000,  8192, resample_1     ;14  PCM_1_16_12

     dd  4096, 0x02000000,  7527, resample_2     ;15  PCM_2_16_11
     dd  2048, 0x02000000,  7527, resample_1     ;16  PCM_1_16_11

     dd  4096, 0x02000000,  5462, resample_2     ;17  PCM_2_16_8
     dd  2048, 0x02000000,  5462, resample_1     ;18  PCM_1_16_8

     dd 16384,          0,     0, s8_stereo      ;19  PCM_2_8_48
     dd  8192,          0,     0, m8_stereo      ;20  PCM_1_8_48

     dd  8192, 0x08000000, 30109, resample_28    ;21  PCM_2_8_44
     dd  4096, 0x08000000, 30109, resample_18    ;22  PCM_1_8_44

     dd  8192, 0x08000000, 21846, resample_28    ;23  PCM_2_8_32
     dd  4096, 0x08000000, 21846, resample_18    ;24  PCM_1_8_32

     dd  8192, 0x08000000, 16384, resample_28    ;25  PCM_2_8_24
     dd  4096, 0x08000000, 16384, resample_18    ;26  PCM_1_8_24

     dd  4096, 0x04000000, 15052, resample_28    ;27  PCM_2_8_22
     dd  2048, 0x04000000, 15052, resample_18    ;28  PCM_1_8_22

     dd  4096, 0x04000000, 10923, resample_28    ;29  PCM_2_8_16
     dd  2048, 0x04000000, 10923, resample_18    ;30  PCM_1_8_16

     dd  4096, 0x04000000,  8192, resample_28    ;31  PCM_2_8_12
     dd  2048, 0x04000000,  8192, resample_18    ;32  PCM_1_8_12

     dd  2048, 0x02000000,  7527, resample_28    ;33  PCM_2_8_11
     dd  1024, 0x02000000,  7527, resample_18    ;34  PCM_1_8_11

     dd  2048, 0x02000000,  5462, resample_28    ;35  PCM_2_8_8
     dd  1024, 0x02000000,  5462, resample_18    ;36  PCM_1_8_8


play_count   dd 0

stream_count dd 0

align 8
hSound	     dd 0

m7	     dw 0x8000,0x8000,0x8000,0x8000
mm80	     dq 0x8080808080808080
mm_mask      dq 0xFF00FF00FF00FF00

mix_input    dd 16 dup(0)

align 16
;fpu_state    db 512 dup(0)

align 16
stream	     db STREAM_SIZE*16 dup(0)
stream_map   dd 0xFFFF	      ; 16
mix_buff     dd 0
mix_buff_map dd 0

align 16
IMPORTS:

AttachIntHandler   dd szAttachIntHandler
SysMsgBoardStr	   dd szSysMsgBoardStr
PciApi		   dd szPciApi
PciRead32	   dd szPciRead32
PciRead8	   dd szPciRead8
AllocKernelSpace   dd szAllocKernelSpace
MapPage 	   dd szMapPage
KernelAlloc	   dd szKernelAlloc
KernelFree         dd szKernelFree
GetPgAddr	   dd szGetPgAddr
RegService	   dd szRegService
GetCurrentTask	   dd szGetCurrentTask
GetService	   dd szGetService
ServiceHandler	   dd szServiceHandler
FpuSave            dd szFpuSave
FpuRestore         dd szFpuRestore
		   dd 0

szKernel	    db 'KERNEL', 0
szAttachIntHandler  db 'AttachIntHandler',0
szSysMsgBoardStr    db 'SysMsgBoardStr', 0
szPciApi	    db 'PciApi', 0
szPciRead32	    db 'PciRead32', 0
szPciRead8	    db 'PciRead8', 0
szAllocKernelSpace  db 'AllocKernelSpace',0
szMapPage	    db 'MapPage',0
szRegService	    db 'RegService',0
szKernelAlloc	    db 'KernelAlloc',0
szGetPgAddr	    db 'GetPgAddr',0
szGetCurrentTask    db 'GetCurrentTask ',0
szGetService	    db 'GetService',0
szServiceHandler    db 'ServiceHandler',0
szKernelFree        db 'KernelFree',0
szFpuSave           db 'FpuSave',0
szFpuRestore        db 'FpuRestore',0


szInfinity   db 'INFINITY',0
szSound      db 'SOUND',0

if DEBUG
msgFail      db 'Sound service not found',13,10,0
msgPlay      db 'Play buffer',13,10,0
msgStop      db 'Stop',13,10,0
msgUser      db 'User callback',13,10,0
msgMem       db 'Not enough memory',13,10,0
end if
