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
|