home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 19 / CD_ASCQ_19_010295.iso / dos / prg / midas / sb.asm < prev    next >
Assembly Source File  |  1994-08-06  |  33KB  |  1,351 lines

  1. ;*    SB.ASM
  2. ;*
  3. ;* Sound Blaster series Sound Device, v2.00
  4. ;*
  5. ;* Copyright 1994 Petteri Kangaslampi and Jarno Paananen
  6. ;*
  7. ;* This file is part of the MIDAS Sound System, and may only be
  8. ;* used, modified and distributed under the terms of the MIDAS
  9. ;* Sound System license, LICENSE.TXT. By continuing to use,
  10. ;* modify or distribute this file you indicate that you have
  11. ;* read the license and understand and accept it fully.
  12. ;*
  13.  
  14.  
  15.  
  16. IDEAL
  17. P386
  18. JUMPS
  19.  
  20. INCLUDE "lang.inc"
  21. INCLUDE "errors.inc"
  22. INCLUDE "sdevice.inc"
  23. INCLUDE "dsm.inc"
  24. INCLUDE "dma.inc"
  25.  
  26.  
  27.  
  28. ;/***************************************************************************\
  29. ;*       enum sbFunctIDs
  30. ;*       ----------------
  31. ;* Description:  ID numbers for SB Sound Device functions
  32. ;\***************************************************************************/
  33.  
  34. enum    sbFunctIDs \
  35.         ID_sbDetect = ID_sb, \
  36.         ID_sbInit, \
  37.         ID_sbClose
  38.  
  39.  
  40.  
  41. DATASEG
  42.  
  43. oldIRQ        DD    ?        ; old IRQ vector
  44. oldIRQmask    DB    ?        ; old IRQ mask
  45. sb22C        DW    ?        ; SB DSP data port (2xCh)
  46. sbTimeConstant  DB      ?               ; SB Transfer Time Constant
  47. sbRate        DW    ?        ; SB actual playing rate
  48. sbVersion       DW      ?               ; DSP version number
  49. sbMode          DW      ?               ; actual output mode
  50. sbInterrupt     DB      ?               ; IRQ interrupt number
  51. sbBlockLength   DW      ?               ; DSP playing block length
  52. sbOutputFilter  DB      ?               ; initial output filter status
  53. sbStereoOK      DB      ?               ; flag used by sbSetStereo()
  54.  
  55.  
  56.  
  57.  
  58. IDATASEG
  59.  
  60.  
  61. GLOBAL    SB : SoundDevice
  62.  
  63. SB        SoundDevice    < \
  64.         0, 220h, 05h, 01h, sdUnInitialized, \
  65.         sdMono or sdStereo or sd8bit or sd16bit or sdNormalQ, \
  66.     far ptr sbID,\
  67.     far ptr sbDetect, \
  68.     far ptr sbInit, \
  69.     far ptr sbClose, \
  70.     far ptr dsmGetMixRate, \
  71.     far ptr dsmGetMode, \
  72.     far ptr dsmOpenChannels, \
  73.     far ptr dsmCloseChannels, \
  74.     far ptr dsmClearChannels, \
  75.     far ptr dsmMute, \
  76.     far ptr dsmPause, \
  77.     far ptr dsmSetMasterVolume, \
  78.     far ptr dsmPlaySound, \
  79.     far ptr dsmStopSound, \
  80.     far ptr dsmSetRate, \
  81.     far ptr dsmGetRate, \
  82.     far ptr dsmSetVolume, \
  83.     far ptr dsmSetInstrument, \
  84.     far ptr dsmSetPosition, \
  85.     far ptr dsmGetPosition, \
  86.     far ptr dsmSetPanning, \
  87.     far ptr dsmGetPanning, \
  88.     far ptr dsmMuteChannel, \
  89.     far ptr dsmAddInstrument, \
  90.     far ptr dsmRemInstrument, \
  91.     far ptr dsmSetUpdRate, \
  92.     far ptr dsmPlay >
  93.  
  94. sbID            DB      "Sound Blaster series Sound Device v2.00",0
  95.  
  96.  
  97.         ; "fake" one-byte DMA buffer used by sbSetStereo()
  98. sbStereoDMABuffer dmaBuffer     < 0, 0, 1, 0, -1 >
  99.  
  100.  
  101.  
  102. CODESEG
  103.  
  104.  
  105.  
  106. PUBLIC    sbDetect
  107. PUBLIC    sbInit
  108. PUBLIC    sbClose
  109.  
  110.  
  111.  
  112.  
  113. ;/***************************************************************************\
  114. ;*
  115. ;* Function:    sbWait
  116. ;*
  117. ;* Description: Waits until data can be written to the DSP command/data port
  118. ;*              2xCh
  119. ;*
  120. ;* Destroys:    ax, cx, dx. dx now contains the DSP command/data port value,
  121. ;*              2xCh.
  122. ;*
  123. ;\***************************************************************************/
  124.  
  125. PROC NOLANGUAGE sbWait  NEAR
  126.  
  127.         mov     dx,[sb22C]
  128.         mov     cx,0FFFFh
  129.  
  130. @@wait:
  131.         in      al,dx                   ; read port 22Ch
  132.         test    al,al                   ; is bit 7 set?
  133.         jns     @@ok                    ; if not, DSP is ready
  134.         loop    @@wait                  ; read maximum of 0FFFFh times
  135.  
  136.  
  137.         ; The bit is still set after 0FFFFh reads, so apparently the DSP
  138.         ; is for some reason locked up. Return error.
  139.  
  140.         mov     ax,errSDFailure         ; Sound Device hardware failure
  141.         jmp     @@done
  142.  
  143. @@ok:
  144.         xor     ax,ax
  145.  
  146. @@done:
  147.         ret
  148. ENDP
  149.  
  150.  
  151.  
  152.  
  153. ;/***************************************************************************\
  154. ;*
  155. ;* Macro:    SBCMD
  156. ;*
  157. ;* Description: Writes a command to SB's DSP. Jumps to label @@err if an
  158. ;*              error occurs, with the error code in ax
  159. ;*
  160. ;* Input:    command     command
  161. ;*
  162. ;* Destroys:    see function sbCommand
  163. ;*
  164. ;\***************************************************************************/
  165.  
  166. MACRO    SBCMD    command
  167.         mov     bl,command
  168.     call    sbCommand
  169.         test    ax,ax
  170.         jnz     @@err
  171. ENDM
  172.  
  173.  
  174.  
  175.  
  176. ;/***************************************************************************\
  177. ;*
  178. ;* Function:    sbCommand
  179. ;*
  180. ;* Description: Writes a command to SB's DSP
  181. ;*
  182. ;* Input:       bl      command
  183. ;*
  184. ;* Returns:     MIDAS error code in ax
  185. ;*
  186. ;* Destroys:    ax, dx, cx
  187. ;*
  188. ;\***************************************************************************/
  189.  
  190. PROC NOLANGUAGE sbCommand    NEAR
  191.  
  192.         call    sbWait                  ; wait until data or command can be
  193.         test    ax,ax                   ; written to the DSP
  194.         jnz     @@done
  195.  
  196.         mov     al,bl                   ; write the command
  197.     out    dx,al
  198.  
  199.         xor     ax,ax
  200.  
  201. @@done:
  202.     ret
  203. ENDP
  204.  
  205.  
  206.  
  207.  
  208. ;/***************************************************************************\
  209. ;*
  210. ;* Function:    sbRead
  211. ;*
  212. ;* Description: Reads a byte from the DSP data port
  213. ;*
  214. ;* Returns:     bl              byte read
  215. ;*              ax              MIDAS error copde
  216. ;*
  217. ;* Destroys:    ax, cx, dx
  218. ;*
  219. ;\***************************************************************************/
  220.  
  221. PROC NOLANGUAGE sbRead          NEAR
  222.  
  223.         mov     dx,[SB.port]
  224.         add     dx,0Eh                  ; dx = 2xEh = SB DSP Data Available
  225.         mov     cx,0FFFFh               ; port
  226. @@wait:
  227.         in      al,dx
  228.         test    al,al                   ; wait until bit 7 is set
  229.         js      @@dok
  230.         loop    @@wait
  231.  
  232.         ; Read port 2xEh 65535 time and bit 7 is still zero - failure
  233.         mov     ax,errSDFailure
  234.         jmp     @@done
  235.  
  236. @@dok:  add     dx,0Ah-0Eh              ; dx = 2xAh = SB DSP Data port
  237.         in      al,dx                   ; read data from port
  238.         mov     bl,al                   ; and store it in bl
  239.  
  240.         xor     ax,ax                   ; success
  241.  
  242. @@done:
  243.         ret
  244. ENDP
  245.  
  246.  
  247.  
  248.  
  249. ;/***************************************************************************\
  250. ;*
  251. ;* Function:    int sbDetect(int *result);
  252. ;*
  253. ;* Description: Detects Sound Blaster soundcard
  254. ;*
  255. ;* Returns:     MIDAS error code.
  256. ;*              1 stored to *result if SB was detected, 0 if not.
  257. ;*
  258. ;\***************************************************************************/
  259.  
  260. PROC    sbDetect        FAR     result : dword
  261. USES    si,di
  262. LOCAL    old21 : byte, oldA1 : byte, oldIRQ2 : dword, oldIRQ3 : dword, \
  263.     oldIRQ5 : dword, oldIRQ7 : dword, oldIRQ10 : dword
  264.  
  265.     mov    bx,210h         ; port to try first
  266.  
  267. @@tryport:
  268.     mov    dx,bx
  269.     add    dx,6
  270.     mov    al,1
  271.     out    dx,al
  272.  
  273.     in    al,dx
  274.     in    al,dx
  275.     in    al,dx            ; try to reset SB
  276.     in    al,dx
  277.     in    al,dx
  278.  
  279.     mov    al,0
  280.     out    dx,al
  281.  
  282.  
  283.     add    dx,0Eh-6
  284.     mov    cx,1000
  285.  
  286. @@pwait:
  287.     in    al,dx
  288.     or    al,al            ; wait until bit 7 in port 2xE is 1
  289.     js    @@pok1            ;  or until port has been read 1000
  290.     loop    @@pwait         ;  times
  291.  
  292.     jmp    @@pnot            ; port was read 1000 times, SB is NOT
  293.                     ;  in this port address
  294.  
  295. @@pok1: sub    dx,4
  296.     mov    cx,1000
  297.  
  298. @@pw2:    in    al,dx            ; wait until port 2xAh has a value
  299.     cmp    al,0AAh         ; 0AAh or 1000 times
  300.     je    @@pok
  301.     loop    @@pw2
  302.  
  303.     jmp    @@pnot            ; port 2xAh was not 0AAh, SB is NOT
  304.                     ;  in this port address
  305. @@pnot:
  306.     add    bx,10h
  307.     cmp    bx,260h
  308.     jna    @@tryport
  309.  
  310.         jmp     @@nosb
  311.  
  312.  
  313. @@pok:    mov    [SB.port],bx
  314.     add    bx,0Ch            ; store detected port value
  315.     mov    [sb22C],bx
  316.  
  317.     mov    [SB.DMA],1        ; !!! assume DMA channel 1
  318.  
  319.  
  320.     mov    ax,350Ah
  321.     int    21h            ; save IRQ 2 interrupt vector
  322.     mov    [word oldIRQ2],bx
  323.     mov    [word oldIRQ2+2],es
  324.  
  325.     mov    ax,350Bh
  326.     int    21h            ; save IRQ 3 interrupt vector
  327.     mov    [word oldIRQ3],bx
  328.     mov    [word oldIRQ3+2],es
  329.  
  330.     mov    ax,350Dh
  331.     int    21h            ; save IRQ 5 interrupt vector
  332.     mov    [word oldIRQ5],bx
  333.     mov    [word oldIRQ5+2],es
  334.  
  335.     mov    ax,350Fh
  336.     int    21h            ; save IRQ 7 interrupt vector
  337.     mov    [word oldIRQ7],bx
  338.     mov    [word oldIRQ7+2],es
  339.  
  340.     mov    ax,3572h
  341.     int    21h            ; save IRQ 10 interrupt vector
  342.     mov    [word oldIRQ10],bx
  343.     mov    [word oldIRQ10+2],es
  344.  
  345.  
  346.     push    ds
  347.  
  348.     mov    ax,cs
  349.     mov    ds,ax
  350.  
  351.     mov    ax,250Ah        ; set IRQ2 interrupt vector to
  352.     mov    dx,offset @@IRQ2    ;  @@IRQ2
  353.     int    21h
  354.  
  355.     mov    ax,250Bh        ; set IRQ3 interrupt vector to
  356.     mov    dx,offset @@IRQ3    ;  @@IRQ3
  357.     int    21h
  358.  
  359.     mov    ax,250Dh        ; IRQ5
  360.     mov    dx,offset @@IRQ5
  361.     int    21h
  362.  
  363.     mov    ax,250Fh        ; IRQ 7
  364.     mov    dx,offset @@IRQ7
  365.     int    21h
  366.  
  367.     mov    ax,2572h
  368.     mov    dx,offset @@IRQ10    ; IRQ 10
  369.     int    21h
  370.  
  371.     pop    ds
  372.  
  373.  
  374.     in    al,21h
  375.     mov    [old21],al        ; save old IRQ mask
  376.     and    al,01010011b        ; enable IRQ 2, 3, 5, 7
  377.     out    21h,al
  378.  
  379.     in    al,0A1h
  380.     mov    [oldA1],al        ; save old IRQ mask
  381.     and    al,11111011b        ; enable IRQ 10
  382.     out    0A1h,al
  383.  
  384.  
  385. ; Initialize DMA-controller:
  386.  
  387.     mov    al,5            ; mask out channel 1
  388.     out    0Ah,al
  389.  
  390.     mov    al,0            ; reset counter
  391.     out    0Ch,al
  392.  
  393.     mov    al,49h            ; transfer from memory to DSP
  394.     out    0Bh,al
  395.  
  396.     mov    al,0
  397.     out    02h,al            ; start pointer low & high
  398.     out    02h,al
  399.     out    83h,al            ; start page address
  400.  
  401.     mov    al,1            ; transfer length low
  402.     out    03h,al
  403.  
  404.     mov    al,0            ; transfer length high
  405.     out    03h,al
  406.  
  407.     mov    al,1            ; enable channel 1
  408.     out    0Ah,al
  409.  
  410.  
  411. ; Initialize DSP
  412.  
  413.     SBCMD    40h            ; set speed
  414.     SBCMD    200            ; speed
  415.     SBCMD    14h            ; set length
  416.     SBCMD    1            ; length low
  417.     SBCMD    0            ; length high
  418.  
  419.     xor    bl,bl            ; IRQ number
  420.     mov    cx,65535
  421.     mov    dx,[sb22C]
  422.  
  423. @@w:    test    bl,bl            ; wait until an IRQ comes and sets
  424.     jnz    @@irqok         ; the IRQ number or 65535 times
  425.     in    al,dx            ; delay...
  426.     loop    @@w
  427.  
  428.         jmp     @@nosb                  ; waited 65535 times without an IRQ
  429.                                         ; - no SB
  430.  
  431. @@irqok:
  432.     mov    [SB.IRQ],bl        ; store IRQ number
  433.  
  434.     mov    al,[old21]
  435.     out    21h,al            ; restore old IRQ masks
  436.     mov    al,[oldA1]
  437.     out    0A1h,al
  438.  
  439.     push    ds
  440.  
  441.     mov    ax,250Ah
  442.     lds    dx,[oldIRQ2]        ; restore IRQ 2 interrupt vector
  443.     int    21h
  444.  
  445.     mov    ax,250Bh
  446.     lds    dx,[oldIRQ3]        ; restore IRQ 3 interrupt vector
  447.     int    21h
  448.  
  449.     mov    ax,250Dh
  450.     lds    dx,[oldIRQ5]        ; restore IRQ 5 interrupt vector
  451.     int    21h
  452.  
  453.     mov    ax,250Fh
  454.     lds    dx,[oldIRQ7]        ; restore IRQ 7 interrupt vector
  455.     int    21h
  456.  
  457.     mov    ax,2572h
  458.     lds    dx,[oldIRQ10]        ; restore IRQ 10 interrupt vector
  459.     int    21h
  460.  
  461.     pop    ds
  462.  
  463.  
  464.         les     bx,[result]             ; SB succesfully detected
  465.         mov     [word es:bx],1
  466.         xor     ax,ax
  467.         jmp     @@done
  468.  
  469.  
  470.  
  471. @@IRQ2: mov    bl,2
  472.     jmp    @@id1
  473.  
  474. @@IRQ3: mov    bl,3
  475.     jmp    @@id1
  476.  
  477. @@IRQ5: mov    bl,5
  478.     jmp    @@id1
  479.  
  480. @@IRQ7: mov    bl,7
  481.     jmp    @@id1
  482.  
  483. @@IRQ10:
  484.     mov    bl,10
  485.     jmp    @@id2
  486.  
  487.  
  488. @@id1:    mov    al,20h            ; send EOI to Interrupt controller 1
  489.     out    20h,al
  490.     mov    dx,[SB.port]
  491.     add    dx,0Eh            ; and to SB
  492.     in    al,dx
  493.     iret
  494.  
  495. @@id2:    mov    al,20h
  496.     out    0A0h,al
  497.     mov    dx,[SB.port]
  498.     add    dx,0Eh
  499.     in    al,dx
  500.     iret
  501.  
  502. @@nosb:
  503.         les     bx,[result]
  504.         mov     [word es:bx],0          ; Sound Blaster not detected
  505.         xor     ax,ax
  506.         jmp     @@done
  507.  
  508. @@err:
  509.         ERROR   ID_sbDetect
  510.  
  511.  
  512. @@done:
  513.     ret
  514. ENDP
  515.  
  516.  
  517.  
  518.  
  519. ;/***************************************************************************\
  520. ;*
  521. ;* Function:    int sbInit(ushort mixRate, ushort mode);
  522. ;*
  523. ;* Description: Initializes Sound Blaster
  524. ;*
  525. ;* Input:    mixRate     mixing rate
  526. ;*        mode        output mode (see enum sdMode)
  527. ;*
  528. ;* Returns:     MIDAS error code
  529. ;*
  530. ;\***************************************************************************/
  531.  
  532. PROC    sbInit        FAR    mixRate : word, mode : word
  533.  
  534.         mov     ax,[SB.port]
  535.     add    ax,0Ch            ; set sb22C variable to real SB DSP
  536.     mov    [sb22C],ax        ; command port
  537.  
  538.  
  539.     mov    dx,[SB.port]
  540.     add    dx,6
  541.         mov     al,1                    ; reset SB DSP by first writing 1 to
  542.     out    dx,al            ; port 2x6h
  543.         mov     cx,8
  544. @@delay:
  545.         in      al,dx                   ; wait for a while (3 usecs)
  546.         loop    @@delay
  547.     xor    al,al            ; and write 0 to port 2x6h
  548.     out    dx,al
  549.  
  550.  
  551.     mov    dx,[SB.port]
  552.     add    dx,0Eh            ; SB data available port 2xEh
  553.     mov    cx,1000
  554.  
  555. @@wd1:    in    al,dx
  556.     test    al,al
  557.     js    @@ok1            ; wait until bit 7 (data available)
  558.     loop    @@wd1            ; is 1 or 1000 times
  559.         jmp     @@sberr                 ; no data - no SB
  560.  
  561. @@ok1:    add    dx,0Ah-0Eh        ; read data port (2xAh)
  562.     mov    cx,1000
  563.  
  564. @@wd2:    in    al,dx
  565.     cmp    al,0AAh         ; wait until data is 0AAh or 1000
  566.     je    @@sbok            ; times
  567.     loop    @@wd2
  568.         jmp     @@sberr                 ; no 0AAh - no SB
  569.  
  570. @@sbok:                                 ; SB resetted succesfully
  571.  
  572.  
  573.         SBCMD   0E1h                    ; Get DSP version number
  574.         call    sbRead                  ; read version high byte
  575.         test    ax,ax
  576.         jnz     @@err
  577.         mov     bh,bl
  578.         call    sbRead                  ; read version low byte
  579.         test    ax,ax
  580.         jnz     @@err
  581. ;        mov     bx,100h ;!!!
  582.         mov     [sbVersion],bx          ; store version number
  583.  
  584.         cmp     bx,0400h                ; DSP version >= 4.00?
  585.         jae     @@modeall               ; if yes, all modes supported
  586.         cmp     bx,0300h                ; DSP version >= 3.00?
  587.         jae     @@modestereo            ; if yes, stereo is supported
  588.  
  589.         ; DSP version < 3.00 - only 8-bit mono
  590.         mov     [sbMode],sd8bit or sdMono
  591.         jmp     @@moded
  592.  
  593. @@modestereo:
  594.         ; DSP version < 4.00 - only 8-bit mono or stereo
  595.         mov     ax,sd8bit               ; 8-bit output
  596.         test    [mode],sdMono           ; is mono mode forced?
  597.         jnz     @@smono
  598.         or      ax,sdStereo             ; no, use stereo
  599.         jmp     @@sok
  600. @@smono:
  601.         or      ax,sdMono               ; yes, use mono
  602. @@sok:
  603.         mov     [sbMode],ax             ; store output mode
  604.         jmp     @@moded
  605.  
  606.  
  607. @@modeall:
  608.         ; DSP version >= 4.00 - all output modes
  609.         test    [mode],sd8bit           ; force 8-bit?
  610.     jnz    @@8b
  611.         mov     ax,sd16bit              ; if not, use 16 bits
  612.     jmp    @@bit
  613. @@8b:   mov     ax,sd8bit
  614.  
  615. @@bit:    test    [mode],sdMono        ; force mono?
  616.     jnz    @@mono
  617.         or      ax,sdStereo             ; if not, use stereo
  618.     jmp    @@mst
  619. @@mono: or      ax,sdMono
  620.  
  621. @@mst:  mov     [sbMode],ax
  622.  
  623.  
  624. @@moded:
  625.         test    [mode],sdLowQ           ; force low or high quality?
  626.     jnz    @@lowq
  627.     test    [mode],sdHighQ
  628.     jnz    @@highq
  629.         or      [sbMode],sdNormalQ      ; if not, use normal quality
  630.     jmp    @@mode
  631. @@lowq: or      [sbMode],sdLowQ
  632.     jmp    @@mode
  633. @@highq:
  634.         or      [sbMode],sdHighQ
  635.  
  636. @@mode: ; output mode set up
  637.  
  638.  
  639.     mov    al,[SB.IRQ]
  640.         cmp     al,7                    ; IRQ number > 7 ?
  641.     ja    @@i8
  642.  
  643.     add    al,8            ; no, interrupt number is IRQ+8
  644.     jmp    @@ivect
  645.  
  646. @@i8:    add    al,70h-8        ; yes, interrupt number is IRQ+68h
  647.  
  648. @@ivect:
  649.         mov     [sbInterrupt],al        ; save interrupt number
  650.  
  651.     mov    ah,35h
  652.     int    21h            ; save old IRQ vector
  653.     mov    [word oldIRQ],bx
  654.     mov    [word oldIRQ+2],es
  655.  
  656.     mov    al,[SB.IRQ]
  657.     cmp    al,7            ; is IRQ > 7 ?
  658.     ja    @@i82
  659.  
  660.     mov    cl,al            ; no
  661.     in    al,21h
  662.     mov    [oldIRQmask],al     ; save old IRQ mask
  663.     mov    bl,not 1
  664.     rol    bl,cl            ; enable SB's IRQ
  665.     and    al,bl
  666.     out    21h,al
  667.     jmp    @@idone
  668.  
  669. @@i82:    mov    cl,al
  670.     sub    cl,8
  671.     in    al,0A1h
  672.     mov    [oldIRQmask],al     ; save old IRQ mask
  673.     mov    bl,not 1
  674.     rol    bl,cl            ; enable SB's IRQ
  675.     and    al,bl
  676.     out    0A1h,al
  677.  
  678. @@idone:
  679.  
  680.         cmp     [sbVersion],0400h       ; DSP version >= 4.00 ?
  681.         jae     @@userate               ; if so, the sampling rate is directly
  682.                                         ; used
  683.  
  684.         cmp     [sbVersion],0201h       ; DSP version < 2.01?
  685.         jb      @@limit1                ; if yes, rate limit is 21739Hz
  686.  
  687.         ; DSP >= 2.01 - sampling rate limit is 43478Hz, so the maximum
  688.         ; Time Constant is 233
  689.         mov     ecx,233
  690.         jmp     @@timeconstant
  691.  
  692. @@limit1:
  693.         ; DSP < 2.01 - sampling rate limit is 21739Hz, making the maximum
  694.         ; Time Constant 210
  695.         mov     ecx,210
  696.  
  697. @@timeconstant:
  698.         ; Calculate the Transfer Time Constant for DSP < 4.00.
  699.  
  700.     movzx    ebx,[mixRate]
  701.         test    [sbMode],sdStereo       ; use stereo?
  702.         jz      @@nostt                 ; if yes, multiply rate with 2 when
  703.         shl     ebx,1                   ; calculating Time Constant
  704.  
  705. @@nostt:
  706.         mov     eax,1000000             ; eax = Time Constant =
  707.         cdq                             ; 256 - (1000000 / rate)
  708.         div     ebx
  709.     neg    eax
  710.     add    eax,256
  711.  
  712.         test    eax,eax
  713.         jns     @@non1                  ; Time Constant must be nonnegative
  714.         xor     eax,eax
  715.  
  716. @@non1: cmp     eax,ecx                 ; ecx is the maximum Time Constant
  717.         jbe     @@noa1
  718.         mov     eax,ecx                 ; limit Time Constant to ecx value
  719.  
  720. @@noa1: mov     [sbTimeConstant],al     ; store Transfer Time Constant
  721.  
  722.     mov    ebx,256
  723.     sub    ebx,eax
  724.     mov    eax,1000000        ; calculate actual playing rate
  725.         cdq                             ; (= 1000000 / (256 - TimeConstant))
  726.     div    ebx
  727.  
  728.         test    [sbMode],sdStereo       ; using stereo?
  729.         jz      @@nostt2
  730.         shr     eax,1                   ; divide with 2 to get rate
  731.  
  732. @@nostt2:
  733.     mov    [sbRate],ax
  734.         jmp     @@initdsm
  735.  
  736.  
  737. @@userate:
  738.         ; DSP >= 4.00 - output uses the sampling rate directly
  739.         mov     ax,[mixRate]
  740.         mov     [sbRate],ax
  741.  
  742.  
  743. @@initdsm:
  744.         ; Initialize DSM:
  745.         call    dsmInit LANG, [sbRate], [sbMode]
  746.         test    ax,ax
  747.         jnz     @@err
  748.  
  749.         cmp     [sbVersion],0400h       ; if playing stereo on a DSP < 4.00,
  750.         jae     @@dmaok                 ; set stereo mode and output one
  751.         test    [sbMode],sdStereo       ; silent byte before starting the
  752.         jz      @@dmaok                 ; actual transfer
  753.  
  754.         call    sbSetStereo
  755.         test    ax,ax
  756.         jnz     @@err
  757.  
  758. @@dmaok:
  759.         ; start playing the DSM DMA buffer:
  760.         movzx   ax,[SB.DMA]
  761.     call    dmaPlayBuffer LANG, seg dsmBuffer offset dsmBuffer, \
  762.                 ax, 1
  763.         test    ax,ax
  764.         jnz     @@err
  765.  
  766.         mov     [sbBlockLength],0FFF0h  ; set DSP block length to 0FFF0h
  767.                                         ; samples - autoinit DMA mode takes
  768.                                         ; care of wrapping
  769.  
  770.         cmp     [sbVersion],0200h       ; is DSP version < 2.00 ?
  771.         jb      @@v100                  ; if is, auto-initialize mode is not
  772.                                         ; available
  773.  
  774.         ; set up interrupt service routine for auto-initialize mode:
  775.         push    ds
  776.         mov     ah,25h
  777.         mov     al,[sbInterrupt]
  778.         mov     dx,seg sbAutoinitIRQ
  779.         mov     ds,dx
  780.         mov     dx,offset sbAutoinitIRQ
  781.         int     21h
  782.         pop     ds
  783.  
  784.         cmp     [sbVersion],0400h       ; is DSP version >= 4.00 ?
  785.         jae     @@v400                  ; if is, use DSP 4.00 playing mode
  786.                                         ; for all output modes
  787.  
  788.         cmp     [sbVersion],0201h       ; is DSP version >= 2.01 ?
  789.         jae     @@v201                  ; if is, high-speed output is
  790.                                         ; available
  791.  
  792.         jmp     @@v200
  793.  
  794.  
  795. @@v100:
  796.         ; DSP version < 2.00 - play using mono single-cycle mode
  797.  
  798.         ; set up interrupt service routine for single-cycle mode:
  799.         push    ds
  800.         mov     ah,25h
  801.         mov     al,[sbInterrupt]
  802.         mov     dx,seg sbSingleCycleIRQ
  803.         mov     ds,dx
  804.         mov     dx,offset sbSingleCycleIRQ
  805.         int     21h
  806.         pop     ds
  807.  
  808.         ; start playing:
  809.         call    sbPlayMonoSingleCycle
  810.         jmp     @@playing
  811.  
  812. @@v200:
  813.         ; DSP version 2.00 - play using mono auto-initialize mode
  814.         call    sbPlayMonoAutoinit
  815.         jmp     @@playing
  816.  
  817. @@v201:
  818.         ; DSP version >= 2.01 - high-speed output is available
  819.         test    [sbMode],sdStereo       ; use stereo?
  820.         jnz     @@plstereo              ; if yes, play using stereo mode
  821.  
  822.         cmp     [sbRate],22000          ; is sampling rate over 22000Hz?
  823.         ja      @@highspeed             ; if is, use high-speed mode
  824.  
  825.         ; DSP >= 2.01, mono, rate <= 22000Hz - play using mono
  826.         ; auto-initialize mode
  827.         call    sbPlayMonoAutoinit
  828.         jmp     @@playing
  829.  
  830. @@highspeed:
  831.         ; DSP >= 2.01, mono, rate > 22000Hz - play using mono high-speed
  832.         ; (auto-initialize) mode
  833.         call    sbPlayMonoHighSpeed
  834.         jmp     @@playing
  835.  
  836. @@plstereo:
  837.         ; DSP >= 2.01 (actually >= 3.00), stereo - play using stereo
  838.         ; high-speed auto-initialize mode
  839.         call    sbPlayStereo
  840.         jmp     @@playing
  841.  
  842. @@v400:
  843.         ; DSP >= 4.00 - use DSP v4.00 auto-initialize mode for all output
  844.         ; modes
  845.         call    sbPlay400
  846.  
  847. @@playing:
  848.         test    ax,ax
  849.         jnz     @@err
  850.  
  851.     mov    [SB.status],sdOK
  852.         xor     ax,ax                   ; SB succesfully initialized
  853.     jmp    @@done
  854.  
  855. @@sberr:
  856.         mov     ax,errSDFailure         ; Hardware failure
  857.  
  858. @@err:  ERROR   ID_sbInit
  859.  
  860. @@done:
  861.     ret
  862. ENDP
  863.  
  864.  
  865.  
  866.  
  867. ;/***************************************************************************\
  868. ;*
  869. ;* Function:    sbPlayMonoSingleCycle
  870. ;*
  871. ;* Description: Starts playing the buffer using 8-bit mono Single-Cycle mode
  872. ;*
  873. ;\***************************************************************************/
  874.  
  875. PROC NOLANGUAGE sbPlayMonoSingleCycle   NEAR
  876.  
  877.         SBCMD   0D1h                    ; turn on DAC speaker
  878.         SBCMD   40h                     ; set Transfer Time Constant
  879.         SBCMD   [sbTimeConstant]        ; Time Constant
  880.         SBCMD   14h                     ; 8-bit PCM output
  881.         SBCMD   <[byte sbBlockLength]>    ; block length low byte
  882.         SBCMD   <[byte sbBlockLength+1]>  ; block length high byte
  883.  
  884.         xor     ax,ax
  885.  
  886. @@err:
  887.         ret
  888. ENDP
  889.  
  890.  
  891.  
  892.  
  893. ;/***************************************************************************\
  894. ;*
  895. ;* Function:    sbSingleCycleIRQ
  896. ;*
  897. ;* Description: SB DSP interrupt service routine for 8-bit Single-Cycle mode
  898. ;*
  899. ;\***************************************************************************/
  900.  
  901. PROC NOLANGUAGE sbSingleCycleIRQ
  902.  
  903.         sti
  904.     push    ax
  905.     push    cx
  906.     push    dx            ; save all registers that will be
  907.     push    ds            ; changed
  908.  
  909.     mov    ax,@data
  910.     mov    ds,ax
  911.  
  912.         SBCMD   14h                     ; 8-bit PCM output
  913.         SBCMD   <[byte sbBlockLength]>    ; block length low byte
  914.         SBCMD   <[byte sbBlockLength+1]>  ; block length high byte
  915.  
  916. @@err:  ; no error handling can be done here
  917.  
  918.         mov     dx,[SB.port]
  919.         add     dx,0Eh                  ; acknowledge DSP interrupt
  920.     in    al,dx
  921.  
  922.         cmp     [SB.IRQ],7
  923.     ja    @@upirq
  924.  
  925.     mov    al,20h            ; send End Of Interrupt command to
  926.     out    20h,al            ; PIC
  927.     jmp    @@done
  928.  
  929. @@upirq:
  930.     mov    al,20h            ; send EOI to PIC #2 (IRQ > 7)
  931.     out    0A0h,al
  932.  
  933. @@done:
  934.     pop    ds
  935.     pop    dx
  936.     pop    cx
  937.     pop    ax
  938.  
  939.     iret
  940. ENDP
  941.  
  942.  
  943.  
  944.  
  945. ;/***************************************************************************\
  946. ;*
  947. ;* Function:    sbPlayMonoAutoinit
  948. ;*
  949. ;* Description: Starts playing the buffer using 8-bit Auto-initialize mode
  950. ;*
  951. ;\***************************************************************************/
  952.  
  953. PROC NOLANGUAGE sbPlayMonoAutoinit      NEAR
  954.  
  955.         SBCMD   0D1h                    ; turn on DAC speaker
  956.         SBCMD   40h                     ; set DSP Transfer Time Constant
  957.         SBCMD   [sbTimeConstant]        ; Transfer Time Constant
  958.         SBCMD   48h                     ; set DSP transfer block size
  959.         SBCMD   <[byte sbBlockLength]>    ; block length low byte
  960.         SBCMD   <[byte sbBlockLength+1]>  ; block length high byte
  961.         SBCMD   1Ch                     ; start 8-bit PCM output
  962.  
  963.         xor     ax,ax
  964.  
  965. @@err:
  966.         ret
  967. ENDP
  968.  
  969.  
  970.  
  971.  
  972. ;/***************************************************************************\
  973. ;*
  974. ;* Function:    sbAutoinitIRQ
  975. ;*
  976. ;* Description: SB DSP interrupt service routine for 8-bit Auto-initialize
  977. ;*              mode
  978. ;*
  979. ;\***************************************************************************/
  980.  
  981. PROC NOLANGUAGE sbAutoinitIRQ
  982.  
  983.         sti
  984.     push    ax
  985.     push    cx
  986.     push    dx            ; save all registers that will be
  987.     push    ds            ; changed
  988.  
  989.     mov    ax,@data
  990.     mov    ds,ax
  991.  
  992.         mov     dx,[SB.port]
  993.         add     dx,0Eh                  ; acknowledge DSP interrupt
  994.     in    al,dx
  995.  
  996.         cmp     [SB.IRQ],7
  997.     ja    @@upirq
  998.  
  999.     mov    al,20h            ; send End Of Interrupt command to
  1000.     out    20h,al            ; PIC
  1001.     jmp    @@done
  1002.  
  1003. @@upirq:
  1004.     mov    al,20h            ; send EOI to PIC #2 (IRQ > 7)
  1005.     out    0A0h,al
  1006.  
  1007. @@done:
  1008.     pop    ds
  1009.     pop    dx
  1010.     pop    cx
  1011.     pop    ax
  1012.  
  1013.     iret
  1014. ENDP
  1015.  
  1016.  
  1017.  
  1018.  
  1019. ;/***************************************************************************\
  1020. ;*
  1021. ;* Function:    sbPlayMonoHighSpeed
  1022. ;*
  1023. ;* Description: Starts playing the buffer using 8-bit mono High-Speed
  1024. ;*              Auto-initialize mode
  1025. ;*
  1026. ;\***************************************************************************/
  1027.  
  1028. PROC NOLANGUAGE sbPlayMonoHighSpeed     NEAR
  1029.  
  1030.         SBCMD   0D1h                    ; turn on DAC speaker
  1031.         SBCMD   40h                     ; set DSP transfer Time Constant
  1032.         SBCMD   [sbTimeConstant]        ; transfer Time Constant
  1033.         SBCMD   48h                     ; set DSP transfer block size
  1034.         SBCMD   <[byte sbBlockLength]>    ; block length low byte
  1035.         SBCMD   <[byte sbBlockLength+1]>  ; block length high byte
  1036.         SBCMD   90h                     ; 8-bit PCM high-speed output
  1037.  
  1038.         xor     ax,ax
  1039.  
  1040. @@err:
  1041.         ret
  1042. ENDP
  1043.  
  1044.  
  1045.  
  1046.  
  1047. ;/***************************************************************************\
  1048. ;*
  1049. ;* Function:    sbSetStereo
  1050. ;*
  1051. ;* Description: Sets the SB hardware to stereo mode and plays a single
  1052. ;*              silent byte. Called before starting stereo transfer on
  1053. ;*              DSP < 4.00 to make sure that the channels are the right
  1054. ;*              way and not reversed (left comes from left and right from
  1055. ;*              right).
  1056. ;*
  1057. ;\***************************************************************************/
  1058.  
  1059. PROC NOLANGUAGE sbSetStereo     NEAR
  1060.  
  1061.         SBCMD   0D1h
  1062.  
  1063.         ; set up the IRQ handler for transfer:
  1064.         mov     dx,[SB.port]
  1065.         add     dx,04h
  1066.         mov     al,0Eh
  1067.         out     dx,al                   ; set the mixer to stereo mode
  1068.         inc     dx
  1069.         in      al,dx
  1070.         or      al,2
  1071.         out     dx,al
  1072.  
  1073. ;        xor ax,ax ;!!!
  1074. ;        ret ;!!!
  1075.  
  1076.         push    ds
  1077.         mov     ah,25h
  1078.         mov     al,[sbInterrupt]
  1079.         mov     dx,seg @@irqhandler
  1080.         mov     ds,dx
  1081.         mov     dx,offset @@irqhandler
  1082.         int     21h
  1083.         pop     ds
  1084.  
  1085.         ; program the DMA controller for single-cycle output:
  1086.         movzx   ax,[SB.DMA]
  1087.         call    dmaPlayBuffer LANG, \
  1088.                 seg sbStereoDMABuffer offset sbStereoDMABuffer, ax, 0
  1089.         test    ax,ax
  1090.         jnz     @@err
  1091.  
  1092.         mov     [sbStereoOK],0
  1093.  
  1094.         SBCMD   14h
  1095.         SBCMD   0                       ; program the DSP to output one
  1096.         SBCMD   0                       ; silent byte (80h)
  1097.  
  1098.         ; wait until the IRQ occurs:
  1099. @@w:
  1100.         cmp     [sbStereoOK],1
  1101.         jne     @@w
  1102.  
  1103.         xor     ax,ax
  1104.  
  1105. @@err:
  1106.         ret
  1107.  
  1108.  
  1109. @@irqhandler:
  1110.         ; IRQ handler routine:
  1111.  
  1112.         push    ax
  1113.     push    cx
  1114.     push    dx            ; save all registers that will be
  1115.     push    ds            ; changed
  1116.  
  1117.     mov    ax,@data
  1118.     mov    ds,ax
  1119.  
  1120.         mov     [sbStereoOK],1          ; set interrupt flag
  1121.  
  1122.         mov     dx,[SB.port]
  1123.         add     dx,0Eh                  ; acknowledge DSP interrupt
  1124.     in    al,dx
  1125.  
  1126.         cmp     [SB.IRQ],7
  1127.     ja    @@upirq
  1128.  
  1129.     mov    al,20h            ; send End Of Interrupt command to
  1130.     out    20h,al            ; PIC
  1131.     jmp    @@done
  1132.  
  1133. @@upirq:
  1134.     mov    al,20h            ; send EOI to PIC #2 (IRQ > 7)
  1135.     out    0A0h,al
  1136.  
  1137. @@done:
  1138.     pop    ds
  1139.     pop    dx
  1140.     pop    cx
  1141.     pop    ax
  1142.  
  1143.         iret
  1144. ENDP
  1145.  
  1146.  
  1147.  
  1148.  
  1149. ;/***************************************************************************\
  1150. ;*
  1151. ;* Function:    sbPlayStereo
  1152. ;*
  1153. ;* Description: Starts playing the buffer using 8-bit stereo High-Speed
  1154. ;*              Auto-initialize mode
  1155. ;*
  1156. ;\***************************************************************************/
  1157.  
  1158. PROC NOLANGUAGE sbPlayStereo    FAR
  1159.  
  1160.         SBCMD   0D1h                    ; turn on DAC speaker
  1161.         SBCMD   40h                     ; set DSP transfer Time Constant
  1162.         SBCMD   [sbTimeConstant]        ; transfer Time Constant
  1163.  
  1164.         ; save output filter status and turn it off:
  1165.         mov     dx,[SB.port]
  1166.         add     dx,04h
  1167.         mov     al,0Ch
  1168.         out     dx,al
  1169.         inc     dx
  1170.         in      al,dx
  1171.         mov     [sbOutputFilter],al
  1172.         or      al,20h
  1173.         out     dx,al
  1174.  
  1175.         SBCMD   48h                     ; set DSP transfer block size
  1176.         SBCMD   <[byte sbBlockLength]>    ; block length low byte
  1177.         SBCMD   <[byte sbBlockLength+1]>  ; block length high byte
  1178.         SBCMD   90h                     ; 8-bit PCM high-speed output
  1179.  
  1180.         xor     ax,ax
  1181.  
  1182. @@err:
  1183.         ret
  1184. ENDP
  1185.  
  1186.  
  1187.  
  1188.  
  1189. ;/***************************************************************************\
  1190. ;*
  1191. ;* Function:    sbPlay400
  1192. ;*
  1193. ;* Description: Starts playing the buffer using the DSP 4.00 Auto-initialize
  1194. ;*              transfer
  1195. ;*
  1196. ;\***************************************************************************/
  1197.  
  1198. PROC NOLANGUAGE sbPlay400       NEAR
  1199.  
  1200.         SBCMD   41h                     ; set DSP output sampling rate
  1201.         SBCMD   <[byte sbRate+1]>       ; sampling rate high byte
  1202.         SBCMD   <[byte sbRate]>         ; sampling rate low byte
  1203.  
  1204.         test    [sbMode],sd8bit         ; 8-bit mode?
  1205.         jnz     @@8bit
  1206.  
  1207.         SBCMD   0B6h                    ; 16-bit output
  1208.         test    [sbMode],sdMono         ; mono?
  1209.         jnz     @@mono16
  1210.         SBCMD   30h                     ; 16-bit stereo signed PCM
  1211.         jmp     @@setlen
  1212. @@mono16:
  1213.         SBCMD   10h                     ; 16-bit mono signed PCM
  1214.         jmp     @@setlen
  1215.  
  1216. @@8bit:
  1217.         SBCMD   0C6h                    ; 8-bit output
  1218.         test    [sbMode],sdMono         ; mono?
  1219.         jnz     @@mono8
  1220.         SBCMD   20h                     ; 8-bit stereo unsigned PCM
  1221.         jmp     @@setlen
  1222. @@mono8:
  1223.         SBCMD   00h                     ; 8-bit mono unsigned PCM
  1224.  
  1225. @@setlen:
  1226.         SBCMD   <[byte sbBlockLength]>    ; transfer length low byte
  1227.         SBCMD   <[byte sbBlockLength+1]>  ; transfer length high byte
  1228.  
  1229. @@err:
  1230.         ret
  1231. ENDP
  1232.  
  1233.  
  1234.  
  1235.  
  1236. ;/***************************************************************************\
  1237. ;*
  1238. ;* Function:    int sbClose(void)
  1239. ;*
  1240. ;* Description: Uninitializes Sound Blaster
  1241. ;*
  1242. ;* Returns:     MIDAS error code
  1243. ;*
  1244. ;\***************************************************************************/
  1245.  
  1246. PROC    sbClose     FAR
  1247.  
  1248.     cmp    [SB.status],sdOK
  1249.         je      @@sok
  1250.  
  1251.         mov     ax,errSDFailure
  1252.         jmp     @@err
  1253.  
  1254. @@sok:
  1255.         ; Reset DSP _twice_ to stop playing and reset it: (In High-Speed mode
  1256.         ; the first DSP reset just stops the playing. Besides, this should
  1257.         ; not hurt anyone anyway.)
  1258.         mov     bx,2
  1259.         mov     dx,[SB.port]
  1260.         add     dx,06h
  1261.  
  1262. @@reset:
  1263.         mov     al,1                    ; reset SB DSP by first writing 1 to
  1264.     out    dx,al            ; port 2x6h
  1265.         mov     cx,8
  1266. @@delay:
  1267.         in      al,dx                   ; wait for a while (3 usecs)
  1268.         loop    @@delay
  1269.     xor    al,al            ; and write 0 to port 2x6h
  1270.     out    dx,al
  1271.  
  1272.         mov     cx,8
  1273. @@delay2:                               ; another delay
  1274.         in      al,dx
  1275.         loop    @@delay2
  1276.  
  1277.         dec     bx                      ; and reset again
  1278.         jnz     @@reset
  1279.  
  1280.  
  1281.         ; stop DMA playing:
  1282.     movzx    ax,[SB.DMA]
  1283.         call    dmaStop LANG, ax
  1284.         test    ax,ax
  1285.         jnz     @@err
  1286.  
  1287.     mov    bl,[SB.IRQ]
  1288.     cmp    bl,7            ; is IRQ number > 7 ?
  1289.     ja    @@i8
  1290.  
  1291.     mov    al,[oldIRQmask]
  1292.         out     21h,al                  ; restore old IRQ mask, IRQ <= 7
  1293.     jmp    @@ivect
  1294.  
  1295. @@i8:   mov     al,[oldIRQmask]         ; restore old IRQ mask, IRQ > 7
  1296.     out    0A1h,al
  1297.  
  1298. @@ivect:
  1299.     push    ds
  1300.         mov     al,[sbInterrupt]
  1301.     mov    ah,25h            ; restore old IRQ vector
  1302.     lds    dx,[oldIRQ]
  1303.     int    21h
  1304.     pop    ds
  1305.  
  1306.         ; uninitialize DSM:
  1307.         call    dsmClose LANG
  1308.         test    ax,ax
  1309.         jnz     @@err
  1310.  
  1311.         SBCMD   0D3h                    ; turn off the DAC speaker
  1312.  
  1313.         cmp     [sbVersion],0400h
  1314.         jae     @@stok                  ; using stereo mode on DSP < 4.00 ?
  1315.         test    [sbMode],sdStereo
  1316.         jz      @@stok
  1317.  
  1318.         ; stereo on DSP < 4.00 - restore the output filter status and set
  1319.         ; hardware to mono mode:
  1320.  
  1321.         mov     dx,[SB.port]
  1322.         add     dx,04h                  ; write 04h to port 2x4h
  1323.         mov     al,0Ch
  1324.         out     dx,al
  1325.         inc     dx
  1326.         mov     al,[sbOutputFilter]     ; write output filter value to 2x5h
  1327.         out     dx,al
  1328.  
  1329.         dec     dx
  1330.         mov     al,0Eh
  1331.         out     dx,al
  1332.         inc     dx                      ; turn off stereo mode
  1333.         in      al,dx
  1334.         and     al,not 02h
  1335.         out     dx,al
  1336.  
  1337. @@stok:
  1338.         mov     [SB.status],sdUnInitialized
  1339.         xor     ax,ax
  1340.         jmp     @@done
  1341.  
  1342. @@err:  ERROR   ID_sbClose
  1343.  
  1344. @@done:
  1345.     ret
  1346. ENDP
  1347.  
  1348.  
  1349.  
  1350. END
  1351.