MOV DS,AX ; 2)AX goes to DS
这是很明显的。如果你只是在学习汇编,那么我可以看到这对你来说很重要(弄清楚指令在做什么),但是当你编写汇编代码并且你已经知道已经是基础知识的时候,这些注释是没有用的。你想知道什么想法促使你将该指令放在那里,所以像“将ds设置为INFORMS段”这样的东西可以更好地保留代码背后的想法。
对于关于指令本身的推理,您应该很快就能从头部正确地获得它们的大部分内容,否则不要犹豫,请参阅英特尔的CPU指令详细说明参考指南,这是最安全和最快捷的方式。验证你所有的假设。并经常使用它(像DIV
和MUL
可能每次检查它)。
LEA DX,MESSAGE ;4)load the word MESSAGE to DX
它加载的是“消息”的字符串的第一个字符的存储器地址转换成dx
。因此,如果INFORMS段开始,例如在物理存储器地址3000:0004
,然后dx
将等于4
,ds
将3000
(从码的开始),和装载一个字节从ds:dx
,在写为[ds:dx]
英特尔的语法,将取字节值为10
(这是在“MESSAGE”标签之后定义的第一个字节)。
“word MESSAGE”没有多大意义,在x86汇编中“word”通常意味着16b的值,而MESSAGE
就是标签,只是一个标记进入编译器的内存。它可能在符号表中的目标文件中结束,因此链接器/ os加载器可以重定位最终指令数据以包含正确的物理地址,否则它对于CPU来说是透明的,自动编译为无。
我的意思是MESSAGE DB 10
将编译为单个字节值为10
,“MESSAGE”不是机器代码的直接部分。
MOV AH,9 ;5)Give to 9 to AH
INT 21H ;6)Calls a subroutine. In this case it will print MESSAGE on screen
int 21h
是在调用的软件中断数21h
(33进制),这是在DOS主“OS”的服务处理器处理器CPU方面(DOS代表“磁盘操作系统” - 流行的替代品CP/M和其他操作系统......在有人称Linus之前,他写了商业UNIX SYSTEM V的无耻副本,并免费赠给其他人,所以现在你可以享受真正的操作系统而不是DOS,它是扩展)。
如何在使用int 21h
之前设置寄存器,以及它将如何操作,这一切都由DOS供应商定义,因为操作系统的代码在那里运行。由于MS-DOS几年前很流行,你可以找到许多描述这个的在线资源,例如this one。
也就是说,你应该宁愿以5) call "write string" DOS service
之类的评论结束,而不是显而易见的ah = 9
。
MOV AH,8H ;7)8h goes to AH?
为什么要标记问题?当然可以。它有什么问题是?您查看的文档!
ah=8
是“没有回音的字符输入”服务,并将用户输入的字符返回到寄存器al
中。
CMP AL,48;'0'30H ;8)Compare AL with 48?
48
是字符0
的ASCII编码。对于计算机,每个信息都必须转换为位,8位形成一个字节,它可以(不是“必须”)被视为0-255范围内的数值。出于各种方便和历史的原因,当文本信息被编码时,每个字符都有1个字节(在旧的ASCII编码中 - 在现代UTF-8扩展编码中,这不再是真实的!)。并且在ASCII表中定义哪些字符与从0
到255
的那些值配对。所以人类已知的字符编号为'0'
被编码为字节值48(以位本身的形式,它是00110000
)。
JB STARTING ;9)jump if below i think it is
CMP AL,57;'9' H 39H ;10)compare AL .I dont know
JA STARTING ;11) jumb if above
因此,4行代码部分正在测试中,用户输入的字符是否在48-57(含)范围内。否则,代码将“跳转”到标签STARTING
,稍后更多。此时检查ASCII表格,您会看到48-57的值覆盖了从'0'
到'9'
的所有数字字符,因此只有来自用户的数字字符输入才会使代码继续执行下一条指令。
MOV DL,AL ;12) AL goes to DL
MOV AH,2 ;13)2 goes to AH
INT 21H
这将使用另一个DOS服务“output char”。它也将在al
中返回最后输出的字符,因此在这种特殊情况下,这意味着al
的值不会改变,并且它仍然会保存用户输入的数字。
CMP AL,48 ;15) compare 48 with AL
JNE NEXT ;16)jump to next
当不相等时跳转。 cmp
将从al(temp = al - 48)中减去48,丢弃结果,并根据结果设置标志寄存器。
当算术运算结果为零时,称为“零标志”或“ZF”的标志之一被设置为1
。 ZF也用于JE/JNE
跳转(JZ/JNZ
的别名,用于更好的语义描述代码,当您在平等之后,而不是在零之后时)。所以当用户输入'0'
时,cmp
将设置ZF并且JNE NEXT
不会跳转(因为'0'等于48),但继续下一个指令,在屏幕上显示消息“ZERO”。
DIV BL ;22)0 divide the 2 or 2 divide the 0
超前DIV
寄存器的内容是ah=0, al=user input, bl=2
。 DIV r8
将做ax/bl
,并设置:ah
与其余和al
与商。
如果用户确实输入了例如字符数字'7'
,那就是al
= 55
。 ah
是0
。因此整个表格号码为55
(ax = ah * 256 + al)。
除以2 =>ah
= 1
(余数),al
= 27
。
其他代码现在应该更有意义,如果你通过关于最初部分的文本墙。因为同样的原则被反复使用。
INC SI
CMP SI,5
JB STARTING
这将作为全局计数器,所以要求用户提供一个数字的5倍(码“跳跃”到STARTING
当si
< 5
。在此之后,代码调用DOS服务终止程序执行(正确的方法退出)
现在总算对那些跳跃:
CPU是确定性的状态机,从一个国家到另一个,所有的时间,通过芯片上的时钟同步的转换。
在这样的过渡的开始它读取来自地址cs:ip
(cs
=代码段,ip
=指令指针)的字节(一个或多个),并对其进行解码作为一个单一的指令 - 它读取那么多的字节,直到命令解码器报告指令完成。它也将更新ip
的值由所使用的字节数(注意cs
永远不会更新,因此在ip
到达FFFFh
将在下一条指令,在相同的代码段从0000
环绕)。
然后它将解码后的指令分派到剩下的数百万个晶体管中去执行。
因此,在您的代码中,CPU正在按指令执行指令,一次执行一条指令,通过指令修改其内部状态,除非修改ip
中的值(对于“near “跳转操作码”)或cs:ip
(用于“远”跳转变体之一)。但是,这将使CPU执行新的ip
值的下一条指令,从而从程序员的角度来看源代码中的“跳转”。
各种jne, je, ...
指令是条件跳转(有时缩短为Jcc
),只有在满足某些条件时才执行“跳转”。对于jne
,助记符表示“跳转不等于”,所以跳转只在ZF为0时执行。如果您在jne
之前执行了cmp
,则效果可以描述为“比较两个值并在不相等时跳转”。但jne
不限于使用cmp
,您可以随时在代码中使用它,当您认为ZF包含一些有趣的值并且想要在其上分支时(尽管通常别名jz/jnz
=“跳转时零/当不为零“对于读者来说更有意义)。
当条件不满足时,cs:ip
不受影响,所以它仍然包含“下一条指令”(由CPU的指令解码器提供)的地址,因此您继续使用下一条指令读取源而不是跳转任何地方。
这个问题是题外话,因为,这不是一个问题。 –
装配很困难。它有助于解释你正在尝试做什么,你期望什么输出,以及实际发生了什么。 –
我想解释一下jmp的使用和所有以jmp,jne等为开头的练习(如果我正在做和更多的错误请告诉我) –