Skip to main content
© Stigels of Elektra

THE XBIOS 'DOSOUND' FUNCTION EXPLAINED by Richard Karsmakers

Being as much a music freak as I am, I really desired to go right 
through  to  the  bottom of the  ST's  built-in  XBIOS  'Dosound' 
routine, XBIOS number 32. According to Data Becker's "ST Intern", 
this function offered a really comfortable way to program  sounds 
on the ST. Well, I thought, let's go and have a look at the whole 
thing  and look what's really happening downthere when  you  hear 
music.

First,  let's  have  a look at the disassembled listings  of  the 
routines that have to do with sound on the ST.

The actual sound routine - SNDIRQ (Sound Interrupt)

FC2F84 MOVEM.L D0-D1/A0,-(A7)    !Save registers for later use
FC2F88 MOVE.L  $0E44(A5),D0      !Check location - Data present?
FC2F8C BEQ     $FC3016           !No - leap to end of routine
FC2F90 MOVE.L  D0,A0             !Put address of sound data in A0
FC2F92 MOVE.B  $0E48(A5),D0      !Load timer value
FC2F96 BEQ     $FC2FA0           !New sound started?
FC2F98 SUBQ.B  #1,D0             !Timer-1 if new sound started
FC2F9A MOVE.B  D0,$0E48(A5)      !Put back timer
FC2F9E BRA     $FC3016           !Ready!
FC2FA0 MOVE.B  (A0)+,D0          !Get sound command from table
FC2FA2 BMI     $FC2FD2           !Bit 7 set (Negative)?
                                 !This identifies special command
FC2FA4 MOVE.B  D0,$FF8800        !Register select to soundchip
FC2FAA CMPI.B  #$07,D0           !Register 7?
FC2FAE BNE     $FC2FCA           !No
FC2FB0 MOVE.B  (A0)+,D1          !Read data for register 7 in D1
FC2FB2 ANDI.B  #$3F,D1           !Isolate bits 0-5
FC2FB6 MOVE.B  $FF8800,D0        !Read mixer
FC2FBC ANDI.B  #$C0,D0           !Isolate bits 6-7
FC2FC0 OR.B    D1,D0             !'Or' data with that
FC2FC2 MOVE.B  D0,$FF8802        !Byte to soundchip
FC2FC8 BRA     $FC2FA0           !Get next sound command
FC2FCA MOVE.B  (A0)+,$FF8802     !Data directly to soundchip
FC2FD0 BRA     $FC2FA0           !Get next sound command
                                 !This is special command routine
FC2FD2 ADDQ.B  #1,D0             !Check if command was $FF
FC2FD4 BPL     $FC3008           !Branch to timer-wait loop
FC2FD6 CMPI.B  #$81,D0           !Was the command $80?
FC2FDA BNE     $FC2FE2           !No
FC2FDC MOVE.B  (A0)+,$0E49(A5)   !Store temporary register
FC2FE0 BRA     $FC2FA0           !Get next sound command
FC2FE2 CMPI.B  #$82,D0           !Was the command $81?
FC2FE6 BNE     $FC3008           !No; branch to timer-wait loop
FC2FE8 MOVE.B  (A0)+,$FF8800     !Select register
FC2FEE MOVE.B  (A0)+,D0          !Increment value
FC2FF0 ADD.B   D0,$0E49(A5)      !Add temporary value
FC2FF4 MOVE.B  (A0)+,D0          !End value
FC2FF6 MOVE.B  $0E49(A5),$FF8802 !Temporary value to sound chip
FC2FFE CMP.B   $0E49(A5),D0      !Compary temp. value with D0
FC3002 BEQ     $FC3012           !End reached? Then end!
FC3004 SUBQ.W  #4,A0             !Pointer back to same command
FC3006 BRA     $FC3012           !End
FC3008 MOVE.B  (A0)+,$0E48(A5)   !Next value as wait-timer
FC300C BNE     $FC3012           !Not equal to zero?
FC300E MOVE.W  #$0000,A0         !Sound vector to 0
FC3012 MOVE.L  A0,$0E44(A5)      !And save that
FC3016 MOVEM.L (A7)+,D0-D1/A0    !Get back registers
FC301A RTS                       !Return from subroutine

And here's the actual 'Dosound' routine,  that is called by XBIOS 
function 32.

FC2ECE MOVE.L  $0E44(A5),D0      !Get soundstatus
FC2ED2 MOVE.L  $0004(A7),D1      !Get address of sound table
FC2ED6 BMI     $FC2EE0           !Negative? Then don't set it
FC2ED8 MOVE.L  D1,$0E44(A5)      !Set new table
FC2EDC CLR.B   $0E48(A5)         !Start sound timer for the
                                 !above routine
FC2EE0 RTS                       !Return from subroutine

This is the routine that causes the bell to sound.

FC201C BTST    #$02,$0484(A5)       !Bell tone enabled?
FC2022 BEQ     $FC2032              !No? Don't sound
FC2024 MOVE.L  #$00FC301C,$0E44(A5) !Move bell table address
FC202C MOVE.B  #$00,$0E48(A5)       !Start sound timer
FC2032 RTS                          !Return from subroutine

And this is the routine that sounds the keyclick.

FC2A14 BTST    #$00,$0484(A5)       !Keyclick enabled?
FC2A1A BEQ     $FC2A2A              !No? Don't sound
FC2A1C MOVE.L  #$00FC303A,$0E44(A5) !Move click table address
FC2A24 MOVE.B  #$00,$0E48(A5)       !Start sound timer
FC2A2A BCHG    #$04,D1              !Invert bit 4 of D1
FC2A2E MOVE.B  D1,$0E1B(A5)         !Store that in $0E1B
FC2A32 RTS                          !Return from subroutine

The SNDIRQ routine is actually always executed,  but you normally 
never notice that;  when no command is given (no pointer is  set) 
to  execute a sound (this can be a key click,  a bell tone  or  a 
complete musical composition) the routine just immediately  exits 
and  gives  back  control to the OS program until  it  is  called 
again, at which moment it again test whether a pointer is set.

As  can be seen in the disassembled listing (made with  Templemon 
V1.6,  by  the way),  that pointer has to be located on  location 
$0E44 (by which the contents of address register 5 are added). So 
theoretically  any  address  in memory can be used  to  put  that 
pointer.
Whenever an pointer to a sound data table is put in that address, 
the music starts playing. Simple, eh?

But  now,  let's  have a look at the specific way in  which  that 
sound data is built up. What is necessary to create a sound?

In the sound data table, the following command may be used:

  $0x              Load the next byte in soundchip register x,
                    where 'x' can vary from $0 to $F
  $80              Loads the next byte into a temporary register
                    (This is $0E49, to which A5 is added)
  $81              This command is followed by three bytes:
                    1st: Specifies the register ($0-$F)
                    2nd: Increment value (compare with STEP
                          value in Basic)
                    3rd: End criterium (when the temporary
                          register, to which the increment
                          value is added all the time, has
                          reached this criterium, current
                          execution is stopped)

  $82-$FF          Timer wait loop command (this is followed
                    a byte that signifies the number of timer
                    ticks that should pass before the next
                    command should be executed. If 0, the
                    whole sound is stopped; end of music)

In  the first 'newlook' issue of ST NEWS (Volume 2 Issue  1),  we 
had included a piece of music that was programmed using the Xbios 
'Dosound' command table syntax.  All the musical compositions  in 
our Synth Sample IV were also made using this convention.

Many games use the 'Dosound' function (e.g. "Wanderer" and "Space 
Pilot"), because it offers some basic sound possibilities, and it 
automatically  adapts  the speed to the current  resolution  (VBL 
techniques  take  care that a sound runs much faster  in  high-or 
even  in medium res mode than in low res mode).  Really  terrific 
sounds,  however,  will probably have to be programmed using  own 
routines  (that's  what  Rob  Hubbard  and  Holger  Gehrmann  do, 
anyway).  Most programmers who don't use the 'Dosound'  function, 
use  the VBL-queue (that's a list of longword addresses  starting 
on $4CE, that are executed at every vertical blank (VBL)).

An example of a music program that uses the 'Dosound' function is 
"Musix32"  from Tommy Software in Germany (read our review in  ST 
NES
  Volume 2 Issue 1).  It enables you to save in the  'Dosound' 
format,  so  you can easily include the musical  compositions  in 
your own programs.

A final hint:  In GfA Basic,  it should be possible to keep track 
of 'Dosound' music using this following short routine:

  !First you'll have to execute the music
  Do
    A=Xbios(32,L:-1)
    ?A
  Loop

This  can be easily concluded when you have a look at the  actual 
'Dosound' listing;  on address $FC2ED6 the program checks if  the 
pointer  is negative.  If it is,  then it doesn't set  the  sound 
anew, but just continues playing it.

One last thing:  The routines explained in this article are taken 
from ROM TOS version 0.19.  If you try some of the hints in  this 
article on disk TOS, you'll find out that they don't work!

Disclaimer
The text of the articles is identical to the originals like they appeared in old ST NEWS issues. Please take into consideration that the author(s) was (were) a lot younger and less responsible back then. So bad jokes, bad English, youthful arrogance, insults, bravura, over-crediting and tastelessness should be taken with at least a grain of salt. Any contact and/or payment information, as well as deadlines/release dates of any kind should be regarded as outdated. Due to the fact that these pages are not actually contained in an Atari executable here, references to scroll texts, featured demo screens and hidden articles may also be irrelevant.