繁体   English   中英

装配x86 TASM排序

[英]Assembly x86 TASM Sorting

我是汇编语言(TASM 86x)的入门者,致力于我的第一次程序分配。 它本质上并不复杂,但是对于这种语言来说我是新手,我很难弄清一个简单的冒泡排序。

到目前为止,我只编写了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

返回非法的覆盖寄存器错误,找不到任何内容

逻辑错误

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

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

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         

读取字符串末尾的内容
在这个代码片段中,您有一个正确的主意,但是由于让[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

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

为了提高速度 ,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

短剑

如果要向存储位置写入立即数,则需要指定要访问的字节数,只有一个字节,一个字或一个双字。

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

短剑

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM