I want to make a program in assembly/8086/masm/dosbox that turns the keyboard into various musical instruments so i need to be able to play some .wav files to produce the required sounds.I am aware of beep char and producing sound via sending frequencies to pc speaker (ports 41h,42h and 61h) but both ways are clearly not going to get me there.
I searched around and found that I need to use int 21h for opening files, knowledge of .wav format and knowledge of sound programming using Sound Blaster.
Unfortunately I couldn't find any helpful documentation on how to use the Sound Blaster in Dosbox (or in general) so kindly if you can help me with my problem of how to play .wav files on dosbox or if you have any workarounds I am all ears ( more accurate eyes).
This present a demo program that plays a specific WAV file (to avoid introducing a RIFF parser to the already too-long-for-SO code. The program has been tested in DOSBox, but a lot of things can go wrong on different configurations.
Finally, I was forced to split the code into two answers.
This is part 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With