Sorting 汇编x86 TASM排序
我是汇编语言(TASM 86x)的乞丐,正在完成我的第一个程序作业。 这在本质上并不复杂,但是由于对这种语言还不熟悉,我很难想出一个简单的冒泡排序 目前为止,我只做了C++编程,最难的部分是掌握语法。 任务是获取任何字符串(由用户键入)并按ASCII值升序重新排列(如中所示,如果键入beda,则应给出abde) 我不确定我的输出,但那应该在排序完成后进行 我很困惑,因为只允许我输入字符串,然后退出命令提示符。无法跟踪我在哪里犯了错误,它过早地指向了代码的结尾。 如果有更有经验的人能看看我的代码,给我指出正确的方向,甚至向新手解释一两件事,我会非常感激Sorting 汇编x86 TASM排序,sorting,assembly,x86,bubble-sort,tasm,Sorting,Assembly,X86,Bubble Sort,Tasm,我是汇编语言(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.
请注意,不允许内存到内存移动,数据必须来自常量:
mov [di],1000 <-- direct assignment using a constant
这应该是:
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
s。替换此代码:
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
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
使用更简单的变体,在过程中保存寄存器:
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]
一直运行到字符串的末尾,所以您读取的字节太多了
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
为此:
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 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
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
Dirk如果我们想向内存位置写入一个立即值,那么我们需要指定要访问的字节数,只有一个字节、一个字或一个dword
mov word [di],1000 <-- direct assignment using a constant (immediate value)
mov字[di],1000塔斯米?俄罗斯学校,如塔斯曼大学(TASM?:)关于错误:只有一个指令操作数可以作为内存位置。第二个必须是寄存器。一个提示和一个小广告:如果你是汇编语言的初学者,如果你想学习它(不仅仅是为了做作业),请不要使用TASM或MASM!FASM是今天和明天的组装商。嗯,纳斯姆也是<代码>公司[si];转到下一个字节
-否,您正在递增si
指向现在的字节。您只需要inc si
。我认为这可能是你问题的一大部分。唯一可用作覆盖前缀的寄存器是段寄存器-dx
将不起作用(无论如何,您都不会想要它)。我非常同意johnfound关于汇编程序的观点。。。但是“TD”,涡轮调试器,过去很不错,我记得。。。如果你拥有并学会使用它,我想这会对你有很大帮助。我支持NASM。“只有一个指令操作数可以是内存位置。第二个必须是寄存器。”不一定。虽然在这种情况下它没有用处,但是mem、immediate
操作数组合也是有效的。只要不使用TASM;使用TASM学习x86汇编并不难,只要您有一些像样的参考资料(我刚开始使用TASM时使用过,但当时有一些教科书使用TASM编写代码示例)。NASM的优点是,您可以轻松地针对DOS以外的其他平台,而且它是免费的。您正在处理Pascal短字符串吗?普通C字符串的开头没有长度字节。
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)