Alive
News Team Current issue History Online Support Download Forum @Pouet

01 - 02 - SE - 03 - 04 - 05 - 06 - 07 - 08 - 09 - 10 - 11 - 12 - 13 - 14

Alive 11
Microwired
LMC1992 for the coder
        The LMC1992 is the piece of silicon that gives bass, treble,
        volume and balance controls on quite a few Atari 16/32 machines.
        The three computers that include a LMC1992 are the STe, Mega STe
        and TT.

        In order to setup the LMC1992 chip there is an interface known
        as 'microwire'. In theory it should be possible to control four
        devices over this microwire, but Atari never added anything else
        than the LMC1992.

        Now, the LMC1992 has been used ever since the STe first came out
        and should hide no mysteries to most Atari coders... or maybe it
        does ;) In coding the microwire routines for the latest version
        of my chiptracker maxYMiser I found quite a few problems with
        the previously published routines, particularly in getting
        things work reliably on all the three different microwired
        machines.

        For the coder this article shows the development of a reliable
        low level microwire routine, initialising the microwire, and
        finally how to do some cool stuff.


What is Microwire?
        Microwire is a three line serial interface of a type common in
        many digital systems. In the implementation of microwire on
        16/32 machines Atari has chosen to give the programmer quite
        low-level control over the interface.

        The three microwire signals include data, enable and clock. As a
        coder we have full control over the data and enable lines by
        setting the appropriate 16bit hardware register. The clock is
        generated automatically by the hardware.

        The problem with compatibility comes from the fact that the
        microwire clock is independent of the processor clock speed.
        With the TT being the fastest official 16/32 and the STe among
        the slowest this gives quite a speed range our routine must work
        over.


How to use Microwire
        As a coder you must set 16bit mask ($ffff8924.w) and data
        ($ffff8922.w) registers. Then these are sent over the microwire
        interface. Later, we'll discuss exactly what these have to be
        set to do different things.

        Each microwire transfer consists of a 10bit long data word. A
        valid data bit is indicated with the mask line going high.
        Ignoring fancy stuff, the mask word as set by the coder should
        therefore contain 11 bits with value 1. The value $7FF is
        probably the simplest, that means the 11 least significant bits
        of the data word will be used. Set the other 6 bits to what you
        like, zero being probably the easiest ;)

        Compared to the speed of the processor, the microwire is pretty
        slow and it is important to avoid sending any new data before
        the transfer is completed. Fortunately, as a coder we can
        observe the microwire transfer in progress and know when it has
        finished.

        During a microwire transfer the mask and data registers are
        rotated right by a single bit 16 times. We observe this rotation
        and when the original values of data and mask are restored we
        know the transfer is completed.

* Routine 1 - the naive method

set_LMC1992:

          move.w      #%11111111111,$ffff8924.w           ;set microwire mask
          move.w      d0,$ffff8922.w                      ;set microwire data
.wait:
          cmpi.w      #%11111111111,$ffff8924.w           ;wait for microwire 
                                                          ;write to finish

          bne.s       .wait
          rts

        set_LMC1992 is called with d0 equal to the microwire data value
        needed, we assume a constant mask of $7FF.

        Here we set the mask and data, and then wait while the mask is
        rotated and eventually restores the original value. This routine
        works fine on plain STe - nice ;)) On TT and Mega STe more than
        likely this routine could lead to unstable microwire transfers.
        These machines are so fast that the transfer did not even start
        before the test statements to see if the transfer finished. The
        original data is still there anyway, and the routine ends
        straightaway. If you do another microwire transfer right away
        the LMC1992 will become confused and some of your microwire
        commands will be lost or misinterpreted.

* Routine 2 - the unstable method

set_LMC1992:

          move.w      #%11111111111,$ffff8924.w           ;set microwire mask
          move.w      d0,$ffff8922.w                      ;set microwire data
.waitstart:
          cmpi.w      #%11111111111,$ffff8924.w           ;wait for microwire
                                                          ;write to start
          beq.s       .waitstart
.waitend:
          cmpi.w      #%11111111111,$ffff8924.w           ;wait for microwire
                                                          ;write to finish

          bne.s       .waitend
          rts

        This one works much better. First we wait for the microwire to
        actually start. Then when it's going we wait for it to finish.
        What can go wrong? Usually nothing :) But sometimes an interrupt
        (SID sound effect?) could occur while waiting for the transfer
        to start. While the interrupt is being processed the transfer
        could finish. Returning to this routine and continuing to wait
        for the microwire transfer to begin is obviously going to lead
        to a nasty system hang.

* Routine 3 - the stable method

set_LMC1992:           
          move.w      sr,-(sp)
          move.w      #$2700,sr                           ;interrupts off 
                                                          ;during start of
                                                          ;operation
          move.w      #%11111111111,$ffff8924.w           ;set microwire mask
          move.w      d0,$ffff8922.w
.waitstart
          cmpi.w      #%11111111111,$ffff8924.w           ;wait for microwire
                                                          ;write to start
          beq.s       .waitstart
          move.w      (sp)+,sr                            ;now microwire write
                                                          ;started we can
                                                          ;safely re-enable
                                                          ;interrupts
.waitend
          cmpi.w      #%11111111111,$ffff8924.w           ;wait for microwire 
                                                          ;write to finish
          bne.s       .waitend
          rts

        That's more like it ;) Finally a bullet proof microwire routine
        working on STe, Mega STe and TT. This is the routine used in
        maxYMiser. Later Pink/RG asked about some microwire stuff for
        the wonderful Res God's game "Clogged Up" - that gave me the
        idea for this article, so thanks to him. Thanks also to Paranoid
        for a short test on his TT.


Now what?
        So we have a stable microwire routine we can call as often as we
        like on any machine. Almost ready to do something cool ;) Here's
        a little routine I use to initialise the microwire to a known
        state:

init_LMC1992:          move.l      d0,-(sp)
                      move.w      #%10000000001,d0        ;mix DMA+YM equally
                      bsr.s       set_LMC1992
                      move.w      #%10001000110,d0        ;+0db bass
                      bsr.s       set_LMC1992
                      move.w      #%10010000110,d0        ;+0db treble
                      bsr.s       set_LMC1992
                      move.w      #%10011101000,d0        ;-0db master volume
                      bsr.s       set_LMC1992
                      move.w      #%10100010100,d0        ;-0db right
                      bsr.s       set_LMC1992
                      move.w      #%10101010100,d0        ;-0db left
                      bsr.s       set_LMC1992
                      move.l      (sp)+,d0
                      rts

        As we talked about earlier a microwire command is made up of 11
        bits. To understand the above routine you need to know what each
        of these 11 bits do.

        To simplify things I assumed a constant mask of $7FF... By all
        means use whatever you like if you want to complicate things ;)

        A  9  8  7  6  5  4  3  2  1  0

        ====  -------  _-_-_-_-_-_-_-_-

        The ==== bits set the microwire device the message is for. Atari
        only included the LMC1992, so these two bits have to be '10' in
        order to do anything.

        The ---- bits give the address value. The LMC1992 has some
        functions not used by Atari (actually for 4 channel surround
        sound!). But here are the addresses for the used stuff:

        Mixer           '000'

        Bass            '001'

        Treble          '010'

        Master volume   '011'

        Right volume    '100'

        Left volume     '101'

        The _-_- bits give the data value. Here are the valid data
        values for each function:

        * Mixer

        If this is set to 'xxxx01' the YM and DMA are mixed together,
        otherwise the YM is not included. Supposedly 'xxxx00' mixes the
        YM and DMA with a different ratio, but I couldn't get it to work
        ;) The 'x' represents a don't care - set it to what you like,
        probably zero.

        * Bass/Treble

        'xx0000' gives a -12dB treble or bass cut. 'xx1100' gives a
        +12dB bass or treble boost. Setting them to 'xx0110' gives a
        flat response as you might guess. Each increase by 1 increases
        the bass or treble by 2dB.

        * Master Volume

        '101000' gives the maximum volume output. Reducing this by 1
        reduces the output by 2dB. '000000' gives the minimum volume
        output -80dB lower than the maximum.

        * Right/Left Volume

        'x10100' gives the maximum volume output. Reducing this by 1
        reduces the output by 2dB. '000000' gives the minimum volume
        output -40dB lower than the maximum.

        That should give all information needed to understand the
        microwire initialisation routine. Now the microwire is in a
        known state we can really fuck with the LMC1992. Here are some
        examples:

        * Fix STe distorted output

        STe owners all know the sound output can be distorted at times.
        This is pretty easy to fix, just reduce the master volume
        slightly:

                      move.w      #%10011100110,d0        ;-4db master volume
                      bsr         set_LMC1992



        * Bass boost

        Any chipper knows YM2149 sound has /a lot/ of BASS, both real
        and pouet style ;) If you boost the bass this can lead to
        massively distorted output. We fix this by lowering the master
        volume correspondingly:

                      move.w      #%10011100010,d0        ;-12db master volume
                      bsr         set_LMC1992
                      move.w      #%10001001100,d0        ;+12db BASS!
                      bsr         set_LMC1992

        * Cut the midrange

        We can do this by boosting the bass and treble, and setting the
        master volume lower.

                      move.w      #%10011100010,d0        ;-12db master volume
                      bsr         set_LMC1992
                      move.w      #%10001001100,d0        ;boost bass + treble
                      bsr         set_LMC1992
                      move.w      #%10010001100,d0
                      bsr         set_LMC1992

        * Drop the bass

        WTF you want less BASS!?! Ok then:

                      move.w      #%10001000000,d0        ;-12db BASS :(
                      bsr         set_LMC1992

        * Shake it to the left

                      move.w      #%10100000000,d0        ;-40db right
                      bsr         set_LMC1992
                      move.w      #%10101010100,d0        ;-0db left
                      bsr         set_LMC1992

        * Shake it to the right

                      move.w      #%10100010100,d0        ;-0db right
                      bsr         set_LMC1992
                      move.w      #%10101000000,d0        ;-40db left
                      bsr         set_LMC1992


Finally
        That's probably enough examples for now. Have fun - get
        microwired ;)

                                                gwEm for Alive, 2005-10-10
Alive 11