在汇编中复制字符串
Copying strings in assembly
我正在学习汇编,我正在尝试制作一种可以在汇编中制作字符串的方法。
我开始尝试将一个字符串复制到另一个字符串并将副本存储在一个变量中以备将来使用。
我正在使用 emu8086,但出现以下错误:
unknown opcode skipped: 64
not 8086 instruction - not supported yet.
org 100h
jmp start
msg: db "Hello, World!", 0
a: db 0
start:
mov si,msg
mov di, a
call _make_str
mov a, di
mov dx, a
mov ah, 09h
int 21h
mov ah, 0
int 16h
ret
_make_str:
pusha
_next:
mov al, [si]
mov [di], al
inc si
inc di
cmp al, 0
jne _next
popa
ret
这个错误是什么意思,我做错了什么?
您需要使您的 a 变量有足够的 space 来存储源字符串中的所有字符,现在它只有一个字节。另请查看 rep movsb 指令。
你对字符串是什么抱有一点幼稚的期望。
msg: db "Hello, World!", 0
汇编成机器码为14字节(ASCII编码中每个字符为一个字节(不同的编码有不同的特性,但emu8086是基于ASCII的)加上一个终止零
a: db 0
这被组装为零值的单字节。
然后您的代码开始,在 _make_str
内,它将从地址 msg
复制 14 个字节到地址 a
。但是地址 a+1
等于 start
,所以这 13 个字符会覆盖代码本身的机器代码,越过 call _make_str
并跟随 mov a,di
。然后在 ret
处 mov a,di
应该在的地址上的代码 returns,但是字符串中已经有字节 64
,因此报告了未知的操作码。
emu8086 有内置的调试器,所以使用单步执行指令和内存视图,自己看看字符串是如何编译的,以及你的代码做了什么。
而在汇编程序中没有变量,db/dw/dd/...
前面的名称是"symbols",它们就像计算机内存中的书签,包含该值的第一个字节的地址。
如果19-chars max long strings需要20字节的内存,那么你需要allocate/reserve20字节的内存,内存不会自动增长,或者重新寻址内容做一个意外数据的空间。
What if I don't know how long the string might be, how do I initialize the variable of changing length?
您没有使用未知长度的数据。要么你知道数据可能有多长,要么你必须在那里设置一些最大值(高级编程语言也有限制,如果你足够努力,你会很容易打破 "unlimited" 的概念)。
根据最大值的类型,方法可能会有所不同。
例如,如果您正在读取用户名,您可以说最大为 80 个字节,并在数据区域中保留 80 个字节,仅此而已,用户名较长会使您的应用程序崩溃,或者如果您编写了代码如果正确,他将只能输入 79 或 80 个字符(取决于您是否需要零终止符,在 80 字节数组中)。
如果你正在阅读商店商品描述之类的小文本,你不知道确切的最大值,但你确定它要么在几千以内,要么有什么可怕的错误,那么你可以动态保留 space 在堆栈上,如 sub sp,<length_of_text>
并将文本存储在堆栈区域中。
如果你正在读取长数据,比如 2+GB 文件的二进制编辑器,你将必须有一些数组来描述你当前在内存中的文件块,并动态交换 in/out,使用所有OS 提供的可用内存作为缓存("heap" 一种内存分配),但是如果你 运行 堆外,现代 OS 将通过使用存储的虚拟内存来增加它在磁盘上(交换)。但还是通过更好的算法和应用程序设计来避免这种情况更好。
此外,在 emu8086 中,16 位 x86 实模式的所有其他限制都适用,即在您的类 COM 示例中,您的整个代码+数据+堆栈必须适合单个 64kiB 段(从 0x100 偏移量开始,甚至不是完整的 64kiB ),除非您的代码将检查 DOS 以获取更多可用段,并使用更多它们(在普通 DOS 中加载 COM 文件作为第一个应用程序后,您通常有大约 500-580kiB 的可用内存在您当前的片段之后)。
生活在 16 位世界中并询问 "don't know the size" 是额外的痛苦,在 16 位世界中你必须提前知道大小,并为它们做好计划,否则你会 运行 陷入非常困难的问题很快。
编辑:实际上...您可能遇到了不同的错误,或者很难说出是哪一个。
mov si,msg
mov di, a
这些设置 si
和 di
从地址 msg
和 a
的内存中注册两个字节(16 位)的值。这是 MASM 语法,其中内存访问不需要 []
.
要获取第一个字节的地址,你需要 mov si, OFFSET msg
或 lea si,[msg]
...所以现在我什至不确定你覆盖了哪个内存和哪里,因为你使用字符数据作为内存指针,但显然你覆盖了一些重要的东西。
使用 emu8086 调试器自己看看到底发生了什么。
(在您修复代码以正确加载地址后,您将按照我在回答中的描述点击代码覆盖)
我正在学习汇编,我正在尝试制作一种可以在汇编中制作字符串的方法。 我开始尝试将一个字符串复制到另一个字符串并将副本存储在一个变量中以备将来使用。
我正在使用 emu8086,但出现以下错误:
unknown opcode skipped: 64
not 8086 instruction - not supported yet.
org 100h
jmp start
msg: db "Hello, World!", 0
a: db 0
start:
mov si,msg
mov di, a
call _make_str
mov a, di
mov dx, a
mov ah, 09h
int 21h
mov ah, 0
int 16h
ret
_make_str:
pusha
_next:
mov al, [si]
mov [di], al
inc si
inc di
cmp al, 0
jne _next
popa
ret
这个错误是什么意思,我做错了什么?
您需要使您的 a 变量有足够的 space 来存储源字符串中的所有字符,现在它只有一个字节。另请查看 rep movsb 指令。
你对字符串是什么抱有一点幼稚的期望。
msg: db "Hello, World!", 0
汇编成机器码为14字节(ASCII编码中每个字符为一个字节(不同的编码有不同的特性,但emu8086是基于ASCII的)加上一个终止零
a: db 0
这被组装为零值的单字节。
然后您的代码开始,在 _make_str
内,它将从地址 msg
复制 14 个字节到地址 a
。但是地址 a+1
等于 start
,所以这 13 个字符会覆盖代码本身的机器代码,越过 call _make_str
并跟随 mov a,di
。然后在 ret
处 mov a,di
应该在的地址上的代码 returns,但是字符串中已经有字节 64
,因此报告了未知的操作码。
emu8086 有内置的调试器,所以使用单步执行指令和内存视图,自己看看字符串是如何编译的,以及你的代码做了什么。
而在汇编程序中没有变量,db/dw/dd/...
前面的名称是"symbols",它们就像计算机内存中的书签,包含该值的第一个字节的地址。
如果19-chars max long strings需要20字节的内存,那么你需要allocate/reserve20字节的内存,内存不会自动增长,或者重新寻址内容做一个意外数据的空间。
What if I don't know how long the string might be, how do I initialize the variable of changing length?
您没有使用未知长度的数据。要么你知道数据可能有多长,要么你必须在那里设置一些最大值(高级编程语言也有限制,如果你足够努力,你会很容易打破 "unlimited" 的概念)。
根据最大值的类型,方法可能会有所不同。
例如,如果您正在读取用户名,您可以说最大为 80 个字节,并在数据区域中保留 80 个字节,仅此而已,用户名较长会使您的应用程序崩溃,或者如果您编写了代码如果正确,他将只能输入 79 或 80 个字符(取决于您是否需要零终止符,在 80 字节数组中)。
如果你正在阅读商店商品描述之类的小文本,你不知道确切的最大值,但你确定它要么在几千以内,要么有什么可怕的错误,那么你可以动态保留 space 在堆栈上,如 sub sp,<length_of_text>
并将文本存储在堆栈区域中。
如果你正在读取长数据,比如 2+GB 文件的二进制编辑器,你将必须有一些数组来描述你当前在内存中的文件块,并动态交换 in/out,使用所有OS 提供的可用内存作为缓存("heap" 一种内存分配),但是如果你 运行 堆外,现代 OS 将通过使用存储的虚拟内存来增加它在磁盘上(交换)。但还是通过更好的算法和应用程序设计来避免这种情况更好。
此外,在 emu8086 中,16 位 x86 实模式的所有其他限制都适用,即在您的类 COM 示例中,您的整个代码+数据+堆栈必须适合单个 64kiB 段(从 0x100 偏移量开始,甚至不是完整的 64kiB ),除非您的代码将检查 DOS 以获取更多可用段,并使用更多它们(在普通 DOS 中加载 COM 文件作为第一个应用程序后,您通常有大约 500-580kiB 的可用内存在您当前的片段之后)。
生活在 16 位世界中并询问 "don't know the size" 是额外的痛苦,在 16 位世界中你必须提前知道大小,并为它们做好计划,否则你会 运行 陷入非常困难的问题很快。
编辑:实际上...您可能遇到了不同的错误,或者很难说出是哪一个。
mov si,msg
mov di, a
这些设置 si
和 di
从地址 msg
和 a
的内存中注册两个字节(16 位)的值。这是 MASM 语法,其中内存访问不需要 []
.
要获取第一个字节的地址,你需要 mov si, OFFSET msg
或 lea si,[msg]
...所以现在我什至不确定你覆盖了哪个内存和哪里,因为你使用字符数据作为内存指针,但显然你覆盖了一些重要的东西。
使用 emu8086 调试器自己看看到底发生了什么。
(在您修复代码以正确加载地址后,您将按照我在回答中的描述点击代码覆盖)