找回密码
 register

QQ登录

只需一步,快速开始

查看: 31|回复: 0

[汇编语言] [零基础入门学习]·28·[BX]和loop指令·一段安全的空间

[复制链接]

[汇编语言] [零基础入门学习]·28·[BX]和loop指令·一段安全的空间

[复制链接]
  • 打卡等级:热心大叔
  • 打卡总天数:91
  • 打卡月天数:14
  • 打卡总奖励:91
  • 最近打卡:2025-01-15 02:59:34
Waylee

主题

0

回帖

1万

积分

仙帝

积分
11890
Waylee 2025-1-5 11:56 | 显示全部楼层 |阅读模式 | Google Chrome | Windows 10

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?register

×


5.7 一段安全的空间

在8086模式中,随意向一段内存空间写入内容是很危险的,因为这段空间中可能存放着重要的系统数据或代码。比如以下指令:

mov ax,1000h
mov ds,ax
mov al,0
mov ds:[0],al

我们以前在Debug中,为了讲解上的方便,写过类似的指令。但这种做法是不合理的,因为之前我们并没有论证过1000:0中是否存放着重要的系统数据或代码。如果1000:0中存放着重要的数据或代码,“mov ds:[0],al”将其改写,将引发错误。

比如下面的程序。

程序5.7

assume cs:code
code segment

    mov ax,0
    mov ds,ax,
    mov ds:[26],ax

    mov ax,4c00h
    int 21h

code ends
end

将源程序编辑位p7.asm,编译、连接后生成p7.exe,用Debug加载,跟踪它的运行,如图5.18所示。

5.18.webp

图5.18中,我们可以看到,源程序中的“mov ds:[26],ax”被masm翻译为机器码“a3 26 00”,而Debug将这个机器码解释为“mov [0026],ax”。可见,汇编源程序中的汇编指令“mov ds:[26h],ax”和Debug中的汇编指令“mov [0026],ax”同义。

我们看一下“mov [0026],ax”的执行结果,如图5.19所示。

图5.19中,是在Windows 2000的DOS方式下,在Debug里指向“mov [0026],ax”的结果。如果在实模式(即纯DOS方式)下执行程序p7.exe,将引起死机。产生这种结果的原因是0:0026存放着重要的系统数据,而“mov [0026],ax”将其改写。

5.19.webp

可见,在不能确定一段内存空间中是否存放着重要的数据或代码的时候,不能随意向其中写入内容。
不要忘记,我们是在操作系统的环境中运行,操作系统管理所有的资源,也包括内存。如果我们需要向内存空间写入数据的话,要使用操作系统给我们分配的空间,而不应直接用地址任意指令内存单元,向里面写入。下一章我们会对“使用操作系统给我们分配的空间”有所认识。

但是,同样不能忘记,我们正在学习的是汇编语言,要通过它来获得底层的编程体验,理解计算机底层的基本工作机理。所以我们劲量直接对硬件编程,而不去理会操作系统。

我们似乎面临一种选择,是在操作系统中安全、规矩地编程,还是自由、直接地用汇编语言去操作真实的硬件,了解那些早已被层层系统软件覆盖的真相?在大部分的情况下,我们选择后者,除非我们就是在学习操作系统本身的内容。

注意,我们在纯DOS方式(实模式)下,可以不理会DOS,直接用汇编语言去操作真实的硬件,因为运行在CPU实模式下的DOS,没有能力对硬件系统进行全面、严格的管理。但在Windows 2000 、Unix这些运行于CPU保护模式下的操作系统中,不理会操作系统,用汇编语言去操作真实的硬件,是根本不可能的。硬件已被这些操作系统利用CPU保护模式所提供的功能全面而严格地管理了。

在后面的课程中,我们需要直接向内存中写入内容,可我们又不希望发生图5.19中的那种情况。所以要找到一段安全的空间供我们使用。在一般的PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用0:200~0:2ff(00200h~002ffh)的256个字节的空间。所以,我们使用这段空间是安全的。不过为了谨慎起见,在进入DOS后,我们可以先用Debug查看一下,如果0:200~0:2ff单元的内容都是0的话,则证明DOS和其他合法的程序没有使用这里。

为什么DOS和其他合法的程序一般都不会使用0:200~0:2ff这段空间?我们将在以后的课程中讨论这个问题。

好了,我们总结一下:

  1. 我们需要直接向内存中写入内容;
  2. 这段内存空间不应存放系统或其他程序的数据或代码,否则写入操作很可能引发错误;
  3. DOS方式下,一般情况,0:200~0:2ff空间中没有系统和其他程序的数据或代码;
  4. 以后,我们需要直接向一段内存中写入内容时,就使用0:200~0:2ff这段空间。

5.8 段前缀的使用

我们考虑一个问题,将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中。

分析一下。

(1)0:200~0:2ff单元等同于0020:0~0020:b单元,它们描述的是同一段内存空间。
(2)复制的过程应用循环实现,简要描述如下。

初始化
X=0
循环12次
将ffff:X单元中的数据送入0020:X(需要用一个寄存器中转)
X=X+1
(3)在循环中,源始单元ffff:X和目标单元0020:X的偏移地址X是变量。我们用bx来存放。
(4)将0:200~0:20b用0020:0~0020:b来描述,就是为了使目标单元的偏移地址和源始单元的偏移地址从同一数值0吗开始。

程序如下。

程序5.8

assume cs:code

code segment

    mov bx,0        ;(bx)=0,偏移地址从0开始
    mov cx,12       ;(cx)=12,循环12次

s:  mov ax,0ffffh
    mov ds,ax       ;(ds)=0ffffh
    mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl

    mov ax,0020h
    mov ds,ax       ;(ds)=0020h
    mov [bx],dl ;((ds)*16+(bx))=(dl),将dl的数据送入0020:bx

    inc bx      ;(bx)=(bx)+1
    loop s

    mov ax,4c00h
    int 21h

code ends

end

因源始单元ffff:X和目标单元0020:X相距大于64KB,在不同的64KB段里,程序5.8中,每次循环要设置两次ds。这样做是正确的,但是效果不高。我们可以使用两个段寄存器分别存放源始单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。

改进的程序如下。

程序5.9

assume cs:code

code segment
    mov ax,0ffffh
    mov ds,ax       ;(ds)=0ffffh

    mov ax,0020h
    mov es,ax       ;(es)==0020h

    mov bx,0        ;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0

    mov cx,12       ;(cx)=12,循环12次

s:  mov ds,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
    mov es:[bx],dl  ;((es)*16+(bx))=(dl),将dl中的数据送入0020:bx
    inc bx      ;(bx)=(bx)+1
    loop s

    mov ax,4c00h
    int 21h

code ends

end

程序5.9中,使用es存放目标空间 0020:0~0020:b的段地址,用ds存放源始空间ffff:0~ffff:b的段地址。在访问内存单元的指令“mov es:[bx],al”中,显式地用段前缀“es:”给出单元的段地址,这样就不必在循环中重复设置ds。

您需要登录后才可以回帖 登录 | register

本版积分规则

雪舞知识库 | 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 |天天打卡

GMT+8, 2025-1-15 13:22 , Processed in 0.104385 second(s), 8 queries , Redis On.

Powered by XueWu Licensed

Copyright © Tencent Cloud.

快速回复 返回顶部 返回列表