📄 sbx.asm
字号:
;/////////////////////////////////////////////////////////////////////////////
;// sbx.asm
;//
;// Creative Labs SoundBlaster 2.0+ DSP driver module
;//
;// 10/08/1999 fOSSiL Initial version
;// 28/10/1999 fOSSiL All debug code nuked; better timing added
;// IRQ verification
;// 2000/01/14 The Owl nasm port
;// 2000/01/17 The Owl nasm port
;// 2000/06/21 The Owl dword aligned data variables
%include "util.mac"
global DspBase
global DspIRQ
global DspDMA
global DspIs16
global DspStereo
global DspRate
global DspVer
global DspName
global DspPlay
global DspCheckInt
global DspStop
global DspPause
global DspResume
global DspDetect
global DspReset
global DspRead
global DspWrite
bits 32
segment _LDATA
DspName db 'Creative Labs SoundBlaster',0
align 4
DspBase dd 0 ; IO base addr
DspIRQ db 0 ;
DspDMA db 0 ; DRQ channel
DspIs16 db 0 ; card capability
DspStereo db 0 ; card capability
DspRate dd 0 ; maximum playback sampling rate
DspVer dw 0 ; chip version
Is16bitPlay db 0
IsHSDmaPlay db 0
DSP_REG_RESET EQU 6
DSP_REG_MADDR EQU 4
DSP_REG_MDATA EQU 5
DSP_REG_READ EQU 0Ah
DSP_REG_WRITE EQU 0Ch
DSP_REG_W_STAT EQU 0Ch
DSP_REG_R_STAT EQU 0Eh
DSP_REG_I16_ACK EQU 0Fh
DSP_READY EQU 0AAh
DSP_CMD_VERSION EQU 0E1h
DSP_CMD_BLOCK_SIZE EQU 048h
DSP_CMD_SPEAKER_ON EQU 0D1h
DSP_CMD_TIME_CONST EQU 040h
DSP_CMD_OUT_RATE EQU 041h
DSP_CMD_AUTO_INIT_8 EQU 01Ch
DSP_CMD_AUTO_INIT_8_HS EQU 090h
DSP_CMD_AUTO_INIT_16 EQU 0B6h
DSP_CMD_STOP_8 EQU 0DAh
DSP_CMD_STOP_16 EQU 0D9h
DSP_CMD_PAUSE_8 EQU 0D0h
DSP_CMD_RESUME_8 EQU 0D4h
DSP_CMD_PAUSE_16 EQU 0D5h
DSP_CMD_RESUME_16 EQU 0D6h
DSP_PARAM_MONO EQU 000h
DSP_PARAM_STEREO EQU 020h
DSP_PARAM_SIGNED EQU 010h
DSP_MREG_IRQ EQU 80h
DSP_MREG_DMA EQU 81h
DSP_MREG_ISTAT EQU 82h
DETECT_START EQU 210h
DETECT_STEP EQU 10h
DETECT_COUNT EQU 6
BIT_STEREO EQU 16
BIT_16BIT EQU 17
IRQ_16BIT_IO EQU 2
IRQ_8BIT_IO EQU 1
segment _LTEXT
DspPlay:
; EBX = sampling rate
; ECX = stereo << 16 | block size in bytes
; no testing will be done for invalid params
call DspReset ; just in case =)
; turn the speaker on
mov al, DSP_CMD_SPEAKER_ON
call DspWrite
cmp word [DspVer], 400h
jb .timeconst
; ver 4.x supports better timing
mov al, DSP_CMD_OUT_RATE
call DspWrite
mov al, bh
call DspWrite
mov al, bl
call DspWrite
jmp short .iotype
; output time constant
; const = 256 - 1000000 / Sampling freq
; for below 4.0 we dont play stereo
.timeconst:
mov al, DSP_CMD_TIME_CONST
call DspWrite
mov eax, 1000000
xor edx, edx
idiv ebx
neg al
call DspWrite
.iotype:
xor ebx, ebx ; 8-bit mode is default
; no one will play 8-bit stereo on 4.0+
; at least i hope so =)
bt ecx, BIT_16BIT
jc .ver4_16
mov al, DSP_CMD_BLOCK_SIZE
call DspWrite
jmp short .block
.ver4_16:
; program for 16bit xfer
mov al, DSP_CMD_AUTO_INIT_16
call DspWrite
mov al, DSP_PARAM_SIGNED
bt ecx, BIT_STEREO ; check if Stereo
jnc .mono
or al, DSP_PARAM_STEREO
.mono:
call DspWrite
inc bl ; 16-bit mode
.block:
mov [Is16bitPlay], bl
movzx eax, cx
bt ecx, BIT_16BIT
jnc .not16
shr eax, 1
.not16:
dec eax
call DspWrite
shr eax, 8
call DspWrite
xor ebx, ebx ; non-HighSpeed DMA is default
bt ecx, BIT_16BIT
jc .done
mov al, DSP_CMD_AUTO_INIT_8
cmp word [DspVer], 201h
jb .start_8
mov al, DSP_CMD_AUTO_INIT_8_HS
inc bl ; HighSpeed DMA mode
.start_8:
call DspWrite
.done:
mov [IsHSDmaPlay], bl
retn
DspCheckInt:
; CF=0 if should handle
; CF=1 if interrupt is not ours
cmp word [DspVer], 400h
jb .oldchip
; read mixer reg IRQ status
mov edx, [DspBase]
add edx, byte DSP_REG_MADDR
mov al, DSP_MREG_ISTAT
out dx, al
call delay
inc edx
in al, dx
call delay
cmp byte [Is16bitPlay], 1
jne .not16
; we are playing 16bit
test al, IRQ_16BIT_IO
jz .notours
; ack 4.0 style
mov edx, [DspBase]
add edx, byte DSP_REG_I16_ACK
in al, dx
call delay
clc
retn
.not16:
test al, IRQ_8BIT_IO
jnz .oldchip ; it is 8-bit io int, ack it
.notours:
stc ; int not ours
retn
.oldchip:
; just ack the irq by reading stat reg
mov edx, [DspBase]
add edx, byte byte DSP_REG_R_STAT
in al, dx
call delay
clc
retn
DspStop:
cmp byte [IsHSDmaPlay], 1
jne notHSDma
; for HighSpeed DMA we have to issue a RESET to stop
call DspReset
retn
notHSDma:
mov al, DSP_CMD_STOP_8
cmp byte [Is16bitPlay], 1
jne .not16
mov al, DSP_CMD_STOP_16
.not16:
call DspWrite
retn
DspPause:
cmp byte [IsHSDmaPlay], 1
jne .can_pause
stc
retn
.can_pause:
mov al, DSP_CMD_PAUSE_8
cmp byte [Is16bitPlay], 1
jne .not16
mov al, DSP_CMD_PAUSE_16
.not16:
call DspWrite
retn
DspResume:
cmp byte [IsHSDmaPlay], 1
jne .can_resume
stc
retn
.can_resume:
mov al, DSP_CMD_RESUME_8
cmp byte [Is16bitPlay], 1
jne .not16
mov al, DSP_CMD_RESUME_16
.not16:
call DspWrite
retn
DspDetect:
mov ecx, DETECT_COUNT
mov dword [DspBase], DETECT_START
.next:
call DspReset
jnc .found
add dword [DspBase], byte DETECT_STEP
loop .next
; not found
mov dword [DspBase], 0
stc
retn
.found:
; get DSP verision
mov al, DSP_CMD_VERSION
call DspWrite
call DspRead
mov bh, al ; major ver
call DspRead
mov bl, al ; minor ver
mov word [DspVer], bx
cmp bh, 4
jb .Not4
mov byte [DspIs16], 1
mov byte [DspStereo], 1
mov dword [DspRate], 44100
; read IRQ and DMA from mixer regs
mov edx, [DspBase]
add edx, byte DSP_REG_MADDR
mov al, DSP_MREG_IRQ
out dx, al
call delay
inc edx
in al, dx
call delay
dec edx
and al, 0fh ; mask out reserved bits - only 4 LSBs used
xor ecx, ecx
dec ecx
@@
inc ecx
shr al, 1
jnc @B
mov al, [irqs + ecx]
mov [DspIRQ], al
mov al, DSP_MREG_DMA
out dx, al
call delay
inc edx
in al, dx
call delay
and al, 0ebh ; mask out reserved bits
xor cl, cl
dec cl
test al, 0e0h ; check if 16bit DMA is available
jz @F
.dma16: ; skip to 16-bit DMA bits
shr al, 5
add cl, 5
@@
inc cl
shr al, 1
jnc @B
mov [DspDMA], cl
clc
retn
.Not4:
mov byte [DspIRQ], 5
mov byte [DspDMA], 1
mov byte [DspIs16], 0
mov byte [DspStereo], 0
mov eax, 44100
cmp bx, 201h
jae .setrate
mov eax, 23000
.setrate:
mov [DspRate], eax
clc
retn
segment _LDATA
irqs db 2,5,7,10
segment _LTEXT
DspReset:
mov edx, [DspBase]
add edx, byte DSP_REG_RESET
mov al, 1
out dx, al
mov al, 0
.delay:
dec al
jnz .delay
out dx, al
call DspRead
jc .SB
.gotit:
cmp al, DSP_READY
je .SB
stc
.SB:
retn
DspRead:
push ecx
mov edx, [DspBase]
add edx, byte DSP_REG_R_STAT
mov ecx, 10000
; wait until ready or timeout
.delay:
in al, dx
test al, 80h
jnz .avail
loop .delay
; timeout, no data
stc
pop ecx
retn
.avail:
add edx, byte (DSP_REG_READ - DSP_REG_R_STAT)
in al, dx
pop ecx
clc
retn
DspWrite:
push ecx
push eax
mov edx, [DspBase]
add edx, byte DSP_REG_WRITE
mov ecx, 10000
; wait until ready
.delay:
in al, dx
test al, 80h
jz .avail
loop .delay
; timeout, can't write
pop eax
pop ecx
stc
retn
.avail:
pop eax
out dx, al
pop ecx
clc
retn
delay:
push ecx
mov ecx,16
loop $
pop ecx
retn
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -