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 re
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