主要是放一些做过的题,以后可能就不想做了
自我感觉我的注释还是很详细的,希望愿意看我代码的人能因为我的详细的注释而理解方便,获得思路

题目集合

  • 题目:从键盘输入两个1位十进制数,计算这两个数之和,并将结果在屏幕上显示出来。
  • 题目:从键盘输入0~9中任意一个自然数,将其立方值在屏幕上显示出来(使用查表法实现)。
  • 题目:求两个有符号字节型内存数X和Y的绝对值之和,保存在Z中
  • 题目:输入一个数N(0-65535),计算Σ(i从1到N)i(0-65535)并输出
  • 题目:输入数字之后冒泡排序
  • 题目:从键盘输入十个无符号的十进制数(小于256),将其转换为二进制数并存放在NUM字节型变量中,找出其中的最大数,并在屏幕上显示出来。
  • 题目:从键盘输入N个十进制数,求它们的和(累加和要求不大于65 535),并将累加结果在屏幕上显示出来。要求给出必要的提示信息(用宏调用完成);累加功能由子程序调用实现;二进制数形式的累加和转换为十进制数并显示由子程序调用实现。
  • 题目:从键盘输入一个带符号字节数据(如-56)存入内存(10进制在内存中显示),并将结果在显示器上以十进制形式显示输出。
  • 编写子程序段实现:字节变量S1中存放着一个字符串,以0DH为结束标志,试求串长度并送字变量CD中
  • 编写宏汇编程序段实现把S1区中所有字符逆向传送到O1区
  • 期中复习

题目:从键盘输入两个1位十进制数,计算这两个数之和,并将结果在屏幕上显示出来。

分析:DOS功能调用介绍

1.从键盘输入一个1位十进制的方法。
通过功能号为1的DOS功能调用实现从键盘输入一个字符,格式如下:

MOV AH, 01H
INT 21H
SUB AL, 30H

2.提示信息字符串的显示。
通过功能号为9的DOS功能调用实现字符串显示,注意字符串的最后一个字符必需为‘$’。指令格式如下:

MOV DX, OFFSET INFOR1
MOV AH,09H
INT 21H

3.非压缩型BCD码加法调整指令的使用。
设从键盘输入的数值已存放在寄存器AL,BL中,可用下列程序完成数据相加和调整操作:

XOR AH, AH
ADD AL, BL
AAA

4.计算结果的显示。
执行完AAA指令后,只需分别将AH(十位上的数值)和AL(个位上的数值)加上30H,并依次调用功能号为2的DOS功能调用来显示字符,具体指令格式如下:

PUSH AX
MOV DL, AH
ADD DL, 30H
MOV AH, 02H
INT 21H
POP AX
MOV DL, AL
ADD DL, 30H
MOV AH, 02H
INT 21H

全部代码:

DATA SEGMENT INFOR1 DB "Please Input the First Data(<10): $"INFOR2 DB 0AH, 0DH, "Please Input the Second Data(<10): $"INFOR3 DB 0AH, 0DH, "The Result is: $"
;0AH和0DH分别为换行和非显示字符回车的ASCII码值,
;美圆符号“”为字符串结束标志,不能省略。
DATA ENDSCODE SEGMENT
ASSUME CS:CODE, DS:DATASTART:MOV AX, DATA
MOV DS, AX
;功能调用,显示提示信息INFOR1并从键盘接收一个字符的指令序列
MOV DX, OFFSET INFOR1
MOV AH,09H
INT 21H
;功能调用,现从键盘输入一个字符
MOV AH, 01H
INT 21H
;从键盘输入的是十六进制的ascii码,减30H变成十六进制数
SUB AL, 30H
;第一个数原本存在AL中,为了复用输入字符的代码(使用AL接受),将AL中的数据转移到BL中,给AL腾出空间
MOV BL, AL
;功能调用,显示提示信息INFOR2并从键盘接收一个字符的指令序列
MOV DX, OFFSET INFOR2
MOV AH,09H
INT 21H
;功能调用,从键盘输入一个字符
MOV AH, 01H
INT 21H
;从键盘输入的是十六进制的ascii码,减30H变成十六进制数
SUB AL, 30H
;求和并调整指令序列
XOR AH, AH;清空AH,为了AL和BL的加法做准备
ADD AL, BL;AL加BL
AAA;进位?
PUSH AX;保护AX
;功能调用,显示提示信息INFOR3的指令序列
MOV DX, OFFSET INFOR3
MOV AH,09H
INT 21H
POP AX;弹回AX
PUSH AX;保护AX
;显示十位及个位上数字的指令序列
;因为要显示的字符的ascii码保存在DL,所以先把数据移动到DL,这里是移动高位到DL
MOV DL, AH
ADD DL, 30H
;功能调用,单字符输出一个字符,要显示的字符的ascii码保存在DL
MOV AH, 02H
INT 21H
;弹回AX,恢复AX的值
;这里程序分别取AH和AL的值,看上去好像不用维护AX,看上去好像AX的值始终不变。实际上,在DOS功能调用的时候,程序的返回值会保存在AX中,会改变AX的值。因此还是有必要维护AX的。POP AX
;因为要显示的字符的ascii码保存在DL,所以先把数据移动到DL,这里是移动低位到DL
MOV DL, AL
ADD DL, 30H
;功能调用,单字符输出一个字符,要显示的字符的ascii码保存在DL
MOV AH, 02H
INT 21H
;功能调用,结束当前程序,返回DOS
MOV AH, 4CH
INT 21H
CODE ENDS
END START


(图1 注释掉POP AX之后,8086执行功能调用的第一步)

(图2 注释掉POP AX之后,8086执行功能调用的第二步。可以在寄存器面板里看到AL的值被悄悄修改了)

题目:从键盘输入0~9中任意一个自然数,将其立方值在屏幕上显示出来(使用查表法实现)。

代码实现:

DATA SEGMENTINPUT DB 'PLEASE INPUT X(0-9):$'OUTPUT DB 0DH,0AH,'THE CUBE OF X IS:$'TAB DW 0,1,8,27,64,125,216,343,512,729
;2^8=256,但是TAB表中最大值是729,超过了8位数据的表示范围,所以必须要将数据扩展到16位
DATA ENDS
CODE SEGMENT ‘CODE’ASSUME CS:CODE,DS:DATA
START:    MOV AX,DATA
MOV DS,AX
MOV DX,OFFSET INPUT
MOV AH,9
INT 21H
MOV AH,1
INT 21H
;把高位清零,原来输入的是ASCII码保存在AL中,所以相当于减30,但是比减法更快
AND AL,0FH
;一个字占两个地址,地址加上他本身相当于地址乘2,同时AL最大才9,不会溢出
ADD AL,AL
MOV BL,AL
;清零BH,为了在TAB表中找到第BL+1个数做准备
MOV BH,0
;AX用为指针,要用到BX作为基址,找到了TAB表的第BX+1个元素,寄存器间接寻址;程序的目标是输入一个0到9的数,查表获得它的立方值,那么你可能认为输入的数X就对应TAB中第X+1个元素,所以你可能会认为AL不需要自乘2。实际上,TAB表是以DW型存储,那么表中第X+1个元素的地址应该是2*(X+1)和2*(X+1)+1
MOV AX,TAB[BX]
PUSH AX
LEA DX,OUTPUT
MOV AH,9
INT 21H
POP AX;AX除以BL
MOV BL,100D
DIV BL
;调用DOS前,使用堆栈维护AX
;除数为一个字节,被除数为AX,除法结束后,AL是商,AH是余数
;输出百位数字
PUSH AX
MOV DL,AL
ADD DL,30H
MOV AH,02H
INT 21H
;将余数摆到AL,清零AH,使得AX的值等于上一次除法的余数
POP AX
MOV AL,AH
AND AH,0
MOV BL,10D
DIV BL
;调用DOS前,使用堆栈维护AX
;除数为一个字节,被除数为AX,除法结束后,AL是商,AH是余数
;输出十位数字
PUSH AX
MOV DL,AL
ADD DL,30H
MOV AH,02H
INT 21H
;输出个位数字
POP AX
MOV DL,AH
ADD DL,30H
MOV AH,02H
INT 21H
;程序结束
MOV AH,4CH
INT 21H
CODE ENDS
END START

题目:求两个有符号字节型内存数X和Y的绝对值之和,保存在Z中

代码:

DATA SEGMENT
X DB -99
Y DB 65
Z DB 0
DATA ENDSCODE SEGMENT
ASSUME CD:CODE, DS:DATA
START:;------------------取代码段首地址-------------------MOV AX, DATA
MOV DS, AX;-----------------------结束--------------------------;-------------------求绝对值再相加-------------------;先清零
MOV AX, 0
MOV BX, 0;因为加数都是字节,所以都放寄存器的低位
MOV AL, X
MOV BL, Y;有符号数在计算机中以补码表示,需要按位求反之后+1CMP AL,0
;有符号数之间判断大小,如果AL<=0,说明需要求相反数,则跳转到MARK1
JLE MARK1
;不需要求相反数则直接跳转到ENDIF1结束本回合判断
JMP ENDIF1
MARK1:
;返回自己的相反数
NEG AL
ENDIF1:;同理
CMP BL,0
JLE MARK2
JMP ENDIF2
MARK2:
NEG BL
ENDIF2:;将得到的绝对值相加(未考虑溢出
ADD AL,BL;将结果保存在Z中(题目要求
MOV Z,ALCODE ENDS
END START

题目:输入一个数N(0-65535),计算Σ(i从1到N)i(0-65535)并输出

分析:
该程序的作用是输入一个数N(0-65535),计算Σ(i从1到N)i(0-65535)并输出
不需要输入的数大于627才出现问题,根据等差数列求和公式,输入的数大于361时就会溢出
解决方法是在“AX中存放的二进制数转换为十进制数并显示的指令序列”这一步中将AX扩展为双字

代码:

DATA SEGMENT INF1 DB "Please input a number(0-65535):$";缓冲区第一字节存放接受字符串的最大字符个数,缓冲区第二字节存放实际输入的字符(不包括回车符),第三字节开始存放接收的字符串;在这里,开了最多7个字节的位置,第一字节占一个,第二字节占一个,后面还剩5个字节的位置,第二字节和后面5个字节的位置合起来就是六个变量所以是6 DUP(?)IBUF DB 7, 0, 6 DUP('?')OBUF DB 6 DUP('?')
DATA ENDS
CODE SEGMENTASSUME CS:CODE, DS: DATA
START: ;取代码段
MOV AX, DATA
MOV DS, AX;接收从键盘输入的十进制数,并将其转换为二进制数(存AX寄存器)的指令序列;输出字符串INF1
MOV DX, OFFSET INF1
MOV AH, 09H
INT 21H;字符串输入,寄存器DS存放接收缓冲区段地址,DX存放接收缓冲区偏移地址
MOV DX, OFFSET IBUF
MOV AH, 0AH
INT 21H;将实际接受的字符数移到CL中,用于计数
MOV CL, IBUF+1
;将CX的高位CH清零,方便计数
MOV CH, 0;将接收到的第一个字符的偏移地址移到SI中
MOV SI, OFFSET IBUF+2
;清零AX
MOV AX, 0AGAIN: ;乘数为10
MOV DX, 10
;字乘时,DX,AX<-(AX)*(src),乘积的高16位存在DX中,低16位存在AX中,此处src为DX
MUL DX;用word ptr指明了指令访问的内存单元是一个字单元
;用byte ptr指明了指令访问的内存单元是一个字节单元
;IBUF中第SI个元素原来是一个ASCII码,只保留一个16位数,相当于减30
AND BYTE PTR [SI], 0FH
;将IBUF中第SI个元素加到AX中
ADD AL, [SI]
;上一步中如果CF=1,说明AL最高位出现了进位,所以下一步需要对AH执行可能的进位操作
;ADC dest,src中,如果src=0,那么就相当于对dest执行可能的进位
ADC AH, 0;SI自加1,即接下来要对IBUF数组中的下一个数进行操作
INC SI;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,AX等于输入的第一个十进制数,一轮结束,CX自减1
;(CX)=2,AX等于输入的两个数组成的十位十进制数,一轮结束,CX自减1
;(CX)=1,AX等于输入的三个数组成的百位十进制数,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,AX内保存N位十进制数,可以满足程序目标
LOOP AGAIN;若(AX)=N,计算Σ(i从1到N)i;将AX里的数移到CX中,结合后面的LOOP指令可知这个移动是想要使用原先AX里面的数作为计数
MOV CX, AX
;AX清零
MOV AX, 0
;BX赋初值1
MOV BX, 1LOOP2:
;计算累加和
ADD AX, BX
;BX作为累加元素,自加1
INC BX
;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,(AX)=1,一轮结束,CX自减1
;(CX)=2,(AX)=1+2,一轮结束,CX自减1
;(CX)=1,(AX)=1+2+3,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,(AX)=Σ(i从1到N)i,可以满足程序目标
LOOP LOOP2;将AX中存放的二进制数转换为十进制数并显示的指令序列;DS已存代码段的段地址;在OBUF的末尾放入$符号作为结束符号
MOV BX, OFFSET OBUF+5
MOV BYTE PTR [BX], '$';CX放入10作为除数
MOV CX, 10LOOP1:
;字除时,被除数是(DX,AX),所以清零DX
MOV DX, 0
;(DX,AX)/(CX),商存在AX中,余数是个位数,存在DX中
DIV CX
;余数加30变成ASCII码形式
ADD DL, 30H
;BX自减1,从OBUF的末尾往前走一位
DEC BX
;将ASCII码形式的DL从后往前放入OBUF
MOV [BX], DL;AX与自己进行或运算,AX不变,但是可以影响符号位SF,ZF,PF,且CF,OF清零
;这里的目的是影响ZF,如果(AX)=0,那么ZF=1,JNZ不执行,否则ZF=0,JNZ进行,跳转到标号处,即判断AX是否为0
OR AX, AX
JNZ LOOP1;BX中本来存着OBUF的偏移地址,现在由于要使用字符串输出的DOS功能调用,转移给DX
;DS已经存好了段地址
MOV DX, BX;回车换行
PUSH DX
MOV AH,02H
MOV DL,0DH
INT 21H
MOV AH,02H
MOV DL,0AH
INT 21H
POP DX;字符串输出
MOV AH, 09H
INT 21H;程序结束
MOV AH, 4CH
INT 21HCODE ENDS
END START普通的冒泡法DATA_SEG SEGMENT
ARRAY DB 38, 25, 63, 17, 22
;这里不可以写成 N DB 5,因为后面用到的N是一个立即数,这样写的话N就变成了一个地址
;$代表它自己所在位置的地址,ARRAY代表数组的首地址,则$-ARRAY代表数组元素个数
N=$-ARRAY
DATA_SEG ENDSCODE_SEG SEGMENT
ASSUME CD:CODE_SEG, DS:DATA_SEG
START:;取代码段
MOV AX, DATA_SEG
MOV DS, AX;CL获得N-1,N为数据的总数
MOV CL, N
DEC CXOUTER:
;进栈,保存第一个计数器,相当于for(i=N-1;i--;i>=0)中的i被保存
PUSH CX
;进栈之后的CX作为第二个计数器,相当于for(j=N-1;j--;j>=0)中的j
;BX作为ARRAY的序号,从0开始
MOV BX,0INNER:
;ARRAY[BX]与ARRAY[BX+1]比较
MOV AL,ARRAY[BX]
CMP AL,ARRAY[BX+1];ARRAY[BX]<=ARRAY[BX+1]时,无需交换,跳转到NEXT
JNG NEXT;ARRAY[BX]>ARRAY[BX+1]时,需交换
XCHG AL, ARRAY[BX+1]
MOV ARRAY[BX],ALNEXT:
;BX自加1,以便进行下一组ARRAY[BX]与ARRAY[BX+1]比较
INC BX
;相当于for(j=N-1;j--;j>=0)中的j--,只是这里自减的是CX,如果CX=0则结束循环,否则转移到INNER标号处
LOOP  INNER;程序运行到这里,说明内层的第二个计数器CX=0,说明已经进行了一轮内层的比较
;出栈,返回外层的第一个计数器
POP CX
;相当于for(i=N-1;i--;i>=0)中的i--,只是这里自减的是CX,如果CX=0则结束循环,否则转移到OUTER标号处
LOOP OUTER;程序运行到这里,说明外层的第一个计数器CX=0,说明外层比较已结束
EXIT:
MOV AH,4CH
INT 21HCODE_SEG ENDS
END从键盘输入一个无符号十进制数(小于65535),将其转换为二进制数,统计该二进制数中包含的1的个数,并将统计结果在屏幕上显示出来DATA SEGMENT INF1 DB "Please input a number(0-65535):$";缓冲区第一字节存放接受字符串的最大字符个数,缓冲区第二字节存放实际输入的字符(不包括回车符),第三字节开始存放接收的字符串;在这里,开了最多7个字节的位置,第一字节占一个,第二字节占一个,后面还剩5个字节的位置,第二字节和后面5个字节的位置合起来就是六个变量所以是6 DUP(?)IBUF DB 7, 0, 6 DUP('?')OBUF DB 6 DUP('?')
DATA ENDS
CODE SEGMENTASSUME CS:CODE, DS: DATA
START: ;取代码段
MOV AX, DATA
MOV DS, AX;接收从键盘输入的十进制数,并将其转换为二进制数(存AX寄存器)的指令序列;输出字符串INF1
MOV DX, OFFSET INF1
MOV AH, 09H
INT 21H;字符串输入,寄存器DS存放接收缓冲区段地址,DX存放接收缓冲区偏移地址
MOV DX, OFFSET IBUF
MOV AH, 0AH
INT 21H;将实际接受的字符数移到CL中,用于计数
MOV CL, IBUF+1
;将CX的高位CH清零,方便计数
MOV CH, 0;将接收到的第一个字符的偏移地址移到SI中
MOV SI, OFFSET IBUF+2
;清零AX
MOV AX, 0AGAIN: ;乘数为10
MOV DX, 10
;字乘时,DX,AX<-(AX)*(src),乘积的高16位存在DX中,低16位存在AX中,此处src为DX
MUL DX;用word ptr指明了指令访问的内存单元是一个字单元
;用byte ptr指明了指令访问的内存单元是一个字节单元
;IBUF中第SI个元素原来是一个ASCII码,只保留一个16位数,相当于减30
AND BYTE PTR [SI], 0FH
;将IBUF中第SI个元素加到AX中
ADD AL, [SI]
;上一步中如果CF=1,说明AL最高位出现了进位,所以下一步需要对AH执行可能的进位操作
;ADC dest,src中,如果src=0,那么就相当于对dest执行可能的进位
ADC AH, 0;SI自加1,即接下来要对IBUF数组中的下一个数进行操作
INC SI;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,AX等于输入的第一个十进制数,一轮结束,CX自减1
;(CX)=2,AX等于输入的两个数组成的十位十进制数,一轮结束,CX自减1
;(CX)=1,AX等于输入的三个数组成的百位十进制数,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,AX内保存N位十进制数,可以满足程序目标
LOOP AGAIN;--------------下面开始统计二进制AX中1的个数-----------------;把BX和CX清零
MOV BX, 0
MOV CX, 0MARK2:;为了每一次右移CL位之后得到正确的数字,要保护AX
PUSH AX;AX逻辑右移CL位
SHR AX,CL
;取移位后的AX的最低一位上的数字
AND AX,0000000000000001B;CX计数器自加一
INC CX;AX的最低一位上的数字与1比较,相等则给BX计数器加1,不相等则接着判断CX计数器
CMP AX, 1;恢复AX,为了下一次的比较做准备
POP AXJNZ MARK
INC BX;如果CX计数器的数字等于16,说明已经左移比较了15次,程序达成目标
MARK:
CMP CX,16JNZ MARK2;回车换行
PUSH BX
MOV AH,02H
MOV DL,0DH
INT 21H
MOV AH,02H
MOV DL,0AH
INT 21H
POP BXMOV AX, BX
MOV BX, 10
DIV BLPUSH AX
MOV DL,AL
ADD DL,30H
MOV AH,02H
INT 21H
POP AXPUSH AX
MOV DL,AH
ADD DL,30H
MOV AH,02H
INT 21H
POP AX;程序结束
MOV AH, 4CH
INT 21HCODE ENDS
END START

调试代码的时候,明明代码相同,输入相同,但是单步运行和不中断运行的结果就是不一样
我还以为是编译器的问题,没想到学霸提点我是,单步运行在输入时中断之后,不承认之前的输入,是我傻了ᕕ( ゚∀。)ᕗ
一开始不懂的时候还上贴吧去问了……

测试程序:

DATA SEGMENT
INF1 DB "Please input a number(0-65535):$"
;缓冲区第一字节存放接受字符串的最大字符个数,缓冲区第二字节存放实际输入的字符(不包括回车符),第三字节开始存放接收的字符串
;在这里,开了最多7个字节的位置,第一字节占一个,第二字节占一个,后面还剩5个字节的位置,第二字节和后面5个字节的位置合起来就是六个变量所以是6 DUP(?)
IBUF DB 7, 0, 6 DUP('?')
OBUF DB 6 DUP('?')
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS: DATA
START:;取代码段
MOV AX, DATA
MOV DS, AX;接收从键盘输入的十进制数,并将其转换为二进制数(存AX寄存器)的指令序列;输出字符串INF1
MOV DX, OFFSET INF1
MOV AH, 09H
INT 21H;字符串输入,寄存器DS存放接收缓冲区段地址,DX存放接收缓冲区偏移地址
MOV DX, OFFSET IBUF
MOV AH, 0AH
INT 21H;将实际接受的字符数移到CL中,用于计数
MOV CL, IBUF+1
;将CX的高位CH清零,方便计数
MOV CH, 0;程序结束
MOV AH, 4CH
INT 21HCODE ENDS
END START

就是说,我输入数字没按回车的时候停下程序,看一看,瞅一瞅,再继续程序的时候,我要重新输入一遍……

这么简单的事情却是我当时一辈子都想不到的,如果学霸没跟我说的话……

图1 单步运行中断输入之后我没有重新输入就直接按回车了

图2 不中断运行,正常

图3 单步运行中断输入之后我重新输入

题目:输入数字之后冒泡排序

遇到的问题1:

感觉程序也没有错误,但是就是输出有问题,所以我先输入了一组不需要冒泡排序的数组想要输出,结果也发现了输出错误,因此判断是输出函数的问题
单步调试之后发现,输出函数居然会改变我要输出的BUF数组内数据的值
观察正在运行的代码,发现是输出函数使用的OUTPUT数据区和我的BUF数据区重叠了,我对OUTPUT赋值时便会修改BUF
回看我在数据区对数组的定义,我当时完全没有想到这个错误

解决方法:
缓冲区定义为

INPUT DB 4 0, 3 DUP ('?')
OUTPUT DB 4 0, 3 DUP ('?')

就好了

原因:
请看实例:

buf db 100
db ?
db 100 dup (0)

解析:在内存中申请一个缓冲区为100个字节,首地址给BUF。
缓冲区的第一个字节内放的是100,表示申请的存放数据的缓冲区的字节数为100个
第二个字节“?”表示的是实际存放的字节个数(就是说,你放入2个字节的数据,“?”变成2,放10个字节的数据,变成10)
输入的数据从第三个字节开始存放,第100个字节存放回车符(0DH),0DH作为输入数据的结束。
DUP(0)表示的是存放数据的100个字节初始值全为0,即为:100 0 0 0 0······0 ODH。

也就是说,这种三字节结构的缓冲区格式是对于所有缓冲区数组都生效的
我的书本上只有在DOS功能调用的0AH字符串输入功能才讲到了这个缓冲区格式,我就以为只是这个DOS功能要求缓冲区数组这样的格式,不用于字符串输入的数组可以不用这个格式
实际上不是我想的这样的

遇到的问题2:

输出的数据还是被修改了,下图为运行结果

初步调试,输入的数据保存下来是没有问题的,下图的单步运行到输入结束

调试到冒泡法结束,排序并不正确,于是开始重看冒泡法

最后发现原来是我在冒泡法判断大小的时候使用了JNG而不是使用JNA
正确的应该是使用JNA

最终的正确代码:

DATA SEGMENT
INFOR1 DB 0AH,0DH,"How many nubmers do you want to enter(<256):$"
INFOR2 DB 0AH,0DH,"Enter a number(<256):$"
N DB 0
INPUT DB 4 0, 3 DUP ('?')
OUTPUT DB 4 0, 3 DUP ('?')
BUF DB 200 DUP ('?')
DATA ENDSCODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:;------------------取代码段首地址-------------------MOV AX, DATA
MOV DS, AX;-----------------------结束--------------------------;---------获得处理数据的个数N存在CL中-------------;输出字符串INFOR1
MOV DX, OFFSET INFOR1
MOV AH, 09H
INT 21H;调用函数,要获得一个不大于255的三位十进制整数存在AX里,传入NUM1的串首的偏移地址
CALL GETNUMBER;调用一次函数之后,AX中是得到的“处理数据的个数N”,所以AL存到N中
MOV N,AL;-------------------结束-------------------------------;---------获得若干个数据存在BUF中-------------------;BX用来计算放入数据的序号,先清零
MOV BX,0;CX作为LOOP的计数器,获得N的值
MOV CX,0
MOV CL,NAGAIN2:;输出字符串INFOR2
MOV DX, OFFSET INFOR2
MOV AH, 09H
INT 21H;调用函数,要获得一个不大于255的三位十进制整数存在AX里,传入NUM1的串首的偏移地址
CALL GETNUMBER;将这一次输入的数存入BUF数组的第BX个元素
MOV BUF[BX],AL;
;BX作为BUF元素的序号,自加1
INC BX;;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,BUF存入一个数,一轮结束,CX自减1
;(CX)=2,BUF存入一个数,一轮结束,CX自减1
;(CX)=1,BUF存入一个数,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,BUF存入N个数,可以满足程序目标
LOOP AGAIN2;-------------------结束-------------------------------;---------------冒泡法开始,CX作为计数器,BUF为待排序数组---------------;CL获得N-1,N为数据的总数
MOV CL, N
DEC CXOUTER:;进栈,保存第一个计数器,相当于for(i=N-1;i--;i>=0)中的i被保存
PUSH CX
;进栈之后的CX作为第二个计数器,相当于for(j=N-1;j--;j>=0)中的j;BX作为BUF的序号,从0开始
MOV BX,0INNER:;BUF[BX]与BUF[BX+1]比较
MOV AL, BUF[BX]
CMP AL, BUF[BX+1];BUF[BX]<=BUF[BX+1]时,无需交换,跳转到NEXT(无符号数,用JNA不用JNG!)
JNA NEXT;BUF[BX]>BUF[BX+1]时,需交换
XCHG AL, BUF[BX+1]
MOV BUF[BX],ALNEXT:;BX自加1,以便进行下一组BUF[BX]与BUF[BX+1]比较
INC BX;相当于for(j=N-1;j--;j>=0)中的j--,只是这里自减的是CX,如果CX=0则结束循环,否则转移到INNER标号处
LOOP INNER;程序运行到这里,说明内层的第二个计数器CX=0,说明已经进行了一轮内层的比较
;出栈,返回外层的第一个计数器
POP CX
;相当于for(i=N-1;i--;i>=0)中的i--,只是这里自减的是CX,如果CX=0则结束循环,否则转移到OUTER标号处
LOOP OUTER;程序运行到这里,说明外层的第一个计数器CX=0,说明外层比较已结束;---------------------------冒泡法结束-----------------------------------;---------------------下面开始输出数据串--------------------------------;BX赋初值0作为序号
MOV BX,0AGAIN3:;因为BUF每一个数最大不超过255,所以只使用AL
MOV AL, BUF[BX]
;高位清零
MOV AH,0;输出AX的十进制格式
CALL SHOWNUMBER;BX作为序号自加1
INC BX;如果BL的数目等于N,说明已经输出了从BUF[0]到BUF[N-1],输出目标达成
CMP BL,N
JNZ AGAIN3MOV AH,4CH
INT 21H;-----------------------------------------
;子程序名:GETNUMBER
;功能:得到一个3位十进制数字
;入口参数:内存区数据区字符串的串首的偏移地址INPUT
;出口参数:AX
;-----------------------------------------GETNUMBER PROC;保护现场
PUSH BX
PUSH CX
PUSH DX
PUSHF;------------------字符串输入到INPUT-----------------;寄存器DS存放接收缓冲区段地址,DX存放接收缓冲区偏移地址
MOV DX, OFFSET INPUT
MOV AH, 0AH
INT 21H;-------------------------结束--------------------------;-------------------将INPUT中的数转化成二进制数存在AX中------------------;INPUT+1为输入的个数
;CL不能随便定义,一定要用INPUT+1.
;比如如果你认为输入的是三位十进制数,就想让(CL)=3
;但是当你输入一位十进制数x的时候若(CL)=3,程序结束时AX存的不是x而是x*100!
MOV CL, INPUT+1
;将CX的高位CH清零,方便计数
MOV CH, 0;将接收到的第一个字符的偏移地址移到SI中
MOV SI, OFFSET INPUT+2
;清零AX
MOV AX, 0AGAIN1:;乘数为10
MOV DX, 10
;字乘时,DX,AX<-(AX)*(src),乘积的高16位存在DX中,低16位存在AX中,此处src为DX
MUL DX;用word ptr指明了指令访问的内存单元是一个字单元
;用byte ptr指明了指令访问的内存单元是一个字节单元
;NUM中第SI个元素原来是一个ASCII码,只保留一个16位数,相当于减30
AND BYTE PTR [SI], 0FH
;将INPUT中第SI个元素加到AL中
;AL不超过256所以可以不用看AH
ADD AL, [SI];SI自加1,即接下来要对INPUT数组中的下一个数进行操作
INC SI;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,AX等于输入的第一个十进制数,一轮结束,CX自减1
;(CX)=2,AX等于输入的两个数组成的十位十进制数,一轮结束,CX自减1
;(CX)=1,AX等于输入的三个数组成的百位十进制数,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,AX内保存N位十进制数,可以满足程序目标
LOOP AGAIN1;----------------------------------结束----------------------------------------;恢复现场
POPF
POP DX
POP CX
POP BX
;回到主程序
RETGETNUMBER ENDP;-----------------------------------------
;子程序名:SHOWNUMBER
;功能:输出一个3位十进制数字
;入口参数:AX,内存区数据区字符串的串首的偏移地址OUTPUT
;出口参数:无
;-----------------------------------------SHOWNUMBER PROC;保护现场
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSHF;------------将AX中存放的二进制数转换为十进制数存在OUTPUT中---------------;在OUTPUT的末尾放入$符号作为结束符号
MOV BX,OFFSET OUTPUT+3
MOV BYTE PTR [BX], '$';CX放入10作为除数
MOV CX, 10LOOP1:
;字除时,被除数是(DX,AX),所以清零DX
MOV DX, 0
;(DX,AX)/(CX),商存在AX中,余数是个位数,存在DX中
DIV CX
;余数加30变成ASCII码形式
ADD DL, 30H
;BX自减1,从OBUF的末尾往前走一位
DEC BX
;将ASCII码形式的DL从后往前放入OUTPUT
MOV [BX], DL;AX与自己进行或运算,AX不变,但是可以影响符号位SF,ZF,PF,且CF,OF清零
;这里的目的是影响ZF,如果(AX)=0,那么ZF=1,JNZ不执行,否则ZF=0,JNZ进行,跳转到标号处,即判断AX是否为0
OR AX, AX
JNZ LOOP1;-------------------------------结束---------------------------------------------;回车换行
PUSH DX
MOV AH,02H
MOV DL,0DH
INT 21H
MOV AH,02H
MOV DL,0AH
INT 21H
POP DX;--------------输出OUTPUT字符串---------------MOV DX,OFFSET OUTPUT
MOV AH, 09H
INT 21H;----------------------结束----------------------;恢复现场
POPF
POP DX
POP CX
POP BX
POP AX
;回到主程序
RETSHOWNUMBER ENDPCODE ENDS
END START

题目:从键盘输入十个无符号的十进制数(小于256),将其转换为二进制数并存放在NUM字节型变量中,找出其中的最大数,并在屏幕上显示出来。

要求:
①在屏幕上显示字符串提示信息的功能由宏指令DSTRING实现;
②将键盘输入的十进制数转换成二进制数由子程序DTOB实现;
③在N个无符号字节型二进制数中找出最大数的功能由子程序FMAX实现;
④将一个无符号字节型二进制数转换为十进制数并在屏幕上显示的功能由子程序BTOAD实现

我的实现:

COUNT EQU 10
DATA SEGMENT
NUM DW 10 DUP('?')
INPUT DB 7, 0, 6 DUP('?')
OUTPUT DB 6 DUP(0)
INFOR1 DB "Please Input the 10 Numbers:", 0AH, 0DH, '$'INFOR2 DB "The max found in the 10 numbers is $"INFOR3 DB 0AH, 0DH, '$'
DATA ENDS
STACK SEGMENT stackDATA2 DW 40 DUP(?)TOP='$'-DATA2
STACK ENDS
CODE SEGMENTASSUME CS:CODE, DS:DATA, SS:STACK
START:;--------------------------取代码段首地址--------------------------------MOV AX, DATA
MOV DS, AX
MOV AX, STACK
MOV SS, AX
MOV AX, TOP
MOV SP, AX;------------------------------取地址结束---------------------------------;------------------定义显示字符串信息的宏指令DSTRING------------------DSTRING MACRO STRING
PUSH DX
PUSH AX
MOV DX, OFFSET STRING
MOV AH, 09H
INT 21H
POP AX
POP DX
ENDM;------------------------------宏定义结束-----------------------------------;------------------------输入数组-------------------------;输出提示信息INFOR1
DSTRING INFOR1
MOV BX, OFFSET NUM
;CX作为接下来的LOOP循环的计数器
MOV CX, COUNTAGAIN3: ;从屏幕输入一个不大于65536的数字,保存在AX中
CALL GETNUMBER
;输出提示信息INFOR3换行
DSTRING INFOR3
;将输入得到的AX保存到NUM数组的某一元素中
MOV [BX], AX
;BX自加2,下一次AX将保存在NUM数组的下一个元素中
ADD BX,2;CX计数器的初值是COUNT,即循环进行COUNT次之后结束,NUM数组中存了COUNT个数
LOOP AGAIN3;--------------------------结束--------------------------;-----------------------找最大数-------------------------;利用堆栈传入NUM偏移地址和COUNT
MOV AX, OFFSET NUM
PUSH AX
MOV CX, COUNT
PUSH CX
;取NUM的最大数保存在AX中
CALL FINDMAX;--------------------------结束---------------------------;------------------------输出结果-------------------------;输出提示信息INFOR3换行
DSTRING INFOR2
;将存在AX中的十六进制数显示成十进制数
CALL SHOWNUMBER;---------------------------结束--------------------------;程序结束
MOV AH, 4CH
INT 21H;-----------------------------------------
;子程序名:GETNUMBER
;功能:得到一个不大于2^16=65536的十进制数字
;入口参数:内存区数据区字符串的串首的偏移地址INPUT
;出口参数:AX
;-----------------------------------------GETNUMBER PROC;保护现场
PUSH BX
PUSH CX
PUSH DX
PUSHF;------------------字符串输入到INPUT-----------------;寄存器DS存放接收缓冲区段地址,DX存放接收缓冲区偏移地址
MOV DX, OFFSET INPUT
MOV AH, 0AH
INT 21H;-------------------------结束--------------------------;-------------------将INPUT中的数转化成二进制数存在AX中------------------;INPUT+1为输入的个数
;CL不能随便定义,一定要用INPUT+1.
;比如如果你认为输入的是三位十进制数,就想让(CL)=3
;但是当你输入一位十进制数x的时候若(CL)=3,程序结束时AX存的不是x而是x*100!
MOV CL, INPUT+1
;将CX的高位CH清零,方便计数
MOV CH, 0;将接收到的第一个字符的偏移地址移到SI中
MOV SI, OFFSET INPUT+2
;清零AX
MOV AX, 0AGAIN1:;乘数为10
MOV DX, 10
;字乘时,DX,AX<-(AX)*(src),乘积的高16位存在DX中,低16位存在AX中,此处src为DX
MUL DX;用word ptr指明了指令访问的内存单元是一个字单元
;用byte ptr指明了指令访问的内存单元是一个字节单元
;NUM中第SI个元素原来是一个ASCII码,只保留一个16位数,相当于减30
AND BYTE PTR [SI], 0FH
;将INPUT中第SI个元素加到AL中
;AL不超过256所以可以不用看AH
ADD AL, [SI];SI自加1,即接下来要对INPUT数组中的下一个数进行操作
INC SI;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,AX等于输入的第一个十进制数,一轮结束,CX自减1
;(CX)=2,AX等于输入的两个数组成的十位十进制数,一轮结束,CX自减1
;(CX)=1,AX等于输入的三个数组成的百位十进制数,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,AX内保存N位十进制数,可以满足程序目标
LOOP AGAIN1;----------------------------------结束----------------------------------------;恢复现场
POPF
POP DX
POP CX
POP BX
;回到主程序
RETGETNUMBER ENDP;-----------------------------------------
;子程序名:SHOWNUMBER
;功能:输出一个6位十进制数字
;入口参数:AX,内存区数据区字符串的串首的偏移地址OUTPUT
;出口参数:无
;-----------------------------------------SHOWNUMBER PROC;保护现场
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSHF;------------将AX中存放的二进制数转换为十进制数存在OUTPUT中---------------;在OUTPUT的末尾放入$符号作为结束符号
MOV BX,OFFSET OUTPUT+6
MOV BYTE PTR [BX], '$';CX放入10作为除数
MOV CX, 10AGAIN2:
;字除时,被除数是(DX,AX),所以清零DX
MOV DX, 0
;(DX,AX)/(CX),商存在AX中,余数是个位数,存在DX中
DIV CX
;余数加30变成ASCII码形式
ADD DL, 30H
;BX自减1,从OBUF的末尾往前走一位
DEC BX
;将ASCII码形式的DL从后往前放入OUTPUT
MOV [BX], DL;AX与自己进行或运算,AX不变,但是可以影响符号位SF,ZF,PF,且CF,OF清零
;这里的目的是影响ZF,如果(AX)=0,那么ZF=1,JNZ不执行,否则ZF=0,JNZ进行,跳转到标号处,即判断AX是否为0
OR AX, AX
JNZ AGAIN2;-------------------------------结束---------------------------------------------;回车换行
PUSH DX
MOV AH,02H
MOV DL,0DH
INT 21H
MOV AH,02H
MOV DL,0AH
INT 21H
POP DX;--------------输出OUTPUT字符串---------------MOV DX,OFFSET OUTPUT
MOV AH, 09H
INT 21H;----------------------结束----------------------;恢复现场
POPF
POP DX
POP CX
POP BX
POP AX
;回到主程序
RETSHOWNUMBER ENDP;-----------------------------------------
;子程序名:FINDMAX
;功能:在传入的数组中寻找最大数
;入口参数:内存区数据区字符串的串首的偏移地址,数组元素个数
;出口参数:AX
;-----------------------------------------FINDMAX PROC;保护现场
PUSH BX
PUSH CX
PUSH DX
PUSHF;----------------------------比较初始化-----------------------;取栈指针SP的值
MOV BP,SP
;后入栈的在低地址,CX存计数器
;堆栈PUSH传入参数两次,CALL一次,保护现场PUSH四次,总共使SP-14
MOV CX,[BP+10]
;先入栈的在高地址,BX存NUM的偏移地址
MOV BX,[BP+12]
;只用进行COUNT-1次比较
DEC CX
;将NUM数组的一个数存在AX中
MOV AX,[BX];-----------------------------结束------------------------------;---------------------------循环比较---------------------------AGAIN4:;如果NUM数组的下一个数比AX大,那么把下一个数存到AX中
CMP AX,[BX+2]
JAE MARK
MOV AX,[BX+2];比较完成之后BX自加2,选择NUM数组的下一个数
MARK:
ADD BX,2LOOP AGAIN4;-----------------------------结束------------------------------
;恢复现场
POPF
POP DX
POP CX
POP BX
;回到主程序
RET 4FINDMAX ENDPCODE ENDSEND START

只需要在GETNUMBER子程序里实现字的乘法,在SHOWNUMBER子程序里实现字的除法,就可以实现输入小于65536的数

题目:从键盘输入N个十进制数,求它们的和(累加和要求不大于65 535),并将累加结果在屏幕上显示出来。要求给出必要的提示信息(用宏调用完成);累加功能由子程序调用实现;二进制数形式的累加和转换为十进制数并显示由子程序调用实现。

代码实现:

DATA SEGMENT
COUNT DW 0
INPUT DB 7, 0, 6 DUP('?')
OUTPUT DB 6 DUP(0)INFOR1 DB "How many numbers do you want to input:$"INFOR2 DB "OK, please continue inputing:$"INFOR3 DB "The add is(<65536): $"INFOR4 DB 0AH, 0DH, '$'
DATA ENDS
STACK SEGMENT stackDATA2 DW 40 DUP(?)TOP='$'-DATA2
STACK ENDS
CODE SEGMENTASSUME CS:CODE, DS:DATA, SS:STACK
START:;--------------------------取代码段首地址--------------------------------MOV AX, DATA
MOV DS, AX
MOV AX, STACK
MOV SS, AX
MOV AX, TOP
MOV SP, AX;------------------------------取地址结束---------------------------------;------------------定义显示字符串信息的宏指令DSTRING------------------DSTRING MACRO STRING
PUSH DX
PUSH AX
MOV DX, OFFSET STRING
MOV AH, 09H
INT 21H
POP AX
POP DX
ENDM;------------------------------宏定义结束-----------------------------------;输出提示信息INFOR1,提示输入要输入的数的总数
DSTRING INFOR1
;要输入的数的总数存在AX中
CALL GETNUMBER
;输出提示信息INFOR4换行
DSTRING INFOR4
;要输入的数的总数存在COUNT中
MOV COUNT,AX;------------------------输入数组-------------------------;CX作为接下来的LOOP循环的计数器
MOV CX, COUNT
;BX清零保存加法的和
MOV BX,0AGAIN3: ;提示输入NUM数组元素
DSTRING INFOR2
;从屏幕输入一个不大于65536的数字,保存在AX中
CALL GETNUMBER
;输出提示信息INFOR4换行
DSTRING INFOR4;累加
ADD BX,AX;CX计数器的初值是COUNT,即循环进行COUNT次之后结束,NUM数组中存了COUNT个数
LOOP AGAIN3;--------------------------结束--------------------------;------------------------输出结果-------------------------;输出提示信息INFOR3换行
DSTRING INFOR2
;将存在AX中的十六进制数显示成十进制数
CALL SHOWNUMBER;---------------------------结束--------------------------;程序结束
MOV AH, 4CH
INT 21H;-----------------------------------------
;子程序名:GETNUMBER
;功能:得到一个不大于2^16=65536的十进制数字
;入口参数:内存区数据区字符串的串首的偏移地址INPUT
;出口参数:AX
;-----------------------------------------GETNUMBER PROC;保护现场
PUSH BX
PUSH CX
PUSH DX
PUSHF;------------------字符串输入到INPUT-----------------;寄存器DS存放接收缓冲区段地址,DX存放接收缓冲区偏移地址
MOV DX, OFFSET INPUT
MOV AH, 0AH
INT 21H;-------------------------结束--------------------------;-------------------将INPUT中的数转化成二进制数存在AX中------------------;INPUT+1为输入的个数
;CL不能随便定义,一定要用INPUT+1.
;比如如果你认为输入的是三位十进制数,就想让(CL)=3
;但是当你输入一位十进制数x的时候若(CL)=3,程序结束时AX存的不是x而是x*100!
MOV CL, INPUT+1
;将CX的高位CH清零,方便计数
MOV CH, 0;将接收到的第一个字符的偏移地址移到SI中
MOV SI, OFFSET INPUT+2
;清零AX
MOV AX, 0AGAIN1:;乘数为10
MOV DX, 10
;字乘时,DX,AX<-(AX)*(src),乘积的高16位存在DX中,低16位存在AX中,此处src为DX
MUL DX;用word ptr指明了指令访问的内存单元是一个字单元
;用byte ptr指明了指令访问的内存单元是一个字节单元
;NUM中第SI个元素原来是一个ASCII码,只保留一个16位数,相当于减30
AND BYTE PTR [SI], 0FH
;将INPUT中第SI个元素加到AL中
;AL不超过256所以可以不用看AH
ADD AL, [SI];SI自加1,即接下来要对INPUT数组中的下一个数进行操作
INC SI;如果(CX)=0,那么结束循环,否则跳转到标号处
;简单计算一下,假设进入循环之前(CX)=3,第一次进入循环,CX没变,AX等于输入的第一个十进制数,一轮结束,CX自减1
;(CX)=2,AX等于输入的两个数组成的十位十进制数,一轮结束,CX自减1
;(CX)=1,AX等于输入的三个数组成的百位十进制数,一轮结束,CX自减1,此时(CX)=0,循环结束,往下执行
;因此(CX)=N时,AX内保存N位十进制数,可以满足程序目标
LOOP AGAIN1;----------------------------------结束----------------------------------------;恢复现场
POPF
POP DX
POP CX
POP BX
;回到主程序
RETGETNUMBER ENDP;-----------------------------------------
;子程序名:SHOWNUMBER
;功能:输出一个6位十进制数字
;入口参数:AX,内存区数据区字符串的串首的偏移地址OUTPUT
;出口参数:无
;-----------------------------------------SHOWNUMBER PROC;保护现场
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSHF;------------将AX中存放的二进制数转换为十进制数存在OUTPUT中---------------;在OUTPUT的末尾放入$符号作为结束符号
MOV BX,OFFSET OUTPUT+6
MOV BYTE PTR [BX], '$';CX放入10作为除数
MOV CX, 10AGAIN2:
;字除时,被除数是(DX,AX),所以清零DX
MOV DX, 0
;(DX,AX)/(CX),商存在AX中,余数是个位数,存在DX中
DIV CX
;余数加30变成ASCII码形式
ADD DL, 30H
;BX自减1,从OBUF的末尾往前走一位
DEC BX
;将ASCII码形式的DL从后往前放入OUTPUT
MOV [BX], DL;AX与自己进行或运算,AX不变,但是可以影响符号位SF,ZF,PF,且CF,OF清零
;这里的目的是影响ZF,如果(AX)=0,那么ZF=1,JNZ不执行,否则ZF=0,JNZ进行,跳转到标号处,即判断AX是否为0
OR AX, AX
JNZ AGAIN2;-------------------------------结束---------------------------------------------;回车换行
PUSH DX
MOV AH,02H
MOV DL,0DH
INT 21H
MOV AH,02H
MOV DL,0AH
INT 21H
POP DX;--------------输出OUTPUT字符串---------------MOV DX,OFFSET OUTPUT
MOV AH, 09H
INT 21H;----------------------结束----------------------;恢复现场
POPF
POP DX
POP CX
POP BX
POP AX
;回到主程序
RETSHOWNUMBER ENDPCODE ENDSEND START

题目:从键盘输入一个带符号字节数据(如-56)存入内存(10进制在内存中显示),并将结果在显示器上以十进制形式显示输出。

DATA SEGMENT
DATA ENDS
CODE SEGMENTASSUME CS:CODE, DS:DATA, SS:STACK
START:;-----------------------取首地址-----------------------MOV AX, DATAMOV DS, AX;-----------------------变量初始化-----------------------MOV DX,0 ;DX作为是否有负号的标记,DX=1表示有符号输出,否则无负号输入MOV AX,0 ;前面取首地址时用到了AX,下面接受字符也要用到AX,所以给AX清零MOV BX,0 ;后面互换指令用到了BX;-----------------------先判断是否有负号-----------------------   ;单字符输入   MOV AH,1    INT 21H;先判断是否有负号CMP AL,02DH  JNZ NOFALG ;没有负号则不给DX赋1,直接进入数值部分的第一次输入MOV DX,1;-----------------------循环输入数值部分-----------------------
INPUT:;单字符输入   MOV AH,1    INT 21H
NOFALG: SUB AL, 30HCMP AL, 0         JL FUHAO ;小于0,不是输入数字代表输入结束           CMP AL, 9JG FUHAO ;大于9,不是输入数字代表输入结束CBWXCHG  AX, BXMOV   CX, 10PUSH DXMUL   CXPOP DXXCHG  AX, BXADD   BX, AXJMP   INPUT;-----------------------选择性输出负号-----------------------
FUHAO:CMP DX,1JNZ FUHAOENDMOV DL,02DHMOV AH,2INT 21H
FUHAOEND:;-----------------------获得输入的数的十进制表示,存入堆栈-----------------------   MOV AX,BX ;原来的数保存在BX中,现在存在AX中MOV CX,0 ;要使用CX作为计数器MOV BL,10 ;BL作为除数
COUNT:DIV BL ;商存在AL,余数存在AHPUSH AX ;我们只需要AHMOV AH,0 ;上一次除法的商是下一次除法的被除数INC CX ;堆栈存入了一个数;判断商是不是0,如果商是0,说明这是最后一次除法,同时余数仍可能有值CMP AL,0JZ COUNTEND ;若是最后一次除法,那么余数已经输出,可以直接结束程序JMP COUNT ;不是最后一次除法,继续循环
COUNTEND:;-----------------------从堆栈获得输入的数的十进制表示,输出-----------------------
OUTPUT:;取每一次除法的余数POP DXMOV DL,DHDEC CXADD DL,30H;单字符输出,输出值在DL中MOV AH,2INT 21H;是否所有余数都取完了CMP CX,0JNZ OUTPUTMOV AH,04CHINT 21H
code ends
end start

编写子程序段实现:字节变量S1中存放着一个字符串,以0DH为结束标志,试求串长度并送字变量CD中

;-----------------------------------------
;子程序名:LENGTH
;功能:获得字符串长度
;入口参数:字节变量S1存字符串首地址,以0DH为结束标志
;出口参数:字变量CD保存串长度
;-----------------------------------------LENGTH PROC;保护现场
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSHFMOV CX,0 ;初始化
MOV SI,0AGAIN:
INC CX
MOV SI,OFFSET S1
ADD SI,CX
DEC SI
MOV AL,[SI]
CMP AL,0DH ;第CX个数与0DH比较
JZ ENDING
JMP AGAIN
ENDING:DEC CX
MOV CD,CX;-----------------------------结束------------------------------
;恢复现场
POPF
POP DX
POP CX
POP BX
POP AXLENGTH ENDP

编写宏汇编程序段实现把S1区中所有字符逆向传送到O1区

;---------------宏程序要求S1以0DH结尾,且要求S1和O1都为字节型-------------------
TRANS MACRO PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSHF;----------------求S1长度存在CX中----------------
MOV CX,0 ;初始化
MOV SI,0AGAIN:
INC CX
MOV SI,OFFSET S1
ADD SI,CX
DEC SI
MOV AL,[SI]
CMP AL,0DH ;第CX个数与0DH比较
JZ ENDING
JMP AGAIN
ENDING:DEC CX
MOV DX,CX ;DX存计数器;---------------顺序将S1中数据压入堆栈--------------
MOV AH,0
MOV SI,OFFSET S1AGAIN2:
MOV AL,[SI]
PUSH AX
INC SI
LOOP AGAIN2;-----------------将堆栈中数据放入O1-----------------
MOV CX,DX
MOV SI,OFFSET O1
AGAIN3:
POP AX
MOV [SI],AL
INC SI
LOOP AGAIN3POPF
POP DX
POP CX
POP BX
POP AX
ENDM

期中复习

笑死,在教材上看到数据段定义里面有一个NDS,EGMENT,我还以为是什么新指令,上网查了半天没找到,才意识到他要写的是ENDS,SEGEMENT,印刷出现错误了而已
STACK SEGMENT stack

STACK NDS
DATA EGMENT

MUL,IMUL
都是(AX)<-(AL)*src, (DX,AX)<-(AX)*src
都只影响OF和CF,对其他标志位无意义
MUL时,积的高8位(字节乘)或者高16位(字乘)是低字节或者低字的扩展时,OF=CF=1,否则OF=CF=0
IMUL相反,积的高8位(字节乘)或者高16位(字乘)是低字节或者低字的扩展时,OF=CF=0,否则OF=CF=1
我觉得这么写更加直观一点,书上没有把MUL和IMUL放在一起对比

不知道为什么我在书上CBW和CWD的地方看到了我“配合除法”的标注,可能是上课的时候听到了然后断章取义了把
起因是我看到双字的加法:
MOV AX,Z //Z是内存数,(BX,CX)已存XY,要计算XY+Z
CWD //AX拓展成双字
ADD CX,AX //多字加法,(BX,CX)+(DX,AX),低位相加,不带进位加法ADD
ADC BX,DX //多字加法,(BX,CX)+(DX,AX),高位相加,带进位加法ADC
//多字减法同理,先低位相减,不带借位减法SUB,后高位相减,带借位减法SBB
//如果多字加法或者多字减法的加数或者减数、被减数之中有一数无高位,那相当于高位为0

突然想到,除法的时候,商会不会缺位呢?
试了一下,FFFFFFFF/1,商保存在AX,结果是AX为FFFF,确实缺了高位FFFF
这个情况是很自然能够理解,想要验证缺位也很容易,比如字除,只要将除法前的(DX,AX)与除法后的AX*src+DX比较就好了

;测试程序
; multi-segment executable file template.data segment; add your data here!pkey db "press any key...$"
endsstack segmentdw   128  dup(0)
endscode segment
start:
; set segment registers:mov ax, datamov ds, axmov es, ax; add your code heremov AX,0FFFFHMOV DX,0FFFFHMOV BX,01DIDIV BXmov ax, 4c00h ; exit to operating system.int 21h
endsend start ; set entry point and stop the assembler.

main proc far
定义一个子程序的第一行。
子程序名是main,类型far。
就是说main这个子程序可以被不跟main在同一个段内的程序调用。即主程序和子程序的段基址不同时主程序也可以调用子程序。
配合main endp使用

网上看到的题,我注释了一下:
1.请任意输入一个字符串,将该串倒序后在屏幕上显示。
2.请任意输入一个字符串,统计其中空格和A的个数,并将结果显示在屏幕上。
3.请任意输入一个字符串,将其中的空格全部删除,并将结果显示在屏幕上。
4.请任意输入一个字符串,将其中A换成B,并将结果显示在屏幕上。
5.请任意输入一个字符串,将其中大写字母变成小写字母,并将结果显示在屏幕上。

Answer1:
data    segment
buf1    db 100db ?db 100 dup(?) ;buf1用于保存输入字符串
buf2    db 100 dup(?) ;buf2用于保存输出字符串
buf3    db '$'
buf4    db 0dh,0ah,'$' ;buf4为回车换行
data    ends
code    segmentassume cs:code,ds:data,es:data
main    proc far ;--------------------取段首地址----------------------mov ax,datamov ds,axmov es,ax;------------------输入字符串------------------------mov ah,0ahlea dx,buf1 ;取buf1偏移地址int 21h;-------------------初始化循环参数----------------lea si,buf1lea bx,buf1+2 ;BX取输入的字符串的第一个字母mov cl,[si+1] ;CL取输入的字母总数。这里令人感到奇怪,既然前面lea si,buf1了但是这里si只有一个用途,那就是找CL的值。
;所以可以直接这一句可以改成MOV CL,[buf1+1],然后把前面的lea si,buf1删掉mov ch,0 ;高位清零mov si,cx ;此时CX的高位为0,低位为buf1[0],则SI为输入的字母总数dec si ;SI自减1,因为buf的元素序号从0开始lea di,buf2 ;取输出字符串首地址;-------------------循环将buf1倒数第i位字母放到buf正数第i位上----------------
again:  mov ax,[bx][si]mov [di],axdec siinc didec cljnz againmov al,buf3 ;放置buf2的结尾$mov [di],al;------------------------输出回车换行-----------------------------mov ah,9lea dx,buf4int 21h;--------------------输出buf1倒序结果buf2----------------------mov ah,9lea dx,buf2int 21h;-------------------------结束----------------------------------------mov ah,4chint 21hmain    endp
code    endsend mainAnswer2:
data  segment
buf1  db  100db  ?db  100 dup(?)
buf2  db  0dh,0ah,'$'
mem1  db  0
mem2  db  0
data  ends
code  segmentassume cs:code,ds:data,es:data
main  proc far;--------------------取段首地址----------------------mov  ax,datamov  ds,axmov  es,ax;------------------输入字符串------------------------mov  ah,0ahlea  dx,buf1int  21h;-------------------初始化循环参数----------------lea  di,buf1+2 ;DI取输入的字符串的头元素的偏移地址lea  si,buf1+1 ;SI取输入的字符的总数的偏移地址mov  cl,[si] ;CI取输入的字符的总数mov  ch,0 ;高位清零mov  bl,0 ;BL清零做计数器,统计A的总数mov  bh,0 ;BH清零做计数器,统计空格的总数;--------------------循环统计buf1中A和空格的个数---------------
again:mov  al,[di]cmp  al,41h ;AL是否是ASCII码表示的字母Ajnz  next ;不是则跳转至next标号处inc  bl ;是则计数器BL自加1jmp  disp ;是则计数器加1后跳转至disp标号处
next: cmp  al,20h ;AL是否是ASCII码表示的空格jnz  disp ;不是则跳转至disp标号处inc  bh ;是则计数器BH自加1
disp: inc  di ;buf1元素序号的计数器DI自加1dec  cl ;CL自减1,不是0则继续循环jnz  again;------------------------输出回车换行-----------------------------mov  ah,9lea  dx,buf2int  21h;------------------------输出BL-----------------------------mov  al,blmov  ah,0call printf;------------------------输出回车换行-----------------------------mov  ah,9lea  dx,buf2int  21h;------------------------输出BH-----------------------------mov  al,bhmov  ah,0call printf;程序结束mov  ah,4chint  21h
main  endp
printf  proc nearlea si,mem1mov cl,10div clmov [si],ahadd al,30hmov ah,2mov dl,alint 21hmov al,[si]mov ah,0mov cl,1div cladd al,30hmov ah,2mov dl,alint 21hret
printf endpcode endsend mainAnswer3:
data   segment
buf    db  100db  ?db  100  dup(?)
buf1   db  0dh,0ah,'$'
data   ends
code   segmentassume cs:code,ds:data,es:data
main   proc far;--------------------取段首地址----------------------mov ax,datamov ds,axmov es,ax;------------------输入字符串------------------------mov ah,0ahlea dx,bufint 21h;-------------------初始化循环参数----------------lea si,buf+1 ;SI取输入的字符的总数的偏移地址mov al,[si] ;AL取输入的字符的总数mov cl,al ;CL取输入的字符的总数,作为循环次数的计数器mov ch,al ;CH取输入的字符的总数,记录要输出的字符串的总数mov dh,al ;DH取输入的字符的总数inc si ;SI自减1,因为buf的元素序号从0开始;--------------------循环循环检查buf所有位置是否为空格---------------
again: mov al,[si]cmp al,20h ;AL是否是空格jnz next ;不是则跳转至nextcall delete ;是则调用删除元素的子程序dec ch ;删除一个元素之后,CH自减1jmp kaka
next:  inc si ;检查buf下一个元素
kaka:  dec cl ;CL自减1,循环检查了buf所有位置之后跳出againjnz again;---------------------------循环参数初始化-----------------------lea di,buf+2 ;取buf第一个元素的偏移地址;---------------------------循环取要输出的最后一个元素的偏移地址-----------------------
disp:  inc didec ch ;CH已经是要输出的字符串的总数,而DI在循环之初已经指向了要输出的第一个元素,因此这里是循环检查要输出的数据区是否为要输出的字符,循环结束时,DI指向要输出的最后一个元素的后一个元素的偏移地址jnz disp;----------------------输出字符串参数初始化-------------------mov ah,24h ;在要输出的字符串的最后一个元素的后一个元素放字符串结束标志$mov [di],ah;------------------------输出回车换行-----------------------------mov ah,9lea dx,buf1int 21h;------------------输出去掉空格之后的字符串-------------------mov ah,9lea dx,buf+2int 21h;---------------------------程序结束----------------------------------mov ah,4chint 21hmain   endp
delete proc nearmov di,simov dl,dhagain1:mov ah,[di+1]mov [di],ahinc didec dljnz again1 ret
delete endpcode endsend main            Answer4:
data   segment
buf    db 100db ?db 100 dup(?)
buf1   db 0dh,0ah,'$'
count  db 0
data   ends
code   segmentassume cs:code,ds:data,es:data
main   proc far;--------------------取段首地址----------------------mov ax,datamov ds,axmov es,ax;------------------输入字符串------------------------mov ah,0ahlea dx,bufint 21h;-------------------初始化循环参数----------------lea si,buf+1 ;SI取输入的字符总数的偏移地址mov cl,[si] ;CL取输入的字符总数mov ch,[si] ;CH取输入的字符总数lea si,buf+2 ;SI取输入的字符串的第一个字符的偏移地址;--------------------循环检查字符串里每一个元素是不是字母A------------------
again: mov al,[si]cmp al,41h ;检查元素是不是Ajnz next ;不是则跳转到nextcall insert ;是则调用insert子程序inc chinc count ;字符串元素总数计数器自加1inc si ;元素序号自加1
next:  inc si ;元素序号自加1inc count ;字符串元素总数计数器自加1dec cl ;循环输入的字符总数的次数之后跳出again循环jnz againlea di,buf+2
kaka:  inc didec chjnz kaka;----------------------输出字符串参数初始化-------------------mov al,24h ;在要输出的字符串的最后一个元素的后一个元素放字符串结束标志$mov [di],al;------------------------输出回车换行-----------------------------mov ah,9lea dx,buf1int 21h;------------------输出A换B之后的字符串-------------------mov ah,9lea dx,buf+2int 21h;---------------------------程序结束----------------------------------mov ah,4chint 21hmain   endp
insert proc nearmov dh,chsub dh,countmov dl,chlea di,buf+2
mac:   inc didec dljnz mac
again1:mov bl,[di]mov [di+1],bldec didec dhjnz again1mov bh,43hmov [si],bhmov [si+1],bhret
insert endpcode endsend mainanswer5:
data   segment
buf    db 100db ?db 100 dup(?)
buf1   db 0dh,0ah,'$'
data   ends
code   segmentassume cs:code,ds:data,es:data
main   proc far;--------------------取段首地址----------------------mov ax,datamov ds,axmov es,ax;------------------输入字符串------------------------mov ah,0ahlea dx,bufint 21h;-------------------初始化循环参数----------------lea si,buf+1 ;SI取输入的字符总数的偏移地址mov cl,[si] ;CL取输入的字符总数mov ch,0 ;高位清零inc si ;SI取输入的字符串的第一个字符的偏移地址 again: mov al,[si] ;初始时,SI取输入的字符串的第一个字符cmp al,60h ;如果AL大于ASCII码60h,说明AL一定不可能是大写字母,跳转到nextja  nextmov ah,20h ;20H=0010 0000Bor  al,ah ;或运算之后,八位的AL第六位上的数字一定是1,即给大写字母+32变成小写字母mov [si],al
next:  inc siloop again;----------------------输出字符串参数初始化-------------------mov al,24h ;在要输出的字符串的最后一个元素的后一个元素放字符串结束标志$mov [si],al;------------------------输出回车换行-----------------------------mov ah,9lea dx,buf1int 21h;------------------输出A换B之后的字符串-------------------mov ah,9lea dx,buf+2int 21h;---------------------------程序结束----------------------------------mov ah,4chint 21hmain   endpcode endsend main

[EMU8086]基于8086的汇编语言学习相关推荐

  1. 搭建8086汇编语言学习环境——dosbox

    搭建8086汇编语言学习环境 资源网盘链接:链接:https://pan.baidu.com/s/1gamz6WYIWLOv5WP9H8L2Ng 提取码:ydvv 复制这段内容后打开百度网盘手机App ...

  2. 【电路分享】基于8086投票器积分器系统设计、8086数码管数字钟计时器、8086密码锁可修改仿真、8086可控交通灯系统设计、8086流水灯系统设计、8086三位电压表电压采集仿真

    微机原理-基于8086投票器积分器系统设计-protues仿真-361 利用所学8086系统的知识设计一个可以实际使用的八人投票表决器,利用开关按钮作为投票按键,通过8255采集八人的投票信息作为输入 ...

  3. 8086为什么不用c语言,现代汇编教材还是基于8086,对理解当今CPU(如i9)有帮助吗,还是教程太滞后?_科技数码通...

    汇编没用?胡说八道! 汇编难学?胡说八道! 任何一种高端应用场景,都需要汇编带来的高效率和高可靠性!何为高端?军事用途,工业用途,医疗领域!这些领域对运行效率和可靠性的需求都是无止境的高.你用c++可 ...

  4. 基于8086的步进电机控制器设计(计算机接口技术设计报告)

    注:图片展示连线未跑通,因为我们不需要交工程文件,只用交word,时间比较紧,大体上理论是正确的.我的未跑通仿真文件 文章目录 题目设计要求 一.概述 二.硬件设计方案 三.硬件详细设计 1.总线模块 ...

  5. 汇编语言学习篇1——DOSBox、汇编语言、汇编器的认识

    说明:   本文章旨在总结备份.方便以后查询,由于是个人总结,如有不对,欢迎指正:另外,内容大部分来自网络.书籍.和各类手册,如若侵权请告知,马上删帖致歉.   QQ 群 号:513683159 [相 ...

  6. AM335X的汇编语言与c语言,X86汇编语言学习手记 -- 汇编和C协同

    X86汇编语言学习手记(3) 2004年12月 在X86汇编语言学习手记(1)(2)中,可以看到栈(Stack)作为进程执行过程中数据的临时存储区域,通常包含如下几类数据: 局部变量 函数调用的返回地 ...

  7. 8086为什么不用c语言,现代汇编教材还是基于8086,对理解当今CPU(如i9)有帮助吗,还是教程太滞...

    基于8086的汇编教材对理解CPU i9没有多大帮助,至少应当从早期的32位X86的汇编教材入手,对32位的系统能有一个基础的了解,如保护地址模式.协处理器.高速缓存等,进而对后期多核多线程的i3,i ...

  8. 【汇编语言学习之路】第一章 汇编语言核心方法论

    版权声明:本学习笔记是本人根据小甲鱼"汇编语言学习课程"和<汇编语言>(王爽)的书籍,来记录笔记的 1 汇编语言核心方法论 1.1 学习汇编语言的必要性 汇编语言与机器 ...

  9. 汇编语言学习之基本指令(上)

    汇编语言学习之基本指令(上) 汇编语言对大小写不敏感,所以笔者全部采用大写说明语法规则,实例中采用小写英文 文章目录 汇编语言学习之基本指令(上) 1.数据传送类指令 1.1传送指令MOV 1.2交换 ...

最新文章

  1. CSDN:借助工具对【本博客访问来源】进行数据图表可视化(网友主要来自美国、新加坡、日本、英德加澳等)——记录数据来源截止日期20200718晚上22点
  2. cocos2d-x初探学习笔记(1)--HelloWorld .
  3. java----监听器的作用_一、理解监听器的作用
  4. CCF201912-4 区块链(100分)【模拟】
  5. 狂野!利用Fastjson注入Spring内存马~
  6. 我们为什么要学习JAVA编程语言
  7. 使用Control Flash 更新AB PLC的固件版本(通过网线)
  8. 长字符串的算术编码matlab,算术编码及MATLAB实现
  9. 【Bluetooth蓝牙开发】三、蓝牙调试工具【集合汇总】
  10. 计算机安全反思报告书,计算机数据安全教学反思.doc
  11. 微信小程序添加开发者、赋予权限、添加体验者
  12. Spring Boot整合ActiveMQ及场景举例(点对点模式、订阅模式)
  13. 什么是蛮力攻击,您能做什么来防止这些攻击的发生?
  14. 用什么软件测试mate9的闪存_荣耀v9怎么看闪存类型 荣耀v9怎么测试闪存
  15. maya的BonusTools
  16. 好物分享-最详细的Depay万事达虚拟卡 开卡使用教程
  17. 分享一篇文章——2013年《意林》 华文 《二十岁无资本无未来》 值得我们思考一生
  18. 关于winglink安装
  19. 电脑硬盘分区软件哪个好用,无损分区软件哪个好
  20. Spring Cloud 阿里哨兵

热门文章

  1. 编码风格:Mvc模式下SSM环境,代码分层管理
  2. MyBatis 实践 -Mapper与DAO
  3. hadoop--HDFS_NameNode和SecondaryNameNode工作机制
  4. Hadoop--克隆3x虚拟机
  5. View、Text、Button的drawableLeft左侧图片自定义大小
  6. C++ 基于 Visual C++6.0 的 DLL 编程实现
  7. 5.Servlet 对象(request-response)
  8. 推荐一篇关于多租户Multi-Tenant数据架构的文章
  9. GMF 教程 Mindmap 6
  10. myeclipse jsp头