《汇编语言:基于X86处理器》第10章 编程练习
本篇记录《汇编语言:基于X86处理器》第9章 编程练习的学习笔记。
10.8 编程练习
*1.宏 mReadkey
编写一个宏,等待一次按键操作并返回被按下的键。宏参数要包括ASCII码和键盘扫描码提示:调用本书链接库的ReadChar。编写程序对宏进行测试。比如,下面的代码等待一次按键当它返回时,两个实参分别为按键的ASCI码和扫描码:
.data
ascii BYTE ?
scan BYTE?
.code
mReadkey ascii, scan
完整代码测试笔记
;10.8_1.asm 10.8 编程练习 *1.宏 mReadkeyINCLUDE Irvine32.incmReadkey MACRO asciiVal, scanValcall ReadCharmov asciiVal, almov scanVal, ah
ENDM.data
ascii BYTE ?
scan BYTE ?
prompt BYTE "Press any key... (ESC to exit)", 0Dh, 0Ah, 0.code
main PROCmov esi, OFFSET ascii;显示提示信息mov edx, OFFSET promptcall WriteString ;如果按下的是字符键,ascii保存在al中,扫描码保存在ah中,如果用户按下的是扩展键,;如功能键、方向键、Ins键或Del键,则过程就把AL清零,而AH包含的是键盘扫描码;call ReadChar
L1:mReadkey ascii, scan;检查是否是ESC键(ASCII码1Bh)cmp ascii, 1Bhje quitmov bx, axmovzx eax, alcall WriteDec ;显示ASCII码mov al, 9 ;tab符call WriteChar ;显示tab符mov al, bh ;把扫描码赋给alcall WriteChar ;显示扫描码call Crlfjmp L1quit:call CrlfINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
*2.宏mWritestring
(需提前阅读 11.1.11节。)编写一个宏,用指定文本颜色向控制台写一个空字节结束的字符串。宏参数需包括字符串的名称和颜色。提示:调用本书链接库的 SetTextColor。编写程序,用不同的颜色和字符串测试该宏。示例调用如下:
.data
myString db "Here is my string", 0
.code
mWritestring myString, white
完整代码测试笔记
;10.8_2.asm 10.8 编程练习 *2.宏mWritestring
;black=0 red=4 gray=8 lightRed=12
;blue=1 magenta=5 lightBlue=9 lightMagenta=13
;green=2 brown=6 lightGreen=10 yellow=14
;cyan=3 lightGray=7 lightCyan=11 white=15INCLUDE Irvine32.incmWritestring MACRO name, colorpush edxpush eaxmov edx, OFFSET namemov eax, colorcall SetTextColorcall WriteStringcall Crlfpop eaxpop edx
ENDM.data
buffer BYTE 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
myString db "Here is my string", 0
color BYTE ?.code
main PROCmov esi, OFFSET buffer;获取控制台窗口当前的前景色和背景色,它没有输入参数,;返回时,AL中的高四位是背景色,低四位是前景色call GetTextColormov color, AL;设置输出广西的前景色和背景色,调用SetTextColor时,给EAX分配一个颜色属性;mov eax, white + (blue * 16) ;白底黄字;call SetTextColormov ecx, LENGTHOF buffer
L1:mWritestring myString, [esi]inc esiloop L1;恢复颜色call SetTextColorINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
*3.宏mMove32
编写宏 mMove32,接收两个32 位的内存操作数,并将源操作数传送到目的操作数。编写程序对宏进行测试。
;10.8_3.asm 10.8 编程练习 *3.宏mMove32
;编写宏 mMove32,接收两个32 位的内存操作数,
;并将源操作数传送到目的操作数。编写程序对宏进行测试。INCLUDE Irvine32.incmMove32 MACRO src, dstpush eaxmov eax, srcmov dst, eaxpop eax
ENDM.data
source DWORD 1111h, 2222h, 3333h, 4444h
target DWORD 4 DUP(?).code
main PROCmov esi, OFFSET sourcemov edi, OFFSET targetmov ecx, LENGTHOF source
L1:mMove32 [esi], [edi]add esi, 4add edi, 4loop L1call CrlfINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
调用宏之后
*4.宏 mMult32
创建宏 mMult32,将两个32 位内存操作数相乘,生成一个32 位的乘积。编写程序对宏进行测试。
;10.8_4.asm 10.8 编程练习 *4.宏 mMult32INCLUDE Irvine32.incmMult32 MACRO val1, val2mov eax, val1mul val2
ENDM.data
value1 DWORD 12h
value2 DWORD 13h.code
main PROCmMult32 value1, value2call CrlfINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
**5.宏 mReadInt
创建宏 mReadInt,从标准输入读取一个16 位或 32 位的有符号整数,并用实参返回该值。用条件运算符使得宏能适应预期结果的大小。编写程序,向宏传递不同大小的操作数以对其进行测试。
;10.8_5.asm 10.8 编程练习 **5.宏 mReadIntINCLUDE Irvine32.incmReadInt MACRO val, valW, valD, eltTypemov eax, valIFIDNI <eltType>, <WORD> ;;不区分大小写mov valW, axELSEmov valD, eaxENDIFENDM.data
resW WORD ?
resD DWORD 0.code
main PROCmReadInt 12345678h, resW, resD, dwordmReadInt 1234h, resW, resD, wordcall CrlfINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
**6.宏 mWriteInt
创建宏 mWritelnt,通过调用 Writelnt 库过程向标准输出写一个有符号整数。向宏传递的参数可以是字节、字或双字。在宏内使用条件运算符,使之能适应实参的大小。编写程序,向宏传递不同大小的实参以对其进行测试。
;10.8_6.asm 10.8 编程练习 **6.宏 mWriteIntINCLUDE Irvine32.incmWriteInt MACRO val1, val2, val3
LOCAL prompt, prompt1, prompt2 ;;解决重入的问题
LOCAL LB, LW, LD, PRINT, quit
.data
prompt byte "Please enter an integer: ", 0
prompt1 byte "Please enter integer's type (0 for BYTE, 1 for WORD, 2 for DWORD): ", 0
prompt2 byte "Data type input error! ", 0.codemov edx, OFFSET promptcall WriteString ;;输入提示call ReadInt ;;从键盘读入一个整数mov ebx, eax ;;保存到ebx中call Crlf ;;换行mov edx, OFFSET prompt1call WriteString ;;输入提示call ReadInt ;;从键盘读入整数类型call Crlf ;;换行cmp eax, 0je LBcmp eax, 1je LWcmp eax, 2je LDmov edx, OFFSET prompt2call WriteString ;;错误提示jmp quit
LB:movzx eax, blmov val1, bljmp PRINT
LW:mov ax, bxmov val2, bxjmp PRINT
LD:mov eax, ebxmov val3, ebxPRINT:call WriteDecquit:call Crlf
ENDM.data
resB BYTE ?
resW WORD ?
resD DWORD ?.code
main PROCmov esi, OFFSET resBmWriteInt resB, resW, resDmWriteInt resB, resW, resDmWriteInt resB, resW, resDmWriteInt resB, resW, resDinvoke ExitProcess, 0
main ENDP
END main
运行调试:
***7.教授丢失的手机
当10.1.6节的醉酒教授在校园里绕圈子时,我们发现他在路上的某个地方丢失了手机。在对醉酒路线进行模拟时,程序必须在教授停留随机时长的任何地方丢掉手机。每次运行程序,都必须在不同的时间间隔(和位置)丢失手机。
;10.8_7.asm 10.8 编程练习 ***7.教授丢失的手机INCLUDE Irvine32.inc
WalkMax = 50
StartX = 25
StartY = 25DrunkardWalk STRUCTpath COORD WalkMax DUP(<0, 0>)pathsUsed WORD 0
DrunkardWalk ENDS
DisplayPosition PROTO currX:WORD, currY:WORD
DropPhonePosition PROTO position:DWORD, currX:WORD, currY:WORD.data
aWalk DrunkardWalk <>.code
main PROCmov esi, OFFSET aWalkcall TakeDrunkenWalkINVOKE ExitProcess, 0
main ENDP
;---------------------------------------------
;向随机方向行走(北、南、东、西)
;接收:ESI为Drunkardwalk结构的指针
;返回:结构初始化为随机数
;---------------------------------------------
TakeDrunkenWalk PROCLOCAL currX:WORD, currY:WORDpushad;用OFFSET运算符获取path--COORD对象数组--的地址,并将其复制到EDImov edi, esiadd edi, OFFSET DrunkardWalk.pathmov ecx, WalkMax ;循环计数器mov currX, StartX ;当前X的位置mov currY, StartY ;当前Y的位置;生成随机丢手机的位置mov eax, WalkMaxcall RandomRangemov ebx, eax ;把位置保存在ebx中mov edx, 0 ;比对位置
Again:;把当前位置插入数组mov ax, currXmov (COORD PTR [edi]).X, axmov ax, currYmov (COORD PTR [edi]).Y, axcmp edx, ebxjne nextINVOKE DropPhonePosition, ebx, currX, currYnext:INVOKE DisplayPosition, currX, currYmov eax, 4 ;选择一个方向(0-3)call RandomRange.IF eax == 0 ;北dec currY.ELSEIF eax == 1 ;南inc currY.ELSEIF eax == 2 ;西dec currX.ELSE ;东(EAX=3)inc currX.ENDIF ;指向下一个COORDadd edi, TYPE COORDinc edxloop Again
Finish:mov (DrunkardWalk PTR [esi]).pathsUsed, WalkMaxpopadret
TakeDrunkenWalk ENDP
;-----------------------------------------------
;显示当前x和Y的位置。
;------------------------------------------------
DisplayPosition PROC currX:WORD, currY:WORD
.data
commaStr BYTE ",",0
.codepushadmovzx eax, currX ;当前X的位置call WriteDecmov edx, OFFSET commaStr ;“,”字符串call WriteStringmovzx eax, currY ;当前Y的位置call WriteDeccall Crlfpopadret
DisplayPosition ENDP
;-----------------------------------------------
;显示丢手机的位置。
;------------------------------------------------
DropPhonePosition PROC position:DWORD, currX:WORD, currY:WORD
.data
commaString BYTE ",",0
msg BYTE "Phone droped at pos: ",0
.codepushadmov edx, OFFSET msg call WriteString ;显示丢手机的位置mov eax, positioncall WriteDecmov al, 9 ;TAB空位call WriteCharmovzx eax, currX ;当前X的位置call WriteDecmov edx, OFFSET commaString ;“,”字符串call WriteStringmovzx eax, currY ;当前Y的位置call WriteDeccall Crlfpopadret
DropPhonePosition ENDP
END main
运行调试:
***8.带概率的醉汉行走问题
在测试 DrunkardWalk 程序时,你可能已经注意到教授徘徊的位置距离起点不会太远。这种情况毫无疑问是由教授在各方向移动的等概率造成的。因此按照如下条件来修改程序:教授有 50%的概率沿着与上一步相同的方向行走;有 10% 的概率选择相反的方向;有20%的概率会向右或向左转。循环开始前指定一个默认的起步方向。
;10.8_8.asm 10.8 编程练习 ***8.带概率的醉汉行走问题INCLUDE Irvine32.inc
WalkMax = 30
StartX = 25
StartY = 25DrunkardWalk STRUCTpath COORD WalkMax DUP(<0, 0>)pathsUsed WORD 0
DrunkardWalk ENDS
DisplayPosition PROTO currX:WORD, currY:WORD
DropPhonePosition PROTO position:DWORD, currX:WORD, currY:WORD
GetNextDirection PROTO position:DWORD.data
aWalk DrunkardWalk <>.code
main PROCmov esi, OFFSET aWalkcall TakeDrunkenWalkINVOKE ExitProcess, 0
main ENDP
;---------------------------------------------
;向随机方向行走(北、南、东、西)
;接收:ESI为Drunkardwalk结构的指针
;返回:结构初始化为随机数
;---------------------------------------------
TakeDrunkenWalk PROCLOCAL currX:WORD, currY:WORD, lastDirection:DWORDpushad;用OFFSET运算符获取path--COORD对象数组--的地址,并将其复制到EDImov edi, esiadd edi, OFFSET DrunkardWalk.pathmov ecx, WalkMax ;循环计数器mov currX, StartX ;当前X的位置mov currY, StartY ;当前Y的位置;生成随机丢手机的位置mov eax, WalkMaxcall RandomRangemov ebx, eax ;把位置保存在ebx中mov edx, 0 ;比对位置mov lastDirection, 0 ;默认位置Again:;把当前位置插入数组mov ax, currXmov (COORD PTR [edi]).X, axmov ax, currYmov (COORD PTR [edi]).Y, axcmp edx, ebxjne nextINVOKE DropPhonePosition, ebx, currX, currYnext:INVOKE DisplayPosition, currX, currY;mov eax, 4 ;选择一个方向(0-3);call RandomRange;调用概率方向INVOKE GetNextDirection, lastDirection.IF eax == 0 ;北dec currY.ELSEIF eax == 1 ;南inc currY.ELSEIF eax == 2 ;西dec currX.ELSE ;东(EAX=3)inc currX.ENDIF ;指向下一个COORDmov lastDirection, eax ;保存上一次的方向add edi, TYPE COORDinc edxloop Again
Finish:mov (DrunkardWalk PTR [esi]).pathsUsed, WalkMaxpopadret
TakeDrunkenWalk ENDP
;-----------------------------------------------
;获取概率方向。50%概率同上一步, 10%概率相反
;20%概率左, 20%概率右
;------------------------------------------------
GetNextDirection PROC direction:DWORDmov eax, 10 ;按对应的比例,分成4个方向(0~3)call RandomRange ;生成随机数0——9.IF (eax >=0) && (eax < 5) ;50%的概率同上一步mov eax, direction.ELSEIF eax == 5 ;10%概率相反方向.IF direction == 0mov eax, 1.ELSEIF direction == 1mov eax, 0.ELSEIF direction == 2mov eax, 3.ELSEIF direction == 3mov eax, 2.ENDIF.ELSEIF (eax == 6) || (eax == 7) ;20%概率向左mov eax, 2.ELSEIF (eax == 6) || (eax == 7) ;20%概率向右mov eax, 3.ENDIFret
GetNextDirection ENDP
;-----------------------------------------------
;显示当前x和Y的位置。
;------------------------------------------------
DisplayPosition PROC currX:WORD, currY:WORD
.data
commaStr BYTE ",",0
.codepushadmovzx eax, currX ;当前X的位置call WriteDecmov edx, OFFSET commaStr ;“,”字符串call WriteStringmovzx eax, currY ;当前Y的位置call WriteDeccall Crlfpopadret
DisplayPosition ENDP
;-----------------------------------------------
;显示丢手机的位置。
;------------------------------------------------
DropPhonePosition PROC position:DWORD, currX:WORD, currY:WORD
.data
commaString BYTE ",",0
msg BYTE "Phone droped at pos: ",0
.codepushadmov edx, OFFSET msg call WriteString ;显示丢手机的位置mov eax, positioncall WriteDecmov al, 9 ;TAB空位call WriteCharmovzx eax, currX ;当前X的位置call WriteDecmov edx, OFFSET commaString ;“,”字符串call WriteStringmovzx eax, currY ;当前Y的位置call WriteDeccall Crlfpopadret
DropPhonePosition ENDP
END main
运行调试:
****9.移位多个双字创
建一个宏,使用 SHRD 和SHLD 指令,将一个32位整数数组向任意方向移动可变的位数。编写程序对宏进行测试,把同一个数组向两个方向移动并显示结果。可以假设数组为小端顺序。宏声明示例如下:
mShiftDoublewords MACRO arrayName, direction, numberOfBits
Parameters:arrayName Name of the arraydirection Right (R) or Left (L)numberOfBits Number of bit positions to shift
***10.三操作数指令
有些计算机指令集允许算术运算指令有三个操作数。这种操作有时出现在用于向学生介绍汇编语言概念的简单虚拟汇编器中,或是在编译器中使用中间语言的时候。在下面的宏中,假设EAX 被保留用于宏操作,但是还未保存。其他会被宏修改的寄存器必须保存。所有的参数都是有符号的内存双字。编写宏模拟下述操作;
例如,下面的宏调用实现的是表达式x=(w+y)*z;
编写程序测试宏,要求实现 4个算术表达式,每个表达式都要包含多个操作