Playing .wav files on DOSBox's Sound Blaster device

前端 未结 2 473
一向
一向 2020-12-18 14:07

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

2条回答
  •  甜味超标
    2020-12-18 14:56

    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
    

提交回复
热议问题