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! Originally published in ST NEWS Volume 2 Issue 3.