home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sound Sensations!
/
sound_sensations.iso
/
voice
/
dvpt20
/
vpmod.asm
< prev
next >
Wrap
Assembly Source File
|
1991-12-12
|
16KB
|
763 lines
page 58,132
;****************************************************************
;* *
;* Digitized Voice Programmer's Toolkit *
;* ------------------------------------ *
;* *
;* Sound Playback Primitives *
;* *
;* Copyright (c) 1991, Farpoint Software *
;* *
;****************************************************************
.8086
.MODEL LARGE
;**********************************************************************
;* *
;* Equates *
;* *
;**********************************************************************
tccount equ 72 ;reload count to produce 16572 Hz
countmax_18hz equ 910 ;ratio of new timer rate to standard rate
tcaddrc equ 43h ;timer/counter control register address
tcaddrd0 equ 40h ;timer/counter data register zero (sys clock)
tcaddrd2 equ 42h ;timer/counter data register two (spkr)
tcmode0a equ 34h ;two-byte mode for system clock timer
tcmode0b equ 24h ;high-byte mode for system clock timer
tcmode2a equ 0B0h ;mode for speaker timer (for initialization)
tclatchcmd0 equ 00h ;latch command for reading timer 0
tclatchcmd2 equ 80h ;latch command for reading timer 2
ppiaddr equ 61h ;programmable peripheral interface address
kbdonly_mask equ 0FDh ;mask config to allow only keyboard interrupt
intmaskaddr equ 21h ;address of the interrupt mask register
kbdint_number equ 9 ;vector number of the keyboard hardware int
;CPU speed calibration initialization parameters
init_delayctr equ 1024
init_cal_increment equ 512
init_cal_passes equ 10
;**********************************************************************
;* *
;* Global variables *
;* *
;**********************************************************************
.DATA
;CPU speed calibration data
delayctr dw init_delayctr-1
cal_increment dw init_cal_increment
cal_passes dw init_cal_passes
;keyboard hardware interrupt occurrence flag
keyflag db 0
;interrupt controller mask save byte
original_mask db 0
;port 61h default configuration
ppibyte db 0
;**********************************************************************
;* *
;* Code *
;* *
;**********************************************************************
.CODE
assume ds:DGROUP
;code-segment storage for keyboard interrupt chain vector
original_kbdint_vec dd 0
;**********************************************************************
;* Keyboard Hardware Interrupt Intercept Routine *
;**********************************************************************
;This routine sets the "keyflag" variable to 1 whenever a keyboard
; interrupt occurs.
kbdint proc far
push ds
push ax
mov ax,DGROUP
mov ds,ax
mov keyflag,1
pop ax
pop ds
jmp dword ptr cs:original_kbdint_vec
kbdint endp
;**********************************************************************
;* Calibrated software delay routine *
;**********************************************************************
;lots of nops
delay proc near
db init_delayctr dup (90h)
ret ;starting delay value
db (init_delayctr-1) dup (90h)
ret ;should never actually be executed
delay endp
;**********************************************************************
;* Initialize timer for correct speaker operation. *
;**********************************************************************
;This procedure returns 0 if successful and 1 if unsuccessful.
set_ppibyte proc near
;return value is kept in BX register
mov bx,0 ;set to 1 later if required
;set gate 2 drive to one
in al,ppiaddr
and al,0FDh
or al,01h
mov ppibyte,al
out ppiaddr,al
;set timer chip to mode zero (int on terminal count)
mov al,tcmode2a
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
;set count to 0001h for quick termination
mov al,1
out tcaddrd2,al
jmp short $+2
jmp short $+2
sub al,al
out tcaddrd2,al
sti
jmp short $+2
jmp short $+2
;write the standard setup to timer zero
mov al,tcmode0a
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
mov al,0
out tcaddrd0,al
jmp short $+2
jmp short $+2
out tcaddrd0,al
sti
jmp short $+2
jmp short $+2
;set timer zero to high-byte mode
mov al,tcmode0b
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
mov al,0
out tcaddrd0,al
sti
jmp short $+2
jmp short $+2
;wait for next timer tick
mov ah,0
mov al,tclatchcmd0
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
sti
jmp short $+2
jmp short $+2
mov dx,ax
spi_sync:
mov al,tclatchcmd0
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
sti
jmp short $+2
cmp ax,dx
mov dx,ax
jbe spi_sync
;Wait for another timer tick (18Hz); this should be
; plenty of time for timer 2 to reach terminal count.
spi_wt1:
mov al,tclatchcmd0
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
sti
sub ah,ah
cmp ax,dx
mov dx,ax
jbe spi_wt1
;read the content of timer 2 and check for 0001h
mov al,tclatchcmd2
cli
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd2
mov ah,al
jmp short $+2
jmp short $+2
in al,tcaddrd2
sti
or al,al ;test high byte for 00h
jnz spi_countdone
cmp ah,1 ;test low byte for 01h
jnz spi_countdone
;(arrive here if timer 2 is NOT counting)
;If timer 2 does not count,
; we flag an error and keep going.
mov bx,1
;set gate 2 drive to zero (disable counting)
spi_countdone:
in al,ppiaddr
and al,0FCh
mov ppibyte,al
out ppiaddr,al
;count is (supposedly) finished; test timer 2 output readback bit
in al,ppiaddr
test al,20h ;bit 5 should be high
jnz spi_done
;wrong polarity, set error flag and continue
mov bx,1
spi_done:
mov ax,bx
ret
set_ppibyte endp
;**********************************************************************
;* Set Calibration Constant *
;* *
;* This call simply sets the calibration constant to the *
;* value specified in the parameter. *
;**********************************************************************
;Accepts the following parameters with PASCAL parameter-passing convention:
;Position Size Description
;-------- ---- -----------
; 1 2 The calibration constant to be set.
;There is no return value.
psc_parm1 equ [bp+6] ;length = 2
psc_parmlength equ 2
public PSETCAL
PSETCAL proc far
push bp
mov bp,sp
;in case this routine has been called before, erase the "ret" instruction
; that was placed in the delay routine
inc delayctr ;since we decremented it before
lea bx,delay
add bx,delayctr
mov byte ptr cs:[bx],90h ;this is a "nop" instruction
;get the new delay parameter
mov ax,psc_parm1
mov delayctr,ax
;insert a "ret" instruction at the new location in the delay routine
lea bx,delay
add bx,delayctr
mov byte ptr cs:[bx],0C3h ;this is a "ret" instruction
dec delayctr ;for benefit of playback routine
pop bp
ret psc_parmlength
PSETCAL endp
;**********************************************************************
;* Calibration procedure *
;* *
;* This routine must be called once before any calls are made *
;* to the playback routine. It measures the CPU execution *
;* speed and saves compensation values. *
;**********************************************************************
;There are no entry parameters.
;The return value is a double word, defined as follows:
;Return value low word Meaning
;--------------------- -------
; 0 success
; 1 this CPU is too slow to accomplish
; normal-speed playback
; 2 operation not possible under Windows
; in "Enhanced" mode
; 3 unusual speaker drive circuit encountered,
; might not output sound
;The return value high word is the actual speed calibration constant
; for this computer
cal_temp1 equ [bp-2] ;length = 2
cal_locallength equ 2
public PCALIBRATE
PCALIBRATE proc far
push bp
mov bp,sp
sub sp,cal_locallength
push si
push di
;test for the "Enhanced Mode Windows" environment
mov ax,1600h
int 2Fh
cmp al,00h
je cal_no_enh_win
cmp al,80h
je cal_no_enh_win
mov delayctr,1
mov ax,2
jmp cal_exit
cal_no_enh_win:
;test speaker drive circuitry and set configuration
call set_ppibyte
mov cal_temp1,ax
;in case this routine has been called before, erase the "ret" instruction
; that was placed in the delay routine
inc delayctr ;since we decremented it before
lea bx,delay
add bx,delayctr
mov byte ptr cs:[bx],90h ;this is a "nop" instruction
;initialize parameters
mov delayctr,init_delayctr
mov cal_increment,init_cal_increment
mov cal_passes,init_cal_passes
;insert a "ret" instruction into the starting location in the delay routine
lea bx,delay
add bx,delayctr
mov byte ptr cs:[bx],0C3h ;this is a "ret" instruction
;no interrupts until we are through calibrating
cli
;write the standard setup to timer zero
mov al,tcmode0a
out tcaddrc,al
jmp short $+2
jmp short $+2
mov al,0
out tcaddrd0,al
jmp short $+2
jmp short $+2
out tcaddrd0,al
jmp short $+2
jmp short $+2
;set timer zero to high-byte mode
mov al,tcmode0b
out tcaddrc,al
jmp short $+2
jmp short $+2
mov al,0
out tcaddrd0,al
jmp short $+2
jmp short $+2
;initialize loop count
cal_zerocnt:
sub cx,cx
;synchronize to timer tick
mov ah,0
mov al,tclatchcmd0
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
jmp short $+2
jmp short $+2
mov si,ax
cal_sync:
mov al,tclatchcmd0
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
jmp short $+2
cmp ax,si
mov si,ax
jbe cal_sync
;calibration loop
cal_dummyloop:
mov al,80h
nop
nop
or ax,ax
jz cal_scale
cal_scale:
sub ah,ah
mov bx,1 ;dummy value
mul bx
mov al,ah
mov ah,dl
lea bx,delay
add bx,ax
mov word ptr cs:[bx],9090h ;these are "nop" instructions
mov al,ppibyte
or al,00h
out ppiaddr,al
and al,0FFh
call delay
mov word ptr cs:[bx],9090h ;these are "nop" instructions
inc cx
mov al,tclatchcmd0
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
sub ah,ah
nop
nop
cmp ax,si
mov si,ax
nop
nop
jbe cal_dummyloop
;kill the old "ret" instruction in the delay routine
lea bx,delay
add bx,delayctr
mov byte ptr cs:[bx],90h ;this is a "nop" instruction
;adjust delay counter
mov ax,cal_increment
shr cal_increment,1
cmp cx,countmax_18hz
je cal_setret
jb cal_makefaster
add delayctr,ax
jmp cal_setret
cal_makefaster:
sub delayctr,ax
cal_setret: ;here we insert a new return
lea bx,delay
add bx,delayctr
mov byte ptr cs:[bx],0C3h ;this is a "ret" instruction
dec cal_passes
cmp cal_passes,0
je cal_restore
jmp cal_zerocnt
;repair loss of system timer ticks and restore interrupts
cal_restore:
mov bx,40h
mov es,bx
mov bx,6Ch
add word ptr es:[bx],init_cal_passes+1
adc word ptr es:[bx+2],0
cmp word ptr es:[bx+2],18h
jb cal_tc_end
ja cal_tc_midnite
cmp word ptr es:[bx],0B0h
jb cal_tc_end
cal_tc_midnite:
mov byte ptr es:[bx+4],1
sub word ptr es:[bx],0B0h
sbb word ptr es:[bx+2],18h
cal_tc_end:
sti
;determine if CPU is too slow
dec delayctr ;for benefit of playback routine
cmp delayctr,64
jb cal_cpuslow
sub ax,ax
jmp cal_exit
cal_cpuslow:
mov ax,1
cal_exit:
or ax,ax
jnz cal_rdc
cmp word ptr cal_temp1,0
jz cal_rdc
mov ax,3
cal_rdc:
mov dx,delayctr
pop di
pop si
add sp,cal_locallength
pop bp
ret
PCALIBRATE endp
;**********************************************************************
;* Playback procedure *
;**********************************************************************
;Accepts the following parameters with PASCAL parameter-passing convention:
;Position Size Description
;-------- ---- -----------
; 1 4 A far pointer to the memory block used for voice data.
; 2 4 A dword indicating the length of the memory block.
;The return value is a dword indicating the number of bytes actually played.
pv_parm1 equ [bp+10] ;length = 4
pv_parm2_hi equ [bp+8] ;length = 2
pv_parm2_lo equ [bp+6] ;length = 2
pv_parmlength equ 8
pv_temp1 equ [bp-2] ;length = 2
pv_temp2 equ [bp-4] ;length = 2
pv_locallength equ 4
public PLAYVOICE
PLAYVOICE proc far
push bp
mov bp,sp
sub sp,pv_locallength
push si
push di
;setup speaker and timer 2
call set_ppibyte
;mask all interrupts except the keyboard
cli
in al,intmaskaddr
mov original_mask,al
mov al,kbdonly_mask
out intmaskaddr,al
;install the keyboard interrupt intercept routine
sub ax,ax
mov es,ax
mov bx,kbdint_number*4
mov ax,es:[bx]
mov word ptr cs:original_kbdint_vec,ax
mov ax,es:[bx+2]
mov word ptr cs:original_kbdint_vec+2,ax
lea ax,kbdint
mov es:[bx],ax
mov ax,cs
mov es:[bx+2],ax
sti
;set up initial conditions
mov keyflag,0
les si,dword ptr pv_parm1
mov cx,pv_parm2_lo
mov di,pv_parm2_hi
jcxz pv_playloop
inc di
;playback loop
pv_playloop:
mov al,es:[si]
inc si
jz pv_incrseg
pv_scale:
sub ah,ah
mov bx,delayctr
mul bx
mov al,ah
mov ah,dl
lea bx,delay
add bx,ax
mov word ptr cs:[bx],61E6h ;this is an "out 61h,al" instruction
mov al,ppibyte
or al,02h
out ppiaddr,al
and al,0FDh
call delay
mov word ptr cs:[bx],9090h ;these are "nop" instructions
nop
mov al,tclatchcmd0
out tcaddrc,al
jmp short $+2
jmp short $+2
in al,tcaddrd0
sub ah,ah
cmp keyflag,0
jne pv_stop
loop pv_playloop
dec di
jnz pv_playloop
pv_stop:
jmp short pv_fixkbi
pv_incrseg:
mov bx,es
add bx,1000h
mov es,bx
jmp short pv_scale
;remove the keyboard interrupt intercept routine
pv_fixkbi:
cli
sub ax,ax
mov es,ax
mov bx,kbdint_number*4
mov ax,word ptr cs:original_kbdint_vec
mov es:[bx],ax
mov ax,word ptr cs:original_kbdint_vec+2
mov es:[bx+2],ax
;restore the original interrupt mask
mov al,original_mask
out intmaskaddr,al
sti
;leave speaker in a known state
mov al,ppibyte
out ppiaddr,al
;calculate the number of bytes played
jcxz pv_calcbytes
dec di
pv_calcbytes:
mov ax,pv_parm2_lo
mov dx,pv_parm2_hi
sub ax,cx
sbb dx,di
mov pv_temp2,ax
mov pv_temp1,dx
;compensate for lost timer ticks
mov cx,countmax_18hz
div cx
mov bx,40h
mov es,bx
mov bx,6Ch
cli
add es:[bx],ax
adc word ptr es:[bx+2],0
cmp word ptr es:[bx+2],18h
jb pv_tc_end
ja pv_tc_midnite
cmp word ptr es:[bx],0B0h
jb pv_tc_end
pv_tc_midnite:
mov byte ptr es:[bx+4],1
sub word ptr es:[bx],0B0h
sbb word ptr es:[bx+2],18h
pv_tc_end:
sti
;set the return value
mov ax,pv_temp2
mov dx,pv_temp1
pop di
pop si
add sp,pv_locallength
pop bp
ret pv_parmlength
PLAYVOICE endp
end