ATARI'S TIMING SYSTEM PT. II
- USED WITH SOUND
- USED WITH SOUND
by Bob Cockroft
It is assumed that the reader of this article either has a basic understanding of the Atari's timing system, or has read Part 1 of this series.(see ROM issue 8 p.25). In addition, the reader will need to know the fundamentals of the computer's sound system.(refer to `Atari Sound' in ROM issue 10) It is the goal of this article to explain how to use the system timers in conjunction with the sound system. Sound (music) routines do not need to completely occupy the computer's attention. By using the timing system, sound routines can be added to the regularly occurring chores of the Operating System. As a result, music sequences can be made an inherent part of the machine. In fact, it is possible to have the sound system play a favourite tune while the computer is being used for some unrelated purpose.
It was mentioned in Part 1 of this series that the Atari computer has 5 system timers. Because these timers have interrupt capabilities, the regular workings of the operating system can be temporarily stopped in order that a user subroutine can perform some function. To the computer, the system timers are much like an alarm clock. They can be set so that the computer is forced to perform some operation at a predetermined time. Below is the list of the system timers, their addresses, and interrupt vect ors(or flags).
Table 1: System Timers
As displayed on table 1, system timers use a 2 byte memory configuration in lo/hi byte form. This means that numbers larger than 255 can be used as settings for the timers. For example, suppose you want to set timer #2 with a value of 5500 decimal. The first step is to convert this decimal number into its hexadecimal equivalent. (see below)
Starting decimal number: 5500
|.7500* 16||= 12.000||C
Hexadecimal equivalent: 157C
Because bytes that have 8 bits can hold only numbers between `0' and `255,' the second step is to convert the hexadecimal equivalent number (157C) into 2 digit halves so that it can be stored as 2 bytes. (see below)
hi byte lo byte
157C becomes 15 7C
The third step is to convert the 2 digit hexadecimal numbers into to/hi byte form. This is accomplished by multipling the first digit by 16 and adding the product to the second digit.
7C = 7* 16 + 12 = 124 lo byte
15 = 1*16 + 5 = 21 hi byte
The final step is to store the 2 decimal numbers into timer #2-lo byte first and hi byte second.
Once activated, these timers decrement from a user defined starting value that can range anywhere from 1 to 65536. For example, if a value of '100' were POKEd into one of these timers, it would be reduced by `1' every VBLANK(1/60th of a sec). What occurs when this value reaches zero depends on whether the timer uses an interrupt vector or an interrupt flag.
If the Timer uses an interrupt vector (like Timers # 1 & #2), control is directed to the address specified by its vector. For example, if timer #2 counts-down to `0,' and its interrupt vector has a lo byte of `0' and a hi byte of `6,' control will be passed to address $600 (1536 dec.)
An interrupt flag is used by Timers #3, #4, and #5 as an alternative system. Using only 1 byte, the flag provides some of the same services as does the vector. When a timer counts-down to zero and an interrupt occurs, its corresponding interrupt flag will be set. For example, when Timer 3 reaches zero, its interrupt flag (CDTMA3: 554 dec) will be set equal to '1.'
The program at the end of this article, called the `Automatic Music Generator,' uses the timing system to add the playing of a musical tune to the regularly occurring chores of the operating system. Because of this, the tune will play even if the computer is being used for some other purpose. In other words, any software the computer may be RUNning would not affect the timing of the musical notes. Many professional programmers used this technique to simplify sound routines. In fact, if it were possible to stop the execution of some game programs, the musical background would still be playing, as long as the Operating System were functional. Although the `Automatic Music Generator' uses some fairly advanced techniques, it is easily understood as consisting of the 3 steps: (1) Table-Driven Subroutine, (2) the Operating System, and (3) Timer #2 Interrupt. It will be helpful to refer to this table while each of its steps is being explained.
Combining with the Operating System
The first step is to make a table-driven subroutine that plays musical notes. A table-driven subroutine is a small machine language program that uses tables of memory data as input in much the same way as a BASIC program uses DATA statements. This subroutine will perform 4 functions: (1) to load 2 bytes of data from the Music Data Table, (2) to generate a musical note, (3)to increment the data counter, and (4) to reset the Timer.
The first function of the subroutine will be to load 2 pieces of data from the Music Data Table. But before the manner in which the routine performs this operation can be explained, how the Music Data Table operates must be understood.
The Music Data Table is like sheet music to the computer in the sense that it tells the machine what notes to play. As a result, it is possible to have the computer play different sound routines by simply rewriting this table. The Music Data Table contains 2 categories of information: (1)the musical notes, and (2) their lengths. The pitch of every note and its length in the sound routine are stored as a pair of numbers. The first number represents the pitch of the note; the second, its length. When more notes are added, the Musical Data Table becomes a list of alternating note, and note length values. This pattern can be seen in the partial listing of the music data table below.
Partial Music Data Table
The `Automatic Music Generator' uses DATA statements on lines 10100 and 10110 to store the Music Data Table. The Table is entered into memory locations 15000 to 15033, using the READ and POKE commands on lines 120 to 135.
||128 Note: B||1st
||121 Note: C||2st
||108 Note: D||3st
" " "
The first operation of the subroutine is to load the starting pair of numbers from the Music Data Table. As a value that represents a musical note, the first number in this pair is taken from the table and stored to Sound Frequency Register 1. As a result, the tone identified by this first number will be generated. Because the second number in this pair represents the note's length, it is used to set one of the system timers. In this program, Timer #2 is used for this purpose. It was explained in the first article of this series that any of the System Timers should be set by using the SETVBV routine. By storing the timer number in the Accumulator (`A'), the low byte of the timer value in the `Y' Register, and the corresponding high byte in the `X' Register, jumping to the SETVBV routine will set the timer automatically. Therefore, by storing a `2' in the accumulator, an `0' in the `X' Register, and the note's length number in the `Y' Register, Timer #2 will be activated with a time setting equal to that of the length of the note when the SETVBV routine is activated.
SETVBV Routine Application
LDA #2 ;Set Timer 2
LDY #(note's length) ;low byte
;(2th number in
;in the pair
LDX #0 ;high byte
In order to read the next pair of music data numbers, the subroutine increments the table pointers by 2. In addition, an RTS (return from subroutine) instruction would need to be placed at the end of the routine so that control would be given back to the Operating system. Although the code from this subroutine could be stored anywhere in RAM, the program uses a group of bytes beginning at 1536 ($600 hex). Below is a Table-driven subroutine that contains all the specifications described above.
00040 Table-Driven SUBROUTINE
00110 .OR $600
00120 PLA ;PULL BYTE OF STACK
00130 LDX $3A97 ;ENTER DATA COUNTER
00140 L2 LDA $3A98,X ;ENTER TONE DATA
00150 STA $D200 ;NEW SOUND
00160 INC $3A97 ;INCREMENT DATA COUNTER
00165 INC $3A97 ;INCREMENT DATA COUNTER
00170 LDA $3A99,X ;ENTER TIME DATA
00180 CMP #0 ;CHECK FOR DATA END
00190 BNE L1
00200 LDX #0 ;RESET DATA COUNTER
00210 STX $3A97
00220 JMP L2 ;RE-ENTER DATA
00240 * ;SET UP TIMER #2 (SETVBV)
00260 L1 TAY ;SET TIME (LOW BYTE)
00270 LDX #0 ;SET TIME (HIGH BYTE)
00280 LDA #2 ;TIMER #2
00290 JSR $E45C ;TO SETVBV
The Operating System(2)
After the subroutine has instructed Sound Channel 1 to play a note from the Music Data Table, control is given to the Operating System. From this point, the Operating System will perform its usual chores and allow the a programmer to use the machine for whatever purposes he wishes. Because the computer is controlled by the Operating System for the greatest amount of time, there will be no noticeable difference in the machine's operation.
Timer #2 Interrupt(3)
As you remember, back in the subroutine, Timer #2 was set to the length of the current musical note. Since that time, the Operating System has been controlling the computer in the usual manner. When Timer #2 counts-down to zero, it's indicated to the computer that it is now time to play the next note in the Music Data Table. As a result of the Interrupt which naturally occurs when Timer #2 counts-down to zero, control is passed from the Operating System back to the Table-Driven Subroutine so that the next note can be played. This is accomplished by pointing the Interrupt Vectors of Timer #2 to the starting address of the Subroutine (1536/$601 hex). The `Automatic Music Generator' sets the Vectors on line 145. (see below)
Starting addr of Subroutine = $601 hex
Timer #2 Vector: 552,553
145 POKE 552,1 :POKE 553,6
By returning control back to the Subroutine, a cycle has been completed. Because one of these cycles is made for every note from the Music Data Table, control is continuously being passed back and forth between the Operating System and the Subroutine.
5 REM *************************
6 REM * *
7 REM * SIMPLE MUSIC DEMO *
8 REM * (no timers Used) *
9 REM *************************
10 TRAP 40
40 FOR Y=1 TO 17
45 READ D,A
50 POKE 53761,164
70 POKE 53760,D
80 FOR X=1 TO A*5:NEXT X
90 FOR W=1 TO 50:NEXT W
100 NEXT Y
120 RESTORE 500
130 GOTO 40
10100 DATA 128,40,121,10,108,60,128,10,144,20,128,10,162,45
10110 DATA 128,10,0,10,128,10,144,20,162,10,193,40,128,10,0,10,128,10,144,40
10 REM *************************
20 REM * *
30 REM * AUTOMATIC *
40 REM * MUSIC GENERATOR *
50 REM * PROGRAM 1 *
60 REM * *
70 REM *************************
80 REM *
95 REM * STORE SUBROUTINE (at 1536)*
100 FOR X=1536 TO 1536+39
105 READ D
110 POKE X,D
115 NEXT X
117 REM * STORE MUSIC DATA (at 1580)*
120 FOR X=15000 TO 15000+33
125 READ D
130 POKE X,D
135 NEXT X
140 REM * SET TIMER POINTERS TO *
142 REM * THE SUBROUTINE (1536) *
145 POKE 552,1:POKE 553,6
146 POKE 53761,162
147 REM *
155 REM * STOP THE BASIC PROGRAM *
160 POKE 17,0
9990 REM *
9992 REM *
9995 REM * TABLE-DRIVEN SUBROUTINE *
9998 REM *
10000 DATA 104,174,151,58,189,i52,58,141,0,210,238,151,58,238,151,58,189,153
10010 DATA 58,201,0,208,8,162,0,142,151,58,76,4,6,168,162,0,169,2,32,92,228,96
10090 REM *
10092 REM * MUSIC DATA TABLE *
10094 REM *
10100 DATA 128,40,121,10,108,60,128,10,144,20,128,10,162,45
10110 DATA 128,10,0,X0,128,10,144,20,162,10,193,40,128,10,0,10,128,10,144,40
0 REM CHECK DATA FOR AUTOMATIC MUSIC GENERATOR PROGRAM 1
10 DATA 10922,594,365,757,983,697,369,600,593,451,536,721,868,777,408,618,723,870
135 DATA 8711,779,645,252,107,573,136,645,24,287,869,708,710,496,716,972,53,739
10092 DATA 2653,938,743,83,889