前言
以定时器控制频率发声汇编代码为例,介绍汇编代码基础知识
一、示例代码
代码如下(示例):
; 演奏“友谊地久天长”——使用8254虚拟电路
DATA SEGMENT
FREQ_LIST DW 371,495,495,495,624,556,495,556,624 ;频率表
DW 495,495,624,742,833,833,833,742,624
DW 624,495,556,495,556,624,495,416,416,371
DW 495,833,742,624,624,495,556,495,556,833
DW 742,624,624,742,833,990,742,624,624,495
DW 556,495,556,624,495,416,416,371,495,0
TIME_LIST DB 4,6,2,4,4,6,2,4,4 ;时间表
DB 6,2,4,4,12,1,3,6,2
DB 4,4,6,2,4,4,6,2,4,4
DB 12,4,6,2,4,4,6,2,4,4
DB 6,2,4,4,12,4,6,2,4,4
DB 6,2,4,4,6,2,4,4,12
TIM_CTL EQU 453H ;8254 状态/命令口地址
TIMER1 EQU 451H ;8254 定时计数器1地址
MODE00 db 70h
MODE03 db 76h
sound_FREQ dw 523,587,659,698,784,880,988,1046,1175,1318,1397, 1568,1760,1967
s db "Playing a tune. Please wait a moment...",0dh,0ah,"$"
DATA ENDS
code segment
assume cs:code,ds:data
go: mov ax,data
mov ds,ax
mov dx,offset s ; 显示提示
mov ah,9
int 21h
mov dx,TIM_CTL ; 定时器1工作在方式3
mov al,MODE03
out dx,al
LEA SI,OFFSET FREQ_LIST
LEA DI,OFFSET TIME_LIST
m1: cmp WORD PTR [si],0
jz exit
mov ah,0bh
int 21h ; 检测有无按键,返回AL=0FFh(有按键)或0(无按键)
cmp al,0FFh
jz Exit
call sound
call DALLY
inc si
inc si
inc di
loop m1
exit:
mov dx,TIM_CTL ; 定时器1工作在方式0(关闭)
mov al,MODE00
out dx,al
mov ah,4ch ; 结束
int 21h
DALLY PROC ;延时子程序。若电脑速度慢可减少CX或AX的值
push ax
push bx
push cx
mov bl,[di]
D0: MOV CX,12H
D1: MOV AX,02000H
D2: DEC AX
JNZ D2
LOOP D1
dec bl
jnz d0
pop cx
pop bx
pop ax
RET
DALLY ENDP
sound PROC ;发音
push ax
push bx
push cx
push dx
MOV DX,0fH ;输入时钟为1MHz,1M = 0F4240H
MOV AX,4240h ;除法中被除数默认DX放高16位,AX放低十六位,除数为16位则,AX存商,DX存余数
DIV WORD PTR [SI] ;取出频率值计算计数初值,0F4240H / 输出频率
MOV cx,AX
MOV dx,TIMER1 ; 定时器1计数初值
MOV al,cl
out dx,al
MOV al,ch
out dx,al
pop dx
pop cx
pop bx
pop ax
ret
sound ENDP
code ends
END go
二、代码分析
1.LEA
取FREQ_LIST内偏移地址放入SI,即SI记录频率,DI记录时间
代码如下:
go: LEA SI,OFFSET FREQ_LIST
LEA DI,OFFSET TIME_LIST
取源操作数地址的偏移量,并把它传送到目的操作数所在的单元。
2.WORD PTR
比较判断SI是否为01,若为0则退出
代码如下(示例):
m1: cmp WORD PTR [si],0
jz exit
WORD PTR:指明为两个字节单元
3.PROC
代码如下(示例):
sound PROC
...
sound ENDP
proc是定义子程序的伪指令,位置在子程序的开始处,它和endp分别表示子程序定义的开始和结束两者必须成对出现。
4.DIV
除法,取出频率值计算计数初值,0F4240H / 输出频率,计算出初值,存在cx寄存器
代码如下(示例):
sound: ...
MOV DX,0fH ;输入时钟为1MHz,1M = 0F4240H
MOV AX,4240h
DIV WORD PTR [SI] ;取出频率值计算计数初值,0F4240H / 输出频率
MOV cx,AX
div,(无符号除法)指令执行 8 位、16 位和 32 位无符号数除法。其中,单寄存器或内存操作数是除数
寄存器分布
位数 | 被除数 | 商 | 余数 |
---|---|---|---|
8位 | AX | AL | AH |
16位 | DX:AX | AX | DX |
32位 | EDX:EAX | EAX | EDX |
64位 | RDX:RAX | RAX | RDX |
本代码为16位,DX存高位16位,AX存低位16位
5.声音频率输出
dx存入时间初值,定时器记录初值。由低位到高位依次输出cx内计算得出的声音频率,发声成功
代码如下(示例):
L1: ...
MOV dx,TIMER1 ; 定时器1计数初值
MOV al,cl
out dx,al
MOV al,ch
out dx,al
6.延时
DALLY PROC ;延时子程序。若电脑速度慢可减少CX或AX的值
push ax
push bx
push cx
mov bl,[di]
D0: MOV CX,12H
D1: MOV AX,02000H
D2: DEC AX
JNZ D2
LOOP D1
dec bl
jnz d0
pop cx
pop bx
pop ax
RET
DALLY ENDP
7.loop循环
延时结束后,si值+2,inc值+1,随后跳转回m1开端进入循环,直至si为0,跳转至exit。
代码:
m1: cmp WORD PTR [si],0
jz exit
...
inc si
inc si
inc di
loop m1
exit:定时器转到MODE00,定时器关闭程序结束
exit:
mov dx,TIM_CTL ; 定时器1工作在方式0(关闭)
mov al,MODE00
out dx,al
mov ah,4ch ; 结束
int 21h