下面是闰年程序的代码示例:(我将以这个闰年程序演示如何建立lib函数库)
data segment ;定义数据段
infon db 0dh,0ah,'Please input a year: $' ;infon ,双字节,回车换行,内容
Y db 0dh,0ah,'This is a leap year! $' ;y ,双字节,回车换行,内容
N db 0dh,0ah,'This is not a leap year! $' ;n ,双字节,回车换行,内容($是结束符)
w dw 0 ;w为双字类型
buf db 8 ;开辟8个双字节类型的缓冲区,未赋值
db ?
db 8 dup(?)
data ends
stack segment stack
db 200 dup(0) ;开辟200个双字节类型的存储空间。(保护现场用到)
stack ends
code segment
assume ds:data,ss:stack,cs:code
start:mov ax,data ;寄存器间的数据交换,把data给了数据段
mov ds,ax
lea dx,infon ;在屏幕上显示提示信息
mov ah,9
int 21h
lea dx,buf ;从键盘输入年份字符串
mov ah,10
int 21h
mov cl, [buf+1] ;把buf+1地址指向的内容给cl,buf长度
lea di,buf+2 ;把buf+2的地址给di,buf偏移地址
call datacate ;调用函数datacate,传递参数
call ifyears ;调用函数ifyears,由参数判断是否为闰年
jc a1 ;若产生进位就跳到a1处
lea dx,n ;dx加载n的地址'This is not a leap year!'
mov ah,9 ;显示dx内的内容
int 21h
jmp exit ;跳到exit
a1: lea dx,y ;dx加载y的地址'This is a leap year!'
mov ah,9
int 21h
exit: mov ah,4ch ;结束本程序,返回 DOS 操作系统
int 21h
;实现将字符串的闰年转换为数字存储在ax中
datacate proc near; ;段内调用,子函数1
push cx; ;保护现场
dec cx
lea si,buf+2 ;把buf+2的地址给si
tt1: inc si ;移动到字符串指定位置
loop tt1 ;循环函数是个递增函数, loop指令的格式是:loop标号,cpu执行loop指令的时候,要进行两步操作
;1:(cx)=(cx)-1
;2:判断cx中的值,不为零则转至标号处执行,如果为零,则向下执行
;lea si,cx[di]
pop cx ;恢复现场
mov dh,30h
mov bl,10 ;乘数设置为10
mov ax,1
l1: push ax ;保护现场
sub byte ptr [si],dh ;没有寄存器参与的内存单元访问指令(DMA),si-dh保存在si中
mul byte ptr [si] ;没有寄存器参与的内存单元访问指令(DMA),ax*si,结果放在eax中,这里的寄存器si是16位,被乘数在ax中也是16位。
;MUL r/m(寄存器或者内存) ;mul是字内乘法,把si对应的地址内一个字的长度赋给
;如果参数是 r8/m8, 将把 AL 做乘数, 结果放在 AX
;如果参数是 r16/m16, 将把 AX 做乘数, 结果放在 EAX
;如果参数是 r32/m32, 将把 EAX 做乘数, 结果放在 EDX:EAX
add w,ax ;w+ax,结果存入w,存入的是数字
pop ax ;恢复现场
mul bl ;bl*al,结果存放在ax中
dec si ;si中的内容减一
loop l1 ;l1循环,cx判断次数
ret
datacate endp
;实现ax中年份的判断
ifyears proc near ;段内调用,子函数2
push bx ;保护现场(保护谁,就用到谁)
push cx
push dx
mov ax,w ;把w值赋给cx,w值为
mov cx,ax ;把ax值赋给cx
mov dx,0
mov bx,4 ;ax/4
div bx ;ax/bx,结果在ax中,余数在dx中
;如果除数是8位,那么除法的结果AL保存商,AH保存余数,
;如果除数是16位,那么除法的结果 AX保存商,DX保存余数。
cmp dx,0 ;比较余数和0,结果存入cf标志位(进位标志位)
jnz lab1 ;如果非0,跳转到lab1处
mov ax,cx
mov bx,100 ;ax/100
div bx
cmp dx,0 ;比较余数和0,结果存入cf标志位(进位标志位)
jnz lab2 ;如果非0,跳到lab2
mov ax,cx
mov bx,400 ;ax/400
div bx
cmp dx,0 ;比较余数和0,结果存入cf标志位(进位标志位)
jz lab2 ;cf=0跳到lab2
lab1: clc ;清除cf标志位,设置cf=0
jmp lab3 ;跳到lab3
lab2: stc ;cf置1
lab3: pop dx ;恢复现场
pop cx
pop bx
ret
ifyears endp
code ends
end start
首先要把lib库中的函数分离出来,也就是每个函数成为一个独立的asm文件。
datacate函数部分
extrn infon:byte,y:byte,n:byte,w:word,buf:byte
public datacate
code segment
assume cs:code
datacate proc far
push cx;
dec cx
lea si,buf+2
tt1: inc si
loop tt1
;lea si,cx[di]
pop cx
mov dh,30h
mov bl,10
mov ax,1
l1: push ax
sub byte ptr [si],dh
mul byte ptr [si]
add w,ax
pop ax
mul bl
dec si
loop l1
ret
datacate endp
code ends
end
ifyears函数部分
extrn infon:byte,y:byte,n:byte,w:word,buf:byte
public ifyears
code segment
assume cs:code
ifyears proc far
push bx
push cx
push dx
mov ax,w
mov cx,ax
mov dx,0
mov bx,4
div bx
cmp dx,0
jnz lab1
mov ax,cx
mov bx,100
div bx
cmp dx,0
jnz lab2
mov ax,cx
mov bx,400
div bx
cmp dx,0
jz lab2
lab1: clc
jmp lab3
lab2: stc
lab3: pop dx
pop cx
pop bx
ret
ifyears endp
code ends
end
main主函数部分
注意:库函数中的函数全部为段间调用,所以需要修改函数为far类型。
extrn datacate:far,ifyears:far
public infon,y,n,w,buf
data segment ;定义数据段
infon db 0dh,0ah,'Please input a year: $'
Y db 0dh,0ah,'This is a leap year! $'
N db 0dh,0ah,'This is not a leap year! $'
w dw 0
buf db 8
db ?
db 8 dup(?)
data ends
stack segment stack
db 200 dup(0)
stack ends
code segment
assume ds:data,ss:stack,cs:code
start:mov ax,data
mov ds,ax
lea dx,infon ;在屏幕上显示提示信息
mov ah,9
int 21h
lea dx,buf ;从键盘输入年份字符串
mov ah,10
int 21h
mov cl, [buf+1]
lea di,buf+2
call datacate
call ifyears
jc a1
lea dx,n
mov ah,9
int 21h
jmp exit
a1: lea dx,y
mov ah,9
int 21h
exit: mov ah,4ch
int 21h
code ends
end start
可以看到主函数与子函数之间的参数传递是通过extrn关键字完成的:子函数中没有代码段,主函数中没有子函数的定义。
然后就要用lib.exe生成库函数:(一般在dosbox的bin中)
lib.exe的使用方法:
Operations:放生成的obj文件(放子函数datacate和ifyears)
List file:要放asm文件(功能一般用不到)
生成lib的步骤:
编译3个asm文件(main,datacate,ifyears),生成obj文件
利用lib.exe生成,逐次添加子函数
masm611的lib.exe生成的静态库是可以扩展的。
链接时使用main.obj和mylib进行链接操作生成exe