ROM Computer Magazine Archive ROM MAGAZINE ISSUE 10 — FEBRUARY/MARCH 1985 / PAGE 44

ATARI'S TIMING SYSTEM PT. II
- 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.

System Timers

    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

Symbol
(Timer)
Location
(dec.)
Symbol
Interrupt
Vector

Location
Interrupt
Vector

CDTMV1 536,537 CDTMA1 550,551
CDTMV2 538,539 CDTMA2 552,553
 


Interrupt
Flag
 
Interrupt
Flag
 
CDTMV3 540,541 CDTMA3 554
CDTMV4 542,543 CDTMA4 556
CDTMV5 544,545 CDTMA5 558


    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)

step 1

Starting decimal number: 5500

Decimal
 
conversion Hexadecimal
5500/4096
 
= 1.34277 1
.3427* 16
 
= 5.48433 5
.4843* 16
 
= 7.75000 7
.7500* 16 =    12.000 C

Hexadecimal equivalent: 157C

step 2

    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

step 3

    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.

Interrupt Vector

    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.)


Description/location
 
Number held
Interrupt Vector 2
(lo byte)

552 dec.
 
0
Interrupt Vector 2
(hi byte)

553 dec.
6


Interrupt flag

    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

    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
table driven subroutine
Table-Driven Subroutine(1)

    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

Memory
Location
 
Value
Stored
Musical
Note
Note
Length
15000
128
B

15001
 
40

40 cycles
15002
121
C

15003
 
10

10 cycles
15004
108
D

15005
 
60

60 cycles
15006
128
B

15007
 
10

10 cycles
15008
144
A

15009
 
20

20 cycles
15010
128
B

15011
 
10

10 cycles
"
"
etc.
"   "



    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.

Music Data
Table

addr.
 
value Subroutine Input
15000
128 Note: B 1st
15001
 
  40 length   Pair
15002
121 Note: C 2st
15003
 
  10 length   Pair
15005
108 Note: D 3st
15006
 
  60 length   Pair

   "     "      "
etc


    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.


00020
00030
00040    Table-Driven SUBROUTINE
00050
00060
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
00230 *
00240 *              ;SET UP TIMER #2 (SETVBV)
00250 *
00260 L1 TAY         ;SET TIME (LOW BYTE)
00270 LDX #0         ;SET TIME (HIGH BYTE)
00280 LDA #2         ;TIMER #2
00290 JSR $E45C      ;TO SETVBV
00300 RTS


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
138 REM
140 REM * SET TIMER POINTERS TO *
142 REM * THE SUBROUTINE (1536) *
145 POKE 552,1:POKE 553,6
146 POKE 53761,162
147 REM *
150 X=USR(1536)
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

Check Data

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