;ﾚﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄｿ
;ｳ  FILENAME: PPS.ASM	Version 1.10					     ｳ
;ｳ									     ｳ
;ｳ  DATE    : July 27, 1992						     ｳ
;ｳ                                                                           ｳ
;ｳ  Ideal Mode -- Turbo Assembler v2.01 and above			     ｳ
;ｳ                                                                           ｳ
;ｳ  DESCRIPTION: This file includes all routines for playing music modules.  ｳ
;ｳ		 Many of the routines, before my HD crash, were written by   ｳ
;ｳ		 a friend, Mark Anderson.  I appreciate those original	     ｳ
;ｳ		 routines, and thank him for it, but I had to rewrite	     ｳ
;ｳ		 everything from scratch.  He spent many hours working on    ｳ
;ｳ		 routines for SuperProPlay, especially when I didn't have    ｳ
;ｳ		 a computer that could play modules decently (8mhz).	     ｳ
;ｳ		 Thanks Mark!						     ｳ
;ﾀﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾙ

	ideal				; Use TASM's Ideal mode.
	model	Small,Pascal		; Define the memory model.
	P286				; Set up for 286 instructions.
	jumps				; Have TASM automatically resolve out-
					; of-range jumps.

	include "pps.inc"

dataseg

ifdef OSC

OscData 		dw	320 dup (0)
CurOsc			dw	1

endif

Fatal                   db      0

MusicPlayerInstalled	db	0	; Music handler installed...or not?

;-------- Special MOD Header declarations
SequenceOffset		dw	0	; Where do the Sequences begin?
HeaderSize		dw	0	; How big is the header?
NumberInstruments	dw	0	; Num Ins in module.

TypeOfSample            db      0       ; 0 for <64k, 1 for >64k
					; Set when loading module; Not
					; implemented in this version.

SoundSource		db	0	; Corresponds to position of Sound
                                        ; Source in SoundSources.
label   SoundSources    SoundSrcStruc
	SoundSrcStruc	<"Reserved",0,  0h,0>
	SoundSrcStruc	<"SBlaster",1,  0h,0>
	SoundSrcStruc	<"DAC-LPT1",2,  0h,0>
	SoundSrcStruc	<"DAC-LPT2",2,  0h,0>
	SoundSrcStruc	<"DAC-LPT3",2,  0h,0>
	SoundSrcStruc	<"DAC-LPT4",2,  0h,0>

Channel1  MS      <0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
Channel2  MS      <0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
Channel3  MS      <0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
Channel4  MS      <0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>

MaxVolume	dw	0	; This is the max volume setting
PlayStatus	db	0	; 0 --> Play regular (stop at end).
				; 1 --> Play forever.
NumBlocks       db      0

ModuleLoaded	db	0	; 0 - No, 1 - Yes
MStatus 	db	1	; 0 - Running, 1 - Off

Header          db      1084 dup (0)
InsLoc          dw      31 dup (0)
PatternLoc	dw	64 dup (0)

SampleRate	dw	0	; Generally 15909.
MainFreq        dd      0
LSBCounter      dw      0
IntMaster	dw	0	; Value that is restored into IntCountDown.
IntCountDown	dw	0	; Countdown value.  When it hits zero,
				; UpdateChannels is called.
IntSet		db	0	; 0 if no interrupt, 1 if running.
TypeOfInt	db	0	; 1 for music, 2 for sample.
;Int08		 dd	 0	 ; Storage for the timer interrupt.
IntMask         db      0
FreqMult	dw	0
MainAddr	dw	0	; Address to play out of.
PlayerInterrupt dw	0	; Holding place for the interrupt.
TimeValue	dw	0	; This, divided by 50, is the number of seconds
				; that the music has played.

; ﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿﾚｿ
;ﾄﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀﾙﾀ
; Seg                           Stores the segment of the current sample.  If
;                               the segment is zero, then there is no sample
;                               begin played.
; Offset                        The current offset of the sample being played
;                               in the segment specified.
; MaxRep                        The offset of the place in the sample to
;				repeat from (i.e. the end of the sample).
; Freq                          The frequency to be played out the output
;                               device.  It is actually a playback period,
;                               the upper byte determining the number of
;                               bytes to be added to the sample offset each
;                               interrupt, and the lower byte to be added
;                               over and over to generate carry flags to be
;                               added to additional offsets.
; Vol1                          The actual volume to multiply the frequency
;                               by.
; Repeat                        The place to repeat from when the end of the
;                               loop is found.
; RepLen                        The number of bytes from Repeat to go to.
;                               When the end of this is found, it goes back
;                               to Repeat.
; OFlow                         The overflow variable used to generate the
;                               carry flags.

;ﾄChannel 1 Stuffﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄ
Channel1Seg     dw      0
Channel1Ofs     dw      0
Channel1Repeat  dw      0
Channel1RepLen  dw      0
Channel1Freq	dw	0
Channel1MaxRep	dw	0
Channel1OFlow   dw      0
Channel1Vol1	db	0
Channel1Ins     db      0

;ﾄChannel 2 Stuffﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄ
Channel2Seg	dw	0
Channel2Ofs	dw	0
Channel2Repeat	dw	0
Channel2RepLen	dw	0
Channel2Freq	dw	0
Channel2MaxRep	dw	0
Channel2OFlow   dw      0
Channel2Vol1	db	0
Channel2Ins     db      0

;ﾄChannel 3 Stuffﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄ
Channel3Seg	dw	0
Channel3Ofs	dw	0
Channel3Repeat	dw	0
Channel3RepLen	dw	0
Channel3Freq	dw	0
Channel3MaxRep	dw	0
Channel3OFlow   dw      0
Channel3Vol1	db	0
Channel3Ins     db      0

;ﾄChannel 4 Stuffﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄ
Channel4Seg	dw	0
Channel4Ofs	dw	0
Channel4Repeat	dw	0
Channel4RepLen	dw	0
Channel4Freq	dw	0
Channel4MaxRep	dw	0
Channel4OFlow   dw      0
Channel4Vol1	db	0
Channel4Ins     db      0

codeseg

Int08           dd      0       ; Storage for the timer interrupt.

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_PlayMusic                                                 ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None							     ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : Carry Set - Error                                            ｳ
;ｳ              Carry Clear - Everything OK, music should be playing         ｳ
;ｳ                                                                           ｳ
;ｳ DESCRIPTION: This procedure sets up all pointers, interrupts, and the     ｳ
;ｳ              likes for the current module in memory.  Error checking is   ｳ
;ｳ              done to make sure a module is loaded, the music is already   ｳ
;ｳ              playing, or whatever else may conflict.                      ｳ
;ｳ									     ｳ
;ｳ		*** Written by Joshua C. Jensen 			     ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_PlayMusic
	uses	ds,es
	mov	ax,@data		; Make sure the data segment is set
        mov     ds,ax                   ; to the main data segment in which
                                        ; all of our variables are stationed.

        ; See if the Music Player has been initialized yet.
        cmp     [Byte MusicPlayerInstalled],1
	je	@@MusicIsInstalled	; It has, so continue.

@@Abort:
        stc
	ret

@@MusicIsInstalled:
        cmp     [Byte ModuleLoaded],1   ; Do we have a module in memory?
        jnz     @@Abort                 ; Nope, abort out of here.

        call    sd_ShutDownMusicInt     ; Before doing anything else, shut
                                        ; down any music that is playing.
        call    sd_ReturnInfo           ; Get address information
	mov	dx,offset sd_MusicInterrupt
        mov     [PlayerInterrupt],dx    ; Store the interrupt for later.

        ; ﾄﾄﾄﾄ This is where all of the extra info for the SoundBlaster,
	; ﾄﾄﾄﾄ SoundBlaster Pro, Adlib Gold, etc., is set up.  AL on return
        ; ﾄﾄﾄﾄ from sd_ReturnInfo will contain a number from the structure
        ; ﾄﾄﾄﾄ so we know which card is installed and can set up
        ; ﾄﾄﾄﾄ appropriately.
	call	sd_SetMainAddr
@@SetupForMusic:
        mov     [Byte TypeOfInt],1      ; Specify that the interrupt to be
					; set up is the music interrupt.
        call    sd_SetupForMusic
	clc				; Clear the carry.
        ret                             ; Exit.
endp    sd_PlayMusic

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_SetupForMusic					     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None							     ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ                                                                           ｳ
;ｳ DESCRIPTION: This procedure redefines internal interrupt variables to     ｳ
;ｳ		run at the new sample rate and sets of the interrupt.	     ｳ
;ｳ									     ｳ
;ｳ		*** Written by Joshua C. Jensen.			     ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_SetupForMusic
        mov     [Byte IntSet],1         ; Specify that the interrupt is setup.

	push	es
	mov	ax,3508h		; Get information for interrupt 8,
                                        ; the timer interrupt, which is the
                                        ; main interrupt that we wedge into.
        int     21h                     ; Call DOS's Get Interrupt Vector.
	mov	[Word cs:Int08],bx	   ; Store the returned interrupt so we
	mov	[Word cs:Int08+2],es	   ; can restore it when done.
	pop	es

        in      al,21h                  ; Direct input from the interrupt
                                        ; mask register.
        mov     [IntMask],al            ; Store the interrupt mask so we can
                                        ; restore it later.

        cli                             ; Clear the interrupt flag so the
                                        ; system recognizes no maskable
                                        ; interrupts.
        mov     al,0FFh                 ; Disable every maskable interrupt
	out	21h,al			; while setting up the new one.
        sti                             ; Set the interrupt flag so the
                                        ; system recognizes maskable
                                        ; interrupts.

        push    ds                      ; Since we change the data segment,
                                        ; save it.
        mov     dx,[PlayerInterrupt]    ; Get the Player Interrupt.
        push    cs                      ; Push the code segment into the
        pop     ds                      ; data segment.
        mov     ax,2508h                ; Set information for interrupt 8.
        int     21h                     ; Call DOS's Set Interrupt Vector.
        pop     ds                      ; Retrieve DS.

        mov     al,00110100b            ; Channel 0, Read/write LSB followed
        out     43h,al                  ; by MSB, Operation mode 2, binary
        mov     al,[Byte LSBCounter]
        out     40h,al
	mov	al,[Byte LSBCounter+1]	; Send counter MSB
	out	40h,al

	mov	al,10010000b		; Channel 2, Read/write only MSB,
        out     43h,al                  ; Operation mode 0, Binary
        in      al,61h                  ; Turn speaker on, speaker control
	or	al,00000011b		; through 8253 timer
        out     61h,al

	cli				; Enable old interrupts
        mov     al,[IntMask]
        out     21h,al
        sti

        mov     [Byte MStatus],0        ; Set the music status to playing.
	ret
endp    sd_SetupForMusic

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_CloseAllMusic					     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None							     ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : Carry Set - Fatal Error...abort program.		     ｳ
;ｳ		Carry Clear - Everything OK.				     ｳ
;ｳ                                                                           ｳ
;ｳ DESCRIPTION: Frees up any and all pointers set up when the module was     ｳ
;ｳ		loaded.  Quick and easy way to kill EVERYTHING! 	     ｳ
;ｳ									     ｳ
;ｳ		*** Written by Joshua C. Jensen.			     ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_CloseAllMusic
	uses	ds
	mov	ax,@data
	mov	ds,ax
	cmp	[Byte IntSet],1 	; Do we have the music playing?
	jnz	@@FreeInstruments
	call	sd_ShutDownMusicInt	; Yes, shut it down.

@@FreeInstruments:
	mov	si,offset InsLoc
	mov	cx,31			; 31 Instruments to choose from.
@@FreeInsLoop:
	lodsw				; Pick up a segment.
	or	ax,ax			; If there is a zero there, then there
	jz	@@BotFreeInsLoop	; is nothing to free.
	mov	es,ax			; Otherwise, free it.
	mov	ax,4900h		; Deallocate the memory.
	int	21h
	jnb	@@BotFreeInsLoop	; No problem with allocation.
        mov     [Byte Fatal],1
	stc				; There was a fatal error.
	ret				
@@BotFreeInsLoop:
	loop	@@FreeInsLoop

@@FreePatterns:
	mov	si,offset PatternLoc
	mov	cx,64			; 64 different patterns
@@FreePatLoop:
	lodsw				; Pick up a segment.
	or	ax,ax			; If there is a zero there, then there
	jz	@@BotFreePatLoop	; is nothing to free.
	mov	es,ax			; Otherwise, free it.
	mov	ax,4900h		; Deallocate the memory.
	int	21h
	jnb	@@BotFreePatLoop	; No problem with allocation.
        mov     [Byte Fatal],1
	stc				; There was a fatal error.
	ret				
@@BotFreePatLoop:
	loop	@@FreePatLoop
	ret
endp    sd_CloseAllMusic

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_ShutDownMusicInt					     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None							     ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ                                                                           ｳ
;ｳ DESCRIPTION: Stops the music from playing.  Might be called a "Pause".    ｳ
;ｳ		A call to sd_PlayMusic, sd_PlayIns, or sd_SetupForMusic      ｳ
;ｳ		will start it playing again, right where it left off.	     ｳ
;ｳ									     ｳ
;ｳ		*** Written by Joshua C. Jensen.			     ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_ShutDownMusicInt
	uses	ds,dx,ax
	mov	ax,@data
	mov	ds,ax
	cmp	[Byte MusicPlayerInstalled],1
	jnz	@@End
	cmp	[Byte IntSet],1 	; Is the interrupt set.
	jnz	@@End
	mov	[Byte IntSet],0
	cli
	mov	al,0FFh
	out	21h,al
	sti
	in	al,61h
	and	al,0FCh
	out	61h,al
	mov	al,00110100b		; Channel 0, Read/write LSB followed
	out	43h,al			; by MSB, Operation mode 2, binary
	xor	ax,ax
	out	40h,al
	out	40h,al
	mov	al,10110110b		; Channel 2, Read/write LSB followed
	out	43h,al			; by MSB, Operation mode 3, binary
	mov	ax,0533h
	out	42h,al
	mov	al,ah
	out	42h,al

	push	ds			; Reset the stored interrupt vector.
	mov	dx,[Word cs:Int08]
	mov	ds,[Word cs:Int08+2]
	mov	ax,2508h
	int	21h
	pop	ds

	cli
	mov	al,[Byte IntMask]
	out	21h,al		;DIRECT_OUTPUT
	sti
	sti
@@End:
	mov	[Byte MStatus],1
	ret
endp	sd_ShutDownMusicInt

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_SetFreq						     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None							     ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ                                                                           ｳ
;ｳ DESCRIPTION: Sets standard frequency and LSBCounter information.	     ｳ
;ｳ									     ｳ
;ｳ		*** Written by Joshua C. Jensen.			     ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_SetFreq
	uses	ds
	mov	ax,@data
	mov	ds,ax
	xor	dx,dx			; Clear out DX.
        mov     ax,[SampleRate]         ; Load the sampling rate into AX.
        mov     bx,50                   ; Module timing on the Amiga is
        div     bx                      ; determined by the refresh rate of
                                        ; the screen.
        mov     [IntMaster],ax          ; Set all of these values the same.
        mov     [IntCountDown],ax       ; section below.

        mov     [Word FreqMult],300h    ; Set up standard information.
        mov     dx,0012h                ; Combined DX:AX = 1,193,180 which is
        mov     ax,34DCh                ; the 8253's chip operating frequency.
        div     [Word SampleRate]       ; Divide it by the sampling rate to
                                        ; determine the LSB of the new
                                        ; interrupt counter.
        mov     [LSBCounter],ax         ; Store it for when we set up the
                                        ; interrupt.
        mov     bx,[FreqMult]           ; Now, multiply by FreqMult to get the
        mul     [Word FreqMult]         ; value to divide the period by to get
        mov     [Word MainFreq],ax      ; a "frequency" to skip through the
        mov     [Word MainFreq+2],dx    ; sample with.  Essentially, the 8253's
                                        ; new "operating frequency".
	ret
endp    sd_SetFreq

;ﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄｿ
;ﾚﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾙ
;ﾀﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄｿ
;ﾚﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾙ
;ﾃﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄｿ
;ｳ ***********************  Playback Interrupts  ****************************  ｳ
;ﾀﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄｴ
;ﾚﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾙ
;ﾀﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄｿ
;ﾚﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾙ
;ﾀﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄﾄ
;ｳ The below code was written by Joshua C. Jensen.  These routines are a     ｳ
;ｳ copy of the instrument playback routines with three extra voices.	     ｳ
macro   sd_MonoLess64
	xor	cx,cx
	xor	dx,dx

@@Channel1:				    ;; App. cycles on a 286 ﾄﾄﾄﾄｿ
	cmp	[Word Channel1Seg],0	;;<--Is the channel even on?  ;;6
	jz	@@Channel2		;;<--If not jump to channel 2 ;;7
	mov	es,[Channel1Seg]				      ;;17

	mov	bx,[Channel1Ofs]				      ;;5
	cmp	bx,[Channel1MaxRep]				      ;;6
	jb	@@Channel1_1					      ;;7

	cmp	[Word Channel1RepLen],0 			      ;;6
	jz	@@Channel2					      ;;7

	mov	bx,[Channel1Repeat]				      ;;5
	mov	ax,[Channel1RepLen]				      ;;5
	mov	[Channel1MaxRep],ax				      ;;5
@@Channel1_1:
	mov	ax,[Channel1Freq]				      ;;5
	add	[Byte Channel1OFlow],al 			      ;;7
	mov	cl,ah						      ;;2
	adc	bx,cx						      ;;2
	mov	[Channel1Ofs],bx				      ;;5
	mov	al,[es:bx]					      ;;5
	imul	[Byte Channel1Vol1]				      ;;13
	add	dl,ah						      ;;2

@@Channel2:
	cmp	[Word Channel2Seg],0
	jz	@@Channel3
	mov	es,[Channel2Seg]

	mov	bx,[Channel2Ofs]
	cmp	bx,[Channel2MaxRep]
	jb	@@Channel2_1

	cmp	[Word Channel2RepLen],0
	jz	@@Channel3

	mov	bx,[Channel2Repeat]
	mov	ax,[Channel2RepLen]
	mov	[Channel2MaxRep],ax
@@Channel2_1:
	mov	ax,[Channel2Freq]
	add	[Byte Channel2OFlow],al
	mov	cl,ah
        adc     bx,cx
	mov	[Channel2Ofs],bx
        mov     al,[es:bx]
	imul	[Byte Channel2Vol1]
	add	dh,ah

@@Channel3:
	cmp	[Word Channel3Seg],0	;;<--Is the channel even on?
	jz	@@Channel4		;;<--If not jump to channel 2
	mov	es,[Channel3Seg]

	mov	bx,[Channel3Ofs]
	cmp	bx,[Channel3MaxRep]
	jb	@@Channel3_1

	cmp	[Word Channel3RepLen],0
	jz	@@Channel4

	mov	bx,[Channel3Repeat]
	mov	ax,[Channel3RepLen]
	mov	[Channel3MaxRep],ax
@@Channel3_1:
	mov	ax,[Channel3Freq]
	add	[Byte Channel3OFlow],al
        mov     cl,ah
        adc     bx,cx
	mov	[Channel3Ofs],bx
        mov     al,[es:bx]
	imul	[Byte Channel3Vol1]
	add	dh,ah

@@Channel4:
	cmp	[Word Channel4Seg],0	;;<--Is the channel even on?
	jz	@@ChannelDone		;;<--If not jump to channel 2
	mov	es,[Channel4Seg]

	mov	bx,[Channel4Ofs]
	cmp	bx,[Channel4MaxRep]
	jb	@@Channel4_1

	cmp	[Word Channel4RepLen],0
	jz	@@ChannelDone

	mov	bx,[Channel4Repeat]
	mov	ax,[Channel4RepLen]
	mov	[Channel4MaxRep],ax
@@Channel4_1:
	mov	ax,[Channel4Freq]
	add	[Byte Channel4OFlow],al
        mov     cl,ah
        adc     bx,cx
	mov	[Channel4Ofs],bx
        mov     al,[es:bx]
	imul	[Byte Channel4Vol1]
        add     dl,ah
@@ChannelDone:
endm    sd_MonoLess64

;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
proc    sd_MusicInterrupt near
	pusha
	push	ds es
        mov     ax,@data
	mov	ds,ax
	cmp	[Byte SoundSource],1
	jnz	@@GetValueToOutput
	mov	dx,[MainAddr]
@@0:	in	al,dx
	rol	al,1
	jb	@@0
	mov	al,10h
        out     dx,al
@@GetValueToOutput:
	sd_MonoLess64
	mov	bx,dx
        mov     dx,[MainAddr]
        cmp     [Byte SoundSource],1
	jz	@@FinishTheSB
	cmp	[Byte SoundSource],2
	jge	@@FinishTheDAC
@@FinishTheSB:
	in	al,dx				; tell us to send.
	rol	al,1
	jb	@@FinishTheSB
	mov	ax,bx
	add	al,ah
	xor	al,10000000b	;<-remove the sign
        out     dx,al
        jmp     FinishTheInterrupt
@@FinishTheDAC:
	mov	ax,bx
	add	al,ah
	xor	al,10000000b	;<-remove the sign
	out	dx,al
	jmp	FinishTheInterrupt
FinishTheInterrupt:
ifdef	OSC

	mov	dx,0a000h
        mov     es,dx
        mov     si,[CurOsc]
        shr     al,1
	mov	di,[si+offset OscData]
        mov     [Byte es:di],0
        xor     ah,ah
        inc     al
        mov     cx,320
        mul     cx
        mov     di,ax
        mov     cx,si
        shr     cx,1
        add     di,cx
        mov     [Byte es:di],15
	mov	[si+offset OscData],di
        add     si,2
        cmp     si,639                ;Are we at end of the oscilloscope display?
        jl      Skip
        xor     si,si
        inc     si
Skip:
        mov     [CurOsc],si

endif
	mov	al,20h
        out     20h,al
        dec     [Word IntCountDown]
	jz	HandleUpdateModule
FinishTheInterrupt2:
        pop     es ds
        popa
        sti
	iret
HandleUpdateModule:
	mov	ax,[IntMaster]
	mov	[IntCountDown],ax
	inc	[Word TimeValue]
	call	sd_UpdateChannels
        pop     es ds
        popa
	sti
	iret
endp    sd_MusicInterrupt

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_SetMainAddr						     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None                                                         ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ									     ｳ
;ｳ DESCRIPTION: Gets the output address for the current device and sets it.  ｳ
;ｳ									     ｳ
;ｳ              *** Written by Joshua C. Jensen                              ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_SetMainAddr
	mov	ax,[bx+SoundSrcStruc.AddressLeft]
	mov	[MainAddr],ax
	ret
endp    sd_SetMainAddr

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_ReturnInfo						     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None                                                         ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : AL - Type of device.					     ｳ
;ｳ									     ｳ
;ｳ DESCRIPTION: Returns various pieces of information about the current      ｳ
;ｳ		playing device. 					     ｳ
;ｳ									     ｳ
;ｳ		*** Written by Joshua C. Jensen.			     ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc	sd_ReturnInfo
	uses	ds
	mov	ax,@data
	mov	ds,ax
	xor	ax,ax				; Figure out addresses, etc.
	mov	al,[SoundSource]
	mov	dx,size SoundSrcStruc
	mul	dx
	add	ax,offset SoundSources
	mov	bx,ax
	mov	al,[bx+SoundSrcStruc.Special]
	ret
endp    sd_ReturnInfo

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_InitSound						     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : BX - Force value - 255 = No force, 0-254 = automatically     ｳ
;ｳ		     use this sound source regardless of what was found.     ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ									     ｳ
;ｳ DESCRIPTION: Initializes the module player.	This MUST be called before   ｳ
;ｳ		anything else!	Scans for Soundblaster, DACs, etc.	     ｳ
;ｳ									     ｳ
;ｳ              *** Written by Joshua C. Jensen                              ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_InitSound
	uses	ds
	mov	ax,@data
	mov	ds,ax
	push	bx
	call	sd_SetMaxVolume
	mov	[Word MaxVolume],255
	mov	[Byte PlayStatus],0
	mov	[Word SampleRate],15909 	; Standard sampling rate.
        mov     [Byte SoundSource],0            ; Start with speaker.
	mov	[Byte MusicPlayerInstalled],1
	call	sd_DetectDACs
	call	sd_DetectSB
	pop	bx
	cmp	bx,255
	jz	@@Done
	mov	[Byte SoundSource],bl
@@Done:
        ret
endp    sd_InitSound

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_Set15Ins and sd_Set31Ins				     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None                                                         ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ									     ｳ
;ｳ DESCRIPTION: Sets up pointers to positions in the header based on the     ｳ
;ｳ		number of instruments.					     ｳ
;ｳ									     ｳ
;ｳ              *** Written by Joshua C. Jensen                              ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_Set15Ins
	uses	ds
	mov	ax,@data
	mov	ds,ax
	mov	[Word NumberInstruments],15
	mov	[Word SequenceOffset],01D8h
	mov	[Word HeaderSize],0258h
	ret
endp	sd_Set15Ins

proc	sd_Set31Ins
	uses	ds
	mov	ax,@data
        mov     ds,ax
        mov     [Word NumberInstruments],31
	mov	[Word SequenceOffset],03B8h
	mov	[Word HeaderSize],043Ch
	ret
endp	sd_Set31Ins

;ﾕﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｸ
;ｳ NAME       : sd_SetMaxVolume 					     ｳ
;ｳ                                                                           ｳ
;ｳ ENTRY      : None                                                         ｳ
;ｳ                                                                           ｳ
;ｳ RETURN     : None							     ｳ
;ｳ									     ｳ
;ｳ DESCRIPTION: Sets the four channels to the Maximum volume.		     ｳ
;ｳ									     ｳ
;ｳ              *** Written by Joshua C. Jensen                              ｳ
;ﾔﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍﾍｾ
proc    sd_SetMaxVolume
	uses	ds
	mov	ax,@data
	mov	ds,ax
	mov	al,[Byte MaxVolume]
	mov	[offset Channel1+(offset (MS).MasterVolume)],al
	mov	[offset Channel2+(offset (MS).MasterVolume)],al
	mov	[offset Channel3+(offset (MS).MasterVolume)],al
	mov	[offset Channel4+(offset (MS).MasterVolume)],al
	ret
endp	sd_SetMaxVolume

end

