本人写了个汇编乐曲程序但在命令提示符中运行出现out of memory怎么办~~求高人指点!!! 下面有源程序

本人写了个汇编乐曲程序但在命令提示符中运行出现out of memory怎么办~~求高人指点!!! 下面有源程序,第1张

mus_freq dw 2 dup(262,294,330,262)

dw 2 dup(330,349,392)

dw 2 dup(415,370,330,262)

dw 2 dup(249,392,262)

dw 0ffffh 少了一个结束标志,自然会溢出memory,不一定要用0ffff,其他也行。

OUT BIT P2.3 //该引脚接蜂鸣器

ORG 0000H

LJMP START

ORG 000BH

INC 20H 中断服务,中断计数器加1

MOV TH0,#0D8H

MOV TL0,#0EFH 12M晶振,形成10毫秒中断

RETI

START:

MOV SP,#50H

MOV TH0,#0D8H

MOV TL0,#0EFH

MOV TMOD,#01H

MOV IE,#82H

MUSIC0:

NOP

MOV DPTR,#DAT 表头地址送DPTR

MOV 20H,#00H 中断计数器清0

MOV B,#00H 表序号清0

MUSIC1:

NOP

CLR A

MOVC A,@A+DPTR 查表取代码

JZ END0 是00H,则结束

CJNE A,#0FFH,MUSIC5

LJMP MUSIC3

MUSIC5:

NOP

MOV R6,A

INC DPTR

MOV A,B

MOVC A,@A+DPTR 取节拍代码送R7

MOV R7,A

SETB TR0 启动计数

MUSIC2:

NOP

CPL OUT

MOV A,R6

MOV R3,A

LCALL DEL

MOV A,R7

CJNE A,20H,MUSIC2 中断计数器(20H)=R7否?

不等,则继续循环

MOV 20H,#00H 等于,则取下一代码

INC DPTR

INC B

LJMP MUSIC1

MUSIC3:

NOP

CLR TR0 休止100毫秒

MOV R2,#0DH

MUSIC4:

NOP

MOV R3,#0FFH

LCALL DEL

DJNZ R2,MUSIC4

INC DPTR

LJMP MUSIC1

END0:

NOP

MOV R2,#64H 歌曲结束,延时1秒后继续

MUSIC6:

MOV R3,#00H

LCALL DEL

DJNZ R2,MUSIC6

LJMP MUSIC0

DEL:

NOP

DEL3:

MOV R4,#02H

DEL4:

NOP

DJNZ R4,DEL4

NOP

DJNZ R3,DEL3

RET

NOP

DAT:

db 26h,20h,20h,20h,20h,20h,26h,10h,20h,10h,20h,80h,26h,20h,30h,20h

db 30h,20h,39h,10h,30h,10h,30h,80h,26h,20h,20h,20h,20h,20h,1ch,20h

db 20h,80h,2bh,20h,26h,20h,20h,20h,2bh,10h,26h,10h,2bh,80h,26h,20h

db 30h,20h,30h,20h,39h,10h,26h,10h,26h,60h,40h,10h,39h,10h,26h,20h

db 30h,20h,30h,20h,39h,10h,26h,10h,26h,80h,26h,20h,2bh,10h,2bh,10h

db 2bh,20h,30h,10h,39h,10h,26h,10h,2bh,10h,2bh,20h,2bh,40h,40h,20h

db 20h,10h,20h,10h,2bh,10h,26h,30h,30h,80h,18h,20h,18h,20h,26h,20h

db 20h,20h,20h,40h,26h,20h,2bh,20h,30h,20h,30h,20h,1ch,20h,20h,20h

db 20h,80h,1ch,20h,1ch,20h,1ch,20h,30h,20h,30h,60h,39h,10h,30h,10h

db 20h,20h,2bh,10h,26h,10h,2bh,10h,26h,10h,26h,10h,2bh,10h,2bh,80h

db 18h,20h,18h,20h,26h,20h,20h,20h,20h,60h,26h,10h,2bh,20h,30h,20h

db 30h,20h,1ch,20h,20h,20h,20h,80h,26h,20h,30h,10h,30h,10h,30h,20h

db 39h,20h,26h,10h,2bh,10h,2bh,20h,2bh,40h,40h,10h,40h,10h,20h,10h

db 20h,10h,2bh,10h,26h,30h,30h,80h,00H

END

早期的PC系列机中有一个专门用于定时的集成电路,型号是8253/8254。它有三个通道,第一个通道用于控制系统时钟正常运转;第二个通道用于存储器刷新;这两个通道与我们现在讨论的问题无关。第三个通道是最有意思的,它通过一组电路与喇叭相联。

图4-1所示即为PC机中完整的发声电路。定时器通道3的G端与61H端口的bit0位相联,如果将61H端口的bit0位置成1,那么定时器通道3就被启动,此时将有一组信号从OUT端输出,信号的频率可以用程序控制;若61H端口bit0位为0,则定时器被关闭,OUT端就会恒定为1

此电路用在这里相当一个"可控开关",如果将61H端口的bit0、bit1位都置成1,则相当于既打开了定时器又打开了开关,这时候定时器产生的声音信号就会送到放大器推动喇叭发声;若将bit0位置0,则定时器关闭,此时OUT端为1,这时候如果连续改变bit1位的状态,也可以从喇叭中听到声音,这就是我们在第二章中所用的方法;若将bit1位置0,则开关关闭,此时即使打开定时器也不能听到声音。

这一点可以通过DEBUG加以验证:进入DEBUG,在"-"后打入"O61 3",即可听到喇叭发出连续的叫声。(在纯DOS下实验)

向61H端口输出"03",相当于打开定时器和开关,此时将有连续的声音发出,这个声音的频率约是896Hz,和我们刚开机时听到的蜂鸣音频率一样。

有趣的是声音一旦发出就不会停止,而且不干扰用户的任何 *** 作。

停止这种声音的唯一方法就是进入DEBUG,打入命令"O61 0(也可以是1或2)"。之所以有这种现象是因为定时器的工作并不需CPU直接参与,CPU只要给定时设定好工作状态和频率值并打开定时器,此时定时器就会自主工作,CPU即可去做别的事情。这个特性十分有用,它是实现"背景音乐"的前提。

那么如何改变声音的频率呢?请注意定时器的通道3还有一个输入端CLK,这一端输入了一个固定的信号,频率是1193181.6Hz。输出信号与此信号具有如下关系:

--------------------------------------------------------------------------------

F(OUT)=F(CLK)/N

--------------------------------------------------------------------------------

其中N是一个16bit数据,它的值可以由程序设定。方法很简单:将此16位数据分成高、低两个8位,先把低8位送至42H端口,紧接着再把高8位送至42H端口,输出信号的频率就会改变。我们可以试一下:

C:\ASM\>DEBUG[Enter]

-O61 3[Enter]

-O42 0[Enter]

-O42 3[Enter]

设定新的N值是300H,对应的F(OUT)是1193191.6/300H=1553Hz。声音马上变尖了。

有一点必须说明,定时器具有多种工作状态,并非每种工作状态都能产生声音,所以当我们想通过定时器产生声音时,我们应首先"初始化"定时器,为其建立正确的工作状态。初始化定时器并不复杂,向端口43H输出数据0B6H即可。这个数据的二进制形式是10110110,有些书籍把这个数称为"幻数"(MAGIC BYTE)。

有了上面介绍的这些知识,我们就可以编程控制定时器发出给定频率的声音。程序PROG6可以使喇叭发出1000Hz的声音

------------------------------------------------

0A3E:0100 MOV AL,B6 ;AL寄存器装入定时器初始化设置码

0A3E:0102 OUT 43,AL ;将设置码输出到43H端口 初始化

0A3E:0104 MOV AX,04A9 1193181.6Hz/1000=1193hz =04A9 hexadecimal AX寄存器置入N值

0A3E:0107 OUT 42,AL ;将N值分两次输出到42H端口 因为是8位

0A3E:0109 MOV AL,AH

0A3E:010B OUT 42,AL

0A3E:010D IN AL,61 ;取得61H端口的当前状态

0A3E:010F PUSH AX ;入栈

0A3E:0110 OR AL,03 ;0111

0A3E:0112 OUT 61,AL ;打开定时器及电子开关

0A3E:0114 MOV AH,01 ;AH = 01h Return: AL = character read 等待输入

character is echoed to standard output(回显)

0A3E:0116 INT 21

0A3E:0118 POP AX ;恢复61H

0A3E:0119 OUT 61,AL

0A3E:011B RET

0A3E:011C

我们已经讨论了如何通过定时器的通道3发出确定频率的声音,这一节我们要一起学习怎样精确地定时,这样才能解决演奏音乐的问题。

PC中的定时电路有三个通道,通道3用于发声,通道1用于控制系统内部的时钟。大家都十分清楚用DOS的"TIME"命令可以观察并修改系统内部的一个时钟,这个时钟之所以能连续运转主要依靠定时器的通道1。

通道1的工作方式和通道3一样,但是系统启动时设定其发出一个频率固定为18.2Hz的信号,这个信号直接送到系统中的"中断控制器"。每一个"Hz"都产生一个硬件中断,一般称这个硬中断为"IRQ0",对应的中断号是08H。也就是说,当计算机启动后,我们的机器看上去十分平静,但实际上CPU非常忙碌。在定时器的控制下每隔55毫秒就要执行一个08H号中断,这个中断的主要工作就是连续地计数。

在内存"0040H:006CH"处有四字节的存储空间专门用于保存计数值,CPU每执行一次08H中断,这四字节的计数值就被加1,不难算出这个计数值每增加1091后时间恰好过了1分钟,每增加65454后时间恰好过了1小时。系统内部的时钟之所以能准确走时,靠得就是08H中断和这四字节的计数值。因此我们要想精确的定时,必须依靠时钟计数值才行

---一个能准确发出1000Hz声音的程序,声音持续时间为5秒钟---------------

PORTB equ 61H

code segment

assume cs:code,ds:code

org 100h

main proc near

mov al,10110110b ;初始化定时器

out 43h,al

mov ax,4a9h ;设置N值为04A9H

out 42h,al

mov al,ah

out 42h,al

in al,PORT_B ;打开定时器及与门

or al,3

out PORT_B,al

;------------------以下为定时部分---------------

mov ah,0 ;选择1AH中断的0号功能

int 1ah ;调用1AH中断取得当前时钟计数

add dx,91 ;在当前时钟计数上加91---5秒

mov bx,dx ;保存定时终了时的计数值

delay: int 1ah ;两次调用1AH中断取得时钟计数值

cmp dx,bx ;到达定时终了时的计数值了吗?

jne delay ; 没有到达,则返回DELAY处继续

;----------------------------------------------------

in al,PORT_B ;定时终止,关闭定时器及与门

and al,0fch 1111 1100

out PORT_B,al

int 20h ;结束程序

main endp

code ends

参考资料:PC机汇编语言实践精解/李春生编著。

datasegment

freqdw196,220

dw262,262,262,262,262,220,196

dw262,262,262,262,294,262,220,262

dw294,294,294,294,294,262,220

dw294,294,294,294,330,294,330,392

dw440,440,392,440,392,330

dw294,294,330,294,262,220,196,220

dw262,262,262,262,262,220

dw262,196,220

dw440,440,392,440,524,440

dw392,330,294,262,220,196,220

dw262,262,262,262,294,262

dw262,330,392

dw440,440,440,440,524,440

dw392,392,392,440,392,330,294

dw262,262,262,262,294

dw330,330,294

dw262,262,262,262,524,440

dw392,392,392,440,392,330,392

dw440,524,524,440,392

dw392,330,392

dw440,440,440,440,524,440

dw392,392,392,440,392,330,294

dw262,262,262,262,392

dw330,330,294

dw262,262,262,262,294,330

dw392,392,330,392,330,392

dw440

dw9,9,196,660,294,294,262

dw262,-1

timedw400,400

dw400,200,400,400,800,400,400

dw400,200,400,200,200,800,400,400

dw400,200,400,400,800,400,400

dw400,200,400,200,200,800,400,400

dw400,800,400,800,400,400

dw400,200,200,400,400,800,400,400

dw400,200,400,400,800,800

dw1600,800,800

dw400,800,400,800,400,400

dw400,400,400,400,800,400,400

dw400,800,400,800,400,200

dw2400,400,400

dw400,800,400,800,400,400

dw400,800,200,200,800,400,400

dw400,800,400,800,800

dw2400,400,400

dw400,800,400,800,400,400

dw400,800,200,200,800,400,400

dw800,400,800,400,200

dw2400,400,400

dw400,800,400,800,400,400

dw400,800,200,200,800,400,400

dw400,800,400,800,800

dw2400,400,400

dw400,800,400,800,400,400

dw400,800,400,800,400,400

dw3200

dw800,400,400,400,400,400,400

dw4000

dataends

codesegment

assumecs:code,ds:data

mainprocfar

start:movax,data

movds,ax

movsi,offsetfreq

movdi,offsettime

l1: movcx,[si]

cmpcx,-1

jeexit

movbx,[di]

callgensound

addsi,2

adddi,2

jmpl1

exit:movax,4c00h

int21h

mainendp

gensoundprocnear

pushdx

moval,0b6h

out43h,al

movdx,08h

movax,3208h

divcx

out42h,al

moval,ah

out42h,al

inal,61h

movah,al

oral,3

out61h,al

l2: pushdx

pushax

movdx,8h

movax,0f05h

s1: subax,1

sbbdx,0

jnzs1

popax

popdx

decbx

jnzl2

moval,ah

out61h,al

popdx

ret

gensoundendp

codeends

endstart


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11101045.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-13
下一篇 2023-05-13

发表评论

登录后才能评论

评论列表(0条)

保存