《汇编语言》实验10,dtoc子程序在编译连接都正常的情况下弄崩了虚拟机

系统为WINDOS10系统,运行环境有两个:vscode上的DOSBOX扩展和的vm虚拟机的MS-DOS系统

平时就在vscode上写,发现dosbox环境上程序运行出不来END后放到虚拟机上想知道是什么原因,结果程序直接被弄崩了。

第3题的数值显示,除了dtoc函数其他都正常,希望有朋友帮我看看是怎么回事。

《汇编语言》实验10,dtoc子程序在编译连接都正常的情况下弄崩了虚拟机

assume cs:code

data segment

DB 10 DUP(0)

data ends

code segment

start:

mov ax,DATA ;用ds:si指向字符串

mov ds,ax

mov ax,0B800H ;用es:bx+di指向显存

mov es,ax

mov bx,0

mov di,0

mov si,0

mov dx,000FH

mov ax,4240H

call dtoc

; mov dh,8 ;传入参数并调用函数

; mov dl,3

; mov cl,2

; call show_str

mov ax,4c00h

int 21h

DIVDW: ;函数介绍:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型,参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位),CX(除数,WORD型)

PUSH AX

MOV AX,DX ;先计算高16位

MOV DX,0

DIV CX

MOV BX,AX ;暂时用BX存放结果的高16位,这时DX要存放运算数

POP AX

DIV CX ;计算出结果低16位

MOV CX,DX ;CX存放余数

MOV DX,BX ;DX存放结果高16位

MOV BX,0

RET

DTOC: ;函数介绍:将DWORD型数据转化为十进制字符串放入DS中(从DS:0遍历到DS:X == 0结尾),参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位)

MOV CX,10

CALL DIVDW

ADD CX,30H

MOV WORD PTR DS:[SI],CX

PUSH DS:[SI]

INC SI

MOV CX,AX

JCXZ DTOC_END

JMP DTOC

DTOC_END:

MOV SI,0

DATA_REVERSE: ;当栈不为空(持续弹出到只剩下IP)时,将栈中各值顺序打入DS

POP DS:[SI]

INC SI

MOV BP,SP

MOV BX,SS:[BP]

MOV CX,BX

JCXZ DR_END

JMP DATA_REVERSE

DR_END:

MOV BX,DS:[SI-1] ;用BX存取RET用的IP

MOV CX,0

MOV [SI-1],CX

PUSH BX

MOV SI,0

RET

SHOW_STR: ;函数介绍:将DS中的字符串数据(从DS:0遍历到DS:X == 0结尾)显示在屏幕上,参数为dh(行号),dl(列号),cl(颜色)

push dx ;预处理子程序中要用的参数

push cx

mov ax,00a0h ;根据行和列计算在显存中相应的偏移地址,存入bx中

mul dh

sub ax,00a0h

mov bx,0

mov bl,dl

add bx,bx

sub bx,2

add ax,bx

mov bx,ax

mov ah,cl

mov al,ds:[si]

mov es:[bx + di],ax ;用ax装字符和属性,并经过ES写入显存中

inc si ;指向下一个字符

add di,2

mov cl,ds:[si] ;指向下一个字符后,检查是否到底(即是否为0)

mov ch,0

jcxz show_str_END

pop cx

jmp show_str

show_str_END:pop cx ;还原子程序中用到的参数

pop dx

mov si,0

mov di,0

mov bx,0

mov sp,0FFFEH

ret

code ends

end start

回答

开始研究

检查修改了下自己的程序,发现

assume cs:code 

data segment

DB 10 DUP(0)

data ends

code segment

start:

mov bx,DATA ;用ds:si指向字符串

mov ds,bx

; mov ax,0B800H ;用es:bx+di指向显存

; mov es,ax

mov dx,000FH

mov ax,4240H

call dtoc

mov ax,4c00h

int 21h

DTOC: ;函数介绍:将DWORD型数据转化为十进制字符串放入DS中(从DS:0遍历到DS:X == 0结尾),参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位)

MOV CX,10

CALL DIVDW

ADD CX,30H

MOV WORD PTR DS:[SI],CX

PUSH DS:[SI]

INC SI

MOV CX,AX

JCXZ DTOC_END

JMP DTOC

DTOC_END:

MOV SI,0

DATA_REVERSE: ;当栈不为空(持续弹出到只剩下IP)时,将栈中各值顺序打入DS

POP DS:[SI]

INC SI

MOV BP,SP

MOV BX,SS:[BP]

MOV CX,BX

JCXZ DR_END

JMP DATA_REVERSE

DR_END:

MOV BX,DS:[SI-1] ;用BX存取RET用的IP

MOV CX,0

MOV [SI-1],CX

PUSH BX

MOV SI,0

RET

DIVDW: ;函数介绍:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型,参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位),CX(除数,WORD型)

PUSH AX

MOV AX,DX ;先计算高16位

MOV DX,0

DIV CX

MOV BX,AX ;暂时用BX存放结果的高16位,这时DX要存放运算数

POP AX

DIV CX ;计算出结果低16位

MOV CX,DX ;CX存放余数

MOV DX,BX ;DX存放结果高16位

MOV BX,0

RET

SHOW_STR: ;函数介绍:将DS中的字符串数据(从DS:0遍历到DS:X == 0结尾)显示在屏幕上,参数为dh(行号),dl(列号),cl(颜色)

push dx ;预处理子程序中要用的参数

push cx

mov ax,00a0h ;根据行和列计算在显存中相应的偏移地址,存入bx中

mul dh

sub ax,00a0h

mov bx,0

mov bl,dl

add bx,bx

sub bx,2

add ax,bx

mov bx,ax

mov ah,cl

mov al,ds:[si]

mov es:[bx + di],ax ;用ax装字符和属性,并经过ES写入显存中

inc si ;指向下一个字符

add di,2

mov cl,ds:[si] ;指向下一个字符后,检查是否到底(即是否为0)

mov ch,0

jcxz show_str_END

pop cx

jmp show_str

show_str_END:pop cx ;还原子程序中用到的参数

pop dx

mov si,0

mov di,0

mov bx,0

mov sp,0FFFEH

ret

code ends

end start

 ;  mov  ax,0B800H      	;用es:bx+di指向显存

; mov es,ax

将这两个指令注释掉,程序在DOSBOX中能正常出END,放进虚拟机的MS-DOS系统不会弄蹦系统了,但运行仍然会卡住,原因未知,由于主要是用DOSBOX,就不深究虚拟机了,下面主要看DOSBOX的情况。

我只是操作寄存器指向了某一内存,并没有修改内存,为什么程序会卡住呢?

这两段代码主要是给后面的show_str程序使用的,检查该程序,发现只要代码中设计栈,别说pop,push,就算只mov了个sp,程序都出不了END。

开始拆分问题,先将dtoc和show_str分为两个单独的文件

分文件讨论过程

show_str.asm

show_str.asm,运行该文件代码,发现不出END。

assume cs:code 

data segment

db 'WELCOME TO MASM!',0

data ends

code segment

start:

mov ax,data ;用ds:si指向字符串

mov ds,ax

mov ax,0B800H ;用es:bx+di指向显存

mov es,ax

mov bx,0

mov di,0

mov si,0

mov dh,8 ;传入参数并调用函数

mov dl,3

mov cl,2

call show_str

mov ax,4c00h

int 21h

show_str:

push dx ;预处理子程序中要用的参数

push cx

mov ax,00a0h ;根据行和列计算在显存中相应的偏移地址,存入bx中

mul dh

sub ax,00a0h

mov bx,0

mov bl,dl

add bx,bx

sub bx,2

add ax,bx

mov bx,ax

mov ah,cl

mov al,ds:[si]

mov es:[bx + di],ax ;用ax装字符和属性,并经过ES写入显存中

inc si ;指向下一个字符

add di,2

mov cl,ds:[si] ;指向下一个字符后,检查是否到底(即是否为0)

mov ch,0

jcxz ok

pop cx

jmp show_str

ok:pop cx ;还原子程序中用到的参数

pop dx

mov si,0

mov di,0

mov bx,0

ret

code ends

end start

在show_str.asm中重写show_str:将代码中涉及栈的语句从:

                 mov  ah,cl

mov al,ds:[si]

mov es:[bx + di],ax ;用ax装字符和属性,并经过ES写入显存中

inc si ;指向下一个字符

add di,2

mov cl,ds:[si] ;指向下一个字符后,检查是否到底(即是否为0)

mov ch,0

jcxz show_str_END

pop cx

jmp show_str

show_str_END:pop cx ;还原子程序中用到的参数

pop dx

mov si,0

mov di,0

mov bx,0

mov sp,0FFFEH

ret

改为:

show_str:

mov bx,0B800H ;用es:bx+di指向显存

mov es,bx

mov ax,00a0h ;根据行和列计算在显存中相应的偏移地址,存入bx中

mul dh

sub ax,00a0h

mov bx,0

mov bl,dl

add bx,bx

sub bx,2

add ax,bx

mov bx,ax

show_str_start:

mov ah,cl

mov al,ds:[si]

mov es:[bx + di],ax ;用ax装字符和属性,并经过ES写入显存中

inc si ;指向下一个字符

add di,2

mov [bp],cx ;等效替代push cx

mov cl,ds:[si] ;指向下一个字符后,检查是否到底(即是否为0)

mov ch,0

jcxz ok

mov cx,[bp] ;等效替代pop cx

jmp show_str_start

show_str_END: ;还原子程序中用到的参数

mov si,0

mov di,0

mov bx,0

ret

DOSBOX中能正常出END,仅仅是换了个代码,为什么编译器会看push和pop不顺眼?

dtoc.asm

但是dtoc.asm里也有push和pop,怎么运行能正常出END?

ASSUME CS:CODE

DATA SEGMENT

DB 10 DUP(0)

DATA ENDS

CODE SEGMENT

START:

mov dx,0000H

mov ax,12666

call dtoc

MOV AX,4C00H

INT 21H

DTOC: ;函数介绍:将DWORD型数据转化为十进制字符串放入DS中(从DS:0遍历到DS:X == 0结尾),参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位)

MOV CX,10

CALL DIVDW

ADD CX,30H

MOV WORD PTR DS:[SI],CX

PUSH DS:[SI]

INC SI

MOV CX,AX

JCXZ DTOC_END

JMP DTOC

DTOC_END:

MOV SI,0

DATA_REVERSE: ;当栈不为空(持续弹出到只剩下IP)时,将栈中各值顺序打入DS

POP DS:[SI]

INC SI

MOV BP,SP

MOV BX,SS:[BP]

MOV CX,BX

JCXZ DR_END

JMP DATA_REVERSE

DR_END:

MOV BX,DS:[SI-1] ;用BX存取RET用的IP

MOV CX,0

MOV [SI-1],CX ;这里发现虚拟机DOS系统不允许直接向内存赋值,就改成寄存器了

PUSH BX

MOV SI,0

RET

DIVDW: ;函数介绍:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型,参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位),CX(除数,WORD型)

PUSH AX

MOV AX,DX ;先计算高16位

MOV DX,0

DIV CX

MOV BX,AX ;暂时用BX存放结果的高16位,这时DX要存放运算数

POP AX

DIV CX ;计算出结果低16位

MOV CX,DX ;CX存放余数

MOV DX,BX ;DX存放结果高16位

MOV BX,0

RET

CODE ENDS

END START

合并讨论:problem.asm

尝试将这两个程序重新合在一起,作problem.asm:

assume cs:code 

data segment

DB 10 DUP(0)

data ends

code segment

start:

mov ax,DATA ;用ds:si指向字符串

mov ds,ax

mov dx,0000H

mov ax,12666

call dtoc

mov dh,13 ;传入参数并调用函数

mov dl,40

mov cl,2

call show_str

MOV AX,4C00H

INT 21H

DTOC: ;函数介绍:将DWORD型数据转化为十进制字符串放入DS中(从DS:0遍历到DS:X == 0结尾),参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位)

MOV CX,10

CALL DIVDW

ADD CX,30H

MOV WORD PTR DS:[SI],CX

PUSH DS:[SI]

INC SI

MOV CX,AX

JCXZ DTOC_END

JMP DTOC

DTOC_END:

MOV SI,0

DATA_REVERSE: ;当栈不为空(持续弹出到只剩下IP)时,将栈中各值顺序打入DS

POP DS:[SI]

INC SI

MOV BP,SP

MOV BX,SS:[BP]

MOV CX,BX

JCXZ DR_END

JMP DATA_REVERSE

DR_END:

MOV BX,DS:[SI-1] ;用BX存取RET用的IP

MOV CX,0

MOV [SI-1],CX ;这里发现虚拟机DOS系统不允许直接向内存赋值,就改成寄存器了

PUSH BX

MOV SI,0

RET

DIVDW: ;函数介绍:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型,参数为DX(DWORD型数据高16位),AX(DWORD型数据低16位),CX(除数,WORD型)

PUSH AX

MOV AX,DX ;先计算高16位

MOV DX,0

DIV CX

MOV BX,AX ;暂时用BX存放结果的高16位,这时DX要存放运算数

POP AX

DIV CX ;计算出结果低16位

MOV CX,DX ;CX存放余数

MOV DX,BX ;DX存放结果高16位

MOV BX,0

RET

show_str:

mov bx,0B800H ;用es:bx+di指向显存

mov es,bx

mov ax,00a0h ;根据行和列计算在显存中相应的偏移地址,存入bx中

mul dh

sub ax,00a0h

mov bx,0

mov bl,dl

add bx,bx

sub bx,2

add ax,bx

mov bx,ax

show_str_start:

mov ah,cl

mov al,ds:[si]

mov es:[bx + di],ax ;用ax装字符和属性,并经过ES写入显存中

inc si ;指向下一个字符

add di,2

mov [bp],cx ;等效替代push cx

mov cl,ds:[si] ;指向下一个字符后,检查是否到底(即是否为0)

mov ch,0

jcxz show_str_end

mov cx,[bp] ;等效替代pop cx

jmp show_str_start

show_str_end: ;还原子程序中用到的参数

mov si,0

mov di,0

mov bx,0

ret

code ends

end start

初次运行发现不出END,对这个程序再做了一些探究,发现只要把

mov  ax,DATA       	;用ds:si指向字符串

mov ds,ax

注释掉后,程序正常出END,且正常显示了子程序对参数的处理结果:

也就是不让ds指向data就行了,跟编译器用不了push pop什么没关系,疑似是程序不允许往已经声明好的数据段内赋值,不知道为什么。

现在才发现,课本实验题的子程序程序描述上有把ds指向data的语句,会导致卡程序,最后发现自己写的程序并用不着声明data给ds,自己就这么被课本坑了几个小时,真是无语。

《汇编语言》实验10,dtoc子程序在编译连接都正常的情况下弄崩了虚拟机

以上是 《汇编语言》实验10,dtoc子程序在编译连接都正常的情况下弄崩了虚拟机 的全部内容, 来源链接: utcz.com/a/107618.html

回到顶部