;°±²ÛßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ²±°
;°±²Û  ASIO package				    Driver for PC-Speaker  Û²±°
;°±²Û  (C)opyright 1992 by FRIENDS software	      All rights reserved  Û²±°
;°±²ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ²±°

                .186

Driver          Segment Byte
                Assume  cs:Driver, ds:Driver
                ORG     65*256-20-20-2-2-2-26

; Driver header. Note that it isn't loaded into memory and must be ignored
; by driver itself.
DriverDescr     db      'PC-Speaker',0,0,0,0,0,0,0,0,0,0
Programmer	db	'Andrew Zabolotny',0,0,0,0
DeviceFlags     dw      1000000000000000b
MemoryNeed	dw	offset DriverEnd
StartOffset     dw      65*256                  ; Preserve ~16k of memory for
                                                ; volume table.
OffsetTable     dw      offset Init
                dw      offset SetPorts
                dw      offset SetTempo
                dw      offset StartPlay
                dw      offset SetFreq
                dw      offset SetVolume
                dw      offset SetSample
                dw      offset SetGlobalVolume
                dw      offset GetSamples
		dw	offset Dummy
		dw	offset Dummy
		dw	offset Dummy
		dw	offset Dummy

VolTable        equ     0                       ; Offset of volume table

TimerFreq       dw      04Bh                    ; Frequence of IRQ0
Samples 	dw	0,0			; Four pointers
		dw	0,0			; to samples for
		dw	0,0			; each voice.
		dw	0,0
CFreq		dw	0,0,0,0
InsFB		dw	0,0,0,0 		; Offset of first byte
						; in loop for each sample
InsLB		dw	0,0,0,0 		; 0FFFFh if no loop or offset
						; of last byte in loop.
ShutDown        dw      0,0                     ; Pointer to a array of flags
                                                ; that mean that sample are
                                                ; finished.
GVolume         db      64                      ; Global volume
Temp		dw	0
DecTemp 	dw	0
;Û Offsets of variables in self-modifying code Û
;Û     Segment of this variables is CS:        Û
FreqOfs 	dw	offset Freq1,offset Freq2
		dw	offset Freq3,offset Freq4
InsEOfs 	dw	offset InsE1,offset InsE2
		dw	offset InsE3,offset InsE4
HWordOfs	dw	offset HiWord1,offset HiWord2
		dw	offset HiWord3,offset HiWord4
VolumeOfs	dw	offset Volume1,offset Volume2
		dw	offset Volume3,offset Volume4

Init            proc    far
                pusha
                push    es
                mov     word ptr cs:ShutDown,bx ; Save pointer to array of
                mov     word ptr cs:ShutDown+2,es; flags...
                call    InitTable
                clc                             ; Speaker are always present
                pop     es
                popa
                ret
Init		endp

SetPorts        proc    far
                retf                            ; Dummy entry for Speaker
SetPorts	endp

SetTempo        proc    far
                mov     byte ptr cs:TimerFreq,al
                mov     cs:Temp,cx
                mov     cs:DecTemp,cx
                retf
SetTempo	endp

StartPlay       proc    far
                mov     cs:DecTemp,1
                mov     word ptr cs:Handler,dx
                mov     word ptr cs:Handler+2,es
		call	TimerInit
                retf
StartPlay	endp

SetFreq         proc    far
                push    bx
                push    di
                xor     bh,bh
		shl	bx,1
                mov     di,cs:FreqOfs[bx]
                mov     cs:[di],cx
                mov     di,cs:HWordOfs[bx]
                mov     cs:[di],dx
                pop     di
                pop     bx
                retf
SetFreq 	endp

SetVolume       proc    far
                push    ax
                push    bx
                xor     bh,bh
		shl	bx,1
                mov     al,cs:GVolume
		mul	dl
		shr	ax,6
                cmp     al,40h
                jbe     @SV1
                mov     al,040h
@SV1:           mov     bx,cs:VolumeOfs[bx]
                mov     cs:[bx],al
@SVret:         pop     bx
                pop     ax
                retf
SetVolume	endp

SetSample       proc    far
                push    ax
                push    bx
                push    si
                push    ds
                mov     ax,cs                   ; Better don't touch
                mov     ds,ax                   ; this sequence because
                xor     bh,bh                   ; on some low-speed
                shl     bx,1                    ; machines can appear 
                mov     si,HWordOfs[bx]         ; troubles
                mov     word ptr [si],0
                mov     si,FreqOfs[bx]
                mov     word ptr [si],0
                shl     bx,1
                mov     Samples[bx],di
                mov     Samples[bx+2],es
                shr     bx,1
                mov     InsLB[bx],cx
		mov	InsFB[bx],dx
                cmp     dx,0FFFFh
                jne     @@O1
                mov     InsLB[bx],dx
@@O1:           mov     si,InsEOfs[bx]
                mov     [si],cx
                pop     ds
                pop     si
                pop     bx
                pop     ax
                retf
SetSample	endp

SetGlobalVolume proc    far
                mov     cs:GVolume,al
                retf
SetGlobalVolume endp

GetSamples      proc
                push    cx
                push    di
                push    si
                push    ds
                push    cs
                pop     ds
                cld
                mov     si,offset Samples
                mov     cx,4*4
                rep     movsb
                pop     ds
                pop     si
                pop     di
                pop     cx
                retf
GetSamples      endp

Dummy		proc
		retf
Dummy           endp

Int08Entry      proc    far
@ResetV1:	cmp	word ptr InsLB[0],0FFFFh
                je      @C1
                mov     bx,word ptr InsFB[0]
                mov     word ptr Samples[0],bx
                jmp     @L1
@C1:            xor     bx,bx
                mov     byte ptr Volume1,bl
                mov     word ptr Samples[0],bx
                les     bx,dword ptr ShutDown
                mov     byte ptr es:[bx],0
		jmp	@L1

@ResetV2:	cmp	word ptr InsLB[2],0FFFFh
                je      @C2
                mov     bx,word ptr InsFB[2]
                mov     word ptr Samples[4],bx
                jmp     @L2
@C2:            xor     bx,bx
                mov     byte ptr Volume2,bl
                mov     word ptr Samples[4],bx
                les     bx,dword ptr ShutDown
                mov     byte ptr es:[bx+1],0
		jmp	@L2

IntProc:	push	ax
		push	bx
		push	ds
		push	es
                mov     ax,cs
		mov	ds,ax
;	      Û Voice 1 Û
		add	word ptr CFreq[0],0DEADh
Freq1		equ	word ptr $-2
		adc	word ptr Samples[0],0DEADh
HiWord1 	equ	word ptr $-2
		les	bx,dword ptr Samples[0]
		cmp	bx,0DEADh
InsE1		equ	word ptr $-2
		jae	@ResetV1
@L1:		mov	bl,byte ptr es:[bx]
                mov     bh,00h
Volume1 	equ	byte ptr $-1
                mov     al,[bx]

;	      Û Voice 2 Û
		add	word ptr CFreq[2],0DEADh
Freq2		equ	word ptr $-2
		adc	word ptr Samples[4],0DEADh
HiWord2 	equ	word ptr $-2
		les	bx,dword ptr Samples[4]
		cmp	bx,0DEADh
InsE2		equ	word ptr $-2
		jae	@ResetV2
@L2:		mov	bl,byte ptr es:[bx]
                mov     bh,00h
Volume2 	equ	byte ptr $-1
                add     al,[bx]

;	      Û Voice 3 Û
		add	word ptr CFreq[4],0DEADh
Freq3		equ	word ptr $-2
		adc	word ptr Samples[8],0DEADh
HiWord3 	equ	word ptr $-2
		les	bx,dword ptr Samples[8]
		cmp	bx,0DEADh
InsE3		equ	word ptr $-2
		jae	@ResetV3
@L3:		mov	bl,byte ptr es:[bx]
                mov     bh,00h
Volume3 	equ	byte ptr $-1
                add     al,[bx]

;	      Û Voice 4 Û
		add	word ptr CFreq[6],0DEADh
Freq4		equ	word ptr $-2
		adc	word ptr Samples[12],0DEADh
HiWord4 	equ	word ptr $-2
		les	bx,dword ptr Samples[12]
		cmp	bx,0DEADh
InsE4		equ	word ptr $-2
		jae	@ResetV4
@L4:		mov	bl,byte ptr es:[bx]
                mov     bh,00h
Volume4 	equ	byte ptr $-1
                add     al,[bx]

		mov	bx,offset XLatTable
                xlat
		out	042h,al
		dec	word ptr DecTemp
                jz      @DoHandler
@Play:		pop	es
		pop	ds
		pop	bx
		mov	al,020h
                out     020h,al
                pop     ax
                iret

@ResetV3:	cmp	word ptr InsLB[4],0FFFFh
                je      @C3
                mov     bx,word ptr InsFB[4]
                mov     word ptr Samples[8],bx
                jmp     @L3
@C3:            xor     bx,bx
                mov     byte ptr Volume3,bl
                mov     word ptr Samples[8],bx
                les     bx,dword ptr ShutDown
                mov     byte ptr es:[bx+2],0
                jmp     @L3

@ResetV4:       cmp     word ptr InsLB[6],0FFFFh
                je      @C4
                mov     bx,word ptr InsFB[6]
                mov     word ptr Samples[12],bx
                jmp     @L4
@C4:            xor     bx,bx
                mov     byte ptr Volume4,bl
                mov     word ptr Samples[12],bx
                les     bx,dword ptr ShutDown
                mov     byte ptr es:[bx+3],0
                jmp     @L4

@DoHandler:     mov     ax,Temp
                mov     DecTemp,ax
                db      09Ah
Handler         dw      0,0
                jmp     @Play
Int08Entry	endp

TimerInit	proc	near
                push    es
		cli
		mov	al,0B0h
		out	43h,al
		mov	al,1
		out	42h,al
		dec	al
		out	42h,al
		mov	al,036h
		out	43h,al
                mov     al,byte ptr cs:TimerFreq
		out	40h,al
		mov	al,0
		out	40h,al
		in	al,061h
		or	al,3
		out	61h,al
		mov	al,090h
		out	43h,al
                xor     ax,ax
                mov     es,ax
                mov     word ptr es:[8*4],offset IntProc
                mov     word ptr es:[8*4+2],cs
                pop     es
                sti
		retn
TimerInit	endp

InitTable	proc	near
                xor     di,di
		cld
                push    cs
		pop	es
		mov	ch,0
@M1:		mov	cl,0
@M2:		mov	al,cl
		cmp	al,0
		jge	@M3
		cmp	al,-125
		jl	@M3
		sub	al,3
@M3:		imul	ch
		mov	al,ah
		stosb
		inc	cl
		jne	@M2
		inc	ch
		cmp	ch,65
		jb	@M1
		retn
InitTable	endp

XLatTable	db	35,36,38,39,40,41,43,44,45,46,48,49,50,51,52,53
		db	54,55,57,58,58,59,60,61,62,63,64,64,65,66,66,67
		db	67,67,67,67,67,68,68,68,68,68,68,68,68,68,68,68
		db	68,68,69,69,69,69,69,69,69,69,69,69,69,70,70,70
		db	70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71
		db	71,71,71,71,71,72,72,72,72,72,72,72,72,72,72,72
		db	72,72,73,73,73,73,73,73,73,73,73,73,73,74,74,74
		db	74,74,74,74,74,74,74,74,74,74,75,75,75,75,75,75
		db	 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
		db	 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4
		db	 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5
		db	 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
		db	 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8
		db	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9
		db	 9, 9,10,10,10,10,11,11,12,12,13,14,14,15,16,17
		db	17,18,19,20,21,22,23,24,26,27,28,29,30,31,33,34
DriverEnd	label	near
Driver          ends
		end
