. : : Assembly Language : : .  |  首页  |  我提出的问题  |  我参与的问题  |  我的收藏  |  消息中心   |  游客  登录  | 
刷新 | 提问 | 未解决 | 已解决 | 精华区 | 搜索 |
  《汇编语言》论坛 ->CALL和RET指令
  管理员: assembly   [回复本贴] [收藏本贴] [管理本贴] [关闭窗口]
主题 : :  史上考虑最周全的实验10-3,也是解释最详细的。  [待解决] 回复[ 12次 ]   点击[ 1006次 ]  
helonsy
[帖 主]   [ 发表时间:2010-07-27 11:40 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2010-07-11 16:57
大家下下来运行下就知道它是最好的了,看看解释就知道它解释的很详细了,再看看代码你就知道它是考虑最周全的了。

如果你觉得对你有益,请回帖,您的回帖将是对我最大的支持。

以下是代码:
;友情提醒:
;这里要提醒各位论坛好友,如果你的程序编译连接都正确,就是运行时出错,请务必认真debug和思考,

;不要动不动就贴到论坛上来问人,不要以为debug是浪费时间,如果你真是一点一滴自己找出来的错误,

;那么这种顿悟会让你永远都不会忘记,你会觉得:纸上得来终觉浅,绝知此事需躬行!
assume cs:code 

data segment
db 10 dup(0)
data ends

stack segment
dw 16 dup(0)
stack ends

code segment
start:        mov ax,0600h;实验前清屏
        mov bh,3ah
        mov cx,0000h
        mov dx,184fh
        int 10h 
        
        mov ax,12666
        mov dx,0;这里是为了以后扩充,因为以后的数可能超过16位,此时调用实验二的除法
        mov bx,data
        mov ds,bx
        mov bx,stack
        mov ss,bx
        mov sp,32
        mov si,0
        call dtoc

        mov dh,8
        mov dl,3
        mov cl,2
        call show_str

        mov ah,0;等待输入
        int 16h

        mov ax,0600h;实验后清屏
        mov bh,3ah
        mov cx,0000h
        mov dx,184fh
        int 10h 

        mov ah,0;等待输入
        int 16h

        mov cx,4c00h
        int 21h

show_str:        push si
                push di
                push bx
                push ax;所用寄存器全部入栈

                mov si,0;这一点非常容易忽视,因为前面赋值时已经将si增长为5了,这里需要重置0

                mov ax,0b800h;把显存的段地址赋给es
                mov es,ax

                mov di,0;变址寄存器赋初值
                
                mov al,0a0h;每行160个字符
                mov ah,0;之前ah=0b8,所以在这里将它赋值为0非常必要,也非常容易被忽视
                mul dh
                mov bx,ax;把行×160的结果存入基址寄存器bx中
                add dl,dl;列×2
                mov dh,0
                add bx,dx;把列导致的位移加入基址寄存器bx中

                mov al,cl;转变一下,因为内部要使用cl,所以讲颜色值赋给dl

   cycle:        mov cl,[si]
                mov ch,0
                jcxz endup
                mov es:[bx][di],cl
                mov byte ptr es:[bx][di+1],al
                inc di
                inc di
                inc si
                jmp cycle

   endup:        pop ax
                pop bx
                pop di
                pop si
                
                ret

dtoc:        push di
        push cx

        mov di,0;因为通过余数来求的话,排列结果刚好倒过来了,所以采用栈,di记录进栈多少个数
        
   s2:        mov cx,10
        call divdw
        add cx,30h;余数加上30h变成对应十进制的ASCII值
        push cx;进栈保留
        inc di;进栈数加1
        mov cx,dx;现在dx存储商的高位,ax存储商的低位
        jcxz s0
        jmp s2;不为0则商不为0,要继续除

   s0:        mov cx,ax;进一步检查低位是否为0
        jcxz s1;如果高位低位都为0,则确定商为0,可以退出本子程序
        jmp s2;不为0则商不为0,要继续除

   s1:        mov cx,di;表示进栈的数,现在用这个数表示循环的次数

   s3:        pop ax
        mov [si],al;对于数字,高8位肯定为0
        inc si
        loop s3

        mov byte ptr[si],0;在最后赋上0

        pop cx
        pop di

        ret

divdw:        push bx
        
        ;首先计算int(H/N)*65535
        push ax;被除数低位入栈
        
        mov ax,dx;用单独的被除数高位dx组成新的被除数
        mov dx,0

        div cx;求int(H/N)*65536的int(H/N)
        push dx;将除得的余数dx入栈
        mov bx,ax;商乘上65536就相当于商的结果放到高16位了
        
        ;其次,求[rem(H/N)*65536+L]/N
        pop dx;将上次除得的余数dx出栈
        pop ax;将被除数的低位出栈
        ;此时的dx和ax分别是即将要被除数的高位和低位了
        div cx;计算整体[rem(H/N)*65536+L]/N
        ;注意,此时dx和ax分别存了后半项的余数和商
        mov cx,dx;将余数赋给cx
        mov dx,bx;将以前保存在bx中的int(H/N)*65536乘积的高位

        pop bx

        ret;返回调用程序

code ends

end start
;这个程序我觉得考虑的非常周全,比论坛上其它人做的周全一点,这里不仅仅是12666,这一个简单的5;位,如果是1000000七位数呢?要知道直接除就益处了,所以在这里就直接用了实验2的结果,还有就是在
;显示之前最好清屏,这样有利于观察,大家要养成这个高习惯。
masmaster
[第1楼]   [ 回复时间:2010-07-27 11:55 ]   [引用]   [回复]   [ top ] 
荣誉值:268
信誉值:12
注册日期:2010-06-18 22:19
感谢楼主~
helonsy
[第2楼]   [ 回复时间:2010-07-27 13:25 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2010-07-11 16:57
你的回复就是对我最大的支持!
sleepinglion
[第3楼]   [ 回复时间:2010-08-05 00:19 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2010-07-13 23:22
你要知道,书上给你的代码没有定义栈。你并没有根据书本要求来做。如果自己定义一个栈的话,这题就非常好做了。
jonytan
[第4楼]   [ 回复时间:2010-08-05 08:40 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2010-05-04 17:53
辛苦了!我现在还在徘徊呢?不知咋下手!555
chenchaosx
[第5楼]   [ 回复时间:2010-12-13 00:11 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2010-12-04 20:54
在网吧,下回去看
xueyugaoyuan
[第6楼]   [ 回复时间:2012-09-14 09:34 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2012-08-14 17:57
好好看看
chaoc
[第7楼]   [ 回复时间:2012-10-24 16:23 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:2
注册日期:2012-10-18 15:41
楼主用了后面学到的知识啊。按部就班应该还没学到终端吧。
835984286
[第8楼]   [ 回复时间:2012-10-26 04:12 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2011-08-12 01:04
的确是好!
一看就懂!
但是我觉得dtoc子函数不应该调用divdw子函数,如果直接按照16位的除数的除法不是更好!
fpamc
[第9楼]   [ 回复时间:2012-10-26 09:22 ]   [引用]   [回复]   [ top ] 
荣誉值:30
信誉值:4
注册日期:2012-01-01 16:36
回复:[第8楼]
------------------
面对大数据要防止除法溢出。
mengdana
[第10楼]   [ 回复时间:2013-01-01 20:04 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2013-01-01 17:35
正确设置的堆栈段应该不需要程序里写初始化代码,EXE头文件里已经写好相关数据,操作系统装入EXE文件时已经按其头文件的相关数据自动装配好SS和SP,同时从初始化从相应的CS:IP开始执行(如果是DEBUG装入的则不执行但CS、IP也装配好了)

堆栈段的定义

stack segment stack 'stack'
DW 256 DUP(0xFFFFH)
stack ends

这样的堆栈段根本不需要程序里写初始化代码,编译程序会算好并写入文件头,程序载入时会自动初始化装配好的。
hkcmd
[第11楼]   [ 回复时间:2014-11-25 01:18 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2014-11-19 10:35
我比较赞同第8楼的说法,用16位作为除数。即使是很大的数据,可以先处理下,再交给dtoc子函数转换。
xexexan
[第12楼]   [ 回复时间:2021-05-11 17:28 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2016-03-24 13:33
学习了,新手,学了没多久,很多不是太会。多谢分享。
需要登录后才能回帖 -->> 请单击此处登录
    Copyright © 2006-2024   ASMEDU.NET  All Rights Reserved