Title  DouSound - Two channel sound routine

; Written by Ori Berger
; Phone number 972-(0)3-540-1519
;
Comment @

This program simulates 2 channel sound using the regular PC speaker. This
program was written so that is will work on any speed, not depending on
the CPU. This is doneby re-vectoring and re-speeding interrupt 8 (IRQ0).

The tune here was taken from 'The great Gianna sisters', and was composed
by Chris Huelsback (somewhere in england). The data is given in half tones,
so

C = 1
C#= 2
D = 3

etc.

Try replacing SetC1 with SetC2 because the channels sound differently,
and which channel is better as the main channel (40h,40h,43h) is depending
on the tune.

Should be assembled under MASM 5.0 or TASM 1.0, and either LINK and EXE2BIN
or TLINK /T.

@

SetC1    Macro  Rate
         Mov    Al,0b6h                          ; Set channel two speed!
         Out    43h,Al                           ; Set mode
         Mov    Ax,Rate
         Out    42h,Al
         Mov    Al,Ah
         Out    42h,Al
         Endm

SetC2    Macro  Rate
         Mov    Al,036h
         Out    43h,Al
         Mov    Ax,Rate
; This is a test - now we should try alternating frequencies!
         Shl    Ax,1
         Shl    Ax,1
         Shl    Ax,1
         Shr    Rate,1
         Shr    Rate,1
         Shr    Rate,1
         Shr    Rate,1
         Add    Ax,Rate
         Shr    Ax,1
         Shr    Ax,1
         Shr    Ax,1
; The following line should be removed if there's no need for timing.
         Mov    Cs:C2Rate,Ax
;
         Out    40h,Al
         Mov    Al,Ah
         Out    40h,Al
         Endm

Speed    Equ 4                ; speed in 1/18.2 of second per note.

Code     Segment
         Assume CS:Code,Ds:Code,Es:Code,Ss:Code
         Org    100h
Start:   Jmp    Program

; This area holds timing parameters - count, rate, etc.
; C2 rate is the current rate for channel 2 - must be used to determine time.
; Tick18 is used both as a counter and as an indicator of the time to add to
; the bios ticker value.
; C2 roll is an accumulated value - an roll over means 1/18.2 of a second.

C2Rate   Dw     ?
C2Roll   Dw     ?
Ticks18  Dw     ?

OldInt8  Dw     ?,?

Int8     Proc   Far
         Push   Ax
         In     Al,61h
         Xor    Al,2
         Out    61h,Al

; These commands should be removed if timing is not dealt through clock 0

         Mov    Ax,Cs:C2Rate
         Add    Cs:C2Roll,Ax
         Adc    Cs:Ticks18,0

; This ends the commands to be removed

         Mov    Al,20h
         Out    20h,Al
         Pop    Ax
         Iret
Int8     Endp

; FreqTabl is the Frequency table (compiled!) for lowest octave.

FreqTabl Dw     4560,4304,4062,3834,3619,3416,3224,3043,2873,2711,2559,2416
;                C    C#   D    D#   E    F    F#   G    G#   A    A#   B

Sound    Proc   Near         ; Sound CX on channel 1, DX on channel 2
         Push   Ax
         SetC1  Dx
         SetC2  Cx
         Pop    Ax
         Ret
Sound    Endp

AlterOct Db     0

MakeTAB  Proc   Near         ; Builds a table of output values - from DS:SI and DS:DX into ES:DI, -1 is terminal!
         Push   Ax
         Push   Si
         Push   Dx
         Push   Bx
         Push   Di
         Push   Cx
         Cld
         Mov    Ch,12              ; Length of octave - speed constant!
Loop001: Lodsb                     ; Get next note.
         Inc    Al                 ; is it a terminal?
         Jz     MkTABend           ; if so, EXIT!
         Dec    Al                 ; Restore orig value.
         Add    Al,Ch              ; I've discovered higher sounds better!
         Cbw                       ; If not, we have to find the FREQUENCY!
         Mov    Cl,Ah              ; AH is zero from the CBW.
Loop002: Cmp    Al,Ch              ; Now do octave shifting!
         Jb     NoAdj              ; if in range 0-11 no need to do octave adjusting.
         Sub    Al,Ch              ; Put it in range
         Inc    Cl                 ; And mark one shift.
         Jmp    Loop002            ; Repeat till in range
NoAdj:   Shl    Ax,1               ; It is a WORD table.
         Mov    Bx,Ax              ; AX cannot address
         Mov    Ax,FreqTabl[Bx]    ; Get the exact count for known octave
         Or     Cl,Cl              ; Are we using octave 1? (NoAdjAg means No Adjust Again)
         Jz     NoAdjAg            ; If so, no need to shift on 8086 (80286/Vx0 must not shift!)
         Shr    Ax,Cl              ; Now the RIGHT count is in Ax
NoAdjAg: Stosw                     ; Put it in the output buffer.
         Xor    Ah,Ah              ; Zero AH again.
         Xchg   Dx,Si              ; Now, to the second channel....
         Jmp    Loop001            ; Redo this operation (channels alternate)
MkTABend:
         Xor    Ax,Ax              ; Zero AX
         Stosw                     ; Put two terminals in output
         Stosw
         Pop    Cx
         Pop    Di
         Pop    Bx
         Pop    Dx
         Pop    Si
         Pop    Ax
         Ret
MakeTAB  Endp

Before   Proc   Near               ; BEFORE action handler.
         Push   Ax                 ; 1. Steal INT8.
         Push   Bx
         Push   Es
         Push   Dx
         Push   Ds
         Mov    Ax,3508h           ; Dos code for GetIntVec (8)
         Int    21h
         Mov    OldInt8[0],Bx      ; Offset
         Mov    OldInt8[2],Es      ; Segment
         Mov    Dx,Offset Int8     ; Point DS:DX at int 8 routine
         Mov    Ax,Cs
         Mov    Ds,Ax
         Mov    Ax,2508h
         Int    21h
         In     Al,61h             ; Now we need to gate channel 2 to the speaker
         Or     Al,1               ; GATE value
         Out    61h,Al             ; We must not disturb other bits.
         Mov    Dx,3f2h
         Mov    Al,0ch
         Out    Dx,Al
         Pop    Ds
         Pop    Dx
         Pop    Es
         Pop    Bx
         Pop    Ax
         Ret
Before   Endp

After    Proc   Near
         Push   Ax
         Push   Dx
         Push   Ds
         Mov    Ax,2508h
         Lds    Dx,Dword Ptr OldInt8
         Int    21h
         Mov    Al,36h
         Out    43h,Al
         Sub    Al,Al
         Out    40h,Al
         Out    40h,Al
         In     Al,61h
         And    Al,Not 3
         Out    61h,Al
         Pop    Ds
         Pop    Dx
         Pop    Ax
         Ret
After    Endp

Play     Proc   Near               ; Actually plays the buffer. (DS:SI)
Loop003: Lodsw
         Or     Ax,Ax
         Jz     Terminal
         Mov    Cx,Ax
         Lodsw
         Or     Ax,Ax
         Jz     Terminal
         Mov    Dx,Ax
         Call   Sound
         Mov    Ticks18,0
Loop004: Cmp    Ticks18,Speed
         Jb     Loop004
         Jmp    Loop003
Terminal:
         Ret
Play     Endp

Program  Proc   Near
         Call   Before
         Mov    Si,Offset Channel1
         Mov    Dx,Offset Channel2
         Mov    Di,Offset WorkSpace
         Call   MakeTAB
         Mov    Si,Offset WorkSpace
         Call   Play
         Call   After
         Mov    Ax,4c00h
         Int    21h
Program  Endp

Channel1 Db 22,17,24,17,25,17,24,17,22,17,27,17,25,17,24,17,22,17,24,17,25,17
         Db 27,17,29,17,27,17,25,17,24,17
         Db 22,17,24,17,25,17,24,17,22,17,27,17,25,17,24,17,21,17,22,17,24,17,25,17
         Db 27,17,29,17,25,17,24,17

         Db 22,22,24,24,25,25,24,24,22,22,27,27,25,25,24,24,22,22,24,24,25,25
         Db 27,27,29,29,27,27,25,25,24,24
         Db 22,22,24,24,25,25,24,24,22,22,27,27,25,25,24,24,21,21,22,22,24,24,25,25
         Db 27,27,29,29,25,25,24,24

         Db 24,19,26,19,27,19,26,19,24,19,29,19,27,19,26,19,24,19,26,19,27,19
         Db 29,19,31,19,29,19,27,19,26,19
         Db 24,19,26,19,27,19,26,19,24,19,29,19,27,19,26,19,23,19,24,19,26,19,27,19
         Db 29,19,31,19,27,19,26,19,-1

Channel2 Db 16 dup (10),16 dup(8),16 dup(6),16 dup (5),

         Db 8 dup (10,17),8 dup (8,15),8 dup (6,13), 8 dup (5,12)

         Db 8 dup (12,19),8 dup (10,17),8 dup (8,15), 8 dup (7,14),-1




WorkSpace Db 'WorkSpace'
Code     Ends
         End   Start


