Category : Music and Digitized Voice
Archive   : PAS-SDK1.ZIP
Filename : MVSOUND.ASM

 
Output of file : MVSOUND.ASM contained in archive : PAS-SDK1.ZIP
;$Author: DCODY $
;$Date: 24 Sep 1992 08:54:22 $
;$Header: X:/sccs/mvsound/mvsound.asv 1.6 24 Sep 1992 08:54:22 DCODY $
;$Log: X:/sccs/mvsound/mvsound.asv $
;
; Rev 1.6 24 Sep 1992 08:54:22 DCODY
; changed MVGetHardware to mvGetHardware
;
; Rev 1.5 12 Aug 1992 17:13:18 DCODY
; made call to mvgethardware use a zero (0) parameter to use the current base.
;
; Rev 1.4 17 Jul 1992 14:18:22 DCODY
; moved TheDMAchannel and TheIRQchannel out to another module. Made the
; hardware I/O relocatable. Made initmvsound recallable without ill effects.
;
; Rev 1.3 09 Jul 1992 10:07:50 DCODY
; corrected MV101 bit test and jumps
;
; Rev 1.2 26 Jun 1992 14:13:48 DCODY
; added the parameter to GetHWVersion call
;
; Rev 1.1 23 Jun 1992 17:06:04 DCODY
; PAS2 update
;
; Rev 1.0 15 Jun 1992 09:43:46 BCRANE
; Initial revision.
;$Logfile: X:/sccs/mvsound/mvsound.asv $
;$Modtimes$
;$Revision: 1.6 $
;$Workfile: mvsound.asm $

page 64,131
Title MVSOUND -- Pro Audio Spectrum Sound Support Code

; /*\
;---|*|----====< MVSOUND >====----
;---|*|
;---|*| This module contains the code for supporting PCM I/O.
;---|*|
;---|*| Media Vision, Inc. Copyright (c) 1991,1992. All Rights Reserved.
;---|*|
; \*/

;
; The following set of equates are used to control what routines are compiled
; by doing this, MVSOUND.ASM can contain all the necessary code in one file,
; but generate separate, linkable objects with just the necessary code.
;
ALLOBJS = 0 ; individual objects are being built
DATAOBJ = 0 ; data code to be compiled
MISC1OBJ = 0 ; minimum code needed from the library
MISC2OBJ = 0 ; other miscellaneous code
PCMOBJ = 0 ; PCM code to be compiled

ifdef BUILDDATA
DATAOBJ = 1 ; data code to be compiled
endif

ifdef BUILDMISC1
MISC1OBJ = 1 ; misc module #1 code to be compiled
endif

ifdef BUILDMISC2
MISC2OBJ = 1 ; misc module #2 code to be compiled
endif

ifdef BUILDPCM
PCMOBJ = 1 ; data code to be compiled
endif

ifdef BUILDALL
ALLOBJS = 1 ; all objects are being built
DATAOBJ = 1 ; data code to be compiled
MISC1OBJ = 1 ; miscellaneous code to be compiled
MISC2OBJ = 1 ; miscellaneous code to be compiled
PCMOBJ = 1 ; PCM code to be compiled
endif

.xlist
include model.inc
include masm.inc
include target.inc
include state.inc
include common.inc
.list

;
; structure for pointing to the above table of DMA addresses
;

dmaaddr struc
_dmach db ? ; DMA channel selected
_dmardstat db ? ; DMA read status
_dmawrcntrl db ? ; DMA write command register
_dmawreq db ? ; DMA write request register
_dmawrsmr db ? ; DMA write single mask register
_dmawrmode db ? ; DMA write mode register
_dmaclear db ? ; DMA clear low/high flip-flop
_dmardtemp db ? ; DMA read temp register
_dmawrclr db ? ; DMA write master clear
_dmaclrmsk db ? ; DMA clear mask register
_dmawrall db ? ; DMA write all mask register bits
dmaaddr ends

;
;---------------------------========================---------------------------
;---------------------------====< DATA SECTION >====---------------------------
;---------------------------========================---------------------------
;
if MODELSIZE eq 0
.code
else
.data
endif

if DATAOBJ ; include if we are building DATA.OBJ

;
; These variables are DMA/Buffer control variables
;
TheIRQMask db INT7MSK ; 8259 interrupt mask

TheDMAMode db 00h ; 44h for input, 48h for output
LinearPtr dd 0 ; holds linear address
SegmentedPtr dd 0 ; holds segmented address (real mode)
DMABuffLength dw 0 ; DMA Length (0 based) (MUST BE EVEN!)
DMABuffDivides db 0 ; # of splits in the DMA buffer

TheSampleRate dd 0 ; 3k through 88k
StereoMono db 0 ; ff for mono, 00 for stereo
PCMDirection db 0 ; bit mask for the DMA controller
SampleSize db 0 ; sample size: 0=8,1=12,2=16

public DMAAutoInit ; TRUE(0ffh) or FALSE(000h)
DMAAutoInit db 0ffh ; to allow auto-init on DMA access

UserRoutine dd 0 ; User Code

StatusWord dw 0 ; Holds current status:
; ; 0 = inactive, available
; ; 1 = currently playing
; ; 2 = currently recording

OldIRQRoutine dd 0 ; holds the original routine

polledmask equ bICsamprate+bICsampbuff ; polled mask
dmamask equ bICsampbuff ; dma mask

DMAOUTPUT equ 0+dmamask ; DMA is used to drive the output
POLLEDOUTPUT equ 0+polledmask ; Polling routine drives output
DMAINPUT equ 1+dmamask ; DMA is used to read input
POLLEDINPUT equ 1+polledmask ; Polling routine reads input
TypeOfSetup db 0 ; polled/dma interrupt masks & stuff..

public NumberOfInterrupts
NumberOfInterrupts dw 0 ; number of interrupts that have occured
;
; These variables direct our code to the proper DMA channel
;
OurDMAPageReg dw CH1PAGEREG ; default to DMA channel 1 page reg
OurDMAddress dw DMAC1ADDR ; default to DMA channel 1 address reg

;
; table of address pointers to the various DMA 2 addresses
;
public DMA1AddrTable
DMA1AddrTable label byte
db DEFAULTDMA ; DMA channel selected
db DMARDSTAT ; DMA read status
db DMAWRCNTRL ; DMA write command register
db DMAWREQ ; DMA write request register
db DMAWRSMR ; DMA write single mask register
db DMAWRMODE ; DMA write mode register
db DMACLEAR ; DMA clear low/high flip-flop
db DMARDTEMP ; DMA read temp register
db DMAWRCLR ; DMA write master clear
db DMACLRMSK ; DMA clear mask register
db DMAWRALL ; DMA write all mask register bits
;
; table of address pointers to the various DMA 2 addresses
;
public DMA2AddrTable
DMA2AddrTable label byte
db DEFAULTDMA ; DMA channel selected
db DMA2RDSTAT ; DMA read status
db DMA2WRCNTRL ; DMA write command register
db DMA2WREQ ; DMA write request register
db DMA2WRSMR ; DMA write single mask register
db DMA2WRMODE ; DMA write mode register
db DMA2CLEAR ; DMA clear low/high flip-flop
db DMA2RDTEMP ; DMA read temp register
db DMA2WRCLR ; DMA write master clear
db DMA2CLRMSK ; DMA clear mask register
db DMA2WRALL ; DMA write all mask register bits

public DMAPointer
DMAPointer dw offset DMA1AddrTable ; default to channel 1 table
;
; working variables
;
;
public TheIRQMask ; 8259 interrupt mask
public TheDMAMode ; 55h for input, 59h for output
public LinearPtr ; holds linear address
public SegmentedPtr ; holds segmented address (real mode)
public DMABuffLength ; DMA Length (0 based) (MUST BE EVEN!)
public DMABuffDivides ; # of splits in the DMA buffer
public TheSampleRate ; 3k through 88k
public StereoMono ; ff for mono, 00 for stereo
public PCMDirection ; bit mask for the DMA controller
public SampleSize ; sample size: 0=8,1=12,2=16
public DMAAutoInit ; TRUE/FALSE flag for DMA autoinit bit
public UserRoutine ; User Code
public StatusWord ; Holds current status:
public OldIRQRoutine ; holds the original routine
polledmask equ bICsamprate+bICsampbuff ; polled mask
dmamask equ bICsampbuff ; dma mask
DMAOUTPUT equ 0+dmamask ; DMA is used to drive the output
POLLEDOUTPUT equ 0+polledmask ; Polling routine drives output
DMAINPUT equ 1+dmamask ; DMA is used to read input
POLLEDINPUT equ 1+polledmask ; Polling routine reads input
public TypeOfSetup ; polled/dma interrupt masks & stuff..
public NumberOfInterrupts ; number of interrupts that have occured
public OurDMAPageReg ; default to DMA channel 1 page reg
public OurDMAddress ; default to DMA channel 1 address reg

else ; not building DATA.OBJ, declare all publics

extrn DMAPointer :word ; pointer to the DMA address table
extrn DMA1AddrTable :byte ; 1st DMA controller table
extrn DMA2AddrTable :byte ; 2nd DMA controller table
extrn TheIRQMask :byte ; 8259 interrupt mask
extrn TheDMAMode :byte ; 55h for input, 59h for output
extrn LinearPtr :dword ; holds linear address
extrn SegmentedPtr :dword ; holds segmented address (real mode)
extrn DMABuffLength :word ; DMA Length (0 based) (MUST BE EVEN!)
extrn DMABuffDivides :byte ; # of splits in the DMA buffer
extrn TheSampleRate :dword ; 3k through 88k
extrn StereoMono :byte ; ff for mono, 00 for stereo
extrn PCMDirection :byte ; bit mask for the DMA controller
extrn SampleSize :byte ; sample size: 0=8,1=12,2=16
extrn DMAAutoInit :byte ; TRUE/FALSE flag for DMA autoinit bit
extrn UserRoutine :dword ; User Code
extrn StatusWord :word ; Holds current status:
extrn OldIRQRoutine :dword ; holds the original routine
extrn TypeOfSetup :byte ; polled/dma interrupt masks & stuff..
extrn NumberOfInterrupts:word ; number of interrupts that have occured
extrn OurDMAPageReg :word ; default to DMA channel 1 page reg
extrn OurDMAddress :word ; default to DMA channel 1 address reg


polledmask equ bICsamprate+bICsampbuff ; polled mask
dmamask equ bICsampbuff ; dma mask
DMAOUTPUT equ 0+dmamask ; DMA is used to drive the output
POLLEDOUTPUT equ 0+polledmask ; Polling routine drives output
DMAINPUT equ 1+dmamask ; DMA is used to read input
POLLEDINPUT equ 1+polledmask ; Polling routine reads input
SHADOWTABLELEN equ 28 ; 28 entries in the shadow data table

endif

extrn TheDMAChannel :byte ; defaults to channel 1
extrn TheIRQChannel :byte ; defaults to IRQ 7

extrn mvhwShadowPointer :dword; a common variable for all
extrn _MVHWVersionBits :word ; hardware state bits
extrn _MVTranslateCode :word

.code

externADDR MVInitStatePtr ; pointer to the state table init code
externADDR mvGetHWVersion ; returns the hardware bits
;
; TEXT SEGMENT externals for the different modules
;

if PCMOBJ AND (NOT ALLOBJS) ; added for independent PCMOBJ module
externADDR SelectIRQ ; PCM uses "SelectIRQ"
externADDR _unloadirqvector ; PCM uses "_unloadirqvector"
externADDR _getirqoffset ; PCM uses "_getirqoffset"
endif

;
;---------------------------========================---------------------------
;---------------------------====< CODE SECTION >====---------------------------
;---------------------------========================---------------------------
;
; /*\
;---|*|
;---|*|---------------====< Prototypes >====---------------
;---|*|
;---|*| void far *DMABuffer(char far *, int, int )
;---|*|
;---|*| Passes in a pointer and length to the buffer.
;---|*| Also flushes the buffer. Returns 0 or true DMA buffer ptr.
;---|*|
;---|*| int EnablePCMPlay()
;---|*|
;---|*| Sets up the PCM hardware for polled output
;---|*|
;---|*| int EnablePCMRecord()
;---|*|
;---|*| Sets up the PCM hardware for polled input
;---|*|
;---|*| char huge *FindDMABuffer(char huge *, int )
;---|*|
;---|*| Takes a memory address & return the next 64k boundary
;---|*|
;---|*| MVState far *InitMVSound()
;---|*|
;---|*| Initializes the int 2F interface & some global variables.
;---|*|
;---|*| int InitPCM()
;---|*|
;---|*| Initializes the PCM code.
;---|*|
;---|*| void PausePCM()
;---|*|
;---|*| Temporarily stops the PCM I/O by disabling the timers
;---|*|
;---|*| int PCMInfo( long ,int, int, int )
;---|*|
;---|*| Sets up the transfer rate & stereo/mono/compression/data size
;---|*|
;---|*| int PCMPlay()
;---|*|
;---|*| Starts the DMA feeding the DAC
;---|*|
;---|*| int PCMRecord()
;---|*|
;---|*| Starts the DMA reading the ADC
;---|*|
;---|*| void RemovePCM()
;---|*|
;---|*| kills the PCM code.
;---|*|
;---|*| void ResumePCM()
;---|*|
;---|*| Restarts the PCM I/O by enabling the timers
;---|*|
;---|*| int SelectDMA( int )
;---|*|
;---|*| Selects the DMA channel 1, or 3
;---|*|
;---|*| int SelectIRQ( int )
;---|*|
;---|*| Selects the IRQ line for DMA control
;---|*|
;---|*| void StopPCM()
;---|*|
;---|*| Turn off the PCM timers, interrupts, and state machine.
;---|*|
;---|*| void UserFunc((*long)())
;---|*|
;---|*| Call back routine when Half way buffer is full/empty.
;---|*|
; \*/

if PCMOBJ

; /*\
;---|*|------====< void far * DMABuffer( char far *, int, int ) >====------
;---|*|
;---|*| Passes in a pointer and length to the buffer
;---|*|
;---|*| Entry Conditions:
;---|*| dParm1 points to DMA buffer
;---|*| wParm3 points to the length (wParm3 * 1024 is the total length )
;---|*| wParm4 is the # of divisions of the DMA buffer
;---|*|
;---|*| Exit Conditions:
;---|*| DX:AX = 0 - bad buffer
;---|*| DX:AX = returns buffer pointer
;---|*|
; \*/

public DMABuffer
DMABuffer proc
push bp
mov bp,sp
push es
push di

mov cx,wParm3 ; get the dma size (4/8/16/32/64)
cmp cx,64 ; too high?
ja dmabbad ; yes, bomb out

mov ax,1024
cwd
mul cx ; get the length - 1
dec ax
mov [DMABuffLength],ax ; and save for later use

les di,dParm1 ; get the far pointer to the buffer

mov ax,di
mov bx,es ; convert it to a linear address
mov dx,es
mov cl,4
rol dx,cl
and dx,000fh
shl bx,cl ; add offset portion of seg to offset
add ax,bx
jc dmabbad ; bad if extends over a 64k boundary

mov wptr [LinearPtr+0],ax ; save the linear address
mov wptr [LinearPtr+2],dx
mov wptr [SegmentedPtr+0],di
mov wptr [SegmentedPtr+2],es

mov bx,wParm4 ; get the # of DMA buffer divisions
mov [DMABuffDivides],bl

push ax ; save the buffer offset

mov cx,[DMABuffLength] ; get the clearing length
mov al,80h ; PCM silent code
cld
rep stosb ; flush the buffer length...
stosb ; + 1

mov dx,es
pop ax ; return dx:ax
jmp short dmabdone
;
dmabbad:
sub ax,ax
cwd
;
dmabdone:
pop di
pop es
pop bp
ret

DMABuffer endp


; /*\
;---|*|---------------====< EnablePCMPlay() >====---------------
;---|*|
;---|*| Enabled the DAC hardware for polled output (no interrupts)
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0 good start
;---|*| AX = -1 not started, problem occured
; \*/

public EnablePCMPlay
EnablePCMPlay proc
push es

mov ax,wptr [TheSampleRate+0] ; Validate the sample rate
or ax,wptr [TheSampleRate+2]
jz enaPCMPlay_bad ; zero sample rate, bomb out...

call FFAR ptr EnablePlaying ; go for it...
mov ax,0
jmp short enaPCMPlay_exit
;
enaPCMPlay_bad:
mov ax,-1
;
enaPCMPlay_exit:
pop es
ret

EnablePCMPlay endp


; /*\
;---|*|---------------====< EnablePCMRecord() >====---------------
;---|*|
;---|*| Enabled the ADC hardware for polled input (no interrupts)
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0 recording has started
;---|*| AX = -1 recording did not start
;---|*|
; \*/

public EnablePCMRecord
EnablePCMRecord proc
push es

mov ax,wptr [TheSampleRate+0] ; Validate the sample rate
or ax,wptr [TheSampleRate+2]
jz enaPCMRec_bad

call FFAR ptr EnableRecording ; go for it...
mov ax,0
jmp short enaPCMRec_exit
;
enaPCMRec_bad:
mov ax,-1
;
enaPCMRec_exit:
pop es
ret

EnablePCMRecord endp


; /*\
;---|*|------====< char far *FindDMABuffer(char far *,int ) >====------
;---|*|
;---|*| Finds the next 64k boundary starting with dParm1 address
;---|*|
;---|*| Entry Conditions:
;---|*| dParm1 points to a buffer twice the size needed for the DMA
;---|*| wParm2 is the size of the DMA buffer (4/8/16/32/64)
;---|*|
;---|*| Exit Conditions:
;---|*| DX:AX = returns buffer pointer on a 64k boundary
;---|*| DX:AX = 0 if boundary wraps beyond 1 meg.
;---|*|
; \*/

public FindDMABuffer
FindDMABuffer proc
push bp
mov bp,sp

sub ax,ax ; default to bad...
cwd

mov cx,wParm3 ; get the length
cmp cx,64 ; 64k buffer requested?
ja fdb_exit ; too high, exit
jz fdb_64k ; yes, special case it...
cmp cl,4
jb fdb_exit ; too low, exit

mov ax,1024 ; calculate the length of the DMA buffer
mul cx
mov bx,ax ; bx holds the length

mov dx,wptr dParm1+2
mov ax,dx ; accumulate the offset in ax
and ax,0fffh ; get the offset portion of the segment
and dx,0f000h ; make DX a 64k segment register
mov cl,4
shl ax,cl ; make the segment an offset
add ax,wptr dParm1+0 ; accumulate the rest of the offset
add bx,ax ; does the buffer cross a 64k boundary?
cmc ; carry set if 64k crossing
sbb bx,bx ; bx = ffff if no crossing, 0 if crossed
and ax,bx ; clear ax if crossed
not bx ; bx = ffff if crossed, 0000 if not
and bx,1000h
add dx,bx ; advance dx if we crossed a 64k boundary
jmp short fdb_exit
;
fdb_64k:
mov dx,wptr dParm1+2 ; get the segment
sub ax,ax ; offset is zero
and dx,0f000h ; find the next segment
add dx,1000h ; move to next segment
jnc fdb_exit ; if doesn't wraps past 1 meg, it's good
cwd ; bad, flush it...
;
fdb_exit:
pop bp
ret

FindDMABuffer endp
;
endif ; PCMOBJ
if MISC1OBJ

;
; /*\
;---|*|---------------====< InitMVSound() >====---------------
;---|*|
;---|*| Initializes this body of code. It will try to find the int 2F DOS
;---|*| interface to the MVPROAS device. Once found, the new state table
;---|*| pointer will be loaded.
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| DX:AX point to the state table
; \*/

public InitMVSound
InitMVSound proc
push es
push di
mov ax,ds
mov es,ax
;
; find the hardware. Should be installed for this to run
;
call MVInitStatePtr ; initialize the state table ptr

cmp _MVHWVersionBits,-1 ; initialized yet?
jnz @F ; yes, continue on...
mov ax,USE_ACTIVE_ADDR ; pass in the base address
push ax
call mvGetHWVersion ; no, checkout the hardware
pop ax
;
@@:
mov ax,wptr [mvhwShadowPointer+0]
mov dx,wptr [mvhwShadowPointer+2]

pop di
pop es
ret

InitMVSound endp
;
endif ; MISC1OBJ
if PCMOBJ
;
; /*\
;---|*|---------------====< InitPCM() >====---------------
;---|*|
;---|*| Initializes the PCM code
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| AX = Version of this Code
; \*/

public InitPCM
InitPCM proc
push es

mov [TypeOfSetup],polledmask ; flushes both interrupts
; for upcoming calls
mov al,TheDMAChannel ; initialize the DMA now...
cbw
push ax
call FFAR ptr SelectDMA
pop ax

mov al,TheIRQChannel ; initialize the IRQ now...
cbw
push ax
call FFAR ptr SelectIRQ ; also removes the IRQ
pop ax

call FFAR ptr StopPCM ; kill the Audio Spectrum board

mov dx,INTRCTLRST ; flush any pending PCM irq
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al

sub ax,ax
mov [TypeOfSetup],al ; unknown type

mov wptr [UserRoutine+0],ax
mov wptr [UserRoutine+2],ax

mov wptr [LinearPtr+0],ax
mov wptr [LinearPtr+2],ax
mov wptr [SegmentedPtr+0],ax
mov wptr [SegmentedPtr+2],ax

mov wptr [DMABuffLength],ax

not al
mov [StereoMono],al ; mono

mov ax,11025
cwd

call FFAR ptr _calcsamplerate ; setup the default rate
mov wptr [TheSampleRate+0],ax
mov wptr [TheSampleRate+2],dx

mov ax,0003h ; for compatibility, version 00.03

pop es
ret

InitPCM endp
;
endif ; PCMOBJ
if PCMOBJ
;
;
; /*\
;---|*|---------------====< PausePCM >====---------------
;---|*|
;---|*| Turn off the h/w timer enables to effectively stop pcm temporarily.
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| Nothing
;---|*|
; \*/
;
public PausePCM
PausePCM proc
push es
push di
;
; Setup the audio filter sample bits
;
les di,[mvhwShadowPointer]
mov dx,AUDIOFILT
xor dx,[_MVTranslateCode] ; xlate the board address

disable

mov al,es:[di._audiofilt]
and al,not bFIsrate ; flush the enable bits
mov es:[di._audiofilt],al
out dx,al

enable

pop di
pop es
ret

PausePCM endp

;
; /*\
;---|*|---------------====< PCMInfo ( long, int, int, int ) >====---------------
;---|*|
;---|*| Selects xfer rate & stereo/mono
;---|*|
;---|*| Entry Conditions:
;---|*| Parm #1 (wParm1, wParm2) is the rate
;---|*| Parm #2 (wParm3) is the stereo(1) or mono (0)
;---|*| Parm #3 (wParm4) is the compression flag
;---|*| Parm #4 (wParm5) is the data size (8/12/16 bits per sample)
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0, good data
;---|*| AX = -1, bum transfer rate
;---|*|
; \*/

public PCMInfo
PCMInfo proc
push bp
mov bp,sp

mov al,wParm4+2 ; get the data size
cbw
cmp al,8 ; 8 bit channel?
jz @F

inc ah
cmp al,12 ; 12 bit channel
jz @F

inc ah
cmp al,16 ; 16 bit channel?
jnz pcinf_bad ; no, bomb out...
;
@@:
mov [SampleSize],ah

mov cx,wParm3 ; get the stereo flag
shr cx,1
jnz pcinf_bad ; exit bad if not zero

cmc ; set for mono, cleared for stereo
sbb cx,cx ; make a full bit mask
mov [StereoMono],cl ; save only valid values

mov ax,dParm1+0 ; get the sample rate
mov dx,dParm1+2

or cx,cx ; is it mono?
jnz @F ; yes, skip the doubling
shl ax,1 ; no, stereo, so double the sample rate
adc dx,dx
;
@@:
call FFAR ptr _calcsamplerate
jc pcinf_bad
mov wptr [TheSampleRate+0],ax
mov wptr [TheSampleRate+2],dx

sub ax,ax
jmp short pcinf_exit
;
pcinf_bad:
mov ax,-1
;
pcinf_exit:
pop bp
ret

PCMInfo endp
;
;
; /*\
;---|*|---------------====< PCMPlay() >====---------------
;---|*|
;---|*| Starts the DMA feeding the DAC
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0 good start
;---|*| AX = -1 not started, problem occured
; \*/

public PCMPlay
PCMPlay proc
push es

les ax,LinearPtr ; Validate the buffer pointer
mov bx,es
or ax,bx
jz PCMPlay_bad

mov ax,[DMABuffLength] ; Validate the buffer count
or ax,ax
jz PCMPlay_bad

mov ax,wptr [TheSampleRate+0] ; Validate the sample rate
or ax,wptr [TheSampleRate+2]
jz PCMPlay_bad ; zero sample rate, bomb out...

call FFAR ptr StartPlaying ; go for it...
sub ax,ax
jmp short PCMPlay_exit
;
PCMPlay_bad:
mov ax,-1
;
PCMPlay_exit:
pop es
ret

PCMPlay endp
;
;
; /*\
;---|*|---------------====< PCMRecord() >====---------------
;---|*|
;---|*| Starts the DMA reading the ADC
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0 recording has started
;---|*| AX = -1 recording did not start
;---|*|
; \*/

public PCMRecord
PCMRecord proc
push es

les ax,LinearPtr ; Validate the buffer pointer
mov bx,es
or ax,bx
jz PCMRec_bad

mov ax,[DMABuffLength] ; Validate the buffer count
or ax,ax
jz PCMRec_bad

mov ax,wptr [TheSampleRate+0] ; Validate the sample rate
or ax,wptr [TheSampleRate+2]
jz PCMRec_bad

call FFAR ptr StartRecording ; go for it...
mov ax,0
jmp short PCMRec_exit
;
PCMRec_bad:
mov ax,-1
;
PCMRec_exit:
pop es
ret

PCMRecord endp
;
;
; /*\
;---|*|---------------====< RemovePCM () >====---------------
;---|*|
;---|*| Remove our code from the system
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| None
; \*/

public RemovePCM
RemovePCM proc

call FFAR ptr StopPCM ; kill the Audio Spectrum board

mov dx,INTRCTLRST ; flush any pending PCM irq
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al

call FFAR ptr _unloadirqvector ; restore the original vector

ret

RemovePCM endp
;
;
; /*\
;---|*|---------------====< ResumePCM >====---------------
;---|*|
;---|*| Turn on the h/w from making interrupt and DMA requests. This assumes
;---|*| the hardware has already been setup and is ready to go...
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| Nothing
;---|*|
; \*/
;
public ResumePCM
ResumePCM proc
push es
push di
;
; Setup the audio filter sample bits
;
les di,[mvhwShadowPointer]
call FFAR ptr _ASloadtimer0

disable

mov dx,AUDIOFILT
xor dx,[_MVTranslateCode] ; xlate the board address
mov al,es:[di._audiofilt]
or al,bFIsrate ; enable sample rate and buffer timers
mov es:[di._audiofilt],al
out dx,al

enable

pop di
pop es
ret

ResumePCM endp
;
;
;
; /*\
;---|*|---------------====< SelectDMA( int ) >====---------------
;---|*|
;---|*| Selects the DMA channel 0 - 7, excluding 4
;---|*|
;---|*| Entry Conditions:
;---|*| wParm1 points to DMA number (0, 1, 2, 3, 5, 6, 7 )
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0, good buffer, all okay
;---|*| AX = -1, good buffer, all okay
;---|*|
; \*/

public SelectDMA
SelectDMA proc
push bp
mov bp,sp

mov ax,wParm1 ; get the DMA #

and ax,0111b ; save the channels

mov bx,ax ; get some of the I/O addreses
shl bx,1 ; into DX
jnz seldma_02 ; not channel 0, go use it...
push sp ; 8088/86 class machine?
pop cx
cmp cx,sp
jnz seldma_bad ; yes, can't do channel 0 dma
;
seldma_02:
mov dx,cs:[dmatable+bx]
or dx,dx ; valid entry?
jz seldma_bad ; no, bomb out...

mov TheDMAChannel,al ; select the channel.
mov bptr OurDMAPageReg,dh ; ...the page register,
mov bptr OurDMAddress,dl ; ...the address register,

lea bx,DMA1AddrTable ; get the DMA channel addresses
cmp al,4
jl seldma_05
lea bx,DMA2AddrTable ; get the DMA channel addresses
sub al,4 ; make it zero based
;
seldma_05:
mov [bx._dmach],al ; save the adjusted dma channel #
mov [DMAPointer],bx ; save the pointer to all DMA addrs
sub ax,ax
pop bp
ret
;
seldma_bad:
mov ax,-1
pop bp
ret
;
; dma channels, etc
;
dmatable label word
dw (CH0PAGEREG SHL 8) + DMAC0ADDR
dw (CH1PAGEREG SHL 8) + DMAC1ADDR
dw (CH2PAGEREG SHL 8) + DMAC2ADDR
dw (CH3PAGEREG SHL 8) + DMAC3ADDR
dw 0
dw (CH5PAGEREG SHL 8) + DMA2C5ADDR
dw (CH6PAGEREG SHL 8) + DMA2C6ADDR
dw (CH7PAGEREG SHL 8) + DMA2C7ADDR

SelectDMA endp

endif ; PCMOBJ
if MISC2OBJ

; /*\
;---|*|---------------====< SelectIRQ( int ) >====---------------
;---|*|
;---|*| Selects the IRQ line for DMA control
;---|*|
;---|*| Entry Conditions:
;---|*| wParm1 points to IRQ number (3,5,6,7)
;---|*|
;---|*| Exit Conditions:
;---|*| AX = 0, good buffer, all okay
;---|*| AX = -1, bad IRQ #
;---|*|
; \*/

public SelectIRQ
SelectIRQ proc
push bp
mov bp,sp

call FFAR ptr _unloadirqvector ; attempt to restore original vector

sub ax,ax ; flush ax for a bad return

mov cx,wParm1 ; get the irq # (2-7,10-12,14-15)
and cl,0fh ; save only the valid bits
mov bx,01
shl bx,cl

and bx,1001110010111100b ; save the mask bit only if the irq
jz seirqbad ; is a valid selection

mov TheIRQChannel,cl ; save the channel number

cmp cl,8 ; 2nd interrupt controller?
jb @F ; no, skip
xchg bh,bl
;
@@:
mov TheIRQMask,bl
call FFAR ptr _loadirqvector ; load the new vector
mov ax,1
;
seirqbad:
dec ax
pop bp
ret

SelectIRQ endp
;
endif ; MISC2OBJ
if PCMOBJ

;
; /*\
;---|*|--------------------====< void StopPCM() >====--------------------
;---|*|
;---|*| Turn off the PCM timers, interrupts, and state machine.
;---|*|
;---|*| Entry Conditions:
;---|*|
;---|*| Exit Conditions:
;---|*|
;---|*|
; \*/

public StopPCM
StopPCM proc
push es
push di
les di,[mvhwShadowPointer]
;
; clear the audio filter sample bits
;
mov dx,AUDIOFILT
xor dx,[_MVTranslateCode] ; xlate the board address
disable ; drop dead...
mov al,es:[di._audiofilt] ; get the state
and al,not (bFIsrate+bFIsbuff) ; flush the sample timer bits
mov es:[di._audiofilt],al ; save the new state
out dx,al
;
; clear the PCM enable bit
;
mov al,es:[di._crosschannel]; get the current cross channel
and al,not bCCenapcm ; clear the PCM enable bit
or al,bCCdac
mov dx,CROSSCHANNEL
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al ; end to the hardware
mov es:[di._crosschannel],al
;
; disable the 16 bit stuff
;
test [_MVHWVersionBits],bMV101 ; 101 chip?
jz stpc02 ; no, don't touch this...
mov dx,SYSCONFIG2
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx
and al,not bSC216bit+bSC212bit ; flush the 16 bit stuff
out dx,al
;
stpc02:
;
; clear the appropriate Interrupt Control Register bit
;
mov ah,TypeOfSetup
and ah,bICsamprate+bICsampbuff
not ah
mov dx,INTRCTLR
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx
and al,ah ; kill sample timer interrupts
out dx,al
mov es:[di._intrctlr],al
;
; clear the system interrupt mask only if no other ints are used
;
test al,fICintmaskbits XOR (bICsamprate+bICsampbuff)
jnz stpc10
;
; select the correct IRQ controller, then mask it...
;
cmp TheIRQChannel,2 ; Chained IRQ channel?
jz stpc10 ; yes, leave it open...

mov dx,IRQ1MASKREG
cmp TheIRQChannel,8 ; 2nd IRQ controller?
jl stpc05
mov dl,IRQ2MASKREG
;
stpc05:
in al,dx
or al,[TheIRQMask]
out dx,al
enable ; start again...
;
stpc10:
call FFAR ptr KillDMA ; stop the DMA too...

mov [StatusWord],0 ; inactive, available

pop di
pop es
ret

StopPCM endp

;
; /*\
;---|*|---------------====< UserFunc((*long)()) >====---------------
;---|*|
;---|*| Call back routine when Half way buffer is full/empty.
;---|*|
;---|*| Entry Conditions:
;---|*| dParm1 points to the user routine
;---|*|
;---|*| Exit Conditions:
;---|*| Nothing
;---|*|
; \*/

public UserFunc
UserFunc proc
push bp
mov bp,sp

push es

les ax,dParm1 ; get the routine
mov dx,es
or dx,ax
jz usfu_bad

mov wptr [UserRoutine+0],ax
mov wptr [UserRoutine+2],es
;
usfu_bad:
pop es
pop bp
ret

UserFunc endp

endif ; PCMOBJ

;
;
;----------------------------========================--------------------------
;----------------------------========================--------------------------
;----------------------------========================--------------------------
;
;
; PPPPPPPP CCCCCC MMM MMM
; PPPPPPPP CCCCCCC MMMM MMMM
; PPP PPP CCC MMMMM MMMMM
; PPP PPP CCC MMMMMM MMMMMM
; PPPPPPPP CCC MMM MMMMM MMM
; PPPPPPPP CCC MMM MMM MMM
; PPP CCC MMM M MMM
; PPP CCC MMM MMM
; PPP CCCCCCC MMM MMM
; PPP CCCCCC MMM MMM
;
; rrrrr oooo uu uu tttttt iiiiii nn nn eeeee sssss
; rr rr oo oo uu uu tt ii nnn nn ee ss
; rrrrrr oo oo uu uu tt ii nnnnnn eeeeee ssss
; rr rr oo oo uu uu tt ii nn nnn ee ss
; rr rr oooo uuuuuu tt iiiiii nn nn eeeee sssss
;
;
;
;----------------------------========================--------------------------
;----------------------------========================--------------------------
;----------------------------========================--------------------------

if PCMOBJ

;
;----------------------------====< _ASloadtimer0 >====-------------------------
;
;
; Setup the Sample Timer (T0 & square wave output)
;
; Entry Conditions:
; ES:DI point to the state table.
; Exit Conditions:
; The timer is loaded with the new sample rate
;
public _ASloadtimer0
_ASloadtimer0 proc

mov al,00110110b ; 36h Timer 0 & square wave
mov dx,TMRCTLR
xor dx,[_MVTranslateCode] ; xlate the board address

pushf
cli

out dx,al ; setup the mode, etc
mov es:[di._tmrctlr],al

mov ax,es:[di._samplerate] ; pre-calculated & saved in prior code
mov dx,SAMPLERATE
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al ; output the timer value

pause

xchg ah,al
out dx,al

popf
ret

_ASloadtimer0 endp

;
;----------------------------====< _ASloadtimer1 >====-------------------------
;
public _ASloadtimer1
_ASloadtimer1 proc
push es
push di
les di,[mvhwShadowPointer]

push dx ; do not disturb any register
push ax

mov al,01110100b ; 74h Timer 1 & rate generator
mov dx,TMRCTLR
xor dx,[_MVTranslateCode] ; xlate the board address

disable

out dx,al
mov es:[di._tmrctlr],al ; local timer control register

pop ax

mov dx,SAMPLECNT
xor dx,[_MVTranslateCode] ; xlate the board address
mov es:[di._samplecnt],ax

out dx,al
pause
xchg ah,al
out dx,al

enable

xchg ah,al

pop dx
pop di
pop es
ret

_ASloadtimer1 endp
;
;
;--------------------------====< _calcsamplerate >====-------------------------
;
; Calculate the H/W timer value (internal routine)
;
; Entry Conditions:
; DX:AX hold the users requested sample rate
;
; Exit Conditions:
; carry SET if bad value
; No registers modified
;
;
public _calcsamplerate
_calcsamplerate proc
push es
push di
les di,[mvhwShadowPointer]

push ax
push bx
push cx
push dx
;
; make sure sample rate does not exceed 88200
;
mov cx,ax ; do 32 bit subtraction
sub cx,05888H+1 ; 157C0 is decimal 88200
mov cx,dx ; too high?
sbb cx,00001H ; over 88200 khz is bad
jnc CaSaRa_bad ; bomb out greater than 88200
;
; load 1193180 in bx:cx for 32x32 bit division
;
mov bx,0012h
mov cx,34dch ; load bx:cx with 1193180

xchg bx,dx ; dx:ax = 1193180
xchg cx,ax ; bx:cx = sample rate
;
; since we don't have 32x32 bit division, we'll cheat here. No great loss.
;
or bx,bx ; is value over 64k?
jz @F ; no, continue on...
shr bx,1 ; yes, divide all by 2
rcr cx,1 ; to allow division of 32x16 bits
shr dx,1
rcr ax,1
;
@@:
div cx
mov es:[di._samplerate],ax ; save just the low order
clc
jmp short CaSaRa_exit
;
CaSaRa_bad:
stc
;
CaSaRa_exit:
pop dx
pop cx
pop bx
pop ax

pop di
pop es
ret

_calcsamplerate endp
;
;
;----------------------------====< EnableRecording >====-----------------------
;
public EnableRecording
EnableRecording proc
;
; Save the type of setup. It contains the interrupt masks needed
;
mov [TypeOfSetup],POLLEDINPUT
mov [PCMDirection],0 ; bit 6 is cleared (DAC enable)
;
; enable the onboard sampling timers, disable interrupts
;
call FFAR ptr SetupPCMPolledIO ; Setup the MV Hardware
mov StatusWord,2 ; set the global status word
ret

EnableRecording endp
;
;
;----------------------------====< EnablePlaying >====-------------------------
;
public EnablePlaying
EnablePlaying proc
;
; Save the type of setup. It contains the interrupt masks needed
;
mov [TypeOfSetup],POLLEDOUTPUT
mov [PCMDirection],bCCdac ; bit d6 of interrupt control register
;
; enable the onboard sampling timers, etc.
;
call FFAR ptr SetupPCMPolledIO ; Setup the MV Hardware
mov StatusWord,1 ; set the global status word

ret

EnablePlaying endp
;
endif ; PCMOBJ
if MISC2OBJ
;
;---------------------------====< _getirqoffset >====--------------------------
;
; takes the IRQ # & converts to offset in vector table
;
;
public _getirqoffset
_getirqoffset proc
sub bh,bh
shl bx,1
mov bx,cs:[irqoffsets+bx]
ret

irqoffsets label word
dw (8+0)*4 ; IRQ 0 clock timer
dw (8+1)*4 ; IRQ 1 keyboard
dw (8+2)*4 ; IRQ 2 available
dw (8+3)*4 ; IRQ 3 COM2
dw (8+4)*4 ; IRQ 4 COM1
dw (8+5)*4 ; IRQ 5 available
dw (8+6)*4 ; IRQ 6 floppy controller
dw (8+7)*4 ; IRQ 7 LPT
dw (70h+0)*4 ; IRQ 8 real time clock
dw (70h+1)*4 ; IRQ 9 Re-Directed IRQ2
dw (70h+2)*4 ; IRQ a available
dw (70h+3)*4 ; IRQ b available
dw (70h+4)*4 ; IRQ c available
dw (70h+5)*4 ; IRQ d coprocessor
dw (70h+6)*4 ; IRQ e Hard Disk
dw (70h+7)*4 ; IRQ f available

_getirqoffset endp
;
endif ; MISC2OBJ
if PCMOBJ
;
;------------------------------====< KillDMA >====-----------------------------
;
; KillDMA -- flush our settings
;
; Entry Conditions:
;
KillDMA proc
push es
push di

cmp StatusWord,0 ; is there any activity?
jz kidm_none

mov di,[DMAPointer] ; get the DMA pointer
sub dx,dx ; clear out the high byte
;
; mask out the DMA to stop it
;
disable
mov al,[di._dmach] ; get the adjusted dma channel #
or al,0100b ; disable the DMA
mov dl,[di._dmawrsmr]
out dx,al
;
; remove control on the DRQ line
;
les di,[mvhwShadowPointer]

mov al,es:[di._crosschannel]; get the state
mov dx,CROSSCHANNEL
xor dx,[_MVTranslateCode] ; xlate the board address
and al,not bCCdrq ; clear the DRQ bit
out dx,al

mov es:[di._crosschannel],al; and save the new state
enable
;
kidm_none:
pop di
pop es
ret

KillDMA endp
;
endif ; PCMOBJ
if PCMOBJ
;
;------------------------------====< LoadDMA >====-----------------------------
;
; LoadDMA -- Load the DMA controller to read/write data to the MV board
;
; Entry Conditions:
;
;
public LoadDMA
LoadDMA proc
push es
push di
push si

les di,[mvhwShadowPointer]

mov si,[DMAPointer] ; point to the DMA controller table
sub dx,dx ; clear out the high byte
;
; kill the dma until all programming is done
;
mov al,[si._dmach] ; get the adjusted dma channel #
or al,0100b ; causes all DMA to be suspended
mov dl,[si._dmawrsmr]
out dx,al
;
; program the mode
;
mov al,[TheDMAmode] ; get the app's desired mode
or al,[si._dmach] ; merge the adjusted dma channel #
mov dl,[si._dmawrmode]
out dx,al
;
; adjust the address for a 16 bit channel
;
mov ax,wptr [LinearPtr+2] ; get the page #
;
; setup the page register
;
mov dx,[OurDMAPageReg]
out dx,al
mov bl,al
;
; reset the flip-flop, then output the address, then count
;
mov dl,[si._dmaclear] ; dh is still clear...
out dx,al ; flush...

mov ax,wptr [LinearPtr+0] ; get the low 16 bits
cmp si,offset DMA1AddrTable ; 1st DMA controller?
jz @F ; yes, continue on...
shr bl,1 ; no, divide the buffer in half
rcr ax,1 ; by shifting 17 bits
;
@@:
mov dx,[OurDMAddress]
out dx,al
pause
xchg ah,al
out dx,al

mov ax,[DMABuffLength]
cmp si,offset DMA1AddrTable ; is this the 2nd dma controller?
jz lodma03 ; no, use the full length
shr ax,1
inc dx ; move to next port address
;
lodma03:
inc dx ; move to next port address
out dx,al
pause
xchg ah,al
out dx,al
;
; before we enable the DMA, let's make sure the DRQ is controlled, not floating
;
mov al,es:[di._crosschannel]; get the state
mov dx,CROSSCHANNEL
xor dx,[_MVTranslateCode] ; xlate the board address
or al,bCCdrq ; set the DRQ bit to control it
out dx,al
mov es:[di._crosschannel],al; and save the new state
;
; re-enable the dma now that all programming is done
;
mov al,[si._dmach] ; get the adjusted dma channel #
sub dx,dx ; clear dh
mov dl,[si._dmawrsmr]
out dx,al ; & let'er loose (not moving though...)
;
; all done, return home...
;
pop si
pop di
pop es
ret

LoadDMA endp
;
endif ; PCMOBJ
if MISC2OBJ
;
;--------------------------====< _loadirqvector >====--------------------------
;
; Restore the original vector
;
; Entry Conditions:
;
; Exit Conditions:
; Nothing

public _loadirqvector
_loadirqvector proc
push es

les ax,OldIRQRoutine
mov bx,es
or bx,ax
jnz @F

lea ax,OurIntVector
mov dx,cs

mov bl,[TheIRQChannel]
call FFAR ptr _getirqoffset

push ds
sub cx,cx
mov ds,cx

disable
xchg ds:[bx+0],ax
xchg ds:[bx+2],dx
enable

pop ds

mov wptr [OldIRQRoutine+0],ax
mov wptr [OldIRQRoutine+2],dx
;
@@:
pop es
ret

_loadirqvector endp
;
endif ; MISC2OBJ
if MISC2OBJ
;
;---------------------------====< OurIntVector >====---------------------------
;
; OurIntVector -- process DMA interrupts
;

intsemaphore db -1 ; -1 free, 0+ locked

public OurIntVector
OurIntVector proc
push dx
push ax
push ds

if MODELSIZE eq 0
mov ax,cs ; tiny (.com) model uses cs segment
else
mov ax,@data ; all others use their data segments
endif
mov ds,ax

mov dx,INTRCTLRST ; clear the interrupt
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx

test al,bISsampbuff ; our interrupt?
jz skip_int ; no, continue on...

out dx,al ; yes, flush it...

mov al,EOI ; clear the interrupt
cmp TheIRQChannel,8 ; 2nd IRQ controller?
jb @F
out IRQ2ACKREG,al
;
@@:
out IRQ1ACKREG,al

inc [NumberOfInterrupts]

inc cs:[intsemaphore]
jnz oiv_done

sti ; let interrupts go while we work

cmp wptr [UserRoutine+2],0 ; call the user function?
jz oiv_done ; no, just exit

call dword ptr [UserRoutine]
;
oiv_done:
dec cs:[intsemaphore]
;
exit_int:
pop ds
pop ax
pop dx
iret
;
skip_int:
pushf
call dword ptr ds:[OldIRQRoutine] ; perform the old interrupt
jmp short exit_int

OurIntVector endp
;
endif ; MISC2OBJ
if PCMOBJ
;
;---------------------------====< SetupPCMDMAIO >====--------------------------
;
; SetupPCMDMAIO -- Setup to output to the DAC
;
public SetupPCMDMAIO
SetupPCMDMAIO proc
push es
push di
les di,[mvhwShadowPointer]
;
; setup the sample rate timer
;
call _ASloadtimer0
;

; Setup the Sample Buffer Counter Timer (T1 & rate generator)
;
mov ax,[DMABuffLength] ; get the count
sub dx,dx
add ax,1 ; make it 1 based (1 - 64k)
adc dx,dx
mov cl,[DMABUffDivides] ; get the buffer size
sub ch,ch
div cx ; now, we must be flexible

mov bl,[TheDMAChannel] ; is this a 16 bit channel?
mov bh,[SampleSize] ; CX = sample size, channel

sub cx,cx ; ch = multiplier, cl=divider
cmp bx,0003h ; 8 bits on 8 bit channel?
jbe @F ; yes, continue on...

inc cx ; divide by 2
cmp bx,0007h ; 8 bits on 16 bit channel?
jbe @F ; yes, continue on...

xchg ch,cl ; multiply by 2
cmp bx,0203h ; 16 bits on 8 bit channel?
jbe @F ; yes, continue on...
sub cx,cx ; no multiply or divide
;
@@:
shr ax,cl ; if 8 on 16 divide by 2
xchg ch,cl
shl ax,cl ; if 16 on 8 multiply by 2

sub cx,cx ; The buffer size is # of bytes, so
neg bh ; we must convert it to the data size
adc cx,cx
shr ax,cl

call FFAR ptr _ASloadtimer1
;
; Setup the system interrupt mask (IRQ mask)
;
mov dx,IRQ1MASKREG
cmp TheIRQChannel,8 ; 2nd IRQ controller?
jl @F
mov dl,IRQ2MASKREG
;
@@:
in al,dx ; get the mask
mov ah,TheIRQMask
not ah
and al,ah ; unmask the correct IRQ
out dx,al ; then let the system know
;
; Setup the Interrupt Control Register
;
mov dx,INTRCTLRST ; flush any pending interrupts
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al ; of the PCM circuitry

mov dx,INTRCTLR
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx ; get the real mask
or al,bICsampbuff ; interrupt on sample buffer count
out dx,al ; send it..
mov es:[di._intrctlr],al ; save it..
;
; enable the 12/16 bit stuff
;
test [_MVHWVersionBits],bMV101 ; 101 chip?
jz sdhpas1_05 ; no, don't touch this...

mov cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit+bSC212bit
cmp [SampleSize],1 ; 12 bit?
jz @F
mov cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit
cmp [SampleSize],2 ; 16 bit?
jz @F
mov cx,((NOT(bSC216bit+bSC212bit))*256) + 0
;
@@:
mov dx,SYSCONFIG2
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx
and al,ch ; clear the bits
or al,cl ; set the appropriate bits
out dx,al
;
sdhpas1_05:
;
; setup the direction, stereo/mono and DMA enable bits
;
mov al,bCCmono ; get the stereo/mono mask bit
and al,[StereoMono] ; al = bCCmono if in mono mode
or al,[PCMDirection] ; get the direction bit mask
or al,bCCenapcm ; enable the PCM state machine
mov dx,CROSSCHANNEL
xor dx,[_MVTranslateCode] ; xlate the board address
mov ah,0fh + bCCdrq ; get a mask to load non PCM bits
and ah,es:[di._crosschannel]; grab all but PCM/DRQ/MONO/DIRECTION
or al,ah ; merge the two states
xor al,bCCenapcm ; disable the PCM bit
out dx,al ; send to the hardware
xor al,bCCenapcm ; enable the PCM bit
out dx,al ; send to the hardware
mov es:[di._crosschannel],al; and save the new state
;
; Setup the audio filter sample bits
;
mov al,es:[di._audiofilt]
or al,(bFIsrate+bFIsbuff) ; enable the sample count/buff counters
mov dx,AUDIOFILT
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al
mov es:[di._audiofilt],al

mov NumberOfInterrupts,0

enable ; Fly, baby Fly!!!

pop di
pop es
ret

SetupPCMDMAIO endp
;
;
;---------------------------====< SetupPCMPolledIO >====-----------------------
;
; SetupPCMPolledIO -- Setup to read/write to the ADC/DAC in a polled fashion
;
public SetupPCMPolledIO
SetupPCMPolledIO proc
push es
push di
les di,[mvhwShadowPointer]
;
; Setup the Sample Timer (T0 & square wave output)
;
call _ASloadTimer0
;
; Setup the Sample Buffer Counter Timer (T1 & rate generator)
;
mov ax,1 ; get the count
call FFAR ptr _ASloadtimer1
;
; Setup the Interrupt Control Register
;
mov dx,INTRCTLR
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx ; get the real mask
or al,bICsampbuff+bICsamprate ; enable both sample timer ints
out dx,al ; send it...
mov es:[di._intrctlr],al ; save it...
;
; enable the 16 bit stuff
;
test [_MVHWVersionBits],bMV101 ; 101 chip?
jz sphpas1_05 ; no, don't touch this...

mov cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit+bSC212bit
cmp [SampleSize],1 ; 12 bit?
jz @F
mov cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit
cmp [SampleSize],2 ; 16 bit?
jz @F
mov cx,((NOT(bSC216bit+bSC212bit))*256) + 0
;
@@:
mov dx,SYSCONFIG2
xor dx,[_MVTranslateCode] ; xlate the board address
in al,dx
and al,ch ; clear the bits
or al,cl ; set the appropriate bits
out dx,al
;
sphpas1_05:
;
; setup the direction, stereo/mono and DMA enable bits
;
mov al,bCCmono ; get the stereo/mono mask bit
and al,[StereoMono] ; al = bCCmono if in mono mode
or al,[PCMDirection] ; get the direction bit mask
or al,bCCenapcm + bCCdrq ; enable the PCM & set DRQ for NOT mask
mov dx,CROSSCHANNEL
xor dx,[_MVTranslateCode] ; xlate the board address
mov ah,0fh ; get a mask to load the non-PCM bits
and ah,es:[di._crosschannel]; grab all but PCM/DRQ/MONO/DIRECTION
or al,ah ; merge the two states
xor al,bCCenapcm + bCCdrq ; disable the PCM & DRQ
out dx,al ; send to the hardware
xor al,bCCenapcm ; enable the PCM
out dx,al
mov es:[di._crosschannel],al; and save the new state
;
; Setup the audio filter sample bits to enable sample timer/count
;
mov al,es:[di._audiofilt]
or al,(bFIsrate+bFIsbuff) ; enable the sample timer/counter
mov dx,AUDIOFILT
xor dx,[_MVTranslateCode] ; xlate the board address
out dx,al
mov es:[di._audiofilt],al

sub ax,ax
mov NumberOfInterrupts,ax

enable

pop di
pop es
ret

SetupPCMPolledIO endp
;
;
;----------------------------====< StartPlaying >====--------------------------
;
public StartPlaying
StartPlaying proc
;
; Save the type of setup. It contains the interrupt masks needed
;
mov [TypeOfSetup],DMAOUTPUT
mov [PCMDirection],bCCdac ; bit d6 of interrupt control register
;
; Select the DMA mode for playing
;
mov al,10h ; auto init bit on 8237 chip
and al,[DMAAutoInit] ; al may have the auto-init bit
or al,48h ; merge in the rest of the DMA bits
mov TheDMAMode,al ; save the mode
;
; Program the DMA, then our board circuitry to start the interrupts
;
call LoadDMA ; setup the DMA controller
call SetupPCMDMAIO ; Setup the MV Hardware

mov StatusWord,1 ; set the global status word

ret

StartPlaying endp
;
;
;---------------------------====< StartRecording >====-------------------------
;
public StartRecording
StartRecording proc
;
; Save the type of setup. It contains the interrupt masks needed
;
mov [TypeOfSetup],DMAINPUT
mov [PCMDirection],00h ; bit d6 of interrupt control register
;
; Select the DMA mode for recording
;
mov al,10h ; auto init bit on 8237 chip
and al,[DMAAutoInit] ; al may have the auto-init bit
or al,44h ; merge in the rest of the DMA bits
mov TheDMAMode,al ; save the mode
;
; Program the DMA, then our board circuitry to start the interrupts
;
call LoadDMA ; setup the DMA controller
call SetupPCMDMAIO ; Setup the MV Hardware

mov StatusWord,2 ; set the global status word
ret

StartRecording endp
;
endif ; PCMOBJ
if MISC2OBJ
;
;---------------------====< _unloadirqvector >====---------------
;
; Restore the original vector
;
; Entry Conditions:
; dParm1 points to the user routine
;
; Exit Conditions:
; Nothing
;
;
public _unloadirqvector
_unloadirqvector proc
push es

les ax,OldIRQRoutine
mov bx,es
or bx,ax
jz @F

mov bl,[TheIRQChannel]
call _getirqoffset

push ds
sub cx,cx
mov ds,cx

disable
mov ds:[bx+0],ax
mov ds:[bx+2],es
enable

pop ds

sub ax,ax
mov wptr [OldIRQRoutine+0],ax
mov wptr [OldIRQRoutine+2],ax
;
@@:
pop es
ret

_unloadirqvector endp

endif ; MISC2OBJ

end



  3 Responses to “Category : Music and Digitized Voice
Archive   : PAS-SDK1.ZIP
Filename : MVSOUND.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/