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