汇编输出杨辉三角
1.2 杨辉三角性质
1、每行数字左右对称,由1开始逐渐变大,然后变小,回到1。 2、第n行的数字个数为n个。
3、第n行数字和为2^(n-1)。(2的(n-1)次方)
4、每个数字等于上一行的左右两个数字之和。可用此性质写出整个帕斯卡三角形。 5、将第2n+1行第1个数,跟第2n+2行第3个数、第2n+3行第5个数„„连成一线,这些数的和是第2n个斐波那契数。将第2n行第2个数,跟第2n+1行第4个数、第2n+2行第6个数„„这些数之和是第2n-1个斐波那契数。
6、第n行的第1个数为1,第二个数为1×(n-1),第三个数为1×(n-1)×(n-2)/2,第四个数为1×(n-1)×(n-2)/2×(n-3)/3„依此类推。
7.两个未知数和的n次方运算后的各项系数依次为杨辉三角的第(n+1)行。
图1-2-1 杨辉三角 图 1-2-2 杨辉三角数学公式
第一章 汇编语言简介
2.1 汇编语言概况
根据本次设计要求:通过汇编语言编写汇编程序要求能够在提示信息下,从计算机键盘任意输入一个数据,在输出提示信息后显示相应的杨辉三角。下面对汇编语言作简单的介绍。
汇编语言(AssemblyLanguage)是面向机器的程序设计语言。在汇编语合中,用助记符(Memoni)代替操作码,用地址符号(Symbol)或标号(Label)代替地址码。这样用符号代替机器语言的二进制码,就把机器语言变成了汇编语言。于是汇编语言亦称为符号语言。使用汇编语言编写的程序,机器不能直接识别,要由一种程序将汇编语言翻译成机器语言,这种起翻译作用的程序叫汇编程序,汇编程序是系统软件中语言处理系统软件。汇编程序把汇编语言翻译成机器语言的过程称为汇编。
汇编语言是一种功能很强的程序设计语言,也是利用计算机所有硬件特性并能直接控制硬件的语言。汇编语言,作为一门语言,对应于高级语言的编译器,需要一个“汇编器”来把汇编语言原文件汇编成机器可执行的代码。高级的汇编器如MASM,TASM等等为我们写汇编程序提供了很多类似于高级语言的特征,比如结构化、抽象等。在这样的环境中编写的汇编程序,有很大一部分是面向汇编器的伪指令,已经类同于高级语言。现在的汇编环境已经如此高级,即使全部用汇编语言来编写windows的应用程序也是可行的,但这不是汇编语言的长处。汇编语言的长处在于编写高效且需要对机器硬件精确控制的程序。
汇编语言(Assembly Language)是一种采用助记符表示的程序设计语言,即用助记符来表示指令的操作码和操作数,用符号或标号代表地址、常量或变量。助记符一般都是英文单词的缩写,便于识别和记忆。使用汇编语言编写的程序称为汇编语言源程序。汇编语言源程序不能由机器直接执行,而必须翻译成有机器代码组成的目标程序,这个翻译的过程称为汇编。把汇编语言源程序翻译成目标程序的软件称为汇编程序。
汇编语言与机器语言密切相关,它们之间有明显的对应关系。一条汇编语言指令对应一条机器语言代码,所以汇编语言和机器语言一样都是面向机器的语言。使用汇编语言进行程序设计能充分利用机器的硬件功能和结构特点,从而有效地加快程序的执行速度,减少程序占用的存储空间。所以汇编语言大量用于编写计算机系统程序、实时通信程序和实时控制程序等。
汇编语言作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但重要性却勿庸置疑,因为它能够完成许多其它语言所无法完成的功能。就拿Linux内核来讲,虽然绝大部分代码是用C语言编写的,但仍然不可避免地在某些关键地方使用了汇编代码,其中主要是在Linux的启动部分。由于这部分代码与硬件的关系非常密切,即使是C语言也会有些力不从心,而汇编语言则能够很好扬长避短,最大限度地发挥硬件的性能。
2.2 汇编语言优点及缺点
汇编语言直接同计算机的底层软件甚至硬件进行交互,它具有如下一些优点: (1)能够直接访问与硬件相关的存储器或I/O端口;
(2)能够不受编译器的限制,对生成的二进制代码进行完全的控制;
(3)能够对关键代码进行更准确的控制,避免因线程共同访问或者硬件设备共享引起的死锁;
(4)能够根据特定的应用对代码做最佳的优化,提高运行速度; (5)能够最大限度地发挥硬件的功能。
同时还应该认识到,汇编语言是一种层次非常低的语言,它仅仅高于直接手工编写二进制的机器指令码,因此不可避免地存在一些缺点: (1)编写的代码非常难懂,不好维护; (2)很容易产生bug,难于调试;
(3)只能针对特定的体系结构和处理器进行优化; (4)开发效率很低,时间长且单调。
首先程序开始时在电脑上显示输入提示信息,提醒输入的操作数只能是1到10之间的正整数,超过这个范围或太小则显示提示信息输入的数过大。得到杨辉三角的阶数之后即调用一个算法子程序来求相应阶数的每一个数值,每求出一个数值即将其数压入堆栈中保存起来,方便以后输出数字时直接调用。算完之后,通过外层循环di计数输出每一行,与此同时又通过内层循环si计数输出一行中的每一个数,在输出数字时通过调用show子程序将数字均以十进制输出,在输出数值的时候通过showspace函数来控制数与数之间的空格及行前空格的输出,上半部分的数字输出之后将其所有数值及空格格式都存在一个预制的存储单元内,然后直接实现逆序输出,这样最终能在屏幕中打印出一个菱形的杨辉三角。
第二章 子程序设计
4.1 输入子程序
输入用int 16 ah为0这一功能,从键盘读字符,字符存在al中,输入时用cmp判断令其只能输入字符’0’~‘9’每次输入的字符转化为十进制存并阔展为字存入ax中,再将ax与初始为0的bp交换,再将ax乘以十后与bp相加,结果存在bp中,再返回输入,这样就能输入两位数。 shur proc push cx push bx xor bp,bp mov bx,10 mov cx,2 input:
mov ah,0 ;键盘输入数据存在al中 int 16h
cmp al,0dh ;以回车结束输入 jz ok
cmp al,'0' ;只允许输入0~9 jb input cmp al,'9' ja input
mov ah,0eh ;显示有效输入 int 10h
sub al,30h ;化ASCII为十进制 cbw ;字节扩展为字 xchg ax,bp
mul bx ;扩大10倍 add bp,ax ;加一位 loop input
ok:nop ;数值结果在BP中 pop bx ;恢复用到的寄存器 pop cx ret shur endp
4.2杨辉三角算法子程序
求某m行n列的数C(n, m)时采用递归的方法求出该数具体算法是:
{ C(n, m) = 1 (n
{ C(n, m) = C(n-1, m-1) + C(n-1, m) (n > m)
即某位置组合数等于上一行左右两数之和,先算出左肩上的数并压栈保存,再次调用C过程求出右肩上的数并压栈保存,左肩与右肩的数相加从而可以求出下一行的组合数吗,其间存在递归调用,直至求出最大的那个数才返回初始调用的call的下一个语句。 C proc push bp mov bp, sp
sub sp, 2 ; 预留一个存储位置 mov bx, [bp+6] ; 保存m到bx
cmp bx, [bp+4] ; 如果m > n 返回1 jz L1
cmp bx, 0 ; 如果m = 0 返回1 jz L1
mov ax, [bp+4] ; 保存n到ax dec ax; ax = ax - 1 dec bx; bx = bx - 1 push bx push ax
call C ; 返回上一行左边的那个数 mov [bp-2], ax ; 保存左肩膀上的数
mov ax, [bp+4] ; 以下5句同理,返回上一行右肩膀上的数 dec ax
push [bp+6] push ax call C
add ax, [bp-2] ; 和左肩膀上的数相加得出该组合数 jmp L2 L1:
mov ax, 1 L2:
mov sp, bp pop bp
ret 4 ; ax返回组合数 C endp 4.3
计算输出数字长度子程序
将此数不断除以十,每除一次计数加1,当al即商为0时停止返回计数值即长度。例如:计算输出数字最大长度以便分配行间行前空格时,n阶的最大数应该是最后一行中间那个数即C(n, n/2) ,将此数不断除以十,每除一次计数加1,当al即商为0时停止返回计数值即最大长度。
getdigit proc push dx mov bx, 10 xor dx, dx next:
cmp ax, 0 jle ok2 div bl
and ax, 0ffh inc dx jmp next ok2:
mov ax, dx pop dx ret
getdigit endp
4.4 计行前数字间空格子程序
先定义一个可以接受输入ax个空格的子程序输出行前空格时,n阶杨辉三角的i行行前空格数=(n-i)*cl ,cl是最大长度,某数与下一个数字间空格数=数的最大长度cl+cl-该数的长度。具体程序如下:
call showspace ; 输出行前空格
xor si, si ; 内存循环计数si,内层循环输出一行中的每个数 jmp cp2 up2:
inc si ; 更新di cp2:
cmp si, di ; 测试循环条件,循环di次 jg done2 push si push di
call C ; 获取该行的位于si位置的组合数,调用C(di, si) push ax ; 保存该组合数 push bx mov bx ,dx mov [bx], ax inc dx inc dx pop bx
call show ; 输出该数 mov ax, cx sub ax, 1 push bx
mov [bx], ax inc dx inc dx pop bx
call showspace pop ax
call getdigit ; 获取该组合数长度 mov bx, ax mov ax, cx sub ax, bx ; add ax, 1 push bx mov bx ,dx mov [bx], ax inc dx inc dx pop bx
call showspace
jmp up2 ; 更新内层循环
showspace: push dx mov bx, ax mov ah, 2 mov dl, ' ' nexts:
cmp bx, 0 jle dones int 21h dec bx jmp nexts dones: pop dx
4.5输出子程序
输出分为上半部分和下半部分,上半部分输出利用2号功能将每个数除以10将余数转化为字符倒序输出,下半部分是将算好的数以及行前行间空格数算好然后倒序输出。
上半部分: show proc
push dx mov bx, 10
jz ok1 div bl push ax
and ax, 00ffh call show pop dx mov dl, dh or dl, 30h mov ah, 2 int 21h ok1: pop dx ret
show endp
下半部分: mov ax,da
inc al mov bl,6 mul bl sub dx,ax dec dx dec dx
mov cx,da l11:
mov ax,cx mov bl,6 mul bl sub dx,ax dec dx dec dx mov bx,dx mov ax,[bx]
call showspace push cx l12: inc dx inc dx mov bx,dx mov ax,[bx]
call show inc dx inc dx mov bx,dx
; 输出行前空格输出该数 ;
call showspace ;输出数字间间隔空格 inc dx inc dx mov bx,dx mov ax,[bx]
call showspace ;输出填充的空格 loop l12 pop cx mov ax,cx mov bl,6 mul bl inc dx inc dx sub dx,ax dec dx dec dx push dx
mov ah, 2 ; 以下5句实现换行 mov dl, 13 int 21h mov dl, 10 int 21h pop dx loop l11
附录
附录A
程序代码: data segment org 100h
message db 'Input N(N
error db 0ah,0dh, 'Data out of range!$' da dw 0
dat dw 300 dup(?) data ends
code segment
assume cs:code,ds:data start:
mov ax,data
mov ds,ax
mov dx,offset message mov ah,9 int 21h call shur cmp bp,10 jbe goon
mov dx,offset error mov ah,9 int 21h jmp exit
goon:
mov ah, 2 ; 以下5句实现换行 mov dl, 13 int 21h mov dl, 10 int 21h push bp call yhsj mov ax,da inc al mov bl,6 mul bl sub dx,ax dec dx dec dx
mov cx,da l11:
mov ax,cx mov bl,6 mul bl sub dx,ax dec dx dec dx mov bx,dx mov ax,[bx]
call showspace ; 输出行前空格 push cx l12: inc dx inc dx mov bx,dx mov ax,[bx]
call show ; 输出该数 inc dx inc dx mov bx,dx mov ax,[bx]
call showspace ;输出数字间间隔空格 inc dx inc dx mov bx,dx mov ax,[bx]
call showspace ;输出填充的空格 loop l12 pop cx mov ax,cx mov bl,6 mul bl inc dx inc dx sub dx,ax dec dx dec dx push dx
mov ah, 2 ; 以下5句实现换行 mov dl, 13 int 21h mov dl, 10 int 21h pop dx loop l11
mov ah,4ch int 21h exit:
mov ah,4ch int 21h
shur proc push cx push bx xor bp,bp mov bx,10 mov cx,2 input:
mov ah,0 ;键盘输入数据存在al中 int 16h
cmp al,0dh ;以回车结束输入 jz ok
cmp al,'0' ;只允许输入0~9 jb input cmp al,'9' ja input
mov ah,0eh ;显示有效输入 int 10h
sub al,30h ;化ASCII为十进制 cbw ;字节扩展为字 xchg ax,bp
mul bx ;扩大10倍 add bp,ax ;加一位 loop input
ok:nop ;数值结果在BP中 ;恢复用到的寄存器 pop bx pop cx ret shur endp
; 输出杨辉三角的函数,接受一个栈上的参数N
; 输出N阶杨辉三角 yhsj proc mov bp, sp
mov ax, [bp+2] ; 保存N到ax mov dx,offset dat mov da, ax
shr ax, 1 ; N = N / 2 push ax
mov ax, [bp+2] ; 保存N到ax push ax
call C; C(N, N/2)获取最后一行中间的那个值,即最大值
call getdigit ; 计算该最大值的长度,如252则返回3
mov cx, ax ; 保存最大长度到cx,用于事后格式用
xor di, di ; 外层循环计数di,外层循环输出每一行 jmp cp1 up1:
inc di; 更新di cp1:
cmp di, [bp+2] ; 测试循环条件,循环N次
jg done1
mov ax, [bp+2] ; 以下3句计算行前空格数 = (N-i)*cl,cl是最大长度 sub ax, di mul cl push bx mov bx ,dx mov [bx], ax inc dx inc dx pop bx call showspace ; 输出行前空格
xor si, si ; 内存循环计数si,内层循环输出一行中的每个数 jmp cp2 up2:
inc si; 更新di cp2:
cmp si, di ; 测试循环条件,循环di次 jg done2 push si push di
call C; 获取该行的位于si位置的组合数,调用C(di, si)
push ax ; 保存该组合数 push bx mov bx ,dx mov [bx], ax inc dx inc dx pop bx
call show ; 输出该数
mov ax, cx ;以下输出数字间间隔空格,个数 = N - 1 sub ax, 1 push bx mov bx ,dx mov [bx], ax inc dx inc dx pop bx
call showspace pop ax
call getdigit ; 获取该组合数长度 mov bx, ax mov ax, cx
sub ax, bx ;计算需要填充的空格数 = 最大长度 - 该数长度 + 1 add ax, 1 push bx mov bx ,dx mov [bx], ax inc dx inc dx pop bx
call showspace
jmp up2 ; 更新内层循环 done2: ; 内层循环结束 push dx
mov ah, 2 ; 以下5句实现换行 mov dl, 13 int 21h mov dl, 10 int 21h pop dx
jmp up1 ; 更新外层循环 done1: ; 外层循环结束
ret 2 ; 释放函数参数使用的栈空间 yhsj endp
; 求组合数的递归函数,接受栈上的2个参数n, m(n > m)
; 返回C(n, m),即n选m的个数 ; 算法是:
; { C(n, m) = 1 (n = m 或 m = 0)
; { C(n, m) = C(n-1, m-1) + C(n-1, m) (n > m) ; 即某位置组合数等于上一行左右两数之和 C proc push bp mov bp, sp
sub sp, 2 ; 预留一个存储位置 mov bx, [bp+6] ; 保存m到bx
cmp bx, [bp+4] ; 如果m = n 返回1 jz L1
cmp bx, 0 ; 如果m = 0 返回1 jz L1
mov ax, [bp+4] ; 保存n到ax dec ax; ax = ax - 1 dec bx; bx = bx - 1 push bx push ax
call C; 返回上一行左边的那个数
mov [bp-2], ax ; 保存左肩膀上的数 mov ax, [bp+4] ; 以下5句同理,返回上一行右肩膀上的数 dec ax
push [bp+6] push ax call C
add ax, [bp-2] ; 和左肩膀上的数相加得出该组合数 jmp L2 L1:
mov ax, 1 L2:
mov sp, bp pop bp
ret 4 ; ax返回组合数 C endp
; 递归以10进制输出ax
; 方法很简单,就是求出余数,然后ax = ax / 10
; ax = 0时退出,开始逆序输出求出的各位余数
show proc push dx mov bx, 10 cmp ax, 0 jz ok1 div bl push ax
and ax, 00ffh call show pop dx mov dl, dh or dl, 30h mov ah, 2 int 21h ok1: pop dx ret
show endp
; 获取一个数的长度,ax为参数,如果ax = 252则返回3 ; ax里是返回值 getdigit proc push dx mov bx, 10 xor dx, dx next:
cmp ax, 0 jle ok2 div bl
and ax, 0ffh inc dx jmp next ok2:
mov ax, dx pop dx ret
getdigit endp
; 输出ax个空格,参数ax,无返回值 showspace proc push dx mov bx, ax mov ah, 2 mov dl, ' ' nexts:
cmp bx, 0 jle dones int 21h dec bx jmp nexts dones: pop dx ret
showspace endp
code ends end sta