💾 Archived View for spam.works › mirrors › textfiles › music › ultra captured on 2023-11-14 at 10:55:18.

View Raw

More Information

⬅️ Previous capture (2023-06-16)

-=-=-=-=-=-=-

                       ?  ** UltraDox **  v2.0  ?
                     ??????????????????????????????
                     ?         Created by:        ?
                     ? CyberStrike of Renaissance ?
                     ?     Tran of Renaissance    ?
                     ?(: ...and a few others... :)?
                     ??????????????????????????????

OK, everyone, here is the second document describing the Gravis Ultrasound.
Through the ingeniousness of Forte Technologies and Advanced Gravis, this
32-channel CD-quality digital playback card has been created.  It can, under
the right mail-order sources, be bought for less than a Soundblaster!

Advanced Gravis:  Please, please, make an official document describing the
functions.  We've done our best, and through Internet, we have become YOUR
support group for both programming the card and the technical aspects of the
card.  There are 4 or 5 of us on Internet that spend our time on comp.sys.
ibm.pc.soundcard responding to questions, requests, etc, about the Ultrasound.
We thought that you did have net access...

We BELIEVE in the Ultrasound!  We don't believe it is a dead card!  At first,
Usenet was ragging on the Ultrasound badly.  We defended it, and now, it
appears that comp.sys.ibm.pc.soundcard has become comp.sys.ibm.pc.soundcard.
gus.

Enough of that.  European demo groups and anyone else who can't understand this
document.  I only wrote it in a few hours, and while it is better than the
last doc, it still lacks.  If you can't understand how something is
implemented, take a look at GUSMOD 1.1 and GUSMOD 1.2 when it finally comes
out.

This document is intended as a more advanced supplement to the previous
UltraSound Programming Docs.  As a supplement, it is advisable to pick up
the previous UltraSound Programming Docs, GUSMOD, or any other utilities
that are released with source code in order to determine how this system
works.

If all else fails, contact me at Internet address: cstrike@gompers.gompers.edu

Have fun, all!  (And Gravis, let's see some real stuff coming out.)

                                  * CyberStrike from America's BEST
                                    group, Renaissance!

? All trademarks belong to their respective companies and trademark holders.


????????????????????????????????????????????????????????????????????????????????
Card architecture:
???????????????????
      The Gravis Ultrasound has onboard capabilities of playing up to 32
digital voices through 2 DACs, one left and one right.  Each voice may be
played at sampling rates up to 44.1khz and may be 8 or 16-bit.  The voices
are individually pannable through 15 different pan positions.
			The Gravis Ultrasound is allowed up to a megabyte of on-board RAM to be
installed on the card.  It is highly recommended that Gravis Ultrasound
users upgrade this to a meg of RAM as new products will require this.
      By use of the Ultrasound Poke and Peek functions or by DMA
transfers, data can be transferred to and from the card.  The data that
is loaded to the card MUST be in two's compliment form.  DMA transfers
can adjust this as the data is loaded to or from the card.  If the Poke
and Peek methods are used, the data must be manually converted.
      Lastly, on the subject of DRAM, the sampled data may be 8- or 16-
bit.  If the data is 16-bit, it will take twice the DRAM to store it.
16-bit data is stored as a low-byte and then a high-byte immediately
following it.
      Each voice may also have a specific volume setting.  This is NOT a
linear volume but rather a logarithmic volume.  It is advisable to make a
table to control this.  When panning voices, it appears the volume is
adjusted according to the pan position.
      Volume ramping is a technique used to control the popping of the card.
If the volume is changed by a large degree, a pop will occur.  By ramping the
volume into an inaudible range (usually below 2000), anything can be set on
the voice without hearing it.

??????????????????????????????????????????????????????????????????????????????
Methods:

@DELAY = (in byte 300h)*7

; CX:AX  - Number      ;  Same as 32-bit   shr eax,7    or whatever.
RShift    proc
      mov   bx,cx
      shr   ax,7
      shr   cx,7
      shl   bx,9
      or    ax,bx
      ret
RShift    endp

??????????????????????????????????????????????????????????????????????????????
Ports:
??????????????????????????????????????????????????????????????????????????????
  BASE        - Mixer Control Port            (u_Base)
    ?????????????????????????????????
    ? 7 ? 6 ? 5 ? 4 ? 3 ? 2 ? 1 ? 0 ?
    ?????????????????????????????????
      ?   ?   ?   ?   ?   ?   ?   ?
      ?   ?   ?   ?   ?   ?   ?   ????? 0 = Line-In On, 1 = Line-In Off
      ?   ?   ?   ?   ?   ?   ????????? 0 = Output On, 1 = Output Off
      ?   ?   ?   ?   ?   ????????????? 0 = MicIn Off, 1 = MicIn On
      ?   ?   ?   ?   ????????????????? ?
      ?   ?   ?   ????????????????????? ?
      ?   ?   ????????????????????????? ?
      ?   ????????????????????????????? 0 = u_IRQDMAControl is DMA, 1 is IRQ
      ????????????????????????????????? ?

??????????????????????????????????????????????????????????????????????????????
  BASE +   6h - Status Register Port          (u_Status)
??????????????????????????????????????????????????????????????????????????????
  BASE +   8h - Timer Control Port            (u_TimerControl)
??????????????????????????????????????????????????????????????????????????????
  BASE +   9h - Timer Data Port               (u_TimerData)
??????????????????????????????????????????????????????????????????????????????
  BASE +   Bh - IRQ-DMA Control Port          (u_IRQDMAControl)
??????????????????????????????????????????????????????????????????????????????
  BASE + 100h - Midi Control Port             (u_MIDI_Control)
??????????????????????????????????????????????????????????????????????????????
  BASE + 101h - Midi Data Port                (u_MIDI_Data)
??????????????????????????????????????????????????????????????????????????????
  BASE + 102h - Active Voice Port             (u_Voice)
    Output to u_DataHi the voice number to make the active voice.  All
    commands that pertain to the active voice through the u_Command port will
    be routed to this voice until it is changed again.

      Example:
          mov   dx,u_Voice
          mov   al,1                  ; Use voice #1.
          out   dx,al

??????????????????????????????????????????????????????????????????????????????
  BASE + 103h - Function Select Port          (u_Command)

??? 00h - Write Voice Mode                      (Byte to u_DataHi)
    NOTE: This bit layout may be incorrect. If so, I will post a fixed version
          of this document later.
    ?????????????????????????????????
    ? 7 ? 6 ? 5 ? 4 ? 3 ? 2 ? 1 ? 0 ?
    ?????????????????????????????????
      ?   ?   ?   ?   ?   ?   ?   ?
      ?   ?   ?   ?   ?   ?   ?   ????? 1 = Voice is stopped
      ?   ?   ?   ?   ?   ?   ????????? 1 = Stop Voice
      ?   ?   ?   ?   ?   ????????????? 0 = 8-bit data, 1 = 16-bit data
      ?   ?   ?   ?   ????????????????? 0 = No loop, 1 = Loop
      ?   ?   ?   ????????????????????? 0 = Unidirectional, 1 = Bidirectional
      ?   ?   ????????????????????????? 1 = IRQ on end of loop
      ?   ????????????????????????????? 0 = Go forward, 1 = Go backwards
      ????????????????????????????????? ?

      Example:
          mov   dx,u_Voice
          mov   al,1                  ; Use voice #1.
          out   dx,al
          mov   dx,u_Command
          mov   al,0                  ; Write Voice Mode.
          out   dx,al
          mov   dx,u_DataHi
          mov   al,00000011b          ; Stop voice #1 immediately.
          out   dx,al


??? 01h - Set Voice Frequency                   (Word to u_DataLo)
      Based on the number of voices set through register 0Eh (Number of
      active voices), the divisor changes.  The frequency outputted to the
      Ultrasound is not a frequency such as 22000hz, but rather the hertz
      divided by a modifier (only Gravis and Forte know why).

      We've traced the divisors down to 8 active voices.  Apparently, there
      are a minimum of 14.

          # Voices     Divisor   # Voices     Divisor  # Voices     Divisor
          ????????????????????   ????????????????????  ????????????????????
                                                           24          25
               8          74         16          37        25          24
               9          66         17          35        26          23
              10          60         18          33        27          22
              11          54         19          31        28          21
              12          50         20          30        29          20
              13          46         21          28        30          20
              14          43         22          27        31          19
              15          40         23          26        32          18

      It is recommended that you don't use values below 14 voices (even
      though it appears to work).

      Example:
          mov   dx,u_Command
          mov   al,0Eh                ; Set active voices.
          out   dx,al
          mov   dx,u_DataHi
          mov   al,13 or 0C0h         ; Set number of active voices to 14.
          out   dx,al
          mov   dx,u_Voice
          mov   al,2                  ; Use voice #2.
          out   dx,al
          mov   dx,u_Command
          mov   al,1                  ; Set Voice Frequency.
          out   dx,al
          mov   dx,u_DataHi
          mov   ax,511                ; 22000/43
          out   dx,al

      Do yourself a favor and make a table for your notes.


??? 02h - Set Loop Start Location               (Word to u_DataLo)
??? 03h - Set Loop Start Location               (Word to u_DataLo)
      Through the use of these two registers, the starting location for
      the active voice can be set.  For an unknown reason, any memory
      addresses for the BEGIN, START, and END sample locations in DRAM
      must be divided by 128 and written to u_DataLo, then multiplied
      by 512 and written to u_DataLo.

      Set Sample Begin Location (0ah,0bh) and Set Sample End Location
      (4,5) will refer to this example.  They are both done the same
      way with the exception of the u_Command outs.  @@Lo should be
      replaced with 2, 4, or 0ah (based on what you are doing), and
      @@Hi should be replaced with 3, 5, 0bh.

      @@AddrLo is the lower word of the 32-bit DRAM location.
      @@AddrHi is the upper word of the 32-bit DRAM location.

      Example:
          mov   dx,u_Voice
          mov   al,3                  ; Use voice #3.
          out   dx,al
          mov   dx,u_Command
          mov   al,@@Lo               ; Request new position.
          out   dx,al
          mov   dx,u_DataLo
          mov   ax,@@AddrLo
          mov   cx,@@AddrHi
          call  RShift
          out   dx,ax
          mov   dx,u_Command
          mov   al,@@Hi
          out   dx,al
          mov   dx,u_DataLo
          mov   ax,@@AddrLo
          shl   ax,9
          out   dx,ax


??? 04h - Set Loop End Location                 (Word to u_DataLo)
??? 05h - Set Loop End Location                 (Word to u_DataLo)
      Through the use of these two registers, the ending location for
      the active voice can be set.  For an unknown reason, any memory
      addresses for the BEGIN, START, and END sample locations in DRAM
      must be divided by 128 and written to u_DataLo, then multiplied
      by 512 and written to u_DataLo.


??? 06h - Set Volume Ramp Rate                  (Byte to u_DataHi)
    ?????????????????????????????????
    ? 7 ? 6 ? 5 ? 4 ? 3 ? 2 ? 1 ? 0 ?
    ?????????????????????????????????
      ?   ?   ?   ?   ?   ?   ?   ?
      ?   ?   ?   ?   ?   ?   ?   ??????
      ?   ?   ?   ?   ?   ?   ???????????
      ?   ?   ?   ?   ?   ???????????????? Increment
      ?   ?   ?   ?   ????????????????????
      ?   ?   ?   ???????????????????????
      ?   ?   ??????????????????????????
      ?   ??????????????????????????????   Scale
      ??????????????????????????????????

      Scale defines the rate at which the volume will be ramped based on
      the increment.  The increment will be added or subtracted from the
      volume based on the increment.

      The scale bits are:
          00 - Update every access (fastest)
          01 - Update every 8th access
          10 - Update every 64th access
          11 - Update every 512th access (slowest)

      An increment of 63 will be the fastest possible ramp.  It may,
      depending on the scale, cause a zipper effect.  Usually, increments
      of 8 or less are suggested.

      Also, don't ramp below 63 or above 4032.  It creates an interesting
      but mostly undesirable effect.


??? 07h - Set Volume Ramp Start                 (Byte to u_DataHi)
??? 08h - Set Volume Ramp End                   (Byte to u_DataHi)
      These two registers set the start and end volumes for the volume
      ramps.  Note that only 8 bits per volume are sent, not 12 bits.
      The low 4 bits of the volumes should be stripped off.  The Volume
      Ramp Start volume MUST be less than the Volume Ramp End volume even
      if ramping down.

      The bytes that will be output have this format:

          EEEEMMMM


??? 09h - Set Current Volume                    (Word to u_DataLo)
      This is used to set the current volume for the active voice.  Note
      that the volumes are not linear, but logarithmic.  Volumes can range
      from 0-4095 but must be shifted up 4 bits.  Bits 3-0 are not used,
      but bits 15-4 consist of the volume.

      The volume consists of a 4-bit exponent (E) and an 8-bit mantissa (M).

          EEEEMMMMMMMM----

      As an example:

          Current volume value    Output level
          ??????????????????????????????????????
            1111111111110000      Max Volume
            1110111111110000      Half Volume
            1101111111110000      Quarter Volume
            1100111111110000      Eighth Volume

      The mantissa is used to get a finer breakdown between the exponents.

      Example:
          mov   dx,u_Voice
          mov   al,4                  ; Use voice #4.
          out   dx,al
          mov   dx,u_Command
          mov   al,9                  ; Set current volume.
          out   dx,al
          mov   dx,u_DataLo
          mov   ax,0fff0h             ; Set max volume.
          out   dx,ax

      Again, it would be wise to create a volume table covering the range
      of your volumes.


??? 0ah - Set Loop Begin Location               (Word to u_DataLo)
??? 0bh - Set Loop Begin Location               (Word to u_DataLo)
      Through the use of these two registers, the beginning location for
      the active voice can be set.  For an unknown reason, any memory
      addresses for the BEGIN, START, and END sample locations in DRAM
      must be divided by 128 and written to u_DataLo, then multiplied
      by 512 and written to u_DataLo.

      Note that this command is used to set the position of the pointer
      for the current voice also, even while playing.  Just make sure that
      the voice is within the bounds you set.


??? 0ch - Set Voice Balance                     (Byte to u_DataHi)
      This register will set the voice balance.  Apparently there are 15
      pan positions.  However, this doesn't make sense since all the way
      left is 0, all the way right is 15, and the middle is 7.  Regardless,
      this is how it works.

      Example:
          mov   dx,u_Voice
          mov   al,5                  ; Use voice #5.
          out   dx,al
          mov   dx,u_Command
          mov   al,0Ch                ; Set Pan Position for voice 5.
          out   dx,al
          mov   dx,u_DataHi
          mov   al,7                  ; Put it in the middle.
          out   dx,al


??? 0dh - Set Volume Control Register           (Byte to u_DataHi)
    ?????????????????????????????????
    ? 7 ? 6 ? 5 ? 4 ? 3 ? 2 ? 1 ? 0 ?
    ?????????????????????????????????
      ?   ?   ?   ?   ?   ?   ?   ?
      ?   ?   ?   ?   ?   ?   ?   ????? 1 = Ramp is stopped
      ?   ?   ?   ?   ?   ?   ????????? 1 = Stop Ramp
      ?   ?   ?   ?   ?   ????????????? ?
      ?   ?   ?   ?   ????????????????? 0 = No loop, 1 = Loop
      ?   ?   ?   ????????????????????? 0 = Unidirectional, 1 = Bidirectional
      ?   ?   ????????????????????????? Enable volume ramp IRQ
      ?   ????????????????????????????? Direction (1 = decreasing)
      ????????????????????????????????? ?

      Example:
          mov   dx,u_Voice
          mov   al,1                  ; Use voice #1.
          out   dx,al
          mov   dx,u_Command
          mov   al,0dh                ; Volume Control Register.
          out   dx,al
          mov   dx,u_DataHi
          mov   al,00000011b          ; Stop voice #1 ramp immediately.
          out   dx,al


??? 0eh - Set Highest Active Voices             (Byte to u_DataHi)
      This will set the maximum number of voices the card will process.
      Apparently, the minimum number of voices is 14.  Anything lower
      is automatically set to 14.

      Bear in mind that the more voices your application uses, not as
      much oversampling occurs.

      This byte MUST be OR'ed with 0C0h before output, and NumVoices must
      be one less than those desired!  (i.e. 14 Voices is 13 NumVoices)

      Example:
          mov   dx,u_Command
          mov   al,0eh                ; Set Highest Active Voice to:
          out   dx,al
          mov   dx,u_DataHi
          mov   al,NumVoices
          or    al,0C0h
          out   dx,al


??? 41h - DMA Control Register                  (Byte to u_DataHi)
      Input :  Clear any pending DMA IRQs.
      Output:  Unknown.


??? 43h - Low Word of DRAM address              (Word to u_DataLo)
??? 44h - High Byte of DRAM address             (Byte to u_DataHi)
      DRAM addresses can range from 00000h to fffffh.  By outputting the
      DRAM address and then accessing u_DRAMIO, direct peeks and pokes from
      the DRAM memory can be accomplished.

      Example:
          mov   dx,u_Command
          mov   al,43h
          out   dx,al
          mov   dx,u_DataLo
          mov   ax,@@LoDRAMAddress
          out   dx,ax
          mov   dx,u_Command
          mov   al,44h
          out   dx,al
          mov   dx,u_DataHi
          mov   al,@@HiDRAMAddress
          out   dx,al
          mov   dx,u_DRAMIO
          ; At this point, you can either:
          in    al,u_DRAMIO         ; Peek a byte from the address just output
          ; or:
          out   dx,al               ; Poke a byte to the address just output.

??? 45h - Timer Control Register ???            (Byte to u_DataHi)
      Input :  Clear any pending timer IRQs.
      Output:  Something to do with Adlib Control.  Hard coded into GUSMOD 1.2.


??? 46h - Timer Speed Register ???              (Byte to u_DataHi)
      Used to set the internal 80 microsecond timer.  Equation for output:
      (256-Desired value)*80 microseconds.


??? 49h - Sample Control Register               (Byte to u_DataHi)
      Input :  Clear any pending Sample Control IRQs.
      Output:  Unknown.


??? 4Ch - Initialization Register               (Byte to u_DataHi)
      If bit 1 is off, the Ultrasound is in an init state and cannot be
      accessed.

??? 80h - Read Voice Mode                       (Byte from u_DataHi)
      Reads what was output to register 0, Write Voice Mode.


??? 81h - Read Voice Frequency                  (Word from u_DataLo)
      An input from here will read the current voice's frequency.  Information
      for conversion is contained in 01h - Set Voice Frequency.


??? 82h - Read Loop Start Location              (Word from u_DataLo)
??? 83h - Read Loop Start Location              (Word from u_DataLo)
      Reading these registers will return the loopstart for the active voice.
      Information on conversion is contained in register 8ah-8bh - Read Voice
      Position.


??? 84h - Read Loop End Location                (Word from u_DataLo)
??? 85h - Read Loop End Location                (Word from u_DataLo)
      Reading these registers will return the end location for the active
      voice.  Information on conversion is contained in register 8ah-8bh -
      Read Voice Position.


??? 86h - Read Volume Ramp Rate                 (Byte from u_DataHi)
      Reading this register will return the Volume Ramp Rate (described
      in register 06h - Set Volume Ramp Rate).


??? 87h - Read Volume Ramp Start                (Byte from u_DataHi)
??? 88h - Read Volume Ramp End                  (Byte from u_DataHi)
      Reading these registers will return the Volume Ramp Start and End
      information set with registers 07h - Set Volume Ramp Start and
      08h - Set Volume Ramp End.


??? 89h - Read Volume                           (Word from u_DataLo)
      Reads what was output to register 9, Set Current Volume.


??? 8ah - Read Voice Position                   (Word from u_DataLo)
??? 8bh - Read Voice Position                   (Word from u_DataLo)
      Returns location of the current voice in DRAM that is the modified
      address (i.e. divided by 128, multiplied by 512).  The following
      equation will convert it.
          LOC = ((TEMP0 << 7) | (TEMP1 >> 9))

      Example:
        ; In:   AX - Voice
        ; Out:  DX:AX - Linear Position, not shifted position.
      U_ReadPos   proc
        mov   dx,u_Voice
        out   dx,al
        mov   dx,u_Command
        mov   al,8ah
        out   dx,al
        mov   dx,u_DataLo
        in    ax,dx   ; TEMP0
        mov   cx,ax
        mov   dx,u_Command
        mov   al,8bh
        out   dx,al
        mov   dx,u_DataLo
        in    ax,dx   ; TEMP1
        xor   dx,dx
        mov   bx,cx
        shl   cx,7
        shl   dx,7
        shr   bx,9
        or    dx,bx
        shr   ax,9
        and   ax,7Fh
        or    cx,ax
        mov   ax,cx
        ret
      U_ReadPos   endp


??? 8ch - Read Voice Balance                    (Byte from u_DataHi)
      This register will read the voice balance.  More information is
      available about the returned result in register 0ch - Set Voice
      Balance.


??? 8dh - Read Volume Control Register          (Byte from u_DataHi)
      Reading this register will return a byte corresponding to the active
      voice's Volume Control information as described in register 0dh -
      Read Volume Control Register.


??? 8eh - Read Highest Active Voice             (Byte from u_DataHi)
      Will return a byte corresponding to the number of voices the card
      is mixing.  Equation for English:  Actual_Voices=(in 8eh)&0c0h+1.


??? 8fh - IRQ Status Register                   (Byte from u_DataHi)
      Input :  Unknown.
      Output:  Unknown.


??????????????????????????????????????????????????????????????????????????????
  BASE + 104h - Data Low Port                 (u_DataLo)
??????????????????????????????????????????????????????????????????????????????
  BASE + 105h - Data High Port                (u_DataHi)
??????????????????????????????????????????????????????????????????????????????
  BASE + 107h - DRAM IO Port                  (u_DRAMIO)
??????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????
Techniques:

??? Ultrasound Reset:
        mov   bx,u_Command
        mov   cx,u_DataHi
        mov   dx,bx
        mov   al,4Ch
        out   dx,al
        mov   dx,cx
        mov   al,0
        out   dx,al
        @DELAY
        @DELAY
        mov   dx,bx
        mov   al,4Ch
        out   dx,al
        mov   dx,cx
        mov   al,1
        out   dx,al
        @DELAY
        @DELAY

        mov   dx,bx
        mov   al,41h
        out   dx,al
        mov   dx,cx
        mov   al,0
        out   dx,al
        mov   dx,bx
        mov   al,45h
        out   dx,al
        mov   dx,cx
        mov   al,0
        out   dx,al
        mov   dx,bx
        mov   al,49h
        out   dx,al
        mov   dx,cx
        mov   al,0
        out   dx,al

        mov   dx,bx
        mov   al,0Eh
        out   dx,al
        add   dx,2
        mov   al,MaxVoices
        or    al,0C0h
        out   dx,al

        mov   dx,u_Status
        in    al,dx
        mov   dx,bx
        mov   al,41h
        out   dx,al
        mov   dx,cx
        in    al,dx
        mov   dx,bx
        mov   al,49h
        out   dx,al
        mov   dx,cx
        in    al,dx
        mov   dx,bx
        mov   al,8Fh
        out   dx,al
        mov   dx,cx
        in    al,dx

        push  bx cx
        mov   cx,0
      @@VoiceClearLoop:
        mov   dx,u_Voice
        mov   al,cl
        out   dx,al
        inc   dx
        mov   al,0
        out   dx,al
        add   dx,2
        mov   al,3      ; Turn voice off
        out   dx,al
        sub   dx,2
        mov   al,0Dh    ; Turn ramp off.
        out   dx,al
        add   dx,2
        mov   al,3
        out   dx,al
        inc   cx
        cmp   cx,32
        jnz   @@VoiceClearLoop
        pop   cx bx

        mov   dx,bx
        mov   al,41h
        out   dx,al
        mov   dx,cx
        in    al,dx
        mov   dx,bx
        mov   al,49h
        out   dx,al
        mov   dx,cx
        in    al,dx
        mov   dx,bx
        mov   al,8Fh
        out   dx,al
        mov   dx,cx
        in    al,dx

        mov   dx,bx
        mov   al,4Ch
        out   dx,al
        mov   dx,cx
        mov   al,7
        out   dx,al
        ret


??? Starting a Volume Ramp:
        1) Determine the volume ramp points.
        2) Set the current volume register.
        3) Set start volume.
        4) Set end volume.
        5) Set rate.
        6) Set control register bits. (bits 0 and 1 off)
        7) Wait for bit 0 to turn on.