来自于《Intel汇编语言程序设计》(第四版)第10章----结构和宏。
所谓醉汉走路,即在一个XY坐标的二维地图中,模拟一个醉汉走路的轨迹(原书中说的是一个喝醉了酒还未清醒的教授)。此处仍然用到了结构和宏,来看一下原书代码:
TITLE Drunkard's Walk (Walk.asm)
INCLUDE Irvine32.inc
WalkMax = 50
StartX = 25
StartY = 25
DrunkardWalk STRUCT
path COORD WalkMax DUP(<0,0>)
pathsUsed WORD 0
DrunkardWalk ENDS
DisplayPosition PROTO currX:WORD,currY:WORD
.data
aWalk DrunkardWalk <>
.code
main PROC
mov esi,offset aWalk
call TakeDrunkenWalk
exit
main ENDP
;------------------------------------------------------------------------------
TakeDrunkenWalk PROC
LOCAL currX:WORD,currY:WORD
;
; Take a walk in random direction (north ,south,east,west).
; Receives : ESI points to a DrunkardWalk structure
; Returns : the structure is initialized with random values
;------------------------------------------------------------------------------
pushad
; Point EDI to the array of COORD objects.
mov edi,esi
add edi,OFFSET DrunkardWalk.path
mov ecx,WalkMax ; loop count
mov currX,StartX ; current X-location
mov currY,StartY ; current Y-location
Again:
; insert current location in array
mov ax,currX
mov (COORD PTR [edi]).X , ax
mov ax,currY
mov (COORD PTR [edi]).Y , ax
INVOKE DisplayPosition , currX , currY
mov eax,4 ; choose a direction (0-3)
call RandomRange
.IF eax==0 ; north
inc currY
.ELSEIF eax==1 ; south
dec currY
.ELSEIF eax==2 ; west
dec currX
.ELSE ; east (eax = 3)
inc currX
.ENDIF
add edi,TYPE COORD ; point to next COORD
loop Again
Finish:
mov (DrunkardWalk PTR [esi]).pathsUsed,WalkMax
popad
ret
TakeDrunkenWalk ENDP
;------------------------------------------------------------------------------
DisplayPosition PROC currX:WORD,currY:WORD
; Display the current X and Y position.
;------------------------------------------------------------------------------
.data
commaStr BYTE ",",0
.code
pushad
movzx eax,currX ; current X postion
call WriteDec
mov edx, OFFSET commaStr ; "," string
call WriteString
movzx eax,currY ; current Y position
call WriteDec
call Crlf
popad
ret
DisplayPosition ENDP
END main
让我们用代码注释来具体分析:
TITLE Drunkard's Walk (Walk.asm)
INCLUDE Irvine32.inc ; 包含了作者的库,许多函数就来自于这个库,比如WriteString,WriteDec,RandomRange函数等等
WalkMax = 50 ; 最多能行走的步数
StartX = 25 ; 开始X坐标
StartY = 25 ; 开始Y坐标
DrunkardWalk STRUCT ; 一个醉汉的结构
path COORD WalkMax DUP(<0,0>) ; 一个存储行走过得坐标位置的COORD类型的结构数组
pathsUsed WORD 0 ; 存储行走过的步数
DrunkardWalk ENDS ; 结构结束
DisplayPosition PROTO currX:WORD,currY:WORD ; DisplayPosition 函数的原型声明
.data
aWalk DrunkardWalk <> ; 声明了一个DrunkardWalk 结构变量aWalk
.code
main PROC ; 开始主程序
mov esi,offset aWalk ; 将aWalk 的偏移地址赋值到esi中
call TakeDrunkenWalk ; 调用本逻辑的主要函数TakeDrunkenWalk,让“醉汉”走起来
exit
main ENDP
;------------------------------------------------------------------------------
TakeDrunkenWalk PROC
LOCAL currX:WORD,currY:WORD
;
; Take a walk in random direction (north ,south,east,west).
; Receives : ESI points to a DrunkardWalk structure ;记得调用之前把aWalk赋给了ESI吗?本函数接收esi为参数
; Returns : the structure is initialized with random values
;------------------------------------------------------------------------------
pushad ; 以防万一,将所有寄存器压入堆栈
; Point EDI to the array of COORD objects.
mov edi,esi ; 将ESI赋到edi中
add edi,OFFSET DrunkardWalk.path ; 计算出了aWalk结构中的path成员地址
mov ecx,WalkMax ; loop count ; 最多能走的步数赋到ecx中
mov currX,StartX ; current X-location ; 开始X坐标赋到currX中
mov currY,StartY ; current Y-location ; 开始Y坐标赋到currY中
Again:
; insert current location in array
mov ax,currX ; 将currX保存到ax中
mov (COORD PTR [edi]).X , ax ; 将ax赋到aWalk结构中的path成员的X成员,因为是间接引用,所以这里使用了PTR
mov ax,currY
mov (COORD PTR [edi]).Y , ax
INVOKE DisplayPosition , currX , currY ; 调用DisplayPosition 打印目前的XY坐标
mov eax,4 ; choose a direction (0-3) ; 将4赋值到eax中,用来生成随机数,范围为0-3
call RandomRange ; 生成随机数,仍然保存在eax中
.IF eax==0 ; north ; 判断生成的随机数到底是哪个方向,然后分情况进行相应计算
inc currY
.ELSEIF eax==1 ; south
dec currY
.ELSEIF eax==2 ; west
dec currX
.ELSE ; east (eax = 3)
inc currX
.ENDIF
add edi,TYPE COORD ; 指向path成员的下一个坐标地址
loop Again ; 接着走醉步
Finish:
mov (DrunkardWalk PTR [esi]).pathsUsed,WalkMax ; 不知道这里要为什么要赋回去
popad
ret
TakeDrunkenWalk ENDP
;------------------------------------------------------------------------------
DisplayPosition PROC currX:WORD,currY:WORD
; 用来将X,Y坐标输出的函数,接收X,Y坐标作为参数
;------------------------------------------------------------------------------
.data
commaStr BYTE ",",0
.code
pushad
movzx eax,currX ; current X postion
call WriteDec
mov edx, OFFSET commaStr ; "," string
call WriteString
movzx eax,currY ; current Y position
call WriteDec
call Crlf
popad
ret
DisplayPosition ENDP
END main