ITEEDU

下面给出的程序例是一个音乐程序,与第四章中的程序例不同,这个程序可以产生出"背景音乐",即音乐的播放不会影响到机器进行其它工作。你可以一面听音乐一面格式化磁盘,或者用WPS写文章。总之,音乐的产生与用户的工作是同时的,而且互不干扰。

BKMUSIC.ASM

        code  segment
              assume    cs:code,ds:code
              org       100h
        main  proc      far
              jmp       install	;跳转至安装部分
        freq  dw        0,4552,4029,3617,3415	;各音符频率表
              dw        322,330,422,871
        note  db        3,4,2,4,1,4,2,4,3,4,3,4,	3,8,2,4,2,4,2,8 ;乐谱
              db        3,4,6,4,6,8,3,4,2,4,1,4,	2,4,3,4,3,4,3,4
              db        3,4,2,4,2,4,3,4,2,4,1,16
              dw        -1
     nxtnote  dw        ?	;将要演奏的音符在乐谱中的位置
       delay  db        ?	;当前音符应持续的时间
			
new_1ch:			;新的1CH中断服务程序
              push      ax	;保存寄存器
              push      bx
              push      si
              cmp       byte ptr cs:[delay],0	;当前的音符演奏完了吗?
              jz        play_next	;若当前的音符已奏完,转PLAY_NEXT
              dec       byte ptr cs:[delay]	;将当前音符的持续时间减1
              jmp       return	;退出中断服务程序
  play_next:
              in        al,61h	;关闭时间
              and       al,0fch
              out       61h,al
			
              mov       bx,cs:[nxtnote]	;取得将要演奏的音符在乐谱中的位置
              shl       bx,1
              mov       ax,word ptr cs:note[bx]	;从乐谱中取出音符及时间
			
              cmp       ax,-1	;乐谱已经演奏完了吗?
              jz        play_end	;若乐谱已结束,转PLAY_END
			
              mov       cs:[delay],ah	;将这个音符的时间送入DELAY
              inc       word ptr cs:[nxtnote]	;NXTNOTE指向下一个音符
			
              xor       ah,ah	;将AL中的音符转成16位数据
              shl       ax,1	;将音符乘2,准备在频率表中取N值
              mov       si,offset freq	;SI寄存器指向频率表首
              add       si,ax	;加上AX中的偏移量
			
              mov       ax,word ptr cs:[si]	;取出N值并输出至定时器
              out       42h,al
              mov       al,ah
              out       42h,al
			
              in        al,61h	;启动定时器发声
              or        al,03h
              out       61h,al
     return:
              pop       si	;恢复寄存器
              pop       bx
              pop       ax
              iret      ;退出中断服务程序
   play_end:
              mov       cs:[curnote],0	;若乐谱已奏完,从头开始演奏
              jmp       return	;退出中断服务程序
    install:
              mov       dx,offset new_1ch	;设置新的1CH中断向量
              mov       ax,251ch
              int       21h
			
              mov       dx,offset install	;结束程序并驻留
              int       27h
        main  endp
        code  ends
              end       main

CPU之所以能够一边放音乐一边做其它工作,还有一个重要的原因就是定时器的发声通道可以独立工作。只要CPU给定时器设定了参数N并打开了定时器,则声音就会连续不断的发出,此时CPU就可以进行其它工作而不必再参与声音的产生。剩下的工作就是每隔55MS关照一下正在播放的音符,看看是否应该播放下一个音符了。

至此有关内存驻留技术的讨论可以告一段落,注意内存驻留技术最关键的两点--程序驻留与中断驱动。本书给出的程序例利用了系统一些很有特色的中断,实际上驻留内存的程序可以由任意的中断来驱动,比如DOS系统中的磁盘高速缓存程序SMARTDRV就是重编了"13H"中断服务程序来工作的。