在内联汇编代码中难以解释解引用操作数
Hard to interpret dereference operands in inline assembler code
tl;dr 在底部。
除了几年前在 System z 上进行过一些编程之外,我没有使用汇编程序的经验。此外,据我所知,汇编程序中有类型信息之类的东西——但这里给出了 none,所以显然类型是从提供给代码的 C++ 变量中隐式推断出来的。
我需要摆脱它。意味着我必须辨别它的作用。显然这比我想象的要难:
案例一:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov esi, data
mov edi, screen
xor eax, eax
heightloop2: // do {
mov edx, width1
neg edx
widthloop2: // do {
mov eax, [esi + edx*8] // screen[b] = data[b]
mov ecx, 4[esi + edx*8] // screen[b] = data[b]
mov [edi + edx*8], eax
mov 4[edi + edx*8], ecx
add edx, 1 // b += 1
js widthloop2 // } while(b)
add edi, destpitch // screen += SCREEN_WIDTH
add esi, datapitch // data += datapitch
dec counter
jnz heightloop2 // } while(a)
//emms
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
案例二:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov esi, data
mov edi, screen
xor eax, eax
mov ecx, transcol
heightloop: // do {
mov ebx, 0
mov edx, width1
neg edx
widthloop: // do {
mov ax, [esi + ebx*2] // screen[b] = data[b]
add ebx, step // b += 1
cmp eax, ecx // Check for transparent color
je trans
mov [edi + edx*2], ax
trans:
add edx, 1 // b += 1
jnz widthloop // } while(b)
add edi, destpitch // screen += SCREEN_WIDTH
add esi, datapitch // data += datapitch
dec counter
jnz heightloop // } while(a)
//emms
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
评论不是我的,它们是我必须处理的。
两者都有相似的输入集:
unsigned char * data, screen
uint32_t * palette
int counter, datapitch, destpitch, step, transcol, width1
现在很容易看出(除非我记错了,请指正),这两种情况的控制流程是相等的:
do
{
// int a = 0; // additionally in case 2
int b = -weight1;
do
{
<work>
}
while(++b < 0); // actually ++b != 0 in case 2, but symptoms should be equal
screen += destpitch;
data += datapitch;
}
while(--counter != 0);
现在,"work" 部分略有不同。有两件事让我非常困惑:
- 两条第一个 mov 指令具有相同的注释 - 但解引用地址的计算方式不同。这怎么能合在一起呢?因子在这里究竟有什么作用?
- 案例 2 在解引用括号前有另一个带有数字前缀的 mov 集。我在搜索的各种参考资料中找不到这个成语。这个表达式是什么意思?
不幸的是,VS2015 调试器将这些代码示例中的所有值发出为 "unsigned int" - 我几乎可以肯定这是不正确的(同样,如果我错了,请更正)。
所以,简洁:
- 请验证或更正我推断的控制流程。
- 请解释或link解释这些mov指令。
使用来自@Jester 的指针,我能够用执行相同的 C++ 代码替换这两种情况:
案例 1:
do
{
for(int b = -width1; b < 0; ++b)
memcpy(screen + b * 8, data + b * 8, 8u);
screen += destpitch;
data += datapitch;
}
while(--counter != 0);
案例二:
do
{
int a = 0;
for(int b = -width1; b != 0; ++b)
{
const uint16_t * const c = (uint16_t*)(data + a * 2);
if(transcol != *c)
memcpy(screen + b * 2, c, 2u);
a += step;
}
screen += destpitch;
data += datapitch;
}
while(--counter != 0);
当然,一看这个就知道可以写得更优雅一些,但我还是决定在这里保持这种粗略的状态,这样转换更明显。
该因子按元素大小缩放。如您所见,第一种情况处理 8 个字节单元,第二种情况处理 2 个字节单元。
括号外的数字是数组语法的特例。你可以在 C 中做同样的事情,顺便说一句:5[array]
与 array[5]
相同。在 "normal" intel 语法中,您只需将数字放在括号内,例如mov [edi + edx*8 + 4], ecx
.
tl;dr 在底部。
除了几年前在 System z 上进行过一些编程之外,我没有使用汇编程序的经验。此外,据我所知,汇编程序中有类型信息之类的东西——但这里给出了 none,所以显然类型是从提供给代码的 C++ 变量中隐式推断出来的。
我需要摆脱它。意味着我必须辨别它的作用。显然这比我想象的要难:
案例一:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov esi, data
mov edi, screen
xor eax, eax
heightloop2: // do {
mov edx, width1
neg edx
widthloop2: // do {
mov eax, [esi + edx*8] // screen[b] = data[b]
mov ecx, 4[esi + edx*8] // screen[b] = data[b]
mov [edi + edx*8], eax
mov 4[edi + edx*8], ecx
add edx, 1 // b += 1
js widthloop2 // } while(b)
add edi, destpitch // screen += SCREEN_WIDTH
add esi, datapitch // data += datapitch
dec counter
jnz heightloop2 // } while(a)
//emms
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
案例二:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov esi, data
mov edi, screen
xor eax, eax
mov ecx, transcol
heightloop: // do {
mov ebx, 0
mov edx, width1
neg edx
widthloop: // do {
mov ax, [esi + ebx*2] // screen[b] = data[b]
add ebx, step // b += 1
cmp eax, ecx // Check for transparent color
je trans
mov [edi + edx*2], ax
trans:
add edx, 1 // b += 1
jnz widthloop // } while(b)
add edi, destpitch // screen += SCREEN_WIDTH
add esi, datapitch // data += datapitch
dec counter
jnz heightloop // } while(a)
//emms
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
评论不是我的,它们是我必须处理的。
两者都有相似的输入集:
unsigned char * data, screen
uint32_t * palette
int counter, datapitch, destpitch, step, transcol, width1
现在很容易看出(除非我记错了,请指正),这两种情况的控制流程是相等的:
do
{
// int a = 0; // additionally in case 2
int b = -weight1;
do
{
<work>
}
while(++b < 0); // actually ++b != 0 in case 2, but symptoms should be equal
screen += destpitch;
data += datapitch;
}
while(--counter != 0);
现在,"work" 部分略有不同。有两件事让我非常困惑:
- 两条第一个 mov 指令具有相同的注释 - 但解引用地址的计算方式不同。这怎么能合在一起呢?因子在这里究竟有什么作用?
- 案例 2 在解引用括号前有另一个带有数字前缀的 mov 集。我在搜索的各种参考资料中找不到这个成语。这个表达式是什么意思?
不幸的是,VS2015 调试器将这些代码示例中的所有值发出为 "unsigned int" - 我几乎可以肯定这是不正确的(同样,如果我错了,请更正)。
所以,简洁:
- 请验证或更正我推断的控制流程。
- 请解释或link解释这些mov指令。
使用来自@Jester 的指针,我能够用执行相同的 C++ 代码替换这两种情况:
案例 1:
do
{
for(int b = -width1; b < 0; ++b)
memcpy(screen + b * 8, data + b * 8, 8u);
screen += destpitch;
data += datapitch;
}
while(--counter != 0);
案例二:
do
{
int a = 0;
for(int b = -width1; b != 0; ++b)
{
const uint16_t * const c = (uint16_t*)(data + a * 2);
if(transcol != *c)
memcpy(screen + b * 2, c, 2u);
a += step;
}
screen += destpitch;
data += datapitch;
}
while(--counter != 0);
当然,一看这个就知道可以写得更优雅一些,但我还是决定在这里保持这种粗略的状态,这样转换更明显。
该因子按元素大小缩放。如您所见,第一种情况处理 8 个字节单元,第二种情况处理 2 个字节单元。
括号外的数字是数组语法的特例。你可以在 C 中做同样的事情,顺便说一句:5[array]
与 array[5]
相同。在 "normal" intel 语法中,您只需将数字放在括号内,例如mov [edi + edx*8 + 4], ecx
.