MUSIC已经是我们所编制的最复杂的程序了,在这个程序中我们使用了很重要的间接寻址技术用于取得数据,本节我们将讨论和CALL指令有关的间接寻址技术。
下面给出的程序可以在我们的控制下产生滑音。程序一开始运行就在屏幕上显示一个"菜单",有三个选项:选"1"可以使机器发出连续上升的声音;选"2"可以使机器发出连续下降的声音;其它按键则使程序结束。请看源程序:
SIREN.ASM
PORTB EQU 61H
M_WORD EQU 10110110B
CODE SEGMENT
ASSUME CS:CODE,DS:CODE
ASSUME ES:CODE,SS:CODE
ORG 100H
MAIN PROC FAR
JMP START
ADDR DW OFFSET RISE ;RISE子过程偏移地址
DW OFFSET FALL ;FALL子过程偏移地址
OUTMSG DB 0DH,0AH ;定义菜单
DB '1---RISE',0DH,0AH
DB '2---FALL',0DH,0AH
DB 'OTHER---EXIT',0DH,0AH
DB 'CHOICE:',24H
START: MOV AH,09
MOV DX,OFFSET OUTMSG
INT 21H ;显示菜单
MOV AH,01H
INT 21H ;等待输入选择
CMP AL,'1'
JB EXIT ;小于1则退出
CMP AL,'2'
JA EXIT ;大于2,退出
SUB AL,30H ;ASCII码转换成数字
CBW ;BYTE转换成WORD
DEC AX
SHL AX,1 ;(数字-1)×2,计算地址指针
MOV BX,AX ;地址指针置入BX寄存器
CALL ADDR[BX] ;转至相应子过程
JMP START ;循环
EXIT: INT 20H ;返回DOS
MAIN ENDP ;主过程结束
RISE PROC NEAR
CALL OPEN_T ;调用OPEN_T子程序打开定时器
MOV AX,3000H ;设定频率初值
LOOP1: CALL SOUND ;输出频率
CALL DELAY ;调用延时子程序
DEC AX ;频率升高
JNZ LOOP1 ;循环输出
CALL CLOSE_T ;调用CLOSE_T子程序关闭定时器
RET ;子程序返回
RISE ENDP
FALL PROC NEAR
CALL OPEN_T ;打开定时器
MOV AX,0 ;设定频率初值
LOOP2: CALL SOUND ;输出频率
CALL DELAY ;延时
INC AX ;频率降低
CMP AX,3000H ;是否到预定值?
JNE LOOP2 ;若未到,循环输出
CALL CLOSE_T ;关闭定时器
RET ;子程序返回
FALL ENDP
SOUND PROC NEAR
PUSH AX ;保存AX寄存器至堆栈
OUT 42H,AL ;输出低字节
MOV AL,AH
OUT 42H,AL ;输出高字节
POP AX ;恢复AX寄存器
RET ;子程序返回
SOUND ENDP
OPEN_T PROC NEAR
MOV AL,M_WORD ;MAGIC WORD送入AL寄存器
OUT 43H,AL ;初始化8253定时器通道
IN AL,PORTB ;读61H端口的原值
OR AL,03H ;bit0,bit1置1
OUT 61H,AL ;开启定时器
RET ;子程序返回
OPEN_T ENDP
CLOSE_T PROC AL,61H ;恢复61H端口的原值
IN AL,11111100B
AND 61H,AL
OUT ;子程序返回
RET
CLOSE_T ENDP
NEAR
DELAY PROC CX ;将CX寄存器保存至堆栈
PUSH CX,500H ;CX寄存器置入500H
MOV DELAY1 ;循环等待
DELAY1: LOOP CX ;恢复CX寄存器
POP ;返回主程序
RET
DELAY ENDP
程序中多了两条新的条件跳转指令--JA和JB:
助记符:JA(Jump if above)
用 途:判断比较(减法运算)的结果是不是"前高于后"(为正)
格 式:JA 目的地址
执 行:若两个数比较(相减),前一个数比后一个数大(相减结果为正)则执行转移
助记符:JB(Jump if below)
用 途:判断比较(减法运算)的结果是不是"低于"(为负)
格 式:JB 目的地址
执 行:若两个数比较(相减),前一个数比后一个数小(相减结果为负)则执行转移
这两条指令是功能相对的,它常用于"CMP"指令或"SUB"等算术运算指令后面。应用这两个指令时要注意这样一点那就是只有在两个"符号相同"的数相比较后才可用此指令根据结果转移,它不适用于判断"异符号数"运算结果。