汇编 8086 中的顺序构造
Sequence construction in assembly 8086
我想做的是,从两个字节序列(我们称之为s1和s2)开始,我想获得序列d,代表每个位置的最大值。
我尝试将序列 s1 移动到目标中,以便之后我可以将序列 s2 与新的目标序列进行比较,并在需要时替换元素。
所以.. "movement" 完成了,只是它没有完成它的工作 - 它没有取代任何东西。
欢迎任何建议。 :D
;S1: 1, 3, 6, 2, 3, 7
;S2: 6, 3, 8, 1, 2, 5
;D: 6, 3, 8, 2, 3, 7
ASSUME cs:text_,ds:data_
data_ SEGMENT
s1 db '1362375'
l equ $-s1
s2 db '6381259'
d db l dup (?)
data_ ENDS
text_ SEGMENT
start:
mov ax, data_
mov ds, ax
mov es, ax
mov si, offset s1
mov di, offset d
mov cx, l
cld
rep movsb
push si
mov si, offset s2
Repeta:
cld
lodsb
repne scasb
jg Instruct
Instruct:
stosb
loop Repeta
mov ax, 4c00h
int 21h
text_ ENDS
end start
您正在沉迷于使用 x86 字符串指令并将它们视为字符串。您要解决的问题与字符串无关。
您正在操作的字节数组是数字的 ASCII 码这一事实无关紧要或无用(除了 '0'
到 '9'
的 ASCII 码是连续且有序的, 所以你可以跳过将数字转换为整数的步骤)。
你需要解决的问题是一个简单的每元素最大值,像这样
for (unsigned i=0 ; i<size ; i++)
d[i] = max(S1[i], S2[i]);
你的值(ASCII码)都小于127,所以你可以使用有符号或无符号比较,哪个更方便。
请注意,d[i]
的结果仅取决于 S1[i]
和 S2[i]
,而不取决于任何字符串中任何较早或较晚的字节。一旦你意识到这一点,它应该很容易实现(你应该看到 repne scasb
(字符串搜索,即 strchr
)没有用)。
您可以独立于循环将一个数组复制到目标,但 x86 有足够的寄存器,这不是必需的,尤其是对于静态数据,因此您不需要在寄存器中保留 3 个指针,只需一个索引. (但是如果你愿意,你 可以 在寄存器中保留 3 个指针,使用 bx
、si
和 di
)。只需从两个源中读取并将最大值存储到每次迭代中 d
中的正确位置,既简单又高效。 (您可以为此使用 stosb
,如果愿意,可以使用 lodsb
从两个源数组之一加载。)
您当前的代码实际上接近工作(我认为),但使用 scasb
而不是 repne scasb
。此外,scasb
和 stosb
都会递增 di
,因此您真正想要的是条件 mov [di-1], al
。 (递增 di
的指令不能是有条件的。)此外,分支目标需要 在 您要跳过的指令之后。
循环内不需要 cld
。在 rep movsb
之前做一次就好了。此外,使用调试器单步执行您的代码以获得循环的正确循环计数。
顺便说一句,在 SIMD 术语中,我们称之为 "vertical" 操作,而不是 "horizontal"(您必须在 16B 数据向量内随机播放,例如将相邻元素加在一起)。
另一个最近的问题询问了完全相同的问题,具有相同的数据,但具有 32 位代码。 My answer there shows how to use SSE2 pmaxub
对两个16个无符号字节的向量并行做pairwise-max运算,然后将前6个字节的结果存入[d]
。 8086 当然不支持 SSE2 指令,但 16 位实模式的现代 CPU 支持。
我想做的是,从两个字节序列(我们称之为s1和s2)开始,我想获得序列d,代表每个位置的最大值。
我尝试将序列 s1 移动到目标中,以便之后我可以将序列 s2 与新的目标序列进行比较,并在需要时替换元素。
所以.. "movement" 完成了,只是它没有完成它的工作 - 它没有取代任何东西。
欢迎任何建议。 :D
;S1: 1, 3, 6, 2, 3, 7
;S2: 6, 3, 8, 1, 2, 5
;D: 6, 3, 8, 2, 3, 7
ASSUME cs:text_,ds:data_
data_ SEGMENT
s1 db '1362375'
l equ $-s1
s2 db '6381259'
d db l dup (?)
data_ ENDS
text_ SEGMENT
start:
mov ax, data_
mov ds, ax
mov es, ax
mov si, offset s1
mov di, offset d
mov cx, l
cld
rep movsb
push si
mov si, offset s2
Repeta:
cld
lodsb
repne scasb
jg Instruct
Instruct:
stosb
loop Repeta
mov ax, 4c00h
int 21h
text_ ENDS
end start
您正在沉迷于使用 x86 字符串指令并将它们视为字符串。您要解决的问题与字符串无关。
您正在操作的字节数组是数字的 ASCII 码这一事实无关紧要或无用(除了 '0'
到 '9'
的 ASCII 码是连续且有序的, 所以你可以跳过将数字转换为整数的步骤)。
你需要解决的问题是一个简单的每元素最大值,像这样
for (unsigned i=0 ; i<size ; i++)
d[i] = max(S1[i], S2[i]);
你的值(ASCII码)都小于127,所以你可以使用有符号或无符号比较,哪个更方便。
请注意,d[i]
的结果仅取决于 S1[i]
和 S2[i]
,而不取决于任何字符串中任何较早或较晚的字节。一旦你意识到这一点,它应该很容易实现(你应该看到 repne scasb
(字符串搜索,即 strchr
)没有用)。
您可以独立于循环将一个数组复制到目标,但 x86 有足够的寄存器,这不是必需的,尤其是对于静态数据,因此您不需要在寄存器中保留 3 个指针,只需一个索引. (但是如果你愿意,你 可以 在寄存器中保留 3 个指针,使用 bx
、si
和 di
)。只需从两个源中读取并将最大值存储到每次迭代中 d
中的正确位置,既简单又高效。 (您可以为此使用 stosb
,如果愿意,可以使用 lodsb
从两个源数组之一加载。)
您当前的代码实际上接近工作(我认为),但使用 scasb
而不是 repne scasb
。此外,scasb
和 stosb
都会递增 di
,因此您真正想要的是条件 mov [di-1], al
。 (递增 di
的指令不能是有条件的。)此外,分支目标需要 在 您要跳过的指令之后。
循环内不需要 cld
。在 rep movsb
之前做一次就好了。此外,使用调试器单步执行您的代码以获得循环的正确循环计数。
顺便说一句,在 SIMD 术语中,我们称之为 "vertical" 操作,而不是 "horizontal"(您必须在 16B 数据向量内随机播放,例如将相邻元素加在一起)。
另一个最近的问题询问了完全相同的问题,具有相同的数据,但具有 32 位代码。 My answer there shows how to use SSE2 pmaxub
对两个16个无符号字节的向量并行做pairwise-max运算,然后将前6个字节的结果存入[d]
。 8086 当然不支持 SSE2 指令,但 16 位实模式的现代 CPU 支持。