▶ 书中第七章的程序,使用各种位移运算,加深了对内存、寄存器中整数类型变量存储的认识
● 代码,双字数组右移 4 位
1 INCLUDE Irvine32.inc 2 3 COUNT = 4 ; 右移位数 4 5 .data 6 array DWORD 148B2165h, 8C943A29h, 6DFA4B86h, 91F76C04h, 8BAF9857h 7 8 .code 9 main PROC 10 mov esi, OFFSET array ; 移之前的情况 11 mov ecx, LENGTHOF array 12 mov ebx, TYPE array 13 call DumpMem 14 15 mov bl, COUNT 16 call ShiftDoublewords 17 18 mov esi, OFFSET array ; 移之后的情况 19 mov ecx, LENGTHOF array 20 mov ebx, TYPE array 21 call DumpMem 22 23 call WaitMsg 24 exit 25 main ENDP 26 27 ShiftDoublewords PROC 28 mov esi, OFFSET array 29 mov ecx, (LENGTHOF array) - 1 ; 循环次数等于数组长度减一,最后一个数单独处理 30 31 L1: 32 push ecx ; 需要使用 cl 移动数 33 mov cl, COUNT 34 mov eax, [esi + TYPE DWORD] ; 下一个数字 DWORD 移入 eax 35 shrd [esi], eax, cl ; eax 逐渐移入当前数字 36 37 push esi ; 移了 4 位,显示一下 38 push ecx 39 push ebx 40 mov esi, OFFSET array 41 mov ecx, LENGTHOF array 42 mov ebx, TYPE array 43 call DumpMem 44 pop ebx 45 pop ecx 46 pop esi 47 48 add esi, TYPE DWORD ; 移完了,esi 指向下一个数字 49 pop ecx ; 恢复 ecx 50 loop L1 51 52 shr DWORD PTR [esi], COUNT ; 最后一个数,直接右移,偷偷使用 cf 中的值 53 54 ret 55 ShiftDoublewords ENDP 56 57 END main
● 输出结果
Dump of offset 01156000 // 移之前 ------------------------------- 148B2165 8C943A29 6DFA4B86 91F76C04 8BAF9857 Dump of offset 01156000 // 移第 1 个 DWORD,"9" 来自第二个数的最低 4 位 ------------------------------- 9148B216 8C943A29 6DFA4B86 91F76C04 8BAF9857 Dump of offset 01156000 // 移第 2 个 DWORD ------------------------------- 9148B216 68C943A2 6DFA4B86 91F76C04 8BAF9857 Dump of offset 01156000 // 移第 3 个 DWORD ------------------------------- 9148B216 68C943A2 46DFA4B8 91F76C04 8BAF9857 Dump of offset 01156000 // 移第 4 个 DWORD ------------------------------- 9148B216 68C943A2 46DFA4B8 791F76C0 8BAF9857 Dump of offset 01156000 // 移第 5 个 DWORD,完成移动 ------------------------------- 9148B216 68C943A2 46DFA4B8 791F76C0 08BAF985
● 代码,内存右移 1 位
1 INCLUDE Irvine32.inc 2 3 .data 4 array BYTE 45h, 67h, 89h 5 6 .code 7 main PROC 8 call DisplayArray 9 10 mov esi, 0 11 mov eax, OFFSET array 12 shr array[esi+2], 1 ; 最高字节(第一个元素/字节)右移 13 rcr array[esi+1], 1 ; 中间元素循环右移,偷偷使用 CF 14 rcr array[esi], 1 ; 最低字节循环右移 15 16 call DisplayArray 17 18 call WaitMsg 19 exit 20 main ENDP 21 22 DisplayArray PROC 23 pushad 24 25 mov esi, OFFSET array ; 先用 DumpMem 显示内存情况(注意元素/字节是倒着存的) 26 mov ecx, LENGTHOF array 27 mov ebx, TYPE array 28 call DumpMem 29 30 mov esi, LENGTHOF array ; esi 循环变量 31 dec esi 32 L1: 33 mov al, array[esi] 34 mov ebx, 1 35 call WriteBinB ; display binary bits 36 mov al, ' ' 37 call WriteChar 38 dec esi 39 Loop L1 40 41 call Crlf 42 popad 43 ret 44 DisplayArray ENDP 45 46 END main
● 输出结果
Dump of offset 00F66000 // 右移前,注意元素/字节是顺着存的 ------------------------------- // 用 DumpMem 显示反而会翻转过来 45 67 89 1000 1001 0110 0111 0100 0101 Dump of offset 00F66000 // 右移后 ------------------------------- A2 B3 44 0100 0100 1011 0011 1010 0010
● 代码,二进制数转 ASCII 显示(逐字节打印),结果输出 00010010001101001010101111001101
1 INCLUDE Irvine32.inc 2 3 .data 4 binVal DWORD 1234ABCDh ; 需要输出的数字 5 buffer BYTE 32 dup(0),0 6 7 .code 8 main PROC 9 mov eax, binVal 10 mov esi, OFFSET buffer 11 call BinToAsc 12 13 mov edx,OFFSET buffer 14 call WriteString 15 16 call Crlf 17 call WaitMsg 18 exit 19 main ENDP 20 21 BinToAsc PROC uses ecx esi 22 23 mov ecx, 32 24 25 L1: 26 shl eax, 1 ; eax 中的二进制串左移一位,最高位放入 cf 用于下面的跳转 27 ; binVal 在内存中存成了 cd ab 34 12(4 字节整数模式下可以看到 1234abcd) 28 ; 但对寄存器进行左移的时候还是按照 1234ABCDh 来左移 29 ; 例如第一次左移后得到 2469579Ah 30 mov BYTE PTR [esi],'0' ; 内存先写上 '0' 再说 31 jnc L2 ; 如果 cf 中是 1,把内存改写成 '1',否则跳过改写 32 mov BYTE PTR [esi],'1' 33 34 L2: 35 inc esi 36 loop L1 37 38 ret 39 BinToAsc ENDP 40 41 END main
● 代码,用 adc 指令实现长整数加法
1 INCLUDE Irvine32.inc 2 3 .data 4 op1 BYTE 34h,12h,98h,74h,06h,0A4h,0B2h,0A2h 5 op2 BYTE 02h,45h,23h,00h,00h, 87h,10h, 80h 6 7 sum BYTE 9 dup(0) ; = 0122C32B0674BB5736h 8 9 .code 10 main PROC 11 12 mov esi, OFFSET op1 13 mov edi, OFFSET op2 14 mov ebx, OFFSET sum 15 mov ecx, LENGTHOF op1 16 17 call Extended_Add 18 19 mov esi, OFFSET sum 20 mov ecx, LENGTHOF sum 21 22 add esi, ecx ; 找到数组结尾,从高位逐字节输出 23 sub esi, TYPE BYTE 24 mov ebx, TYPE BYTE 25 26 L1: 27 mov al,[esi] 28 call WriteHexB 29 sub esi, TYPE BYTE 30 loop L1 31 32 33 call Crlf 34 call WaitMsg 35 exit 36 main ENDP 37 38 39 Extended_Add PROC 40 pushad 41 clc ; 清除 CF 42 43 mov edx,0 44 L1: 45 mov al, [esi] 46 adc al, [edi] 47 pushfd ; CF(进位标志)压栈 48 mov [ebx], al 49 add esi, 1 ; 更新指针 50 add edi, 1 51 add ebx, 1 52 popfd ; CF 出栈,准备进行下一次加法 53 loop L1 54 55 mov byte ptr [ebx],0 ; 最后一次加法,最高字节加上剩余的进位标志 56 adc byte ptr [ebx],0 57 58 popad 59 ret 60 Extended_Add ENDP 61 62 END main
● ASCII 长整数加法,结果输出 1000525533291780
1 INCLUDE Irvine32.inc 2 3 DECIMAL_OFFSET = 5 ; 小数点位于位 5 的右边(没用到) 4 .data 5 decimal_one BYTE "100123456789765" ; == 1001234567.89765 6 decimal_two BYTE "900402076502015" ; == 9004020765.02015 7 sum BYTE (SIZEOF decimal_one + 1) DUP(0),0 ; 预留进位 8 9 .code 10 main PROC 11 mov esi, SIZEOF decimal_one - 1 ; 从最低位开始 12 mov edi, SIZEOF decimal_one 13 mov ecx, SIZEOF decimal_one 14 mov bh, 0 ; 旧进位标志,初始化为 0 15 16 L1: 17 mov ah, 0 18 mov al, decimal_one[esi] 19 add al, bh ; 加旧进位 20 aaa ; 调整和 21 mov bh, ah ; ah 存储了进位,将其放进 bh 22 or bh, 30h ; 转成 ASCII 23 add al, decimal_two[esi] ; 加一个数字,过程同上 24 aaa 25 or bh, ah ; 9 + 1 + 9 = 19 < 20,加了两次但最多进位 1 26 or bh, 30h ; 进位和结果都转成 ASCII 27 or al, 30h 28 mov sum[edi], al ; 当前位结果放入内存 29 dec esi ; 指向下一对计算的数字 30 dec edi 31 loop L1 32 mov sum[edi], bh ; 最后的进位放在预留的最高位上 33 34 mov edx, OFFSET sum 35 call WriteString 36 call Crlf 37 call WaitMsg 38 exit 39 main ENDP 40 END main
● 压缩十进制加法,输出结果 00011743。逻辑简单,但是不能理解 daa 的实现方法
1 INCLUDE Irvine32.inc 2 3 .data 4 packed_1 WORD 4536h 5 packed_2 WORD 7207h 6 sum DWORD ? 7 8 .code 9 main PROC 10 mov sum,0 11 mov esi,0 12 mov eax,0 13 14 mov al,BYTE PTR packed_1[esi] ; 低字节,daa 将 al 中的和转化为十进制和的低两位,放入内存 15 add al,BYTE PTR packed_2[esi] 16 daa 17 mov BYTE PTR sum[esi],al 18 19 inc esi ; 中字节,包含进位 20 mov al,BYTE PTR packed_1[esi] 21 adc al,BYTE PTR packed_2[esi] 22 daa 23 mov BYTE PTR sum[esi],al 24 25 inc esi ; 高字节,只考虑进位 26 mov al,0 27 adc al,0 28 mov BYTE PTR sum[esi],al 29 30 mov eax,sum 31 call WriteHex 32 call Crlf 33 call WaitMsg 34 exit 35 main ENDP 36 END main