【问题标题】:Iterating over a string (8086 assembly)遍历字符串(8086 程序集)
【发布时间】:2015-10-26 11:24:33
【问题描述】:

好的,我一个月前开始学习 8086 汇编,直到现在我在学习它时没有遇到太多问题,但现在我被字符串困住了。问题是我如何迭代 8086 中的字符串并操作字符?我的微处理器课程还有一个任务是从给定字符串中删除所有 ' 字符(我的代码中的字符串“proce'so'r”),然后将新获取的字符串与第一个字符串进行比较并检查它们是否相等.问题是我什至不知道如何迭代它。课堂上真的没有解释,所以我在这里寻求帮助。到目前为止,这是我的代码(仅用于字符串迭代和打印字符,它不起作用,不知道为什么):

data segment
 string db "proce'so'r"
ends

stack segment
    dw   128  dup(0)
ends

code segment
start:

    lea di, string
    mov cx, 10

    for:
        cmp cx, 0
        je end


        mov dl, [di]
        mov ah, 02h
        int 21h 

        inc di
        loop for     
    end:


mov ax, 4c00h
int 21h  

ends

end start

【问题讨论】:

  • 看起来不错。而不是di cx,我只使用di,将其初始化为零,然后将其加一,只要di < 10,并使用di作为索引保存一个寄存器。您还可以使用带有适当前缀的 lods 指令。您有什么具体问题吗?
  • 我愿意,代码不起作用,也不好:)。所以有一个错误,我找不到,我删除了 cx 并做了你所说的,但它仍然不起作用。
  • @mousepaad 好的,部分是我的错,但你应该包括一个具体的问题。尝试dec cxjmp for 而不是loop for,这会增加cx,而不是减少它。另外,如果你还没有初始化段寄存器,那就去做吧。
  • 好的,我尝试使用 jmp 而不是循环,我也尝试了此代码 stackoverflow.com/questions/27889010/… ,但它不起作用。我真的不知道问题是什么:(。
  • 您忘记初始化DS。在start之后直接插入两行:mov ax, data & mov ds, ax

标签: string assembly x86 x86-16 emu8086


【解决方案1】:

扩展我的评论:更有效的循环结构是:

data segment
 string db "proce'so'r"
 stringlen equ $-string   ; subtract current string's address from current address
ends

start:

    mov ax, data
    mov ds, ax     ; Assuming rkhb is correct about segments

    lea   di, string
    mov   cx, stringlen   ; having the assembler generate this constant from the length of the string prevents bugs if you change the string

    ;; if stringlen can be zero:
     ;  test cx,cx
     ;  jz end

    ;; .labels are "local" and don't end up as symbols in the object file, and don't have to be unique across functions
    ;; emu8086 may not support them, and this Q is tagged as 8086, not just 16bit DOS on a modern CPU.
    print_loop:
        mov   dl, [di]
        mov   ah, 02h  ; If int21h doesn't clobber ah, this could be hoisted out of the loop.  IDK.
        int   21h 

        inc   di
        dec   cx
        jg   print_loop  ;  or jne
    end:

          ;  Or save a register (and the mov to initialize it) with
          ;   cmp  di, offset string+stringlen 
          ;   jb   print_loop

         ;; loop print_loop  ; or save instruction bytes, but slower on modern CPUs

mov ax, 4c00h
int 21h

处理字符串的一种更常见的方法是用零字节终止它们。所以循环边界是test dl,dl / jnz,不需要计数器。

另外,请注意使用si 作为源指针和di 作为目标指针是典型的。


你可以在跳过某个字符的同时复制你的字符串

    mov   dl, [si]
    inc   si
    cmp   dl, '\''   ; or write the value as a hex constant instead of a character literal
    je  nocopy
     mov   [di], dl
     inc   di
nocopy:

作为循环的一部分。在循环开始时,您希望 si 指向输入字符串中的第一个字符,并且 di 指向一个足够大的缓冲区来保存结果。要跳过的字符可以在寄存器中,而不是硬编码为cmp 的立即操作数。

如果你真的想节省代码字节,以牺牲现代 CPU 的速度为代价,你可以使用字符串移动指令lodsb/stosb,除了它们从al 加载/存储。

【讨论】:

  • 您的意思是jg 而不是jge?由于您首先打印出字符并减少 CX 并进行比较,我认为它应该是jg。 @rkhb 关于手动更新 DS(即使在 EMU8086 上)是正确的。我不相信 EMU8086 支持本地标签的概念。我相信它会在这个时期窒息。对于 EMU8086 示例,您可能需要删除它。
  • @MichaelPetch:谢谢,我的意思是jg。那么EMU8086既是汇编器又是仿真器?因为标签在组装后会消失。我认为自己很幸运,我从未真正编写过 16 位分段内存机器。我在 32 位和 64 位 Linux 上学习了 x86 asm,并发现 gdb 非常适合单步执行等。
  • 是的 EMU8086 是一个组合的汇编器/8086/IBM BIOS/DOS 仿真器/调试器(还算完整,有一些漏洞)。它几乎是一种学术工具。 GDB 并不是真正为调试 16 位代码而设计的(尝试跨过像 int 21h 这样的软件中断并观察会发生什么)。 GDB 也没有 CS:IP 的概念,因此如果您调试一个跨越多个段的程序,您会发现调试器不会按预期工作。有一组扩展可以帮助使用 GDB 进行实模式调试,可以在 here 找到。
  • @MichaelPetch:哦,是的,我并不是说 GDB 对 16 位代码有任何好处。我是说 16 位代码很烂;如果不需要,请不要花时间在上面。 :P 我在 x86 之前学习了 m68k asm,所以也许我没有发现 32 位 x86 asm 太复杂,因为我已经了解了基础知识。但是,16 位有更多奇怪的角落,就像您不能将[cx] 用作有效地址,甚至不能用作索引寄存器。 32bit 寻址模式更加统一,因此在很多方面 32bit 更容易学习。 (更不用说没有分段,还有 386 个功能,例如 movzx 而不是 ax 中的 cbw)。
  • 似乎学术界希望保持 8086 分段汇编程序编程的活力和良好;-)。我同意,尽管我认为学术界可以进行大修。 GNU/Linux 很容易获得,不花钱,而且更有可能成为在现实世界中可用的技能组合。 Win32 平台上一个不错的 MINGW/MSYS2 环境可能是一个合理的解决方案。我同意使用更现代的处理器会使事情变得更简单。
【解决方案2】:

您需要初始化 DS 以使用定义的数据段。

完整的解决方案:

DATA SEGMENT
 STRING DB "PROCE'SO'R"
ENDS

STACK SEGMENT
    DW   128  DUP(0)
ENDS

CODE SEGMENT
START:

    MOV AX,@DATA
    MOV DS,AX

    LEA DI, STRING
    MOV CX, 10H

    FOR:
        CMP CX, 0
        JE END

        MOV DL, [DI]
        MOV AH, 02H
        INT 21H 

        INC DI
        LOOP FOR     
    END:

MOV AX, 4C00H
INT 21H  

ENDS

END START

开始后注意以下几点:

    MOV AX,@DATA
    MOV DS,AX

【讨论】:

  • cmp cx, 0 / je end 不需要在循环内。 loop 指令已经在除第一次之外的每次迭代中执行此操作。如果你要使用the slow loop instruction,你不妨使用jcxz;在需要运行零次时跳过 loop 循环似乎是它的主要预期用例。
  • 是的。我只是复制了给定的代码并添加了所需的部分。
  • 所以你唯一改变的是添加 2 行,并且全部大写?如果您有更多的英文文本来解释问题,这将是一个更好的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 2018-05-15
相关资源
最近更新 更多