我想在assembly / 8086 / masm / dosbox中制作一个程序,将键盘变成各种乐器,所以我需要能够播放一些.wav文件以产生所需的声音。我知道beep char和通过向pc扬声器(端口41h,42h和61h)发送频率来产生声音,但两种方式显然无法让我到那里。
我四处搜索,发现我需要使用int 21h
来打开文件,了解.wav格式以及使用Sound Blaster进行声音编程的知识。
不幸的是我找不到任何有用的文档,说明如何在Dosbox(或一般)中使用Sound Blaster,如果你可以帮我解决我在dosbox上播放.wav文件的问题,或者你有任何问题变通方法我都是耳朵(更准确的眼睛)。
答案 0 :(得分:5)
这是一个播放特定WAV文件的演示程序(以避免将RIFF解析器引入已经太长的SO代码。 该程序已经在DOSBox中进行了测试,但是在不同的配置上可能会出现很多问题。
最后,我被迫将代码分成两个答案 这是第2部分。
<强> dsp.asm 强>
.8086
.MODEL SMALL
FORMAT_MONO EQU 00h
FORMAT_STEREO EQU 20h
FORMAT_SIGNED EQU 10h
FORMAT_UNSIGNED EQU 00h
_CODE SEGMENT PARA PUBLIC 'CODE' USE16
ASSUME CS:_CODE
ResetDSP:
push ax
push dx
;Set reset bit
mov dx, REG_DSP_RESET
mov al, 01h
out dx, al
;Wait 3 us
in al, 80h
in al, 80h
in al, 80h
;Clear reset bit
xor al, al
out dx, al
;Poll BS until bit 7 is set
mov dx, REG_DSP_READ_BS
_rd_poll_bs:
in al, dx
test al, 80h
jz _rd_poll_bs
;Poll data until 0aah
mov dx, REG_DSP_READ
_rd_poll_data:
in al, dx
cmp al, 0aah
jne _rd_poll_data
pop dx
pop ax
ret
;AL = command/data
WriteDSP:
push dx
push ax
mov dx, REG_DSP_WRITE_BS
_wd_poll:
in al, dx
test al, 80h
jz _wd_poll
pop ax
mov dx, REG_DSP_WRITE_DATA
out dx, al
pop dx
ret
;Return AL
ReadDSP:
push dx
mov dx, REG_DSP_READ_BS
_rdd_poll:
in al, dx
test al, 80h
jz _rdd_poll
pop ax
mov dx, REG_DSP_READ
in al, dx
pop dx
ret
;AX = sampling
SetSampling:
push dx
xchg al, ah
push ax
mov al, DSP_SET_SAMPLING_OUTPUT
call WriteDSP
pop ax
call WriteDSP
mov al, ah
call WriteDSP
pop dx
ret
;Starts a playback
;AX = Sampling
;BL = Mode
;CX = Size
StartPlayback:
;Set sampling
call SetSampling
;Start playback command
mov al, DSP_DMA_16_OUTPUT_AUTO
call WriteDSP
mov al, bl
call WriteDSP ;Format
mov al, cl
call WriteDSP ;Size (Low)
mov al, ch
call WriteDSP ;Size (High)
ret
;Stops the playback
StopPlayback:
push ax
mov al, DSP_STOP_DMA_16
call WriteDSP
pop ax
ret
_CODE ENDS
<强> buffer.asm 强>
.8086
.MODEL SMALL
;Block size is 1/100 of a second at 44100 samplings per seconds
BLOCK_SIZE EQU 44100 / 100 * 2
;Buffer size allocated, it is twice the BLOCK_SIZE because there are two blocks.
;Size is doubled again so that we are sure to find an area that doesn't cross a
;64KiB boundary
;Total buffer size is about 3.5 KiB
BUFFER_SIZE EQU BLOCK_SIZE * 2 * 2
_DATI SEGMENT PARA PUBLIC 'DATA' USE16
;This is the buffer
buffer db BUFFER_SIZE DUP(0)
bufferOffset dw OFFSET buffer
bufferSegment dw _DATI
_DATI ENDS
_CODE SEGMENT PARA PUBLIC 'CODE' USE16
ASSUME CS:_CODE, DS:_DATI
;Allocate a buffer of size BLOCK_SIZE * 2 that doesn't cross
;a physical 64KiB
;This is achieved by allocating TWICE as much space and than
;Aligning the segment on 64KiB if necessary
AllocateBuffer:
push bx
push cx
push ax
push dx
;Compute linear address of the buffer
mov bx, _DATI
shr bx, 0ch
mov cx, _DATI
shl cx, 4
add cx, OFFSET buffer
adc bx, 0 ;BX:CX = Linear address
;Does it starts at 64KiB?
test cx, cx
jz _ab_End ;Yes, we are fine
mov dx, cx
mov ax, bx
;Find next start of 64KiB
xor dx, dx
inc ax
push ax
push dx
;Check if next boundary is after our buffer
sub dx, cx
sub ax, bx
cmp dx, BUFFER_SIZE / 2
pop dx
pop ax
jae _ab_end
mov bx, dx
and bx, 0fh
mov WORD PTR [bufferOffset], bx
mov bx, ax
shl bx, 0ch
shr dx, 04h
or bx, dx
mov WORD PTR [bufferSegment], bx
_ab_end:
clc
pop dx
pop ax
pop cx
pop bx
ret
;Free the buffer
FreeBufferIfAllocated:
;Nothing to do
ret
_CODE ENDS