DOSSEG
LOCALS
.MODEL SMALL
.CODE
.386
ASSUME cs:@code, ds:@code
IDEAL
INCLUDE "PROMOD.INC"
;To Initialize:
; DspReset
; TurnOnSpeaker
; SetSampleRate
; CalcForDMA => call whenever song speed changes
;
;To Start:
; StartTransferDma
;
;To End:
; mov [QuitDMA],1
; TurnOffSpeaker
BufferSeg dw 0
BufferOffset1 dw 0
BufferOffset2 dw 0
Buffer1Size dw 1000
Buffer2Size dw 1000
StereoOn db 0 ;0= stereo is OFF.
LptAddress1 dw 3bch
LptAddress2 dw 3bch
BaseAddress dw 220h
IntNumber db 7
DMANumber db 1
DMAPage dw 83h
DMAStart dw 2
DmaChartPage dw 87h,83h,82h
DMAStartChart db 0,2,6
WhichBuffer dw 4
QuitDma dw 0
MoreB db 0,0
IPage db 0,0,0,0
ILength dw 1,1,1,1
IOffset dw 0,0,0,0
OldDmaIntOff dw 0
OldDmaIntSeg dw 0
RealDmaOff dw 0
RealDmaSeg dw 0
DmaRoutine dw offset TransferDmaMONO
OldInt dd 0
DacBuffOff dw 0
DacBuffLen dw 0
;==================
;==- Interrupts -==
;==================
DMAINT1:
pusha
cli
mov [cs:WhichBuffer],3
mov ax,[cs:BufferOffset1]
mov [word cs:QTDS.CurBuffptr],ax
mov ax,[cs:Buffer1Size]
mov [word cs:QTDS.BuffSize],ax
mov dx,[cs:BaseAddress]
add dx,0eh
in al,dx ;acknowledge interrupt
cmp [cs:QUITDMA],0
jne DoneInt7 ;if we are to quit, quit now
cmp [cs:MoreB],0
jne DoDma2
jmp DoDma3
DMAINT2:
pusha
cli
mov dx,[cs:BaseAddress]
add dx,0eh
in al,dx ;acknowledge interrupt
cmp [cs:QUITDMA],0
jne DoneInt7 ;if we are to quit, quit now
jmp DoDma3
DoneInt7:
call Haltdma
call UndoRealInt
jmp SkipMask2
DMAINT3:
pusha
cli
mov [cs:WhichBuffer],4
mov ax,[cs:BufferOffset2]
mov [word cs:QTDS.CurBuffPtr],ax
mov ax,[cs:Buffer2Size]
mov [word cs:QTDS.BuffSize],ax
mov dx,[cs:BaseAddress]
add dx,0eh
in al,dx ;acknowledge interrupt
cmp [cs:QUITDMA],0
jne DoneInt7 ;if we are to quit, quit now
cmp [cs:MoreB],0
jne DoDma4
jmp DoDma1
DMAINT4:
pusha
cli
mov dx,[cs:BaseAddress]
add dx,0eh
in al,dx ;acknowledge interrupt
cmp [cs:QUITDMA],0
jne DoneInt7 ;if we are to quit, quit now
jmp DoDma1
DoDma1:
mov ah,[cs:IPage] ;set up for dma 1
mov cx,[cs:Ilength]
mov dx,[cs:Ioffset]
mov bx,offset DMAInt1
jmp short skipmask
DoDma2:
mov ah,[cs:IPage+1] ;set up for dma 2
mov cx,[cs:ILength+2]
mov dx,[cs:IOffset+2]
mov bx,offset DMAInt2
jmp short skipmask
DoDma3:
mov ah,[cs:IPage+2] ;set up for dma 3
mov cx,[cs:ILength+4]
mov dx,[cs:IOffset+4]
mov bx,offset DMAInt3
jmp short skipmask
DoDma4:
mov ah,[cs:IPage+3] ;set up for dma 4
mov cx,[cs:ILength+6]
mov dx,[cs:IOffset+6]
mov bx,offset DMAInt4
;jmp short skipmask
SkipMask:
call [cs:DmaRoutine]
Skipmask2:
mov al,20h
out 20h,al ;end of hardware interrupt
sti
popa
iret
;======================
;==- Start Transfer -==
;======================
PROC StartTransferDma NEAR
pusha
cmp [cs:StereoOn],0
je NoStereo
mov ax,130eh ;turn stereo on
call SetMixer
mov ax,0ff22h ;set master volume = FF
call SetMixer
mov ax,0ff04h ;set VOC volume = FF
call SetMixer
mov [cs:DmaRoutine],offset TransferDMASTEREO
NoStereo:
mov al,[cs:IntNumber]
add al,8
cbw
shl al,2
mov bx,ax
push es
sub ax,ax
mov es,ax
mov ax,[es:bx]
mov [cs:OldDmaIntOff],ax ; Grab the current interrupt
mov ax,[es:bx+2]
mov [cs:OldDmaIntSeg],ax ; so we can restore it later
pop es
mov ah,[cs:IPage] ;set up for dma 1
mov cx,[cs:Ilength]
mov dx,[cs:Ioffset]
mov bx,offset DMAInt1
call [cs:DmaRoutine] ;start playing
popa
ret
ENDP StartTransferDma
;ah= page number
;dx= BASE_ADDRESS
;cx= DATA_LENGTH (-1)
;bx= address of interrupt
; This function sets up for write only!
PROC TransferDmaSTEREO NEAR
; dec cx
mov al,5
out 0ah,al ;Mask off channel 1
xor al,al
out 0ch,al ;Clear byte pointer F/F to lowerbyte
mov al,49h
out 0bh,al ;Set transfer mode to DAC (ADC = 45h)_
mov al,dl ;LSB of BASE_ADDRESS
out 2,al
mov al,dh ;MSB of BASE_ADDRESS
out 2,al
mov al,ah
out 83h,al ;Page Number
mov al,cl
out 3,al ;LSB of DATA_Length
mov al,ch
out 3,al ;MSB of DATA_Length
mov al,1
out 0ah,al ;Enable Channel 1
mov ax,bx
call DoREALint
mov al,48h ;data length command
call Sendcommand
mov al,cl ;send LSB of DATALENGTH
call SendCommand
mov al,ch ;send MSB of DATALENGTH
call SendCommand
mov al,91h ;tell it to GO!
call SendCommand
ret
ENDP TransferDMASTEREO
;ah= page number
;dx= BASE_ADDRESS
;cx= DATA_LENGTH (-1)
;bx= address of interrupt
; This function sets up for write only!
PROC TransferDmaMONO NEAR
; dec cx
mov al,5
out 0ah,al ;Mask off channel 1
xor al,al
out 0ch,al ;Clear byte pointer F/F to lowerbyte
mov al,49h
out 0bh,al ;Set transfer mode to DAC (ADC = 45h)_
mov al,dl ;LSB of BASE_ADDRESS
out 2,al
mov al,dh ;MSB of BASE_ADDRESS
out 2,al
mov al,ah
out 83h,al ;Page Number
mov al,cl
out 3,al ;LSB of DATA_Length
mov al,ch
out 3,al ;MSB of DATA_Length
mov al,1
out 0ah,al ;Enable Channel 1
mov ax,bx
call DoREALint
mov al,14h ;function 14h DMAmode 8-bit DAC
call Sendcommand
mov al,cl ;send LSB of DATALENGTH
call SendCommand
mov al,ch ;send MSB of DATALENGTH
call SendCommand
ret
ENDP TransferDMAMONO
;ah= SR (Time Constant = 256 - 1,000,000/HZ)
PROC SetSampleRate NEAR
mov al,40h
call SendCommand
mov al,ah
call SendCommand
ret
ENDP SetSampleRate
PROC TurnOffSpeaker NEAR
mov al,0d3h ;turn off speaker
call Sendcommand
ret
ENDP TurnOffSpeaker
PROC HaltDMA NEAR
mov al,0d0h ;Halt DMA
call SendCommand
ret
ENDP HaltDma
;al = command
PROC SendCommand NEAR
push dx
push ax
mov dx,[cs:BaseAddress]
add dx,0ch
sendcommandloop:
in al,dx
test al,10000000b
jnz sendcommandloop
pop ax
out dx,al
pop dx
ret
ENDP SendCommand
PROC TurnOnSpeaker NEAR
mov al,0d1h
call SendCommand
ret
ENDP TurnOnSpeaker
;input- none
;output al=0 successful
; al=1 unsuccessful
;DESTROYED: ax,dx,cx
PROC DspReset NEAR
mov dx,[cs:baseaddress]
add dx,06h
mov al,1
out dx,al
mov cx,1000
push di
rep lodsb ;wait for at least 3æS
pop di
xor al,al
out dx,al
add dx,8 ;check WhichBuffer (22eh)
mov cx,12000
waitforstat:
in al,dx
dec cx
je errorstat
test al,10000000b
jz waitforstat
mov cx,10000
sub dx,4 ;(22ah)
waitforstat2:
in al,dx
dec cx
je errorstat
cmp al,0aah
jne waitforstat2
mov al,0
ret
errorstat:
mov al,1
ret
ENDP DspReset
;========================
;==- Start Interrupts -==
;========================
;cs:ax=location of interrupt
PROC DoRealint NEAR
pushf ; Push flags
push bx
push cx
push dx
mov dx,ax
mov al,[cs:IntNumber]
add al,8
cbw ; Convrt byte to word
shl ax,2 ; Shift w/zeros fill
mov bx,ax
push es
sub ax,ax
mov es,ax
mov [es:bx],dx
mov [es:bx+2],cs
pop es
mov cl,[cs:IntNumber]
mov ah,1
shl ah,cl ; Shift w/zeros fill
not ah
in al,21h ; port 21h, 8259-1 int IMR
and al,ah
out 21h,al ; port 21h, 8259-1 int comands
pop dx
pop cx
pop bx
popf ; Pop flags
ret
ENDP DoRealint
PROC UnDoREALint NEAR
pushf
pusha
mov al,[cs:IntNumber]
add al,8
cbw
shl ax,2
mov di,ax
push es
sub ax,ax
mov es,ax
mov ax,[cs:OldDmaIntOff]
mov [es:di],ax
mov ax,[cs:OldDmaIntSeg]
mov [es:di+2],ax
pop es
mov cl,[cs:IntNumber] ; (=7)
mov ah,1
shl ah,cl ; Shift w/zeros fill
in al,21h ; port 21h, 8259-1 int IMR
or al,ah
out 21h,al ; port 21h, 8259-1 int comands
popa
popf ; Pop flags
ret
ENDP UnDoREALint
PROC CalcForDMA NEAR
pushad
push ds
mov ax,cs
mov ds,ax
mov [MoreB],0
movzx eax,[BufferSeg]
movzx edx,[BufferOffset1] ;offset for part1
call MakePage
cmp cx,[buffer1size] ;is max less than needed?
jb skiplengthset
mov cx,[buffer1size]
jmp nomoreb1
skiplengthset:
mov bx,[buffer1size]
sub bx,cx
mov [MoreB],1
dec bx
mov [ILength+2],bx
mov [IOffset+2],0
mov [IPage+1],ah
inc [IPage+1]
NoMoreb1:
mov [IPage],ah
mov [IOffset],dx
dec cx
mov [ILength],cx
mov [MoreB+1],0
movzx eax,[BufferSeg]
movzx edx,[BufferOffset2] ;offset for part1
call MakePage
cmp cx,[buffer2size] ;is max less than needed?
jb skiplengthset2
mov cx,[buffer2size]
jmp nomoreb2
skiplengthset2:
mov bx,[buffer2size]
sub bx,cx
mov [MoreB+1],1
dec bx
mov [ILength+6],bx
mov [IOffset+6],0
mov [IPage+3],ah
inc [IPage+3]
NoMoreb2:
mov [IPage+2],ah
mov [IOffset+4],dx
dec cx
mov [ILength+4],cx
pop ds
popad
sti
ret
ENDP CalcForDMA
;input: eax=segment
; edx=offset
;output: ah=page
; dx=offset
; cx=MAX_LENGTH
PROC MakePage NEAR
shl eax,4
add edx,eax ;dx = 32bit absolute address
ror edx,16
mov ah,dl ;ah= page
ror edx,16
mov cx,dx
neg cx
ret
ENDP MakePage
;al = selection#, ah= value
PROC SetMixer NEAR
push ax dx
mov dx,224h
out dx,al
inc dx
jmp $+2
jmp $+2
mov al,ah
out dx,al
pop dx ax
ret
ENDP SetMixer
;====
;==== DAC Stuff...
;====
PROC SetUpInterrupt NEAR
pushad
push ds
mov ax,0
mov ds,ax
mov bx,8*4 ;interrupt 8
mov ax,[ds:bx]
mov [Word LOW cs:OldInt],ax
mov ax,[ds:bx+2]
mov [Word HIGH cs:OldInt],ax
mov ax,offset TimerInt
cmp [cs:StereoOn],0
je @@NoStereo
mov ax,offset TimerIntStereo
shr [cs:HZ],1
@@NoStereo:
cli
mov [Word ds:bx],ax
mov [ds:bx+2],cs
mov al,36h
out 43h,al ; timer program
mov eax,1193180
movzx ebx,[cs:HZ] ; # of ticks between interrupts
xor edx,edx
div ebx
; Clock Freq / HZ
out 40h,al
mov al,ah
out 40h,al
; mov al,5ch
; out 21h,al
sti
pop ds
popad
ret
ENDP SetUpInterrupt
PROC RemoveInterrupt NEAR
pusha
push ds
cli
mov ax,0
mov ds,ax
mov bx,8*4
mov ax,[Word cs:OldInt]
mov [ds:bx],ax
mov ax,[Word cs:OldInt+2]
mov [ds:bx+2],ax
mov al,36h
out 43h,al
xor al,al
out 40h,al
out 40h,al
; mov al,0
; out 21h,al
sti
pop ds
popa
ret
ENDP RemoveInterrupt
PROC TimerInt FAR
pusha
push fs
mov fs,[cs:BufferSeg]
mov di,[cs:DacBuffOff]
mov al,[fs:di]
mov dx,[cs:LptAddress1]
out dx,al
inc [cs:DacBuffOff]
mov ax,[cs:DacBuffLen]
cmp [cs:DacBuffOff],ax
jb @@NotOver
;set up for next buffer
cmp [cs:WhichBuffer],3
je SetDac3
cmp [cs:WhichBuffer],5
je SetDac3
mov [cs:WhichBuffer],3
mov ax,[cs:BufferOffset2]
mov [cs:DacBuffOff],ax
add ax,[cs:Buffer2Size]
sub ax,2
mov [cs:DacBuffLen],ax
jmp @@NotOver
SetDac3:
mov [cs:WhichBuffer],4
mov ax,[cs:BufferOffset1]
mov [cs:DacBuffOff],ax
add ax,[cs:Buffer1Size]
sub ax,2
mov [cs:DacBuffLen],ax
@@NotOver:
mov al,20h ;acknowledge hardware interrupt
out 20h,al
pop fs
popa
iret
ENDP TimerInt
PROC TimerIntStereo FAR
pusha
push fs
mov fs,[cs:BufferSeg]
mov di,[cs:DacBuffOff]
mov al,[fs:di]
mov dx,[cs:LptAddress1]
out dx,al
mov al,[fs:di + 1]
mov dx,[cs:LptAddress2]
out dx,al
add [cs:DacBuffOff],2
mov ax,[cs:DacBuffLen]
cmp [cs:DacBuffOff],ax
jb @@NotOver
;set up for next buffer
cmp [cs:WhichBuffer],3
je @@SetDac3
cmp [cs:WhichBuffer],5
je @@SetDac3
mov [cs:WhichBuffer],3
mov ax,[cs:BufferOffset2]
mov [cs:DacBuffOff],ax
add ax,[cs:Buffer2Size]
sub ax,2
mov [cs:DacBuffLen],ax
jmp @@NotOver
@@SetDac3:
mov [cs:WhichBuffer],4
mov ax,[cs:BufferOffset1]
mov [cs:DacBuffOff],ax
add ax,[cs:Buffer1Size]
sub ax,2
mov [cs:DacBuffLen],ax
@@NotOver:
mov al,20h ;acknowledge hardware interrupt
out 20h,al
pop fs
popa
iret
ENDP TimerIntStereo
END