Skip to content

Latest commit

 

History

History
368 lines (277 loc) · 9.5 KB

07-更灵活的定位内存地址的方法.md

File metadata and controls

368 lines (277 loc) · 9.5 KB

更灵活的定位内存地址的方法

and和or指令

  1. and指令: 逻辑与指令, 按位进行与运算
mov al,01100011B
and al,00111011B

执行后: al = 0010011B

  1. or指令: 逻辑或指令, 按位进行或运算
mov al,01100011B
 or al,00111011B

执行后: al=01111011B

关于ASCII码

编码方案,就是一套规则, 约定了用什么样的信息来表示现实对象. 一个文本编辑过程中,就包含着按照ASCII编码规则进行的编码和解码.

在文本编辑过程中, 按下键盘的a键,就会在屏幕上看到a,这是怎样一个过程:

  1. 按下a键, 这个按键信息被送入计算机, 计算机用ASCII码的规则对齐进行编码, 将其转化为61h存储在内存空间中
  2. 文本编辑器软件从内存中取出61h,将其送到显卡上的显存中
  3. 工作在文本模式下的显卡, 用ASCII码的规则解释显存中的内容,61h被当做字符a,显卡驱动显示器,将字符a的图像绘制到屏幕上.

以字符形式给出的数据

在汇编程序中,用'...'单引号的方式指明数据是以字符的形式给出的, 编译器将它们转化为相对应的 ASCII码

如:

  assume cs:code,ds:data
    data segment
      db 'unIX'           ;相当于`db 75h,6eh,49h,58h`
      db 'foRK'           ;相当于`db 66h,6fh,52h,4bh`
    data ends
    code segment
      start: mov al,'a'   ;相当于`mov,al,61h`
             mov bl,'b'   ;相当于`mov,al,62h`
             mov ac,4c00h
             int 21h
    code ends
  end start

大小写转换问题

codesg中填写代码,将datasg中的第一个字符串转换为大写,第二个字符串转化为小写

assume cs:codesg,ds:datasg
datasg segment
  db 'BaSiC'
  db 'iNfOrMaTiOn'
datasg ends
codesg segment

  start: mov ax,datasg
         mov ds,ax          ;设置ds执行datasg段
         mov bx,0           ;设置[bx]=0,ds:bx指向'BaSiC'的第一个字母
         mov cx,5           ;设置循环次数为5,因为'BaSiC'有5个字母
      s: mov al,[bx]        ;将ASCII码从ds:bx所指向的单元中取出
         and al,11011111b   ;将al中的ASCII码的第5位设置为0,变成大写字母
         mov [bx],al        ;将转变后的ASCII码写回原单元
         inc bx             ;[bx]加1,ds:bx指向下一个字母
         loop s

         mov bx,5           ;设置[bx]=5,ds:bx指向'iNfOrMaTiOn'第一个字母
         mov cx,11          ;循环次数设置为11
      s0:mov al,[bx]
         or al,00100000b    ;将al中的ASCII码的第5位设置为1,变为小写字母
         mov [bx],al
         inc bx
         loop s0


         mov ax,4c00h
         int 21h

codesg ends
end

[bx+idata]

[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata(bx中的数值加上idata)

mov ax,[bx+200]含义:

将一个内存单元的内容送入ax,这个内存单元的长度为2个字节(子单元),存放一个字,偏移地址为bx中的数值加上200,段地址在ds中

(ax)=((ds)*16+(bx)+200)

也可以写成如下格式:

  1. mov ax,[200+bx]
  2. mov ax,200[bx]
  3. mov ax,[bx].200

用[bx+idata]的方式进行数组的处理

codesg中填写代码,将datasg中的第一个字符串转换为大写,第二个字符串转化为小写

assume cs:codesg,ds:datasg
datasg segment
  db 'BaSiC'
  db 'MinIX'
datasg ends
codesg segment

  start: mov ax,datasg
         mov ds,ax          ;设置ds执行datasg段
         mov bx,0           ;设置[bx]=0,ds:bx指向'BaSiC'的第一个字母
         mov cx,5           ;设置循环次数为5,因为'BaSiC'有5个字母

      s: mov al,[bx]        ;将ASCII码从ds:bx所指向的单元中取出
         and al,11011111b   ;将al中的ASCII码的第5位设置为0,变成大写字母
         mov [bx],al        ;将转变后的ASCII码写回原单元
         mov al,[5+bx]      ;定位下一个字符串中的字符 也可以表示为 mov al,5[bx]
         or  al,00100000b
         mov [5+bx],al      ;也可以表示为 mov 5[bx],al
         inc bx             ;[bx]加1,ds:bx指向下一个字母
         loop s

         mov ax,4c00h
         int 21h

codesg ends
end

用c语言来表示该程序

char a[5]="BaSiC";
char b[5]="MinIX";
main()
{
  int i;
  i=0;
  do
  {
    a[i]=a[i]&0xdf;
    b[i]=b[i]|0x20;
    i++;
  }while(i<5);
}

对于定位字符串中�字符的方式

c语言: a[i],b[i] 汇编语言: 0[bx],5[bx]

SI和DI

si和di是8086CPU中和bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用.下面的3组指令实现了相同的功能.

mov bx,0
mov ax,[bx]
mov ax,[bx+123]
mov si,0
mov ax,[si]
mov ax,[si+123]
mov di,0
mov ax,[di]
mov ax,[di+123]

用si和di实现将字符串welcome to masm!复制到它后面的数据区中

assume cs:codesg,ds:datasg

datasg segment
  db  'welcome to masm!'
  db  '................'
datasg ends

codesg segment
  mov ax,datasg
  mov ds,ax
  mov si,0
  mov cx,8

s:mov ax,0[si]
  mov 16[si],ax
  add si,2
  loop s
  mov ax, 4c00h
  int 21h
codesg ends

[bx+si]和[bx+di]

[bx+si]和[bx+di]表示一个内存单元, 它的偏移地址为(bx)+(si)(即bx中的数值加上si中的数值)

mov ax,[bx+si]含义: 将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为bx中的数值加上si中的数值,段地址在ds中. (ax)=((ds)*16 + (bx) + (si))

[bx+si+idata]和[bx+di+idata]

[bx+si+idata]和[bx+di+idata]表示一个内存单元, 偏移地址为(bx)+(si)+idata

mov ax,[bx+si+idata]含义: 将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为bx中的数值加上si中的数值再加上idata,段地址在ds中. (ax)=((ds)*16+(si)+idata)

该指令也可以写成如下格式:

mov ax,[bx+200+si]
mov ax,[200+bx+si]
mov ax,200[bx][si]
mov ax,[bx].200[si]
mov ax,[bx][si].200

不同寻址方式的灵活应用

前面用到的几种定位内存地址的方法,可称为寻址方式

  1. [idata]用一个常量来表示地址,可用于直接定位一个内存单元
  2. [bx]用一个变量来表示内存地址,可用于间接定位一个内存单元
  3. [bx+idata]用一个变量和常量表示地址, 可在一个起始地址的基础上用变量间接定位一个内存单元
  4. [bx+si]用两个变量表示地址
  5. [bx+si+idata]用两个变量和一个常量表示地址

[idata]一直到[bx+si+idata],我们可以用更加灵活的方式来定位一个内存单元的地址.这使我们可以从更加结构化的角度来看待所要处理的数据

编程7.6

datasg段中每个单词的头一个字母改为大写字母

assume cs:codesg,ds:datasg

datasg segment

  db '1. file         '
  db '2. edit         '
  db '3. search       '
  db '4. view         '
  db '5. options      '
  db '6. help         '

datasg ends

codesg segment

  start: mov ax,datasg
         mov ds,ax
         mov bx,0
         mov cx,6             ;设置循环次数为6

     s:  mov al,[bx+3]        ;将首字母送入寄存器al
         and al,11011111b     ;与运算,将al中的数据转化成大写字母
         mov [bx+3],al
         add bx,16
         loop s

         mov ax,4c00h
         int 21h

codesg ends

end start

编程7.7

将datasg段中每个单词改为大写字母

思路: 双重循环, 逐个转换

assume cs:codesg,ds:datasg

datasg segment
  db 'ibm             '
  db 'dec             '
  db 'dos             '
  db 'vax             '
  dw 0                     ;定义一个字,用来暂存cx
datasg ends

codesg segment
  start: mov ax,datasg
         mov ds,ax
         
         mov bx,0
         mov cx,4           ;外层循环次数为4
         
    s0:  mov ds:[40h],cx    ;将外层循环cx值保存在datasg:40h单元中

         mov di,0           ;设置字符偏移初始值为0
         mov cx,3           ;内层循环次数为3

    s1:  mov al,[bx+di]
         and al,11011111b
         mov [bx+di],al
         inc di
         loop s1

         mov cx,ds:[40h]    ;内层循环完成之后, 还原cx的值
         and bx,16          ;将bx指向下一个字符串
         loop s0            ;继续执行外层循环
         
         mov ax,4c00h
         int 21h

codesg ends

end start

上面的程序, 用内层来保存数据, 但是做法还需要改进, 因为如果需要保存多个数据的时候, 上述程序需要记住数据放到了哪个单元中, 这样程序容易混乱. 一般来说, 在需要暂存数据的时候, 我们都应该使用栈

assume cs:codesg,ds:datasg,ss:stacksg

datasg segment
  db 'ibm             '
  db 'dec             '
  db 'dos             '
  db 'vax             '
datasg ends

stacksg segmennt            ;定义一个段,用来做栈段,容量为16个字节
  dw 0,0,0,0,0,0,0,0
stacksg ends
codesg segment
  start: mov as,stacksg
         mov ss,ax
         mov sp,16
         mov ax,datasg
         mov ds,ax
         
         mov bx,0
         mov cx,4           ;外层循环次数为4
         
    s0:  push cx            ;将外层循环cx的值压栈

         mov di,0           ;设置字符偏移初始值为0
         mov cx,3           ;内层循环次数为3

    s1:  mov al,[bx+di]
         and al,11011111b
         mov [bx+di],al
         inc di
         loop s1
         and bx,16          ;将bx指向下一个字符串
         pop cx             ;从栈顶弹出原cx的值,恢复cx

         loop s0            ;外层循环的loop指令将cx中的计数值减一
         
         mov ax,4c00h
         int 21h

codesg ends

end start