comment |
 This program uses the 8253 Timer Chip to generate a square wave at a
 given frequency and the 8255 Programmable Peripheral Interface to
 control passage of this square wave to the speaker.  The low-order 2
 bits of the 8255 output register (port 61h) are used to control the
 timer chip and speaker.  If bit 0 is a 1, then the output of channel 2
 of the 8253 timer will be anabled; otherwise, it is disabled.  Bit 1 of
 the 8255 output register effectively goes to an AND gate which also has
 timer channel 2 as an input.  The approach this program takes to
 generating sounds is to set both bits 0 and 1 to 1, thus enabling timer
 channel 2 and also gating its output to the speaker.
 The 8253 timer contains three "channels", each of which can operate in
 six modes.  Mode 3, the one of concern here, generates a square wave on
 the channel's output based on the input clock signal and the setting on
 a 2-byte internal counter register.  The channel 2 output is connected
 to the speaker through the AND gate shown, and is enabled by bit 0 of
 the 8255.  A timer command register (port 43h) must be loaded with the
 proper value to place each channel into the desired mode and to specify
 how transfers are to be made into the channel's counter register.
 Loading the command register with b6h places timer channel 2 in mode 3
 while at the same time permitting the counter register (port 42h) to be
 loaded in two successive byte transfers using a standard OUT
 instruction.
 When in mode 3, a given timer operation begins with the contents of the
 counter register being saved in another internal 8253 register.  The
 counter is then decremented at the input clock frequency until zero is
 reached.  The counter is now reloaded with its saved value, and the
 decrementing process is started.  In mode 3, the output of the channel
 is held high until the counter has decremented to half its initial
 value, and then it is held low until it has decremented to zero.  As
 this process is repeated, a square wave is generated at the channel
 output, and in the case of channel 2 this output is fed (with the proper
 setting of the 8255) into the speaker, thus generating a tone.  This
 cycle continues and the tone remains until a change of mode or a change
 in the 8255 control bits.  Once this is started, no further program
 interaction is necessary. |

 DSEG    SEGMENT    ;Notes of the scale starting with middle C
 notes   dw    262,277,294,311,330,349,370,392,415,440,466,494
 DSEG    ENDS
 CSEG    SEGMENT

 sound   proc  far
         assume    CS:CSEG,DS:DSEG
 start:
         push   ds            ;Stack gets the return segment address
         xor    ax,ax
         push   ax            ;Stack gets a zero return address
         mov    ax,DSEG       ;set up DataSegment register
         mov    ds,ax
         xor    si,si         ;SI is used as a note pointer.
         mov    bx,12         ;BX holds the note count.
 ;Set up timer command register and counter register.
         mov    al,0b6h       ;Set 8253 command register
         out    43h,al        ;  for channel 2, mode 3, etc.
 NLoop:
         mov    ax,34dch      ;AX gets the low part of the clock freq.
         mov    dx,12h        ;DX gets high part of clock frequency
         div    [notes + SI]  ;AX gets most significant byte.
         out    42h,al        ;8253 chip gets most significant byte.
         mov    al,ah         ;Al gets the most significant byte.
         out    42h,al        ;8253 gets the most sig. byte.
 ;Turn on low bits in 8255 output port
         in     al,61h        ;Read in current value of 8255 port
         or     al,3          ;Set low bits.
         out    61h,al        ;Send out new port value.
 ;Loop while note is sounding
         mov    cx,28000
 RPTA:   loop   RPTA          ;change the delay to whatever is cool!
 ;Turn off speaker, check note count, set up next note
         xor    al,3          ;Turn off speaker bit and timer gate.
         out    61h,al        ;Send out new 8255 port value.
         mov    cx,2800
 RPTB:   loop   RPTB          ;Shorter delay...
         inc    si            ;Increment note pointer.
         inc    si            ;  do it twice cause Notes are words!
         dec    bx            ;Decrement note counter.
         jnz    NLoop         ;loop until BX == 0.
         ret
 SOUND   ENDP
 CSEG    ENDS
         END    START

