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"等算术运算指令后面。应用这两个指令时要注意这样一点那就是只有在两个"符号相同"的数相比较后才可用此指令根据结果转移,它不适用于判断"异符号数"运算结果。