是否可以使用bx寄存器来保存索引?

Joe*_*Joe 3 assembly

我在汇编过程中有一个任务来计算字符串中的单词数,我需要将我的答案保存在cx寄存器中.(我正在使用80x86处理器)

所以我设定:

cx为0 - 这将是我的计数器

bx为0 - 这将是我的索引

我想知道我是否正确使用它,这是我的代码:

.model small 
.stack 100h
.data

A db '  this   is  a  test  $'

B db 100 dup('$')

.code
    mov ax, @data
    mov ds, ax

    mov cx, 0 
    mov bx, 0

looping:
    cmp A[bx], ' '
    jne foundchar
    inc bx
    jmp looping


foundchar:
    inc bx
    cmp A[bx], ' '
    je foundword
    cmp A[bx], '$'
    je soff
    jmp foundchar



foundword:
    inc cx
    inc bx
    jmp looping

soff:
    .exit

end
Run Code Online (Sandbox Code Playgroud)

而我班上的其他人也采用了不同的方式,她将si设置为A的偏移...我并不真正理解这个解决方案:

mov cx,0
mov si, offset A

mov dl,0


next2:
    inc si
    mov dl,[si]
    cmp dl,"$"
    JE soff
    cmp dl, " "
    JE test1 ;if the char is space so lets check what is before the char
    jmp next2 ; if the char no space jump back to the loop

test1:
mov al,[si-1]
cmp al, ' '
je  next2
add cx,1
jmp next2

soff:
mov al,[si-1]
cmp al,' '
je sofsofi
add cx,1

sofsofi:

.exit
end 
Run Code Online (Sandbox Code Playgroud)

请帮助我理解它,以及更合适的方法.

谢谢分配

PTw*_*Twr 5

回答标题中的问题:不可以.使用BX作为索引是不可行的.使用目标索引或源索引(取决于您是否写入或读取数据).使用BX会起作用,但它不被认为是"最佳实践",因为DI和SI的目的是保存索引.

现在的解释是:

尽管(x86)寄存器已命名,但您可以(当然有某些限制)将它们用于与其名称冲突的任务,但它可能导致错误和解释代码时出现问题.

我将尝试在这里写一个简单的初学者注册指南.

限制:

  • 一些操作从/向某些寄存器读取和写入数据,例如.MUL使用(E)AX和(E)DX,因此您必须确保不将您的程序仍需要的数据(或PUSH及以后的POP)存储在将被修改的寄存器中.
  • 并非每个寄存器都可以(直接)访问8位部分.
  • 有些寄存器具有很酷的专用功能,所以为了不妥协它们,你需要正确使用它们.

请记住,下面列出的所有寄存器都可以通过在名称中添加E来以扩展(32位)形式使用(例如,EAX是32位版本的累加器,其中低16位包含16位AX).寄存器将按重要性顺序列出(从初学者的角度).

  1. AX - 累加器寄存器通常用作主要参数和结果.在更简单的日子里,仍然需要了解x86处理器通常有两个参数寄存器,例如.Commodore 64上的X和Y,以及存储结果的累加器.简而言之,它的名称现在主要是历史性的,它通常是第一个用于几乎所有内容的寄存器 - 例如算术/逻辑运算和DOS(21h)中断,它们通过AH(AX的高字节)接受参数.除了需要通过AX传递值的操作之外,您可以将其视为主要变量.
  2. DX - 数据寄存器 - 这里描述的并不多,它存储数据.我决定在AX之后列出它,因为这两个经常在一起工作 - 例如MUL(乘法)使用DX来存储从AX溢出的数据.所以是的,它的"正义"次要变量.
  3. CX - 计数器寄存器 - 也很简单,它只是另一个通用寄存器.然而,这次它通过诸如循环(LOOP族操作数)和字符串指令(REP族操作数)之类的操作专门用作计数器(顾名思义).只要您没有使用修改CX的说明,您可以随意使用它(但始终检查您的数据是否没有更好的位置).
  4. BX - 基本寄存器 - 长话短说,因为32位到达这个寄存器失去了"抵消"内存地址的目的.因此它是"免费"寄存器,主要用于向后兼容.可怜的家伙.请使用它,不要让历史的迷雾吞噬这样的小勇敢t̶o̶a̶s̶t̶e̶r̶注册.

禁止登记!是的,有这样的.

  1. SPBP - 堆栈和基本指针 - 默认方法是不直接使用它们,它们由PUSH,POP,CALL和RET指令处理,用于管理堆栈并允许您使用函数.然而,它们可以用于与黑暗魔法接壤的异端技巧,你敢亵渎神圣的叠架吗?更改这些寄存器的值可能会导致实际堆栈溢出.不要使用,除非你真的,确定你想要它(并知道如何).(在某些情况下,BP可以成为像BX这样的另一个"免费"寄存器,但现在让我们忽略它)

现在启动可怕寄存器的一部分,其中包含令人生畏的名称和复杂的操作.此外,它们没有可用的高位和低位8位(但如果需要,您仍然可以使用位掩码来提取它们).

  1. DISI - 目标和源索引 - 这两个用作指向内存块(缓冲区/数组/字符串等)的指针,用于使用REP操作或其他基于字符串/数组的自动数据复制/写入.如果您没有使用这种令人困惑的操作,那么您可以使用DI和SI作为数据寄存器,但不能(轻松)选择较低或较高的字节.基于循环的操作的完美选择.
  2. 段寄存器 - CS,SS,DS,ES,FS,GS ......(eek!现在是它们的整个部队) - 虽然故事可能很长,但有一个简短的解释 - 两个主要部分是代码段和数据段.代码段包含代码(令人惊讶,对吧?)以及您在其中声明的所有数据.段通常与DI和SI寄存器一起用于复制数据 - 例如LODSD期望SI指向数据段中的存储器地址,因此在简短("扁平")代码中,必须首先通知CPU您的数据和代码段是一个.你真的不应该使用那些寄存器,否则Segmentation Fault会让你面对面.

您可以在这个简洁的指南上阅读更多内容.

至于你的代码,我现在可以给你很少的建议(对于你的脚本的全面检修,你需要等到明天或周一,因为我本周有点忙)

使用SI/DI更好.在你的代码中,A [bx]相当于(A内存地址)+(SI中的值),CPU是非常简单的生物,所以它更喜欢简单的[SI]而不是动态数学.

您可以自行设置数据段,因为它取决于环境(系统/ cpu /仿真器)来设置初始值.虽然将段用于专用内容是个好习惯,但对于这样简单的程序,可以随意在代码段中存储一些数据,它甚至可以通过减少内存跳转和地址计算来加快执行速度.然后你可以写:MOV AX,CS MOV DS,AX(不能直接做).保证Code Segment是您当前代码的代码(否则它将不会执行).

归零寄存器可以更酷!想在AX中加0吗?使用XOR AX,AX(AX = AX exclusive或AX,其逻辑状态将始终为0).它不仅内存更短(更小的代码=更快的加载),它的执行速度也比复制值快(而且现在的CPU足够聪明,可以让它更快).其他优点是风格点.

结果反击?好吧,计数器寄存器是循环计数器而不是数据计数器.对于存储数据DX或BX更直观(BX比DS更多,因为不再需要该寄存器中的数据,因此它"只是变量").但是如果需要CX的结果那么你必须遵守,我想.

嗯,这就是今晚的一切.如果不清楚,请发表评论.