对应第五章内容及练习
最大公约数
求两个数的最大公约数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| .data val1 DWORD 120 val2 DWORD 36 .code mov eax, val1 mov ebx, val2 L1: mov edx, 0 div ebx ;余数在EDX,商在EAX cmp edx, 0 je L2 mov eax, ebx ;除数做被除数 mov ebx, edx ;余数做除数 jmp L1 L2: mov esi, ebx ;ESI返回最大公约数
|
需要注意一个问题就是EDX的值(被除数高位),每次计算时需要将其置零,否则容易导致结果溢出(存储商或者余数的寄存器无法放得下商或者余数)
压缩整数加法
课本例题:16位+16位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| INCLUDE Irvine32.inc .data packed_1 WORD 4536h ;4536 packed_2 WORD 7207h ;7207 sum DWORD ? .code main PROC ;初始化数组和索引 mov sum, 0 mov esi, 0 ;低字节相加 mov al, BYTE PTR packed_1[esi] add al, BYTE PTR packed_2[esi] daa mov BYTE PTR sum[esi], al ;高字节相加 inc esi mov al, BYTE PTR packed_1[esi] add al, BYTE PTR packed_2[esi] daa mov BYTE PTR sum[esi], al ;若还有进位,加上进位 inc esi mov al, 0 adc al, 0 mov BYTE PTR sum[esi], al ;16进制显示和数 mov eax, sum call WriteHex call Crlf call WaitMsg exit main ENDP END main
|
结果就是压缩10进制的显示(16进制的11743)
课后拓展:同长度的任意位相加
扩展上面的程序,使其实现两个任意大小(但长度相同)的压缩十进制数加法,假设该过程使用如下寄存器传递参数:
ESI————第一个数的指针
EDI————第二个数的指针
EDX————和数的指针
ECX————相加的字节数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| INCLUDE Irvine32.inc
.data packed1_1 WORD 4536h packed1_2 WORD 7207h packed2_1 DWORD 123456h packed2_2 DWORD 234567h packed3_1 BYTE 56h packed3_2 BYTE 41h sum DWORD ?
.code main PROC mov edx, OFFSET sum mov esi, OFFSET packed1_1 mov edi, OFFSET packed1_2 mov ecx, TYPE packed1_1 call AddPacked mov sum, 0 mov esi, OFFSET packed2_1 mov edi, OFFSET packed2_2 mov ecx, TYPE packed2_1 call AddPacked mov sum, 0 mov esi, OFFSET packed3_1 mov edi, OFFSET packed3_2 mov ecx, TYPE packed3_1 call AddPacked call WaitMsg exit main ENDP
AddPacked PROC push ebx mov ebx, 0 L1: mov al, BYTE PTR [esi+ebx] add al, BYTE PTR [edi+ebx] daa mov BYTE PTR [edx+ebx], al inc ebx loop L1
mov al, 0 adc al, 0 mov BYTE PTR [edx+ebx], al ;16进制显示和数 mov eax, [edx] call WriteHex call Crlf pop ebx ret AddPacked ENDP END main
|
没啥困难的,注意几个点:
过程定义要定义在.code下,别定义到外面去了
mov [esi], 0 报错而直接mov内存和立即数是可以的,而间接寻址的内存却不可以,不知道为啥-_-||
位元乘法
编写名为BitwiseMultiply的过程,仅使用移位和加法实现任意32位无符号数与EAX相乘,过程用EBX寄存器传递参数,用EAX寄存器传递返回值。假设乘积不会超过32位。
提示:一种可能的方法是用循环结构右移乘数,记录在进位标志被置1之前移动的位数,然后把这个位数用到SHL指令中,被乘数作为该指令的目的操作数。重复该过程直到乘数最后一个为1位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| INCLUDE Irvine32.inc .data val1 DWORD 4536h val2 DWORD 2207h .code main PROC mov eax, val1 mov ebx, val2 mov ecx, 32 mov edx, 0 ;结果保存在EDX中 L1: ror ebx, 1 ;循环位移 jnc L2 ;非1时跳转 add edx, eax ;为1时相加 L2: shl eax, 1 ;左移1位 loop L1 mov eax, edx call WriteHex call Crlf mov eax, val1 mov ebx, val2 mul ebx call WriteHex call Crlf call WaitMsg exit main ENDP END main
|
经用标准乘法验证正确,这里我优化了一下,每次循环都让eax移位一次,而只有当CF=1时才让edx和eax相加,写起来就简练很多
复杂的带符号乘除混合运算——2019.11.14补充
若x,y,z,v均为16位带符号数,计算(v-(x*y+z–540))/x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| .data X DW 1200H Y DW 0034H Z DW 0F045H W DW 034AH
.code main PROC xor eax, eax xor edx, edx mov dx, 0 mov ax, X imul Y add ax, Z ;考虑进位, 对高位+cf和0 adc dx, 0 sub ax, 540 ;考虑退位,对高位-cf和0 sbb dx, 0 ;求补码,减法变成加法 neg dx neg ax add ax, W ;同上考虑进位情况 adc dx, 0 idiv X
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ;省略.code和main声明 .data MOV AX, X IMUL Y ; x*y ->(DX,AX) MOV CX, AX ; 临时保存ax MOV BX, DX ; 临时保存dx MOV AX, Z CWD ; Z ->(DX,AX) ADD CX, AX ADC BX, DX ; x*y+z ->(BX,CX) SUB CX, 540 SBB BX, 0 ; x*y+z-540 MOV AX, W CWD ; W ->(DX,AX) SUB AX, CX SBB DX, BX ; w-(x*y+z-540) IDIV X ; (w-(x*y+z-540))/x->(AX)余数->(DX)
|