【问题标题】:Assembly x86 TASM Sorting汇编 x86 TASM 排序
【发布时间】:2013-10-13 19:38:19
【问题描述】:

我是汇编语言 (TASM 86x) 的初学者,正在处理我的第一个程序任务。 它本质上并不复杂,但是对于这门语言的新手来说,我很难弄清楚一个简单的冒泡排序。

到目前为止,我只编写过 Witch C++,总体上最难的部分是掌握语法。

任务是获取任何字符串(由用户输入)并按 ASCII 值升序重新排列它(例如,如果您输入 beda,它应该给出 abde)

我不确定我的输出,但这应该在排序完成后出现 我很困惑,因为 它只允许我输入我的字符串,然后退出到命令提示符。无法追踪我在哪里犯了错误,它过早地指向了代码的末尾。

如果有经验丰富的人看看我的代码并指出正确的方向,甚至向新手解释一两件事,我将不胜感激

.model small
.stack 100h

.data
request     db 'Enter symbols:', 0Dh, 0Ah, '$'  

    buffer      db 100, ?, 100 dup (0)

.code

start:
    MOV ax, @data                   
    MOV ds, ax                      

; request
    MOV ah, 09h
    MOV dx, offset request
    int 21h

; read string                    ;reading string to buffer
    MOV dx, offset buffer           
    MOV ah, 0Ah                     
    INT 21h                         
    MOV si, offset buffer           


    INC si                        ;going from buffer size to actual length 
                                  ;of the string
    MOV cl, [si]              ;string length - loop counter
    mov ch, [si]                  ;string length - loop counter
    mov bl, [si]                  ;bl will be used to reset inner loop counter 
    DEC cl                        ;correcting the values, since count goes
    dec ch                        ; from 0 to n-1 instead of 1 to n

    inc si                        ;moving to strings first byte


outer:                            ;outer loop

    dec ch                        ;decrease counter each pass
    jz ending                     ;when counter reaches 0 end program
    mov cl, bl                    ; reset inner loop counter value


inner:                            ;inner loop
    mov al,byte ptr[si]           ;assigning byte(sybol) to al
    mov ah, byte ptr[si+1]        ;assigning following byte(symbol) to ah
    cmp al,ah                     ;compare the two
    jle after_switch              ;if the latter's value is higher, no need to switch

开关有问题,不确定它是否能在组装中正常工作

    mov bh, al           ;main problem-switching values, tried a few different   
    mov al, ah           ;ways of doing it (will show them below), but to no avail
    mov ah, bh           ;using familiar C syntax

    jmp output           ;outputing the value

after_switch:         ;no switch needed

在外部开关的某个地方应该跳转到输出,但是我想不出在不弄乱序列其余部分的情况下包含它的方法

    inc [si]              ;going to the next byte
    dec cl                ;decreasing inner loop counter
    jnz inner             ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
    jmp outer             ;if counter reaches zero, get back to outer


output:             ;outputting value from the very first bit 
    mov ah, 2
    mov dl, al          ;which after switch is supposed to be stored in al
    int 21h
    jmp inner           ;returning to inner loop to run next course of comparison

ending:
    MOV ax, 4c00h               
    INT 21h                              
end start

之前尝试过的内循环切换方法

    mov al,[si+1]
    mov byte ptr[si+1],[si]
    mov byte ptr[si], al

返回非法内存引用错误,但是这个问题过去在这个板子上已经回答过了,找到了。

尝试了相同的方法,但使用了 dx:di 寄存器

    mov al, byte ptr[si+1]
    mov dx:[di], [si]
    mov byte ptr[si+1], dx:[di]
    mov byte ptr[si], al

返回非法覆盖寄存器错误,在上面找不到任何东西

【问题讨论】:

  • TASM?像TASM这样的俄罗斯学校? :) 关于错误:只有一个指令操作数可以是内存位置。第二个必须注册。一个提示和小广告:如果您是汇编语言的初学者,并且如果您想学习它(不仅仅是为了做作业),请不要使用 TASM 或 MASM! FASM 是今天和明天的汇编器。好吧,NASM 也是如此。 ;)
  • inc [si] ;going to the next byte - 不,您正在增加si 现在指向的字节。你只想要inc si。我认为这可能是您问题的很大一部分。唯一可以用作覆盖前缀的寄存器是段寄存器 - dx 将不起作用(无论如何你都不想要它)。我非常同意 johnfound 关于汇编程序的看法……但我记得,“TD”,Turbo Debugger,曾经非常好……如果你拥有它并学会使用它,它会对你有很大帮助,我想。
  • 我支持 NASM。
  • "只有一个指令操作数可以是内存位置。第二个必须是寄存器。"不一定。尽管在这种情况下它没有用,但 mem, immediate 操作数组合也是有效的。就不使用 TASM 而言;使用 TASM 学习 x86 汇编并不难,只要你有一些不错的参考资料(我刚开始时使用 TASM,但当时有可用的教科书使用 TASM 作为代码示例)。 NASM 的优势在于您可以轻松针对 DOS 以外的其他平台,而且它是免费的。
  • 您在处理 Pascal 短字符串吗?普通的 C 字符串开头没有长度字节。

标签: sorting assembly x86 bubble-sort tasm


【解决方案1】:

逻辑错误

mov al, byte ptr[si+1]
mov dx:[di], [si]             <<-- there is no dx:[di] register.
mov byte ptr[si+1], dx:[di]   <<-- memory to memory move not allowed.
mov byte ptr[si], al          <<-- `byte ptr` is superflous, because `al` is already byte sized.

您可以在此处使用段寄存器,但因为您只使用 ds 段,所以不需要这样做。

所以这是有效的:

mov DS:[di],si   <-- ds segment (but DS is already the default)

请注意,不允许内存到内存的移动,数据必须来自常量:

mov [di],1000    <-- direct assignment using a constant

或者必须通过寄存器

mov ax,[di]
mov [si],ax      <--- memory to memory must be a 2 step process.

记住[reg]是记忆; reg 是一个寄存器
另一个错误在这里:

inc [si] <<-- error, increases some memory location, not `si` the pointer.
dec cl                ;decreasing inner loop counter
jnz inner             ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer             ;if counter reaches zero, get back to outer

这应该是:

inc si                ;next char in the string
dec cl                ;decreasing inner loop counter
jnz inner             ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer             ;if counter reaches zero, get back to outer

简化
翻转两个值不需要movs。替换此代码:

mov bh, al           ;main problem-switching values, tried a few different   
mov al, ah           ;ways of doing it (will show them below), but to no avail
mov ah, bh           ;using familiar C syntax

使用这个更简单的变体,在过程中保存一个寄存器:

xchg ah, al           ;flip chars around

在您完成翻转alah 之后,您需要将它们写回内存,否则所有这些工作都是徒劳的。

xchg ah, al           ;flip chars around
mov [si],al           ;save flipped values
mov [si+1],ah         

读到字符串末尾
在这个 sn-p 中,您有正确的想法,但是因为您让 [si] 一直运行到字符串的末尾,所以您读取的 1 字节太多了。

inner:                            ;inner loop
  mov al,byte ptr[si]           ;<<-- both are correct, but [si+1] will read
  mov ah, byte ptr[si+1]        ;<<-- past the end of the string at the last byte  

所以你需要改变这个部分:

DEC cl                     ;correcting the values, since count goes
dec ch                     ; from 0 to n-1 instead of 1 to n

对此:

sub bl,2                   ;if bl is used to reset inner loop counter it must be
                           ;adjusted as well.
;sub cl,2                  ;inner loop from 0 to n-2
mov cl,bl                  ;bl=cl, so a mov makes more sense
dec ch                     ;outer loop from 0 to n-1 instead of 1 to n

最后的效率提示
如果没有必要,永远不要从内存中读取。 更改此代码:

MOV si, offset buffer           
INC si                        ;going from buffer size to actual length 
                              ;of the string
MOV cl, [si]              ;string length - loop counter
mov ch, [si]                  ;string length - loop counter
mov bl, [si]                  ;bl will be used to reset inner loop counter

到这里:

MOV si, offset buffer+1       ;start at length byte of the string    
MOV cl, [si]                  ;string length - loop counter
mov ch, cl                    ;string length - loop counter
mov bl, cl                    ;bl will be used to reset inner loop counter

还有更多的速度优化可以做,但我不想让事情变得过于复杂。

【讨论】:

    【解决方案2】:

    对于速度优化,xchg 指令在所有 x86 上都不是非常快的指令。所以我更喜欢使用第三个寄存器来切换值并用它替换 xchg 指令,因为简单的 mov 指令可以更好地与指令管道的预取输入队列中的其他简单指令配对,并且 CPU 可以更轻松地翻译简单指令到微操作(μops)。

    考虑到每个流水线的预取输入队列中的阶段数,每条指令都会像获取、解码和存储一样通过,我们可以在我们的之间放置一些其他指令(与其他指令没有依赖关系)用于切换值的 mov 指令,用于防止执行时停顿。

    为防止失速的放置说明示例:

    (注意:并非所有 x86-CPU 在其流水线中使用完全相同数量的执行阶段,但原理图相似。)

    指令放置不当会导致停顿:

    mov eax,value1
    add eax,value2 ; reading after writing the same register = results a stall
    mov ebx,value3
    add ebx,value4 ; stall
    mov ecx,value5
    add ecx,value6 ; stall
    mov edx,value7
    add edx,value8 ; stall
    mov esi,value9
    add esi,value10 ; stall
    mov edi,value11
    add edi,value12 ; stall
    mov ebp,value13
    add ebp,value14 ; stall
    

    更好的说明放置:

    mov eax,value1 ; first instruction
    mov ebx,value3 ; second instruction
    mov ecx,value5 ; third ...
    mov edx,value7 ; ...
    mov esi,value9
    mov edi,value11
    mov ebp,value13
    ; no stall, because the execution progress of the first instruction is fully complete
    add eax,value2
    add ebx,value4 ; ... of the second instruction is fully complete
    add ecx,value6 ; ... of the third instruction is fully complete
    add edx,value8 ; ...
    add esi,value10
    add edi,value12
    add ebp,value14
    

    德克

    【讨论】:

      【解决方案3】:

      如果我们想将立即数写入内存位置,那么我们需要指定要访问的字节数,只有一个字节、一个字或一个双字。

      mov word [di],1000    <-- direct assignment using a constant (immediate value)
      

      德克

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多