|
主题 : : 课程设计1--花了2天半 我的程序恐怕是汇编论坛有史以来最长和最奇怪的了。。 [待解决] |
回复[ 4次 ]
点击[ 431次 ] | |
|
|
|
|
[帖 主]
[ 发表时间:2009-05-20 19:46 ]
[引用]
[回复]
[ top ] | |
荣誉值:30
信誉值:0
注册日期:2009-04-09 10:05 |
;程序没有侥幸,你觉得可能会错他就一定会错我就是犯了这个错误结果花了大力气才检查出原本疏忽的地方。
;关于保存数据:其实我这个程序是树状线性二选式结构的不需要保存寄存;器的数据(所有初始数据都是静态获取的)
;另外模块的数据可以更加具体和通用化但是就本题而言似乎用不到所以
;暂时只对用到的数据进行类处理.
;许多功能可以整合,许多代码都可以类处理实现重用,不过一来现在没精;力,二来对于面向过程的汇编语言,过多用面向对象的处理方法,(把变;量保存在内存中),看起来太抽象不直观,且容易头晕。。。
;从始至终没有用过call和ret,一是还不太会用,二是个人觉得call和;ret要分心去管理栈的数据比较麻烦,且多选择的时候不太实用
assume cs:codesg
data segment
db '1975','1976','1977','1978','1979','1980','1981', '1982','1983'
db '1984', '1985', '1986', '1987', '1988', '1989', '1990','1991', '1992'
db '1993', '1994', '1995'
;以上是表示21年的21个字符窜
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以上是表示21年公司分别总收入的double word型数据
dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001,1442,2258, 2793, 4037, 5635, 8226
dw 11542, 14430, 15257, 17800
;以上是表示21年公司雇员人数的word型数据
data ends
show segment
db 1680 dup (' ')
db 0
show ends
;show段长度为20*84+1B,用来暂存经处理后将被送入显存的数据
temp segment
db 64 dup (' ')
temp ends
;temp段长度为64B,用来暂存临时数据.
change segment
db 160 dup (' ')
change ends
;change段用来存放交换数据
mean segment
dw 32 dup (' ')
mean ends
;用来存放平均收入
codesg segment
;##############这部分主要完成将年份送入show################
;ds:[22]存放调用程序的地址
start:mov ax,temp
mov ds,ax ;ds指向暂存数据段temp
mov ax,show ;es指像show
mov es,ax
mov ax,data
mov ss,ax
mov sp,0 ;ss:sp指向原始数据段data
mov bx,2 ;bx存放show的基础地址
mov cx,21 ;决定char循环次数
char: pop es:[bx] ;char用来将字符型数据送入show
pop es:[bx+2] ;bx为送入地址的基础地址
add bx,80 ;每次送入地址间隔为80b
loop char
;#########这部分主要完成将总收入送入交换段changer##########
s1:mov ax,temp ;ds指向temp
mov ds,ax
mov ax,change ;es指向change
mov es,ax
mov bp,0 ;bp指向交换数据地址
mov sp,84 ;栈顶指向公司总收入(ss为数据段data)
mov si,10 ;si=除数
mov ax,offset sub0 ;指明程序返回到sub0
mov ds:[22],ax
mov word ptr ds:[16],21 ;ds:[16]存放数据个数
sub0:mov di,0 ;di指向保存临时数据地址
mov cx,ds:[16] ;检测数据是否完全转换完毕
jcxz sub1 ;若数据完全转换完毕跳转到sub1
;否则向下执行
pop ax ;ax=低位数据
pop cx ;cx=高位数据
jcxz temp_jcxz ;高位数据=0,转到普通转ASCII除法类模块
jmp divdw ;高位数据!=0则访问防溢转ASCII除法类模块
temp_jcxz: jmp divs ;因jcxz指令范围过小而用的中转指令
;#########这部分主要完成将总收入从change导入show###########
;用ss暂存show段,ds:[24]暂存循环数
;line 100
sub1:mov ax,show ;ss为show
mov ss,ax ;ds为temp
mov sp,0
mov ax,offset sub2 ;确定返回程序地址
mov ds:[22],ax
mov ax,20
mov ds:[26],ax ;show的基础地址
mov ax,21 ;循环次数
jmp charcs
;###########这个部分代码主要作用将人数送change#############
sub2: mov ax,data ;ds为temp
mov ss,ax ;es为change
mov sp,168 ;ss为data
;栈顶指向公司员工数(ss为数据段data)
mov si,10 ;除数
mov ax,offset sub3 ;指明程序返回到sub3
mov ds:[22],ax
mov bp,0 ;es:[bp]从0开始
mov word ptr ds:[16],21 ;ds:[16]存放数据个数 mov bp,0 ;bp指向交换数据地址
sub3:mov di,0 ;di指向保存临时数据地址
mov cx,ds:[16] ;检测数据是否完全转换完毕
jcxz sub4 ;若数据完全转换完毕跳转到sub4
;否则向下执行
pop ax ;ax=低位数据
jmp divs ;访问非溢出转ASCII除法类模块
;##########这部分代码作用将人数从change送入show段##########
sub4:mov ax,show ;ss为show
mov ss,ax ;ds为temp
mov sp,0 ;es为change
mov ax,offset sub5 ;确定返回程序地址
mov ds:[22],ax
mov ax,40
mov ds:[26],ax ;show的基础地址
mov ax,21 ;循环次数
jmp charcs
;###########这个部分代码作用将人均收入送入mean段###########
sub5:mov ax,data
mov ss,ax ;ss为data
mov sp,84
;ds为temp
mov ax,mean ;es为mean
mov es,ax
mov bx,0 ;es:[bx]定位es内存单元
mov ax,offset sub6 ;确定返回程序地址
mov ds:[22],ax
mov ax,21
mov ds:[24],ax ;保存除法次数
mov bp,0 ;bp是除数变址
sub6:mov cx,ds:[24] ;cx=0,除法全部完成执行跳转
jcxz sub7 ;否则向下执行
mov si,ss:[168+bp] ;定位除数
pop ax ;导入低位被除数
pop cx ;导入高为被除数
jmp sdivdw ;进行一般防溢除法
;########主要作用将mean段数据传送到change段###############
sub7:mov ax,mean ;ds为temp
mov ss,ax
mov sp,0 ;ss为mean
;栈顶指向收入平均数(ss为数据段mean)
mov ax,change ;es为change
mov es,ax
mov si,10 ;除数
mov ax,offset sub8 ;指明程序返回到sub8
mov ds:[22],ax
mov bp,0 ;es:[bp]从0开始
mov word ptr ds:[16],21 ;ds:[16]存放数据个数 mov bp,0 ;bp指向交换数据地址
sub8:mov di,0 ;di指向保存临时数据地址
mov cx,ds:[16] ;检测数据是否完全转换完毕
jcxz sub9 ;若数据完全转换完毕跳转到sub9
;否则向下执行
pop ax ;ax=低位数据
pop cx ;cx=高位数据
jcxz temp_jcxz2 ;高位数据=0,转到普通转ASCII除法类模块
jmp divdw ;高位数据!=0则访问防溢转ASCII除法类模块
temp_jcxz2: jmp divs ;因jcxz范围过小设置的中转指令
;#########代码实现将人均收入送入show######################
sub9:mov ax,show ;ss为show
mov ss,ax ;ds为temp
mov sp,0 ;es为change
mov ax,offset sub10 ;确定返回程序地址
mov ds:[22],ax
mov ax,60
mov ds:[26],ax ;show的基础地址
mov ax,21 ;循环次数
jmp charcs
;######################################################
sub10:
mov dh,1 ;行
mov dl,1 ;列
mov cl,2 ;颜色
jmp show_str ;访问show_str接口
;#########charc用来将change段数据送入show################
;es:[si]为change段,ss:[bx+di]为目标段show
;si为change段变址,bx为show段基址,di为show段变址
;ax=ds:[24]保存了程序的运行次数,运行完后返回ds:[22]指向的代码
;bx=ds:[26]=show的基础地址
charcs:mov ds:[24],ax ;ax=ds:[24]=执行次数
mov bx,ds:[26] ; bx存放show的基础地址
mov si,0 ;si存放change段变址
mov di,0 ; di存放show的变址
charc: mov ch,0
mov al,es:[si]
mov cl,al
jcxz charcsub ;遇到0表示一单位数据传输完毕执行跳转
;否则向下执行
mov ss:[bx+di],al ;把change段数据送入show段
inc si
inc di
loop charc ;重复循环直到一单位数据输出完毕
charcsub:dec word ptr ds:[24] ;每转完一个地址循环数-1
mov cx,ds:[24]
jcxz charcret ;如果ds:[24]为0则循环结束
;否向下执行
add bx,80 ;show基址+80
mov di,0 ;show变址清0
inc si ;change指向下个字节单元
jmp charc ;接着进行其他数据的传输
charcret:jmp word ptr ds:[22] ;返回调用程序
;#########sdivdw-普通防溢出除法模块-######################
;es:[bx]=保存除法商的地址 ,ds:[24] 保存程序运行的次数
;sdivdw用来进行普通防溢出的除法,ds:[18]保存原始数据的低位数
;si=除数 ds:[20]保存高位除法的商
sdivdw:mov ds:[18],ax ;ds:[18]=低位被除数
mov ax,cx ;ax=高位被除数
mov dx,0 ;除法高位清0
div si ;高位除法
mov es:[bx+2],ax ;es:[bx+2]储存高位除法的商
mov ax,ds:[18] ;ax=低位除法的低位被除数
div si
mov es:[bx],ax ;es:[bx]储存低位除法的商
add bx,4
add bp,2 ;bp是除数变址
dec word ptr ds:[24] ;运行次数次数-1
jmp word ptr ds:[22] ;ds:[22]存放调用程序的地址
;#########divdw--<转ASCII防溢出除法类模块>###############
;divdw的数据要通过子程序divs来返回
;divdw用来进行防溢出的除法,ds:[18]保存原始数据的低位数
;si=除数 ds:[20]保存高位除法的商
;一个新的段访问此程序需要使bp清0(不然写入的地址是错误的)
divdw: mov ds:[18],ax ;ds:[18]=原始数据低位数
d0: mov ax,cx ;ax=原始数据高位数
mov dx,0 ;除法高位清0
div si ;高位除法
mov ds:[20],ax ;ds:[20]储存高位除法的商
;------高位除法的余数自动成为低位除法的高位被除数dx----------
mov ax,ds:[18] ;ax=低位除法的低位被除数
div si
add dl,30H ;余数+30H表示数字字符窜的ASCII码
mov ds:[di],dl ;用ds段暂存数据
add di,1 ;表示这个字符窜长度
mov cx,ds:[20] ;如果高位除法商为0
jcxz divs ;则转入非溢出除法
;否则跳转到d0继续运行防溢出除法
mov ds:[18],ax ;重写入低位除法被除数
jmp d0
;############ divs--<非溢出转ASCII除法类模块>#############
;divs用来进行非溢出的dword除法,并将结果转换为0~9的ASCII码
;用到寄存器 CX=条件转移,ds:[di]=暂存数据段及地址,di还=字符窜;ASCII码长度,es:[bp]=交换数据段及地址,si=除数
;word ptr ds:[16]=要处理的数据的数量
;一个新的段访问此程序需要使bp清0(不然写入的地址是错误的)
divs:mov dx,0 ;高位清0
mov cx,ax ;检测商是否为0
jcxz next ;遇到0表示商为0. 跳转到next
div si ;否则进行除法
add dl,30H ;余数+30H表示数字字符窜的ASCII码
mov ds:[di],dl ;用ds段暂存数据
add di,1 ;di表示这个字符窜ASCII码长度
loop divs
next: mov cx,di ;进行一单位数字字符窜位数交换
changer: mov al,ds:[di-1] ;因为内存从0开始所以di-1
mov es:[bp],al ;changer接口实现逆序排列
dec di ;并按序保存在es段
inc bp
loop changer
mov byte ptr es:[bp],0 ;添加0表示字符窜结束
inc bp
dec word ptr ds:[16] ;循环次数-1
jmp word ptr ds:[22] ;ds:[22]存放调用程序的地址
;######################################################
show_str: mov si,0 ;si=显存偏移地址
mov bx,0 ;bx=show段数据地址
mov ax,0b800H ;确定显存所在段
mov es,ax
mov ax,show ;改变ds段为show
mov ds,ax
mov al,160 ;确定显存起始偏移地址
dec dh
mul dh ;行偏移地址
mov dh,0
dec dl
add dl,dl ;列偏移地址
add ax,dx ;行偏移地址加列偏移地址
mov bx,ax ;bx表示起始偏移地址
mov al,cl ;al表示颜色
mov ch,0 ;counter高位清0
show_s:mov cl,ds:[si] ;用cl检字符窜是否为0
jcxz return ;如果为0,程序转移到return
;不为0执行下列程序
mov es:[bx],cl ;字符ASCII码写入显存地址
mov es:[bx+1],al ;字符属性写入显存地址
inc si ;si定位字符窜ASCII偏移地址
add bx,2 ;bX定位写入显存的偏移地址
loop show_s ;循环
return: mov ax,4c00H
int 21H
codesg ends
end start
;搞了整整2天半!许多构思想实现却总觉得千头万绪,无奈只好放下,期待更深入学习后能有所寸进,我想我这个课程设计算是最特别的了从头到晚没有用过call和ret,嘿嘿因为我不会用!虽然原理明白- -
;另这个程序恐怕是有史以来最长的了吧,不过所有代码几乎都有做标识,我因为不会用call,ret所以后期用的都是自己研究出来的小方法,里面许多地方也是汇聚了我的小智慧,独立完成这个程序过程中,难度非常之大,特别是构思之初几乎毫无头绪,但是慢慢的抽丝剥茧也终于蹒跚完成,这个程序在即将收尾时,我其实已经对这个程序的臃肿处有了些改善的方法,但是这样将导致整个程序要推倒重新设计,我个人精力有限,而且我的方法是将变量都放置于内存中独立开辟一个段来存放变量,所有重用的代码写成一个类,但是汇编毕竟是面向过程的语言,他不具高级语言的直观性,如果强行把他搞成面向对象恐怕到时候程序连自己都会看不懂,所以最终我还是选择了开起来臃肿但是调试起来教方便的方式。
;这个程序 运行至今有2个我无法攻破的地方,一是 debug中断调式中 连续出栈 会导致 报错,二是 收尾代码
;mov ax,4c00h int 21h 如果用jmp指定的话 范围过大 会导致失效。。- -
;另这个程序在debug中 直接按 g 是调试成功的! | | |
|
|
|
|
[第1楼]
[ 回复时间:2009-05-20 19:50 ]
[引用]
[回复]
[ top ] | |
荣誉值:30
信誉值:0
注册日期:2009-04-09 10:05 |
show_str 作用的是将 show段数据送入显存 不好意思 写的头晕脑胀忘记注释了! | | |
|
|
|
|
[第2楼]
[ 回复时间:2009-05-21 10:28 ]
[引用]
[回复]
[ top ] | |
荣誉值:337
信誉值:0
注册日期:2008-01-01 17:48 |
|
|
|
|
|
[第3楼]
[ 回复时间:2009-05-25 00:47 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:5
注册日期:2009-03-25 17:31 |
.....................................................................kb......................... | | |
|
|
|
|
[第4楼]
[ 回复时间:2009-08-07 20:59 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2009-08-05 23:19 |
看看我的程序:
如下
assume cs:code,ds:data,ss:stack,ds:data1
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1888','1989','1990','1991','1992'
db '1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
stack segment
db 50 dup (0)
stack ends
data1 segment
db 20 dup (0)
data1 ends
code segment
start:
mov ax,data
mov es,ax
mov ax,data1
mov ds,ax
mov si,0
mov ax,stack
mov ss,ax
mov sp,50
mov bx,0
mov di,0 ;以上代码初始化各寄存器
mov dh,10 ;设置显示首行行号
dec dh
mov cx,21 ;设置总循环循环次数
zongxunhuan:
push cx ;循环次数入栈
mov cl,12 ;设置颜色属性,设置一次,以后都用这一个
;将年份显示在屏幕指定位置,一行显示一个
push dx ;dx入栈,目的是暂存dh
mov ax,word ptr es:[bx]
mov dx,word ptr es:[bx+2]
mov [si],ax
mov [si+2],dx
mov byte ptr [si+4],0 ;将es段中存放年份字符串的四字节转移到ds:[si]处,以0结尾
pop dx ;dx出栈,目的是取回dh
inc dh
mov dl,1 ;设置年份显示首列列号
call show_str ;调用子程序显示
;将总收入显示在屏幕指定位置,一行显示一个
push dx
mov ax,word ptr es:[bx+84]
mov dx,word ptr es:[bx+86]
call ddtoc ;将es段中存放总收入的四字节转化为十进制字符串形式,并由ds:[si]指向首字节,以0结尾
pop dx
mov dl,11 ;设置总收入显示首列列号
call show_str ;调用子程序显示
;将雇员数显示在屏幕指定位置,一行显示一个
mov ax,word ptr es:[di+168]
call dtoc ;将es段中存放雇员数的两字节转化为十进制字符串形式,并由ds:[si]指向首字节,以0结尾
mov dl,21 ;设置雇员数显示列首列号
call show_str ;调用子程序显示
;计算人均收入,并将商在屏幕指定位置显示,一行显示一个
push dx
mov ax,word ptr es:[bx+84]
mov dx,word ptr es:[bx+86]
div word ptr es:[di+168] ;用div计算人均收入,其商存在ax中.由于此处不存在除法溢出,因此未调用子程序
call dtoc ;将所得商转化为十进制字符串形式,并由ds:[si]指向首字节,以0结尾
pop dx
mov dl,32 ;设置人均收入显示列首列号
call show_str ;调用子程序显示
;调节数据,为下一次循环做准备
add bx,4
add di,2
pop cx ;循环次数出栈
loop zongxunhuan
mov ah,4ch
int 21h
;将dword型数据转变为表示十进制的字符串,字符串以0为结尾符
;参数:(ax)=dword型数据低16位,(dx)=dword型数据高16位,ds:si指向字符串的首地址
ddtoc:push dx
push cx
push ax
push si
mov cx,0
push cx ;栈低压0
s1:mov cx,10
call divdw
add cx,30h
push cx ;将余数对应的ASCII值入栈
mov cx,ax
jcxz okk
fhqb:jmp short s1
okk:mov cx,dx
jcxz ok1 ;若商为零则跳转到ok1
jmp fhqb
ok1:pop cx
mov ds:[si],cl ;将数据出栈后存在ds:[si]
jcxz s2 ;到达栈低0值后跳转s2
inc si
jmp short ok1
s2:pop si
pop ax
pop cx
pop dx
ret
;进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型
;参数:(ax)=dword型数据的低16位,(dx)=dword行数据的高16位,(cx)=除数
;返回:(dx)=结果的高16位,(ax)=结果的低16位,(cx)=余数
divdw:push bx
mov bx,ax ;将被除数的低16位送入bx暂存
mov ax,dx ;将被除数的高16位送入ax
mov dx,0
div cx
push ax ;将被除数的高16位除以除数得到的商入栈,这就是最终结果的高16位
mov ax,bx ;将被除数的低16位送回ax
div cx
mov cx,dx ;将被除数的高16位除以除数得到的余数乘以10000h后
;再加被除数的低16位的和,除以除数后得到的余数送入cx
pop dx ;将最终结果的高16位出栈送入dx
pop bx
ret
;在指定位置用指定颜色显示一个用0结束的字符串
;参数:(dh)=行号(取值范围0-24),(dl)=列号(取值范围0-79),
;(cl)=颜色,ds:si指向字符串的首地址
show_str:push ax
push es
push bx
push dx
push cx
push ds
push si
mov al,160
mul dh ;ax中为显示行的首个内存单元的偏移地址
mov dh,0
add dx,dx
add ax,dx ;ax中为显示首个字符的内存单元的偏移地址
mov bx,ax
mov ax,0b800h
mov es,ax ;es中为显示内存区域的段地址
mov ah,cl ;al中为显示字符的属性信息
mov ch,0
s3:mov al,ds:[si]
mov cl,al
jcxz ok2 ;ds:[si]这1字节的内容为0则跳转到ok
mov es:[bx],ax
inc si
add bx,2
jmp s3
ok2:pop si
pop ds
pop cx
pop dx
pop bx
pop es
pop ax
ret
;将word型数据转变为表示十进制的字符串,字符串以0为结尾符
;参数:(ax)=word型数据,ds:si指向字符串的首地址
dtoc:push dx
push bx
push cx
push ax
push si
mov bx,0
push bx ;栈底压零
mov bx,10
s4:mov dx,0
div bx
add dx,30h
push dx ;将余数对应的ASCII码入栈
mov cx,ax
jcxz ok3 ;若商数为零跳转到ok3
jmp short s4
ok3:pop cx
mov ds:[si],cl ;将之前入栈之余数逆序写如ds:[si],最后一字节写入0
jcxz s5 ;到达栈底0后跳转到s5
inc si
jmp short ok3
s5:pop si
pop ax
pop cx
pop bx
pop dx
ret
code ends
end start | | |
|