[英]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
在完成al
和ah
翻转之后,您需要将它们写回到内存中,否则所有的工作都是徒劳的。
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.