荣誉值: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 是调试成功的! | | |