精华内容
下载资源
问答
  • Rewirte主要的功能就是实现URL的跳转和隐藏真实地址,基于Perl语言的正则表达式规范。平时帮助我们实现拟静态,拟...本文将针对mod_rewrite和URL匹配的技术细节,以及RewriteCond与RewriteRule 指令格式进行探讨。Rew

    参考:http://www.jb51.net/article/39298.htm

    Rewirte主要的功能就是实现URL的跳转和隐藏真实地址,基于Perl语言的正则表达式规范。平时帮助我们实现拟静态,拟目录,域名跳转,防止盗链等。本文将针对mod_rewrite和URL匹配的技术细节,以及RewriteCond与RewriteRule 指令格式进行探讨。

    Rewirte模块内部处理

    Rewirte模块的内部处理极为复杂,但是为了使一般用户避免犯低级错误,也让管理员能充分利用其功能,在此仍然做一下说明。

    Rewirte模块API阶段

    首先,你必须了解Apache是分若干阶段来处理HTTP请求的。Apache API对每个阶段都提供了一个hook程序。mod_rewrite使用两个hook程序:其一,从URL到文件名的转换hook(用在读取HTTP请求之后、授权开始之前); 其二,修正hook(用在授权阶段和读取目录级配置(.htaccess)之后、内容处理器激活之前)。

    所以,Apache收到一个请求并且确定了响应主机(或虚拟主机)之后,重写引擎即开始处理服务器级配置中的所有mod_rewrite指令(此时处于从URL到文件名转换的阶段),此阶段完成后,最终的数据目录便确定了。接下来进入修正程序段并触发目录级配置中的mod_rewrite指令。这两个阶段并不是泾渭分明的,但都实施了把URL重写成新的URL或者文件名。虽然API最初不是为此目的而设计的,但是现在它已经成为了API的一种用途。记住以下两点,会有助于更好地理解:

    1、虽然mod_rewrite可以将URL重写为新的URL或文件名,甚至将文件名重写为新的文件名,但是之前的API只提供从URL到文件名的hook。在Apache 2.0中,增加了两个丢失的hook以使得处理过程更加清晰。不过这样做并没有给用户带来麻烦,用户只需记住这样一个事实:借助从URL到文件名的hook比最初API设计的目标功能更强大。

    2、令人难以置信的是,mod_rewrite还提供了目录级的URL操作(.htaccess文件),而这些文件必须在将URL转换成文件名之后才会被处理(这是必须的,因为.htaccess存在于文件系统中)。换句话说,根据API阶段,这时再处理任何URL操作已经太晚了。为了解决这个”鸡和蛋”的问题,mod_rewrite使用了一个小技巧:在进行一个目录级的URL/文件名操作时,先把文件名重写回相应的URL(通常这个操作是不可行的,但是参考下面的RewriteBase指令就能明白它是怎么实现的了),然后,对这个新的URL建立一个新的内部的子请求,再重新开始API阶段的执行。

    另外,mod_rewrite尽力使这些复杂的操作对用户透明。但仍须记住:服务器级的URL操作速度快而且效率高,而目录级的操作由于这个”鸡和蛋”的问题速度较慢而且效率也低。但从另一个侧面看,这却是mod_rewrite得以为一般用户提供(局部限制的)URL操作的唯一方法。

    Rewirte模块规则集的处理

    当mod_rewrite在这两个API阶段中开始执行时,它会读取配置结构中配置好的 (或者是在服务启动时建立的服务器级的,或者是在遍历目录采集到的目录级的)规则集,然后,启动URL重写引擎来处理(带有一个或多个条件的)规则集。无论是服务器级的还是目录级的规则集,都是由同一个URL重写引擎处理,只是最终结果处理不同而已。

    规则集中规则的顺序是很重要的,因为重写引擎是按一种特殊的顺序处理的:逐个遍历每个规则(RewriteRule指令),如果出现一个匹配条件的规则,则可能回头遍历已有的规则条件(RewriteCond指令)。由于历史的原因,条件规则是前置的,所以控制流程略显冗长,细节见图-1。


    图-1:重写规则集中的控制流

     

    可见,URL首先与每个规则的Pattern匹配,如果匹配失败,mod_rewrite将立即终止此规则的处理,继而处理下一个规则。如果匹配成功,mod_rewrite将寻找相应的规则条件,如果一个条件都没有,则简单地用Substitution构造的新值来替换URL,然后继续处理其他规则;但是如果条件存在,则开始一个内部循环按其列出的顺序逐个处理。对规则条件的处理有所不同:URL并不与模式进行匹配,而是首先通过扩展变量、反向引用、查找映射表等步骤建立一个TestString字符串,然后用它来与CondPattern匹配。如果匹配失败,则整个条件集和对应的规则失败;如果匹配成功,则执行下一个规则直到所有条件执行完毕。如果所有条件得以匹配,则以Substitution替换URL,并且继续处理。(本部分引用译者:金步国)

    RewriteCond指令格式

    语法: RewriteCond TestString CondPattern [flags]

    RewriteCond指令定义一条规则条件。在一条RewriteRule指令前面可能会有一条或多条RewriteCond指令,只有当自身的模板(pattern)匹配成功且这些条件也满足时规则才被应用于当前URL处理。

    1、 TestString是一个纯文本的字符串,除了包含普通的字符外,还可以包括下列的可扩展结构:

    1)$N:RewriteRule后向引用,其中(0 <= N <= 9) 。$N引用紧跟在RewriteCond后面的RewriteRule中模板中的括号中的模板在当前URL中匹配的数据。

    2)%N:RewriteCond后向引用,其中(0 <= N <= 9) 。%N引用最后一个RewriteCond的模板中的括号中的模板在当前URL中匹配的数据。

    3)${mapname:key|default}:RewriteMap扩展。

    2、CondPattern是条件pattern, 即一个应用于当前实例TestString的正则表达式, 即TestString将会被计算然后与CondPattern匹配。作为一个标准的扩展正则式,CondPattern有以下补充:

    1)可以在模板串前增加一个!前缀,以用表示不匹配模板。但并不是所有的test都可以加!前缀。

    2)CondPattern中可以使用以下特殊变量:

    '>CondPattern’ (大于) 将condPattern当作一个普通字符串,将它和TestString进行比较,当TestString 的字符大于CondPattern为真。

    ‘=CondPattern’ (等于) 将condPattern当作一个普通字符串,将它和TestString进行比较,当TestString 与CondPattern完全相同时为真.如果CondPattern只是 “” (两个引号紧挨在一起) 此时需TestString 为空字符串方为真。

    ‘-d’ (是否为目录) 将testString当作一个目录名,检查它是否存在以及是否是一个目录。

    ‘-f’ (是否是regular file) 将testString当作一个文件名,检查它是否存在以及是否是一个regular文件。

    ‘-s’ (是否为长度不为0的regular文件) 将testString当作一个文件名,检查它是否存在以及是否是一个长度大于0的regular文件。

    ‘-l’ (是否为symbolic link) 将testString当作一个文件名,检查它是否存在以及是否是一个 symbolic link。

    ‘-F’ (通过subrequest来检查某文件是否可访问) 检查TestString是否是一个合法的文件,而且通过服务器范围内的当前设置的访问控制进行访问。这个检查是通过一个内部subrequest完成的, 因此需要小心使用这个功能以降低服务器的性能。

    ‘-U’ (通过subrequest来检查某个URL是否存在) 检查TestString是否是一个合法的URL,而且通过服务器范围内的当前设置的访问控制进行访问。这个检查是通过一个内部subrequest完成的, 因此需要小心使用这个功能以降低服务器的性能。

    3、[flags]是第三个参数,多个标志之间用逗号分隔。

    1)’nocase|NC’ (不区分大小写)   在扩展后的TestString和CondPattern中,比较时不区分文本的大小写。注意,这个标志对文件系统和subrequest检查没有影响.

    2)’ornext|OR’ (建立与下一个条件的或的关系)   默认的情况下,二个条件之间是AND的关系,用这个标志将关系改为OR。例如: RewriteCond %{REMOTE_HOST} ^host1.* [OR] RewriteCond %{REMOTE_HOST} ^host2.* [OR] RewriteCond %{REMOTE_HOST} ^host3.* RewriteRule … 如果没有[OR]标志,需要写三个条件/规则.

    RewriteRule 指令

    语法: RewriteRule Pattern Substitution [flags]

    1) Pattern是一个作用于当前URL的兼容perl的正则表达式. 这里的“当前”是指该规则生效时的URL的值。

    2) Substitution是,当原始URL与Pattern相匹配时,用以替代(或替换)的字符串。

    3) 此外,Substitution还可以追加特殊标记[flags] 作为RewriteRule指令的第三个参数。 Flags是一个包含以逗号分隔的下列标记的列表:

    redirect|R [=code] (强制重定向 redirect)

    以 http://thishost[:thisport]/(使新的URL成为一个URI) 为前缀的Substitution可以强制性执行一个外部重定向。 如果code没有指定,则产生一个HTTP响应代码302(临时性移动)。如果需要使用在300-400范围内的其他响应代码,只需在此指定这个数值即可, 另外,还可以使用下列符号名称之一: temp (默认的), permanent, seeother. 用它可以把规范化的URL反馈给客户端,如, 重写“/~”为 “/u/”,或对/u/user加上斜杠,等等。

    注意: 在使用这个标记时,必须确保该替换字段是一个有效的URL! 否则,它会指向一个无效的位置! 并且要记住,此标记本身只是对URL加上 http://thishost[:thisport]/的前缀,重写操作仍然会继续。通常,你会希望停止重写操作而立即重定向,则还需要使用’L’标记.

    forbidden|F (强制URL为被禁止的 forbidden)

    强制当前URL为被禁止的,即,立即反馈一个HTTP响应代码403(被禁止的)。使用这个标记,可以链接若干RewriteConds以有条件地阻塞某些URL。

    gone|G’(强制URL为已废弃的 gone)

    强制当前URL为已废弃的,即,立即反馈一个HTTP响应代码410(已废弃的)。使用这个标记,可以标明页面已经被废弃而不存在了.

    proxy|P (强制为代理 proxy)

    此标记使替换成分被内部地强制为代理请求,并立即(即, 重写规则处理立即中断)把处理移交给代理模块。你必须确保此替换串是一个有效的(比如常见的以 http://hostname开头的)能够为Apache代理模块所处理的URI。使用这个标记,可以把某些远程成分映射到本地服务器名称空间, 从而增强了ProxyPass指令的功能。

    注意: 要使用这个功能,代理模块必须编译在Apache服务器中。 如果你不能确定,可以检查“httpd -l”的输出中是否有mod_proxy.c。 如果有,则mod_rewrite可以使用这个功能;如果没有,则必须启用mod_proxy并重新编译“httpd”程序。

    last|L (最后一个规则 last)

    立即停止重写操作,并不再应用其他重写规则。 它对应于Perl中的last命令或C语言中的break命令。这个标记可以阻止当前已被重写的URL为其后继的规则所重写。 举例,使用它可以重写根路径的URL(’/’)为实际存在的URL, 比如, ‘/e/www/’.

    next|N (重新执行 next round)

    重新执行重写操作(从第一个规则重新开始). 这时再次进行处理的URL已经不是原始的URL了,而是经最后一个重写规则处理的URL。它对应于Perl中的next命令或C语言中的continue命令。 此标记可以重新开始重写操作,即, 立即回到循环的头部。
    但是要小心,不要制造死循环!

    chain|C (与下一个规则相链接 chained)

    此标记使当前规则与下一个(其本身又可以与其后继规则相链接的, 并可以如此反复的)规则相链接。 它产生这样一个效果: 如果一个规则被匹配,通常会继续处理其后继规则, 即,这个标记不起作用;如果规则不能被匹配,则其后继的链接的规则会被忽略。比如,在执行一个外部重定向时, 对一个目录级规则集,你可能需要删除“.www” (此处不应该出现“.www”的)。

    type|T=MIME-type(强制MIME类型 type)

    强制目标文件的MIME类型为MIME-type。 比如,它可以用于模拟mod_alias中的ScriptAlias指令,以内部地强制被映射目录中的所有文件的MIME类型为“application/x-httpd-cgi”。

    nosubreq|NS (仅用于不对内部子请求进行处理 no internal sub-request)

    在当前请求是一个内部子请求时,此标记强制重写引擎跳过该重写规则。比如,在mod_include试图搜索可能的目录默认文件(index.xxx)时, Apache会内部地产生子请求。对子请求,它不一定有用的,而且如果整个规则集都起作用,它甚至可能会引发错误。所以,可以用这个标记来排除某些规则。

    根据你的需要遵循以下原则: 如果你使用了有CGI脚本的URL前缀,以强制它们由CGI脚本处理,而对子请求处理的出错率(或者开销)很高,在这种情况下,可以使用这个标记。

    nocase|NC (忽略大小写 no case)

    它使Pattern忽略大小写,即, 在Pattern与当前URL匹配时,’A-Z’ 和’a-z’没有区别。

    qsappend|QSA (追加请求串 query string append)

    此标记强制重写引擎在已有的替换串中追加一个请求串,而不是简单的替换。如果需要通过重写规则在请求串中增加信息,就可以使用这个标记。

    noescape|NE (在输出中不对URI作转义 no URI escaping)

    此标记阻止mod_rewrite对重写结果应用常规的URI转义规则。 一般情况下,特殊字符(如’%’, ‘$’, ‘;’等)会被转义为等值的十六进制编码。 此标记可以阻止这样的转义,以允许百分号等符号出现在输出中,如:

    RewriteRule /foo/(.*) /bar?arg=P1\=$1 [R,NE] 可以使’/foo/zed’转向到一个安全的请求’/bar?arg=P1=zed’.

    passthrough|PT (移交给下一个处理器 pass through)

    此标记强制重写引擎将内部结构request_rec中的uri字段设置为 filename字段的值,它只是一个小修改,使之能对来自其他URI到文件名翻译器的 Alias,ScriptAlias, Redirect 等指令的输出进行后续处理。举一个能说明其含义的例子:如果要通过mod_rewrite的重写引擎重写/abc为/def,然后通过mod_alias使/def转变为/ghi,可以这样:

    RewriteRule ^/abc(.*) /def$1 [PT]

    Alias /def /ghi
    如果省略了PT标记,虽然mod_rewrite运作正常, 即, 作为一个使用API的URI到文件名翻译器,它可以重写uri=/abc/…为filename=/def/…,但是,后续的mod_alias在试图作URI到文件名的翻译时,则会失效。

    注意: 如果需要混合使用不同的包含URI到文件名翻译器的模块时, 就必须使用这个标记。。混合使用mod_alias和mod_rewrite就是个典型的例子。

    For Apache hackers

    如果当前Apache API除了URI到文件名hook之外,还有一个文件名到文件名的hook, 就不需要这个标记了! 但是,如果没有这样一个hook,则此标记是唯一的解决方案。 Apache Group讨论过这个问题,并在Apache 2.0 版本中会增加这样一个hook。

    skip|S=num (跳过后继的规则 skip)

    此标记强制重写引擎跳过当前匹配规则后继的num个规则。 它可以实现一个伪if-then-else的构造: 最后一个规则是then从句,而被跳过的skip=N个规则是else从句. (它和’chain|C’标记是不同的!)

    env|E=VAR:VAL (设置环境变量 environment variable)

    此标记使环境变量VAR的值为VAL, VAL可以包含可扩展的反向引用的正则表达式$N和%N。 此标记可以多次使用以设置多个变量。这些变量可以在其后许多情况下被间接引用,但通常是在XSSI (via ) or CGI (如 $ENV{’VAR’})中, 也可以在后继的RewriteCond指令的pattern中通过%{ENV:VAR}作引用。使用它可以从URL中剥离并记住一些信息。

    cookie|CO=NAME:VAL:domain[:lifetime[:path]] (设置cookie)

    它在客户端浏览器上设置一个cookie。 cookie的名称是NAME,其值是VAL。 domain字段是该cookie的域,比如’.apache.org’, 可选的lifetime是cookie生命期的分钟数,可选的path是cookie的路径。

    案例:

    city_map.txt的内容:

    hangzhou 12

    beijing 13

    1、hangzhou.google.com/tianqi/20090401 跳转到 www.google.com/service/detail.html?id=tianqi&date=20090401

    RewriteMap city-map txt:/etc/httpd/conf.d/map/city_map.txt 
      
    RewriteCond %{HTTP_HOST}    ^(.+)\.google\.com$ 
      
    RewriteRule ^/([\w]+)/([\d]+)$ /service/detail\.html\?id=$1&date=$2&c=${city-map:%1|%1} [PT,L]
    


     

     

    解释:

    %{HTTP_HOST}:取请求的域名

    ^(.+)\.google\.com$:^,开头;$结尾。.(逗号),除终止符外的任意字符。+,重复一个或一个以上的字符。\,转义字符。

    ^/([\w]+)/([\d]+)$:[],集合字符。\w,数字或字母。\d,数字。

    $1:表示的是符合RewriteRule 中[\w]+正则式的字符串,也就是tianqi。

    $2:表示的是符合RewriteRule 中[\d]+ 正则式的字符串,也就是20090401。

    %1:表示的是符合RewriteCond 中.+正则式的字符串,也就是hangzhou。

    ${city-map:%1|%1}:表示取city-map中%1也就是hangzhou对应的值,如果没有则为%1也就是hangzhou。

    2、能看出下面的规则是做了什么吗?

    RewriteCond     %{HTTP_HOST}    ^(.+)\.google\.com$ 
      
    RewriteRule ^/([\w]+)/([^-]+)-([^-]+)--([^-]+)-([^-]+)--([^-]+)-([^-]+)--([^-]+-[^-]+--[^-]+-[^-]+--[^-]+-[^-]+)$  /$1/$2=$3&$4=$5&$6=$7&$8   [C]  
      
    RewriteCond     %{HTTP_HOST}    ^(.+)\.google\.com$ 
      
    RewriteRule ^/([\w]+)/([^-]+)-([^-]+)--([^-]+)-([^-]+)--([^-]+)-([^-]+)$     /service/list\.html\?frontCategoryId=${category-map:$1|0}&$2=$3&$4=$5&$6=$7&city=${city-map:%1|%1}   [PT,L]
    


     

    解释:

    这个规则是想把-(中划线)转为=,把- -(两条中划线)转为&。

    [^-]:^在字符集合符号([])之内表示反向选择,之外表示行首,所以表示不以-开头。

    因为$N,N最大为9,所以使用了C,用第二条RewriteRule把第一条RewriteRule中的最后一个节点,即$8,进行继续转换。

    此外,rewrite规则中如果遇到中文,相当有可能会出现乱码问题,因为apache在rewrite时会做一次url解码,这时jk进行请求转发时,就不会再是编码后的字符串了。此种情况,可以在一开始就进行两次编码(encode),或者在接收请求时先用ISO-8859-1取字节流,再使用UFT-8来new String。(new String(str.getBytes(”ISO-8859-1″),”UTF-8″))

     
    来源:cnblogs.com
    展开全文
  • 指令引用的 0x00000000内存,该内存不能written”,然后应用程序被关闭。如果去请教一些“高手”,得到的回答往往是“Windows就是这样不稳定”之类的义愤和不屑。其实,这个错误并不一定是Windows不稳定造成的。本文...

    内存不能read 或written的常见原因:
    使用Windows操作系统的人有时会遇到这样的错误信息:“0X????????指令引用的 0x00000000内存,该内存不能written”,然后应用程序被关闭。如果去请教一些“高手”,得到的回答往往是“Windows就是这样不稳定”之类的义愤和不屑。其实,这个错误并不一定是Windows不稳定造成的。本文就来简单分析这种错误的常见原因。

    一、应用程序没有检查内存分配失败
    程序需要一块内存用以保存数据时,就需要调用操作系统提供的“功能函数”来申请,如果内存分配成功,函数就会将所新开辟的内存区地址返回给应用程序,应用程序就可以通过这个地址使用这块内存。这就是“动态内存分配”,内存地址也就是编程中的“指针”。
    内存不是永远都招之即来、用之不尽的,有时候内存分配也会失败。当分配失败时系统函数会返回一个0值,这时返回值“0”已不表示新启用的指针,而是系统向应用程序发出的一个通知,告知出现了错误。作为应用程序,在每一次申请内存后都应该检查返回值是否为0,如果是,则意味着出现了故障,应该采取一些措施挽救,这就增强了程序的“健壮性”。
    若应用程序没有检查这个错误,它就会按照“思维惯性”认为这个值是给它分配的可用指针,继续在之后的运行中使用这块内存。真正的0地址内存区保存的是计算机系统中最重要的“中断描述符表”,绝对不允许应用程序使用。在没有保护机制的操作系统下(如DOS),写数据到这个地址会导致立即死机,而在健壮的操作系统中,如Windows等,这个操作会马上被系统的保护机制捕获,其结果就是由操作系统强行关闭出错的应用程序,以防止其错误扩大。这时候,就会出现上述的“写内存”错误,并指出被引用的内存地址为“0x00000000”。
    内存分配失败故障的原因很多,内存不够、系统函数的版本不匹配等都可能有影响。因此,这种分配失败多见于操作系统使用很长时间后,安装了多种应用程序(包括无意中“安装”的病毒程序),更改了大量的系统参数和系统文件之后。
    二、应用程序由于自身BUG引用了不正常的内存指针
    在使用动态分配的应用程序中,有时会有这样的情况出现:程序试图读写一块“应该可用”的内存,但不知为什么,这个预料中可用的指针已经失效了。有可能是“忘记了”向操作系统要求分配,也可能是程序自己在某个时候已经注销了这块内存而“没有留意”等等。注销了的内存被系统回收,其访问权已经不属于该应用程序,因此读写操作也同样会触发系统的保护机制,企图“违法”的程序唯一的下场就是被操作终止运行,回收全部资源。计算机世界的法律还是要比人类有效和严厉得多啊!像这样的情况都属于程序自身的BUG,你往往可在特定的操作顺序下重现错误。无效指针不一定总是0,因此错误提示中的内存地址也不一定为 “0x00000000”,而是其他随机数字。
    如果系统经常有所提到的错误提示,下面的建议可能会有帮助:
    1.查看系统中是否有木马或病毒。这类程序为了控制系统往往不负责任地修改系统,从而导致操作系统异常。平常应加强信息安全意识,对来源不明的可执行程序绝不好奇。
    2.更新操作系统,让操作系统的安装程序重新拷贝正确版本的系统文件、修正系统参数。有时候操作系统本身也会有BUG,要注意安装官方发行的升级程序。
    3.试用新版本的应用程序。

    运行某些程序的时候,有时会出现内存错误的提示,然后该程序就关闭。

    “0x????????”指令引用的“0x????????”内存。该内存不能为“read”。

    “0x????????”指令引用的“0x????????”内存,该内存不能为“written”。

    不知你出现过类似这样的故障吗?(0x后面内容有可能不一样。)

    一般出现这个现象有方面的,一是硬件,即内存方面有问题,二是软件,这就有多方面的问题了。

    下面先说说硬件:

    一般来说,内存出现问题的可能性并不大,主要方面是:内存条坏了、内存质量有问题,还有就是2个不同牌子不同容量的内存混插,也比较容易出现不兼容的情况,同时还要注意散热问题,特别是超频后。你可以使用MemTest 这个软件来检测一下内存,它可以彻底的检测出内存的稳定度。

    假如你是双内存,而且是不同品牌的内存条混插或者买了二手内存时,出现这个问题,这时,你就要检查是不是内存出问题了或者和其它硬件不兼容。如果都没有,那就从软件方面排除故障了。

    先简单说说原理:内存有个存放数据的地方叫缓冲区,当程序把数据放在其一位置时,因为没有足够空间,就会发生溢出现象。举个例子:一个桶子只能将一斤的水,当你放入两斤的水进入时,就会溢出来。而系统则是在屏幕上表现出来。这个问题,经常出现在windows2000和XP系统上,Windows 2000/XP对硬件的要求是很苛刻的,一旦遇到资源死锁、溢出或者类似Windows 98里的非法操作,系统为保持稳定,就会出现上述情况。另外也可能是硬件设备之间的兼容性不好造成的。

    下面我从几个例子给大家分析:

    例一:打开IE浏览器或者没过几分钟就会出现"0x70dcf39f"指令引用的"0x00000000"内存。该内存不能为“read”。要终止程序,请单击“确定”的信息框,单击“确定”后,又出现“发生内部错误,您正在使用的其中一个窗口即将关闭”的信息框,关闭该提示信息后,IE浏览器也被关闭。解决方法:修复或升级IE浏览器,同时打上补丁。看过其中一个修复方法是,Win2000自升级,也就是Win2000升级到Win2000,其实这种方法也就是把系统还原到系统初始的状态下。比如你的IE升级到了6.0,自升级后,会被IE5.0代替。

    例二:在windows xp下双击光盘里面的“AutoRun.exe”文件,显示“0x77f745cc”指令引用的“0x00000078”内存。该内存不能为 “written”,要终止程序,请单击“确定”,而在Windows 98里运行却正常。解决方法:这可能是系统的兼容性问题,winXP的系统,右键“AutoRun.exe”文件,属性,兼容性,把“用兼容模式运行这个程序”项选择上,并选择“Windows 98/Me”。win2000如果打了SP的补丁后,只要开始,运行,输入:regsvr32 c://winnt//apppatch//slayerui.dll。右键,属性,也会出现兼容性的选项。

    例三:RealOne Gold关闭时出现错误,以前一直使用正常,最近却在每次关闭时出现“0xffffffff”指令引用的“0xffffffff”内存。该内存不能为 “read” 的提示。解决方法:当使用的输入法为微软拼音输入法2003,并且隐藏语言栏时(不隐藏时没问题)关闭RealOne就会出现这个问题,因此在关闭RealOne 之前可以显示语言栏或者将任意其他输入法作为当前输入法来解决这个问题。

    例四:我的豪杰超级解霸自从上网后就不能播放了,每次都提示 “Ox060692f6”(每次变化)指令引用的“Oxff000011”内存不能为“read”,终止程序请按确定。解决方法:试试重装豪杰超级解霸,如果重装后还会,到官方网站下载相应版本的补丁试试。还不行,只好换就用别的播放器试试了。

    例五:双击一个游戏的快捷方式,“Ox77f5cdO”指令引用“Oxffffffff”内 存,该内存不能为“read” ,并且提示Client.dat程序错误。 解决方法:重装显卡的最新驱动程序,然后下载并且安装DirectX9.0。

    例六:一个朋友发信息过来,我的电脑便出现了错误信息:“0*772b548f”指令引用的“0*00303033”内存,该内存不能为 “written”,然后QQ自动下线,而再打开QQ,发现了他发过来的十几条的信息。解决方法:这是对方利用QQ的BUG,发送特殊的代码,做QQ出错,只要打上补丁或升级到最新版本,就没事了。

    通过上面的几个例子,可以看到,出现故障的原因有好多种,在这里把已经提到和有可能发生的原因列个表,方便查阅。

    解决方法

    1、内存条坏了更换内存条

    2、双内存不兼容使用同品牌的内存或只用一条内存

    3、内存质量问题更换内存条

    4、散热问题加强机箱内部的散热

    5、内存和主板没插好或和其它硬件不兼容等重插内存或换个插糟

    6、硬盘有问题更换硬盘

    7、驱动问题重装驱动。如果是新系统,要先安装主板驱动

    8、软件损坏重装软件

    9、软件有BUG打补丁或用最新的版本。

    10、软件和系统不兼容给软件打上补丁或者试试系统的兼容模式

    11、软件和软件之间有冲突如果最近安装了什么新软件,卸载了试试

    12、软件要使用到其它相关的软件有问题重装相关软件。比如播放某一格式的文件时出错,可能是这个文件的解码器有问题

    13、病毒问题杀毒

    14、杀毒软件与系统或软件冲突由于杀毒软件是进入底层监控系统的,可能与一些软件冲突,卸载了试试

    15、系统本身有问题有时候操作系统本身也会有BUG,要注意安装官方发行的升级程序,像SP的补丁,最好要打上。如果还不行重装系统或更换其它版本的系统了。

    使用Windows出现蓝色屏幕是经常的事,而且每每因为不清楚错误的来源而频繁重新安装系统,劳神费时。下列收集了一些windows死机密码,供大家参考。

    数值 叙述

    0 0x0000 作业完成。

    1 0x0001 不正确的函数。

    2 0x0002 系统找不到指定的档案。

    3 0x0003 系统找不到指定的路径。

    4 0x0004 系统无法开启档案。

    5 0x0005 拒绝存取。

    6 0x0006 无效的代码。

    7 0x0007 储存体控制区块已毁。

    8 0x0008 储存体空间不足,无法处理这个指令。

    9 0x0009 储存体控制区块位址无效。

    10 0x000a 环境不正确。

    11 0x000b 尝试载入一个格式错误的程式。

    12 0x000c 存取码错误。

    13 0x000d 资料错误。

    14 0x000e 储存体空间不够,无法完成这项作业。

    15 0x000f 系统找不到指定的磁碟机。

    16 0x0010 无法移除目录。

    17 0x0011 系统无法将档案移到 其他的磁碟机。

    18 0x0012 没有任何档案。

    19 0x0013 储存媒体为防写状态。

    20 0x0014 系统找不到指定的装置。

    21 0x0015 装置尚未就绪。

    22 0x0016 装置无法识别指令。

    23 0x0017 资料错误 (cyclic redundancy check)

    24 0x0018 程式发出一个长 度错误的指令。

    25 0x0019 磁碟机在磁碟找不到 持定的磁区或磁轨。

    26 0x001a 指定的磁碟或磁片无法存取。

    27 0x001b 磁碟机找不到要求的磁区。

    28 0x001c 印表机没有纸。

    29 0x001d 系统无法将资料写入指定的磁碟机。

    30 0x001e 系统无法读取指定的装置。

    31 0x001f 连接到系统的某个装置没有作用。

    32 0x0020 the process cannot access the file because it is being used by another process.

    33 0x0021 档案的一部份被锁定, 现在无法存取。

    34 0x0022 磁碟机的磁片不正确。 请将 %2 (volume serial number: %3) 插入磁碟机 %1。

    36 0x0024 开启的分享档案数量太多。

    38 0x0026 到达档案结尾。

    39 0x0027 磁碟已满。

    50 0x0032 不支援这种网路要求。

    51 0x0033 远端电脑无法使用。

    52 0x0034 网路名称重复。
    53 0x0035 网路路径找不到。

    54 0x0036 网路忙碌中。

    55 0x0037 the specified network resource or device is no longer available.

    56 0x0038 the network bios command limit has been reached.

    57 0x0039 网路配接卡发生问题。

    58 0x003a 指定的伺服器无法执行要求的作业。

    59 0x003b 网路发生意外错误。

    60 0x003c 远端配接卡不相容。

    61 0x003d 印表机伫列已满。

    62 0x003e 伺服器的空间无法储存等候列印的档案。

    63 0x003f 等候列印的档案已经删除。

    64 0x0040 指定的网路名称无法使用。

    65 0x0041 拒绝存取网路。

    66 0x0042 网路资源类型错误。

    67 0x0043 网路名称找不到。

    68 0x0044 超过区域电脑网路配接卡的名称限制。

    69 0x0045 超过网路 bios 作业阶段的限制。

    70 0x0046 远端伺服器已经暂停或者正在起始中。

    71 0x0047 由于连线数目已达上限,此时无法再连线到这台远端电脑。

    72 0x0048 指定的印表机或磁碟装置已经暂停作用。

    80 0x0050 档案已经存在。

    82 0x0052 无法建立目录或档案。

    83 0x0053 int 24 失败

    84 0x0054 处理这项要求的储存体无法使用。

    85 0x0055 近端装置名称已经在使用中。

    86 0x0056 指定的网路密码错误。

    87 0x0057 参数错误。

    88 0x0058 网路发生资料写入错误。

    89 0x0059 此时系统无法执行其他行程。

    100 0x0064 无法建立其他的系统 semaphore。

    101 0x0065 属于其他行程专用的 semaphore 。

    102 0x0066 semaphore 已经设定,而且无法关闭。

    103 0x0067 无法指定 semaphore 。

    104 0x0068 在岔断时间无法要求专用的 semaphore 。

    105 0x0069 此 semaphore 先前的拥有权已经结束。

    106 0x006a 请将磁片插入 %1。

    107 0x006b 因为代用的磁片尚未插入,所以程式已经停止。

    108 0x006c 磁碟正在使用中或被锁定。

    109 0x006d pipe 已经中止。

    110 0x006e 系统无法开启指定的 装置或档案。

    111 0x006f 档名太长。

    112 0x0070 磁碟空间不足。

    113 0x0071 没有可用的内部档案识别字。

    114 0x0072 目标内部档案识别字不正确。

    117 0x0075 由应用程式所执行的 ioctl 呼叫 不正确。

    118 0x0076 写入验证参数值不正确。

    119 0x0077 系统不支援所要求的指令。

    120 0x0078 此项功能仅在 win32 模式有效。

    121 0x0079 semaphore 超过逾时期间。

    122 0x007a 传到系统呼叫的资料区域 太小。

    123 0x007b 档名、目录名称或储存体标签语法错误。

    124 0x007c 系统呼叫层次不正确。

    125 0x007d 磁碟没有设定标签。

    126 0x007e 找不到指定的模组。

    127 0x007f 找不到指定的程序。

    128 0x0080 没有子行程可供等待。

    129 0x0081 %1 这个应用程式无法在 win32 模式下执行。

    130 0x0082 attempt to use a file handle to an open disk partition for an

    operation other than raw disk i/o.

    131 0x0083 尝试将档案指标移至档案开头之前。

    132 0x0084 无法在指定的装置或档案,设定档案指标。

    133 0x0085 join 或 subst 指令 无法用于 内含事先结合过的磁碟机。

    134 0x0086 尝试在已经结合的磁碟机,使用 join 或 subst 指令。

    135 0x0087 尝试在已经替换的磁碟机,使 用 join 或 subst 指令。

    136 0x0088 系统尝试删除 未连结过的磁碟机的连结关系。

    137 0x0089 系统尝试删除 未替换过的磁碟机的替换关系。

    138 0x008a 系统尝试将磁碟机结合到已经结合过之磁碟机的目录。

    139 0x008b 系统尝试将磁碟机替换成已经替换过之磁碟机的目录。

    140 0x008c 系统尝试将磁碟机替换成已经替换过之磁碟机的目录。

    141 0x008d 系统尝试将磁碟机 subst 成已结合的磁碟机 目录。

    142 0x008e 系统此刻无法执行 join 或 subst。

    143 0x008f 系统无法将磁碟机结合或替换同一磁碟机下目录。

    144 0x0090 这个目录不是根目录的子目录。

    145 0x0091 目录仍有资料。

    146 0x0092 指定的路径已经被替换过。

    147 0x0093 资源不足,无法处理这项 指令。

    148 0x0094 指定的路径这时候无法使用。

    149 0x0095 尝试要结合或替换的磁碟机目录,是已经替换过的的目标。

    150 0x0096 config.sys 档未指定系统追踪资讯,或是追踪功能被取消。

    151 0x0097 指定的 semaphore事件 dosmuxsemwait 数目不正确。

    152 0x0098 dosmuxsemwait 没有执行;设定太多的 semaphore。

    153 0x0099 dosmuxsemwait 清单不正确。

    154 0x009a 您所输入的储存媒体标 元长度限制。

    155 0x009b 无法建立其他的执行绪。

    156 0x009c 接收行程拒绝接受信号。

    157 0x009d 区段已经被舍弃,无法被锁定。

    158 0x009e 区段已经解除锁定。

    159 0x009f 执行绪识别码的位址不正确。

    160 0x00a0 传到 dosexecpgm 的引数字串不正确。

    161 0x00a1 指定的路径不正确。

    162 0x00a2 信号等候处理。

    164 0x00a4 系统无法建立执行绪。

    167 0x00a7 无法锁定档案的部份范围。

    170 0x00aa 所要求的资源正在使用中。

    173 0x00ad 取消范围的锁定要求不明显。

    174 0x00ae 档案系统不支援自动变更锁定类型。

    180 0x00b4 系统发现不正确的区段号码。

    182 0x00b6 作业系统无法执行 %1。

    183 0x00b7 档案已存在,无法建立同一档案。

    186 0x00ba 传送的旗号错误。

    187 0x00bb 指定的系统旗号找不到。

    188 0x00bc 作业系统无法执行 %1。

    189 0x00bd 作业系统无法执行 %1。

    190 0x00be 作业系统无法执行 %1。

    191 0x00bf 无法在 win32 模式下执行 %1。

    192 0x00c0 作业系统无法执行 %1。

    193 0x00c1 %1 不是正确的 win32 应用程式。

    194 0x00c2 作业系统无法执行 %1。

    195 0x00c3 作业系统无法执行 %1。

    196 0x00c4 作业系统无法执行 这个应用程式。

    197 0x00c5 作业系统目前无法执行 这个应用程式。

    198 0x00c6 作业系统无法执行 %1。

    199 0x00c7 作业系统无法执行 这个应用程式。

    200 0x00c8 程式码的区段不可以大于或等于 64kb。

    201 0x00c9 作业系统无法执行 %1。

    202 0x00ca 作业系统无法执行 %1。

    203 0x00cb 系统找不到输入的环境选项。 /r

    205 0x00cd 在指令子目录下,没有任何行程有信号副处理程式。

    206 0x00ce 档案名称或副档名太长。

    207 0x00cf ring 2 堆叠使用中

    展开全文
  • freemarker指令(九)

    千次阅读 2017-12-24 22:53:24
    Freemarker指令 如果你没有在这里发现模板中的指令,可能你需要在废弃的 FTL 结构中来查找它了。 2.1 if ,else ,elseif 指令 2.1.1 概要 ... ... ... ... ... 这里:  condition , condition2 等:...

    Freemarker指令

    如果你没有在这里发现模板中的指令,可能你需要在废弃的 FTL 结构中来查找它了。

    2.1 if ,else ,elseif  指令

    2.1.1  概要

    <#if condition>

    ...

    <#elseif condition2>

    ...

    <#elseif condition3>

    ...

    ...

    <#else>

    ...

    </#if>

    这里:

      condition , condition2 等:表达式将被计算成布尔值。

    2.1.2  描述

    你可以使用 if , elseif 和 else 指令来条件判断是否越过模板的一个部分。这些

    condition -s 必须计算成布尔值,否则错误将会中止模板处理。 elseif -s 和 else -s

    必须出现在 if 的内部(也就是,在 if 的开始标签和技术标签之间)。 if 中可以包含任意

    数量的 elseif -s(包括 0 个)而且结束时 else 是可选的。

    比如:

    只有 if ,没有 elseif 和 else :

    <#if x == 1>

    x is 1

    </#if>

    只有 if 和 else ,没有 elseif :

    <#if x == 1>

    x is 1

    <#else>

    x is not 1

    </#if>

    if 和两个 elseif ,没有 else :

    <#if x == 1>

    x is 1

    <#elseif x == 2>

    x is 2

    <#elseif x == 3>

    x is 3

    </#if>

    if 和 3 个 elseif ,还有 else :

    <#if x == 1>

    x is 1

    <#elseif x == 2>

    x is 2

    <#elseif x == 3>

    x is 3

    <#elseif x == 4>

    x is 4

    <#else>

    x is not 1 nor 2 nor 3 nor 4

    </#if>

    要了解更多布尔表达式,可以参考:模板开发指南/模板/表达式部分内容。

    你(当然)也可以嵌套 if 指令:

    <#if x == 1>

    x is 1

    <#if y == 1>

    and y is 1 too

    <#else>

    but y is not

    </#if>

    <#else>

    x is not 1

    <#if y < 0>

    and y is less than 0

    </#if>

    </#if>

    注意:

    如何测试 x 比 1 大? <#if x > 1> 是不对的,因为 FreeMarker 将会解释第一个 > 作

    为结束标记。因此,编写 <#if (x > 1)> 或 <#if x &gt;1> 是正确的。

    2.2 switch ,case ,default ,break  指令

    2.2.1  概要

    <#switch value>

    <#case refValue1>

    ...

    <#break>

    <#case refValue2>

    ...

    <#break>

    ...

    <#case refValueN>

    ...

    <#break>

    <#default>

    ...

    </#switch>

    这里:

      value , refValue1 等:表达式将会计算成相同类型的标量。

    2.2.2  描述

    这个指令的用法是不推荐的,因为向下通过的行为容易出错。使用 elseif -s 来代替,

    除非你想利用向下通过这种行为。

    Switch 被用来选择模板中的一个片段,如何选择依赖于表达式的值:

    <#switch being.size>

    <#case "small">

    This will be processed if it is small

    <#break>

    <#case "medium">

    This will be processed if it is medium

    <#break>

    <#case "large">

    This will be processed if it is large

    <#break>

    <#default>

    This will be processed if it is neither

    </#switch>

    在 switch 中间必须有一个或多个 <#case value> ,在所有 case 标签之后,有

    一个可选的 <#default> 。当 FreeMarker 到达指令时,它会选择一个 refValue 和

    value 相等的 case 指令来继续处理模板。如果没有和合适的值匹配的 case 指令,如

    果 default 指令存在,那么就会处理 default 指令,否则就会继续处理 switch 结

    束标签之后的内容。现在有一个混乱的事情:当它选择一个 case 指令后,它就会继续处

    理 case 指令中的内容,直到遇到 break 指令。也就是它遇到另外一个 case 指令或

    <#default> 标记时也不会自动离开switch 指令。比如:

    <#switch x>

    <#case x = 1>

    1

    <#case x = 2>

    2

    <#default>

    d

    </#switch>

    如果 x 是 1,那么它会打印 1 2 d;如果 x 是 2,那么就会打印 2 d;如果 x 是 3,那么

    它会打印 d。这就是前面提到的向下通过行为。 break 标记指示 FreeMarker 直接略过剩下

    的 switch 代码段。

    2.3 list ,break  指令

    2.3.1  概要

    <#list sequence as item>

    ...

    </#list>

    这里:

      sequence :表达式将被算作序列或集合

      item :循环变量(不是表达式)的名称

    2.3.2  描述

    你可以使用 list 指令来处理模板的一个部分中的一个序列中包含的各个变量。在开

    始标签和结束标签中的代码将会被处理,首先是第一个子变量,然后是第二个子变量,接着

    是第三个子变量,等等,直到超过最后一个。对于每个变量,这样的迭代中循环变量将会包

    含当前的子变量。

    在 list 循环中,有两个特殊的循环变量可用:

      item_index :这是一个包含当前项在循环中的步进索引的数值。

      item_has_next :来辨别当前项是否是序列的最后一项的布尔值。

    示例 1:

    <#assign seq = ["winter","spring", "summer", "autumn"]>

    <#list seq as x>

    ${x_index + 1}. ${x}<#ifx_has_next>,</#if>

    </#list>

    将会打印:

    1. winter,

    2. spring,

    3. summer,

    4. autumn

    示例 2:你可以使用 list 在两个数字中来计数,使用一个数字范围序列表达式:

    <#assign x=3>

    <#list 1..x as i>

    ${i}

    </#list>

    输出是:

    1

    2

    3

    注意上面的示例在你希望 x 是 0 的时候不会有作用,那么它打印 0 和-1。

    你可以使用 break 指令在它通过最后一个序列的子变量之前离开 list 循环。比如

    这会仅仅打印“winter”和“spring”。

    <#list seq as x>

    ${x}

    <#if x ="spring"><#break></#if>

    </#list>

    通常来说,避免 list 中使用无论何时可能包装了 Iterator 作为参数的集合和使

    用包装了 java.util.Collection 或序列的集合是最好的。但是在某些情况,当你

    处理时仅仅有一个 Iterator 。要注意如果你传递了一个包装了 Iterator 的集合给

    list ,你仅仅可以迭代一次元素,因为Iterator s 是由它们一次性对象的特性决定的。

    当你尝试第二次列出这样一个集合变量时,错误会中止模板的处理。

    2.4 include  指令

    2.4.1  概要

    <#include path>

    or

    <#include path options>

    这里:

      path :要包含文件的路径;一个算作是字符串的表达式。(用其他话说,它不用

    是一个固定的字符串,它也可以是像 profile.baseDir + "/menu.ftl"

    这样的东西。)

    1. winter,

    2. spring,

    3. summer,

    4. autumn

    <#assign x=3>

    <#list 1..x as i>

    ${i}

    </#list>

    1

    2

    3

    <#list seq as x>

    ${x}

    <#if x ="spring"><#break></#if>

    </#list>

      options :一个或多个这样的选项:encoding=encoding ,  parse=parse

      encoding :算作是字符串的表达式

      parse :算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值)

    2.4.2  描述

    你可以使用它在你的模板中插入另外一个 FreeMarker 模板文件(由 path 参数指定)。

    被包含模板的输出格式是在 include 标签出现的位置插入的。被包含的文件和包含它的

    模板共享变量,就像是被复制粘贴进去的一样。 include 指令不能由被包含文件的内容

    所替代,它只是当 FreeMarker 每次在模板处理期间到达 include 指令时处理被包含的文

    件。所以对于如果 include 在 list 循环之中的例子,你可以为每个循环周期内指定不

    同的文件名。

    注意:

    这个指令不能和 JSP(Servlet)的 include 搞混,因为它不涉及到 Servlet 容器中,只是

    处理应外一个 FreeMarker 模板,不能“离开”FreeMarker。关于如何处理“JSP include”,

    可以参考 FAQ 中的内容。

    path 参数可以是如"foo.ftl" 和 "../foo.ftl" 一样的相对路径,或者是如

    "/foo.ftl" 这样的绝对路径。相对路径是相对于使用 import 指令的模板文件夹。绝

    对路径是相对于程序员在配置 FreeMarker 时定义的基路径(通常指代“模板的根路径”)。

    注意:

    这和 FreeMarker 2.1 版本之前的处理方式不同,之前的路径通常是绝对路径。为了保留

    原来的行为,要在 Configuration 对象中开启经典的兼容模式。

    通常使用/(斜杠)来分隔路径成分,而不是\(反斜杠)。如果你从你本地的文件系统

    加载模板,要使用反斜杠(像 Windows 操作系统)。FreeMarker 会自动转换它们。

    比如:

    假设/common/copyright.ftl 包含:

    Copyright 2001-2002 ${me}<br>

    All rights reserved.

    那么这个:

    <#assign me = "JuilaSmith">

    <h1>Some test</h1>

    <p>Yeah.

    <hr>

    <#include"/common/copyright.ftl">

    会打印出:

    <h1>Some test</h1>

    <p>Yeah.

    <hr>

    Copyright 2001-2002 Juila Smith

    All rights reserved.

    支持的 options 选项有:

      parse:如果它为真,那么被包含的文件将会当作FTL 来解析,否则整个文件将被

    视为简单文本(也就是说不会在其中查找 FreeMarker 的结构)。如果你忽略了这个

    选项,那么它默认是 true。

      encoding:被包含文件从包含它的文件继承的编码方式(实际就是字符集),除非

    你用这个选项来指定编码方式。编码名称要和 java.io.InputStreamReader 中支持的

    那些一致(对于 Java API 1.3 版本: MIME  希望的字符集是从 IANA字符集注册处得

    到的)。合法的名字有:ISO-8859-2,UTF-8,Shift_JIS,Big5,EUC-KR,GB2312。

    比如:

    <#include"/common/navbar.html" parse=false

    encoding="Shift_JIS">

    注意,对于所有模板可能会用 Configuration 的“自动包含”设置自动处理通用

    的包含物。

    2.4.2.1  使用获得 机制

    有一个特殊的路径组成,是用一个星号( * )来代表的。它被解释为“当前目录或其他

    任意它的父目录”。因此,如果模板在 /foo/bar/template.ftl 位置上,有下面这

    行:

    那么引擎就会在下面的位置上寻找模板,并按这个顺序:

    /foo/bar/footer.ftl

     /foo/footer.ftl

     /footer.ftl

    这种机制被称为 acquisition  获得并允许设计者在父目录中放置通用的被包含的文件,而

    且当需要时在每个子路径基础上重新定义它们。我们说包含它们的模板获得了从包含它的第

    一个父目录中的模板。注意你不但可以在星号的右面指定一个模板的名字,也可以指定一个

    子路径。也就是说,如果前面的模板由下面这个所替代:

    <#include"*/commons/footer.ftl">

    那么引擎将会从下面的路径开始寻找模板,并按这个顺序:

    /foo/bar/commons/footer.ftl

    /foo/commons/footer.ftl

      /commons/footer.ftl

    然而,在路径中最大只能有一个星号。指定多余一个星号会导致模板不能被发现。

    2.4.2.2  本地化查找

    无论何时模板被加载,它都被分配了一个本地化环境。本地化环境是语言和可选的国家

    或方言标识。模板一般是由程序员编写一些代码来加载的,出于一些方面的考虑,程序员为

    模板选择一种本地化环境。比如:当 FreemarkerServlet 加载模板时,它经常用本

    地化环境匹配浏览器请求 Web 页面的语言偏好来请求模板。

    当一个模板包含另一个模板时,它试图加载以相同的本地化环境加载模板。假定你的模

    板以本地化 en_US 来加载,那就意味着是 U.S. English。当你包含另外一个模板:

    <include "footer.ftl">

    那么引擎实际上就会寻找一些模板,并按照这个顺序:

     footer_en_US.ftl

     footer_en.ftl

      footer.ftl

    要注意你可以使用 Configuration 的 setLocalizedLookup 方法关闭本地化查找特性。

    当你同时使用获得机制和本地化查找时,在父目录中有指定本地化的模板优先于在子目

    录中有很少本地化的模板。假设你使用下面的代码来包含 /foo/bar/template.ftl :

    <include "*/footer.ftl">

    引擎将会查找这些模板,并按照这个顺序:

    /foo/bar/footer_en_US.ftl

    /foo/footer_en_US.ftl

     /footer_en_US.ftl

    /foo/bar/footer_en.ftl

    /foo/footer_en.ftl

     /footer_en.ftl

     /foo/bar/footer.ftl

      /foo/footer.ftl

      /footer.ftl

    2.5 import  指令

    2.5.1  概要

    <#import path as hash>

    这里:

      path :模板的路径。这是一个算作是字符串的表达式。(换句话说,它不是一个固

    定的字符串,它可以是这样的一些东西,比如, profile.baseDir +

    "/menu.ftl" 。)

      hash :哈希表变量的结束名称,你可以由它来访问命名空间。这不是表达式。

    2.5.2  描述

    引入一个库。也就是说,它创建一个新的命名空间,然后在那个命名空间中执行给定

    path 参数中的模板,所以模板用变量(宏,函数等)填充命名空间。然后使得新创建的命

    名空间对哈希表的调用者可用。这个哈希表变量将会在命名空间中,由 import (就像你

    可以用 assign 指令来创建一样。)的调用者被创建成一个普通变量,名字就是 hash 参

    <include "footer.ftl">

    <include "*/footer.ftl">

    数给定的。

    如果你用同一个 path 多次调用 import ,它会创建命名空间,但是只运行第一次

    import 的调用。后面的调用仅仅创建一个哈希表变量,你只是通过它来访问同一个命名

    空间。

    由引入的模板打印的输出内容将会被忽略(不会在包含它的地方被插入)。模板的执行

    是来用变量填充命名空间,而不是写到输出中。

    例如:

    <#import "/libs/mylib.ftl" asmy>

    <@my.copyrightdate="1999-2002"/>

    path 参数可以是一个相对路径,比如"foo.ftl" 和 "../foo.ftl" ,或者是像

    "/foo.ftl" 一样的绝对路径。相对路径是相对于使用 import 指令模板的目录。绝对

    路径是程序员配置 FreeMarker 时定义的相对于根路径(通常指代“模板的根目录”)的路径。

    通常使用 / (斜杠)来分隔路径组成,而不是 \ (反斜杠)。如果你从你本地的文件系统

    中加载模板,那么它使用反斜杠(比如在 Windows 环境下),FreeMarker 将会自动转换它们。

    像 include 指令一样,获得机制和本地化擦找也可以用来解决路径问题。

    注意,对于所有模板来说,它可以自动做通用的引入操作,使用 Configuration

    的“自动引入”设置就行了。

    如果你命名空间不是很了解,你应该阅读:模板开发指南/其他/命名空间部分的内容。

    2.6 noparse  指令

    2.6.1  概要

    <#noparse>

    ...

    </#noparse>

    2.6.2  描述

    FreeMarker 不会在这个指令体中间寻找 FTL 标签,插值和其他特殊的字符序列,除了

    noparse 的结束标记。

    例如:

    <#import "/libs/mylib.ftl" asmy>

    <@my.copyrightdate="1999-2002"/>

    Example:

    --------

    <#noparse>

    <#list animals as being>

    <tr><td>${being.name}<td>${being.price}Euros

    </#list>

    </#noparse>

    将会输出:

    Example:

    --------

    <#list animals as being>

    <tr><td>${being.name}<td>${being.price}Euros

    </#list>

    2.7 compress  指令

    2.7.1  概要

    <#compress>

    ...

    </#compress>

    2.7.2  描述

    当你使用了对空白不敏感的格式(比如 HTML 或 XML)时压缩指令对于移除多余的空白

    是很有用的。它捕捉在指令体(也就是在开始标签和结束标签中)中生成的内容,然后缩小

    所有不间断的空白序列到一个单独的空白字符。如果被替代的序列包含换行符或是一段空间,

    那么被插入的字符也会是一个换行符。开头和结尾的不间断的空白序列将会完全被移除。

    <#assign x = " moo \n\n ">

    (<#compress>

    1 2 3 4 5

    ${moo}

    test only

    I said, test only

    </#compress>)

    会输出:

    (1 2 3 4 5

    moo

    test only

    I said, test only)

    2.8 escape ,noescape  指令

    2.8.1  概要

    <#escape identifier as expression>

    ...

    <#noescape>...</#noescape>

    ...

    </#escape>

    2.8.2  描述

    当你使用 escape 指令包围模板中的一部分时,在块中出现的插值( ${...} )会和

    转义表达式自动结合。这是一个避免编写相似表达式的很方便的方法。它不会影响在字符串

    形式的插值(比如在 <#assign x = "Hello ${user}!"> )。而且,它也不会影

    响数值插值( #{...} )。

    例如:

    <#escape x as x?html>

    First name: ${firstName}

    Last name: ${lastName}

    Maiden name: ${maidenName}

    </#escape>

    事实上它等同于:

    First name: ${firstName?html}

    Last name: ${lastName?html}

    Maiden name: ${maidenName?html}

    注意它和你在指令中用什么样的标识符无关 - 它仅仅是作为一个转义表达式的正式参

    数。

    当你在 include 指令中调用宏时, 理解 在模 板文 本中转 义仅 仅对 出现在

    <#escape ...> 和</#escape> 中的插值起作用是很重要的。也就是说,它不会转义

    文本中 <#escape ...> 之前的东西或 </#escape> 之后的东西,也不会从 escape -d

    部分中来调用。

    <#assign x ="<test>">

    <#macro m1>

    m1: ${x}

    </#macro>

    <#escape x as x?html>

    <#macro m2>m2: ${x}</#macro>

    ${x}

    <@m1/>

    </#escape>

    ${x}

    <@m2/>

    输出将是:

    &lt;test&gt;

    m1: <test>

    <test>

    m2: &lt;test&gt;

    从更深的技术上说, escape 指令的作用是用在模板解析的时间而不是模板处理的时

    间。这就表示如果你调用一个宏或从一个转义块中包含另外一个模板,它不会影响在宏 / 被

    包含模板中的插值,因为宏调用和模板包含被算在模板处理时间。另外一方面,如果你用一

    个转义区块包围一个或多个宏声明(算在模板解析时间,和宏调用想法),那么那些宏中的

    插值将会和转义表达式合并。

    有时需要暂时为一个或两个在转义区块中的插值关闭转义。你可以通过关闭,过后再重

    新开启转义区块来达到这个功能,但是那么你不得不编写两遍转义表达式。你可以使用非转

    义指令来替代:

    <#escape x as x?html>

    From: ${mailMessage.From}

    Subject: ${mailMessage.Subject}

    <#noescape>Message:

    ${mailMessage.htmlFormattedBody}</#noescape>

    ...

    </#escape>

    和这个是等同的:

    From: ${mailMessage.From?html}

    Subject: ${mailMessage.Subject?html}

    Message: ${mailMessage.htmlFormattedBody}

    ...

    转义可以被嵌套(尽管你不会在罕见的情况下来做)。因此,你可以编写如下面代码(这

    个例子固然是有点伸展的,正如你可能会使用 list 来迭代序列中的每一项,但是我们现

    在所做的是阐述这个观点)的东西:

    <#escape x as x?html>

    Customer Name: ${customerName}

    Items to ship:

    <#escape x as itemCodeToNameMap[x]>

    ${itemCode1}

    ${itemCode2}

    ${itemCode3}

    ${itemCode4}

    </#escape>

    </#escape>

    实际上和下面是等同的:

    Customer Name: ${customerName?html}

    Items to ship:

    ${itemCodeToNameMap[itemCode1]?html}

    ${itemCodeToNameMap[itemCode2]?html}

    ${itemCodeToNameMap[itemCode3]?html}

    ${itemCodeToNameMap[itemCode4]?html}

    当你在嵌入的转义区块内使用非转义指令时,它仅仅不处理一个单独层级的转义。因此,

    为了在两级深的转义区块内完全关闭转义,你需要使用两个嵌套的非转义指令。

    2.9 assign  指令

    2.9.1  概要

    <#assign name=value>

    or

    <#assign name1=value1 name2=value2 ...nameN=valueN>

    or

    <#assign same as above... in namespacehash>

    or

    <#assign name>

    capture this

    </#assign>

    or

    <#assign name in namespacehash>

    capture this

    </#assign>

    这里:

      name :变量的名字。不是表达式。而它可以本写作是字符串,如果变量名包含保

    留字符这是很有用的,比如 <#assign "foo-bar" = 1> 。注意这个字符串

    没有展开插值(如 "${foo}" )。

      value :存储的值。是表达式。

      namespacehash :(通过 import )为命名空间创建的哈希表。是表达式。

    2.9.2  描述

    使用这个指令你可以创建一个新的变量,或者替换一个已经存在的变量。注意仅仅顶级

    变量可以被创建/替换(也就是说你不能创建/替换 some_hash.subvar ,除了

    some_hash )。

    关于变量的更多内容,请阅读:模板开发指南/其他/在模板中定义变量

    例如: seasons 变量可以存储一个序列:

    <#assign seasons = ["winter","spring", "summer", "autumn"]>

    比如:变量 test 中存储增长的数字:

    <#assign test = test + 1>

    作为一个方便的特性,你可以使用一个 assign 标记来进行多次定义。比如这个会做

    上面两个例子中相同的事情:

    <#assign

    seasons = ["winter","spring", "summer", "autumn"]

    test = test + 1

    如果你知道什么是命名空间: assign 指令在命名空间中创建变量。通常它在当前的

    命名空间(也就是和标签所在模板关联的命名空间)中创建变量。但如果你是用了 in

    namespacehash ,那么你可以用另外一个命名空间来创建/替换变量。比如,这里你在

    命名空间中 /mylib.ftl 创建/替换了变量 bgColor 。

    <#import "/mylib.ftl" asmy>

    <#assign bgColor="red" inmy>

    assign 的极端使用是当它捕捉它的开始标记和结束标记中间生成的输出时。也就是

    说,在标记之间打印的东西将不会在页面上显示,但是会存储在变量中。比如:

    <#macro myMacro>foo</#macro>

    <#assign x>

    <#list 1..3 as n>

    ${n} <@myMacro />

    </#list>

    </#assign>

    Number of words: ${x?word_list?size}

    ${x}

     

    将会打印:

    Number of words: 6

    1 foo

    2 foo

    3 foo

    请注意你不应该使用它来往字符串中插入变量:

    <#assign x>Hello${user}!</#assign> <#-- BAD PRACTICE! -->

    你可以这么来写:

    <#assign x="Hello${user}!">

    2.10 global  指令

    2.10.1  概要

    <#global name=value>

    or

    <#global name1=value1 name2=value2 ...nameN=valueN>

    or

    <#global name>

    capture this

    </#global>

    这里:

      name :变量的名称。它不是表达式。但它可以被写作是字符串形式,如果变量名

    包含保留字符这是很有用的,比如 <#global "foo-bar" = 1> 。注意这个

    字符串没有扩展插值(如 "${foo}" )。

      value :存储的值,是表达式。

    2.10.2  描述

    这个指令和 assign 相似,但是被创建的变量在所有的命名空间中都可见,但又不会

    存在于任何一个命名空间之中。精确地说,正如你会创建(或替换)一个数据模型变量。因

    此,这个变量是全局的。如果在数据模型中,一个相同名称的变量存在的话,它会被使用这

    个指令创建的变量隐藏。如果在当前的命名空间中,一个相同名称的变量存在的话,那么会

    隐藏由 global 指令创建的变量。

    比如 <#global x = 1> ,用创建了一个变量,那么在所有命名空间中x 都可见,

    除非另外一个称为 x 的变量隐藏了它(比如你已经用 <#assign x = 2> 创建了一个变

    量)。这种情形下,你可以使用特殊变量 globals ,比如 ${.globals.x} 。注意使用

    globals 你看到所有全局可访问的变量;不但由global 指令创建的变量,而且是数据

    模型中的变量。

    自定义 JSP 标记的用户请注意:用这个指令创建的变量集合和 JSP 页面范围对应。这就

    意味着,如果自定义 JSP 标记想获得一个页面范围的属性(page-scope bean),在当前命名

    空间中一个相同名称的变量,从 JSP 标记的观点出发,将不会隐藏。

    <#assign x>Hello${user}!</#assign> <#-- BAD PRACTICE! -->

    <#assign x="Hello${user}!">

    2.11 local  指令

    2.11.1  概要

    <#local name=value>

    or

    <#local name1=value1 name2=value2 ...nameN=valueN>

    or

    <#local name>

    capture this

    </#local>

    这里:

      name :在 root 中局部对象的名称。它不是一个表达式。但它可以被写作是字符串

    形式,如果变量名包含保留字符这是很有用的,比如 <#global "foo-bar" =

    1> 。注意这个字符串没有扩展插值(如"${foo}" )。

      value :存储的值,是表达式。

    2.11.2  描述

    它和 assign 指令类似,但是它创建或替换局部变量。这仅仅在宏和方法的内部定义

    才会有作用。

    要获得更多关于变量的信息,可以阅读:模板开发指南/其他/在模板中定义变量部分内

    容。

    2.12 setting  指令

    2.12.1  概要

    <#setting name=value>

    这里:

      name :设置的名称。不是表达式!

      value :设置的值,是表达式。

    2.12.2  描述

    为进一步的处理而设置。设置是影响 FreeMarker 行为的值。新值仅仅在被设置的模板

    处理时出现,而且不触碰模板本身。设置的初始值是由程序员设定的(参加:程序开发指南

    /配置/设置信息部分的内容)。

    支持的设置有:

      locale :输出的本地化(语言)。它可以影响数字,日期等显示格式。它的值是

    由语言编码(小写两个字母的 ISO-639 编码)和可选的国家码(大写的两个字母

    ISO-3166 编码)组成的字符串,它们以下划线相分隔,如果我们已经指定了国家那

    么一个可选的不同编码(不是标准的)会以下划线分隔开国家。合法的值示例:

    en , en_US , en_US_MAC 。FreeMarker 会尝试使用最特定可用的本地化设置,

    所以如果你指定了 en_US_MAC ,但是它不被知道,那么它会尝试 en_US ,然

    后尝试 en ,然后是计算机(可能是由程序员设置的)默认的本地化设置。

      number_format :当没有指定确定的格式化形式时,用来转化数字到字符串形

    式的数字格式化设置。可以是下列中的一个预定义值 number (默认的),

    computer , currency ,或 percent 。此外,以 Java 小数数字格式化语法

    书写的任意的格式化形式也可以被指定。更多的格式形式请参考处理数字的内建函

    数 string 。

      boolean_format :以逗号分隔的一对字符串来分别展示true 和 false 值,当

    没有指定确定的格式时,转换布尔值到字符串。默认值是 "true,false" 。也

    可以参考处理布尔值的内建函数 string 。

      date_format , time_format ,datetime_format :,当没有指定确定

    的格式时,用来转换日期到字符串的日期/时间格式形式。如 ${someDate} .

    date_format 这个情形,它只影响和日期相关的日期(年,月,日),

    time_format 只 影 响 和 时 间 相 关 的 日 期 ( 时 , 分 , 秒 , 毫 秒 ),

    datetime_format 只影响时间日期类型的日期(年,月,日,时,分,秒,

    毫秒)。这个设置的可能值和处理日期的内建函数 string 的参数相似;可以在

    那部分参考更多内容。比如 "short" , "long_medium" ,"MM/dd/yyyy" 。

      time_zone :时区的名称来显示并格式化时间。默认情况下,使用系统的时区。

    也可以是 Java 时区 API 中的任何值。比如: "GMT" , "GMT+2" , "GMT-1:30" ,

    "CET" ,"PST" , "America/Los_Angeles" 。

      url_escaping_charset :用来 URL 转义(比如${foo?url} )的字符集,

    来计算转义( %XX )的部分。通常包含 FreeMarker 的框架应该设置它,所以你不

    应该在模板中来设置。(程序员可以阅读程序开发指南/其他/字符集问题部分来获

    取更多内容)

      classic_compatible :这是对专业人员来说的,它的值应该是一个布尔值。

    参见 freemarker.template.Configurable 的文档来获取更多信息。

    示例:假设模板的初始本地化是 hu(Hungarian,匈牙利),那么这个:

    ${1.2}

    <#setting locale="en_US">

    ${1.2}

    将会输出:

    1,2

    1.2

    因为匈牙利人使用逗号作为小数的分隔符,而美国人使用点。

    2.13  用户自定义指令(<@...>)

    2.13.1  概要

    <@user_def_dir_exp param1=val1param2=val2 ... paramN=valN/>

    (注意 XML 风格, > 之前的 / )

    如果你需要循环变量,请参考 2.13.2.2 节内容。

    <@user_def_dir_exp param1=val1param2=val2 ... paramN=valN ;

    lv1, lv2, ..., lvN/>

    或者和上面两个相同但是使用结束标签,请参考 2.13.2.1 节内容。

    <@user_def_dir_exp ...>

    ...

    </@user_def_dir_exp>

    <@user_def_dir_exp ...>

    ...

    </@>

    或和上面的相同但是使用位置参数传递,请参考 2.13.2.3 节内容

    <@user val1, val2, ..., valN/>

    等…

    这里:

      user_def_dir_exp :表达式算作是自定义指令(比如宏),将会被调用。

      param1 , param2 等:参数的名称,它们不是表达式。

      val1 , val2 等:参数的值,它们是表达式。

      lv1 , lv2 等:循环变量的名称,它们不是表达式。

    参数的数量可以是 0(也就是没有参数)。

    参数的顺序并不重要(除非你使用了位置参数传递)。参数名称必须唯一。在参数名中

    小写和大写的字母被认为是不同的字母(也就是 Color 和 color 是不同的)。

    2.13.2  描述

    这将调用用户自定义指令,比如宏。参数的含义,支持和需要的参数的设置依赖于具体

    的自定义指令。

    你可以阅读模板开发指南/其他/定义你自己的指令部分。

    示例 1:调用存储在变量 html_escape 中的指令:

    <@html_escape>

    a < b

    Romeo & Juliet

    </@html_escape>

    输出:

    a &lt; b

    Romeo &amp; Juliet

    示例 2:调用有参数的宏

    <@list items=["mouse","elephant", "python"] title="Animals"/>

    ...

    <#macro list title items>

    <p>${title?cap_first}:

    <ul>

    <#list items as x>

    <li>${x?cap_first}

    </#list>

    </ul>

    </#macro>

    输出:

    <p>Animals:

    <ul>

    <li>Mouse

    <li>Elephant

    <li>Python

    </ul>

    ...

    2.13.2.1  结束标签

    你可以在结束标签中忽略 user_def_dir_exp 。也就是说,你可以写 </@> 来替代

    </@anything> 。这个规则当表达式user_def_dir_exp 太复杂时非常有用,因为

    你不需要在结束标签中重复表达式。此外,如果表达式包含比简单变量名和点还多的表达式,

    你就 不 能 再 重 复 它 们 了 。 比 如

    <@a_hash[a_method()]>...</@a_hash[a_method()]>就是错的,你必须

    写为 <@a_hash[a_method()]>...</@> 。 但 是

    <@a_hash.foo>...</@a_hash.foo> 是可以的。

    2.13.2.2  循环变量

    一些自定义指令创建循环变量(和 list 指令相似)。正如预定义指令(如 list )一

    样,当你调用这个指令(如 <#list foos as foo>...</#list> 中的 foo )时循

    环变量的名称就给定了,而变量的值是由指令本身设置的。在自定义指令的情形下,语法是

    循环变量的名称在分号之后给定。比如:

    <@myRepeatMacro count=4 ; x, last>

    ${x}. Something... <#if last> Thiswas the last!</#if>

    </@myRepeatMacro>

    注意由自定义指令创建的循环变量数量和分号之后指定的循环变量数量需要不匹配。也

    就是说,如果你对重复是否是最后一个不感兴趣,你可以简单来写:

    <@myRepeatMacro count=4 ; x>

    ${x}. Something...

    </@myRepeatMacro>

    或者你可以:

    <@myRepeatMacro count=4>

    Something...

    </@myRepeatMacro>

    此外,如果你在分号之后指定更多循环变量而不是自定义指令创建的,也不会引起错误,

    只是最后的循环变量不能被创建(也就是在嵌套内容中那些将是未定义的)。尝试使用未定

    义的循环变量,就会引起错误(除非你使用如 ?default 这样的内建函数),因为你尝试

    访问了一个不存在的变量。

    请参考模板开发指南/其他/定义你自己的指令部分来获取更多内容。

    2.13.2.3  位置参数传递

    位置参数传递(如 <@heading "Preface", 1/> )是正常命名参数传递(如

    <@heading title="Preface"level=1/> )的速记形式,这里忽略了参数的名

    称。如果自定义指令只有一个参数,或者对于经常使用的自定义指令它参数的顺序很好记忆,

    速记形式应该被应用。为了应用这种形式,你不得不了解声明的命名参数的顺序(如果指令

    只有一个参数这是很琐碎的)。也就是,如果 heading 被创建为 <#macro heading

    title level>... , 那 么 <@heading"Preface", 1/> 和 <@heading

    title="Preface"  level=1/> ( 或 <@heading  level=1

    title="Preface"/> ;如果你使用参数名称,那顺序就不重要了)是相等的。要注意

    位置参数传递现在仅仅支持宏定义。

    2.14 macro ,nested ,return  指令

    2.14.1  概要

    <#macro name param1 param2 ...paramN>

    ...

    <#nested loopvar1, loopvar2, ...,loopvarN>

    ...

    <#return>

    ...

    </#macro>

    这里:

      name :宏变量的名称,它不是表达式。然而,它可以被写成字符串的形式,如果

    <@myRepeatMacro count=4 ; x>

    ${x}. Something...

    </@myRepeatMacro>

    <@myRepeatMacro count=4>

    Something...

    </@myRepeatMacro>

    宏名称中包含保留字符时这是很有用的,比如 <#macro "foo-bar">... 。

    注意这个字符串没有扩展插值(如 "${foo}" )。

      param1 , param2 等: 局部变量的名称,存储参数的值(不是表达式),在 =

    号后面和默认值(是表达式)是可选的。默认值也可以是另外一个参数,比如

    <#macro section title label=title> 。

      paramN ,最后一个参数,可以可选的包含一个尾部省略( ... ),这就意味着宏

    接受可变的参数数量。如果使用命名参数来调用, paramN 将会是包含给宏的所

    有未声明的键/值对的哈希表。如果使用位置参数来调用, paramN 将是额外参数

    的序列。

      loopvar1 , loopvar2 等:可选的循环变量的值,是nested 指令想为嵌套

    内容创建的。这些都是表达式。

    return 和 nested 指令是可选的,而且可以在<#macro> 和 </#macro> 之间被

    用在任意位置和任意次数。

    没有默认值的参数必须在有默认值参数( paramName=defaultValue )之前。

    2.14.2  描述

    创建一个宏变量(在当前命名空间中,如果你知道命名空间的特性)。如果你对宏和自

    定义指令不了解,你应该阅读模板开发指南/其他/定义你自己的指令部分。

    宏变量存储模板片段(称为宏定义体)可以被用作自定义指令。这个变量也存储自定义

    指令的被允许的参数名。当你将这个变量作为指令时,你必须给所有参数赋值,除了有默认

    值的参数。默认值当且仅当你调用宏而不给参数赋值时起作用。

    变量会在模板开始时被创建;而不管 macro 指令放置在模板的什么位置。因此,这样

    也可以:

    <#-- call the macro; the macro variableis already created: -->

    <@test/>

    ...

    <#-- create the macro variable: -->

    <#macro test>

    Test text

    </#macro>

    然而,如果宏定义被插在 include 指令中,它们直到 FreeMarker 执行 include 指

    令时参会可用。

    例如:没有参数的宏:

    <#macro test>

    Test text

    </#macro>

    <#-- call the macro: -->

    <@test/>

    输出:

    Test text

    示例:有参数的宏:

    <#macro test foo bar baaz>

    Test text, and the params: ${foo}, ${bar},${baaz}

    </#macro>

    <#-- call the macro: -->

    <@test foo="a"bar="b" baaz=5*5-2/>

    输出:

    Test text, and the params: a, b, 23

    示例:有参数和默认值参数的宏:

    <#macro test foo bar="Bar"baaz=-1>

    Test text, and the params: ${foo}, ${bar},${baaz}

    </#macro>

    <@test foo="a"bar="b" baaz=5*5-2/>

    <@test foo="a"bar="b"/>

    <@test foo="a" baaz=5*5-2/>

    <@test foo="a"/>

    输出:

    Test text, and the params: a, b, 23

    Test text, and the params: a, b, -1

    Test text, and the params: a, Bar, 23

    Test text, and the params: a, Bar, -1

    示例:一个复杂的宏。

    <#macro list title items>

    <p>${title?cap_first}:

    <ul>

    <#list items as x>

    <li>${x?cap_first}

    </#list>

    </ul>

    </#macro>

    <@list items=["mouse","elephant", "python"] title="Animals"/>

    输出:

    <p>Animals:

    <ul>

    <li>Mouse

    <li>Elephant

    <li>Python

    </ul>

    示例:一个支持多个参数和命名参数的宏:

    <#macro img src extra...>

    <img src="/context${src?html}"

    <#list extra?keys as attr>

    ${attr}="${extra[attr]?html}"

    </#list>

    </#macro>

    <@img src="/images/test.png"width=100 height=50 alt="Test"/>

    输出:

    <imgsrc="/context/images/test.png"

    alt="Test"

    height="50"

    width="100"

    2.14.2.1 nested

    nested 指令执行自定义指令开始和结束标签中间的模板片段。嵌套的片段可以包含

    模板中合法的任意内容:插值,指令…等。它在上下文环境中被执行,也就是宏被调用的地

    方,而不是宏定义体的上下文中。因此,比如,你不能看到嵌套部分的宏的局部变量。如果

    你没有调用 nested 指令,自定义指令开始和结束标记中的部分将会被忽略。

    示例:

    <#macro do_twice>

    1. <#nested>

    2. <#nested>

    </#macro>

    <@do_twice>something</@do_twice>

    输出:

    1. something

    2. something

    嵌套指令可以对嵌套内容创建循环变量。比如:

    <#macro do_thrice>

    <#nested 1>

    <#nested 2>

    <#nested 3>

    </#macro>

    <@do_thrice ; x>

    ${x} Anything.

    </@do_thrice>

    这会打印:

    1 Anything.

    2 Anything.

    3 Anything.

    一个更为复杂的示例:

    <#macro repeat count>

    <#list 1..count as x>

    <#nested x, x/2, x==count>

    </#list>

    </#macro>

    <@repeat count=4 ; c, halfc, last>

    ${c}. ${halfc}<#if last>Last!</#if>

    </@repeat>

    输出将是:

    1. 0.5

    2. 1

    3. 1.5

    4. 2 Last!

    2.14.2.2 return

    使用 return 指令,你可以在任意位置留下一个宏或函数定义。比如:

    <#macro test>

    Test text

    <#return>

    Will not be printed.

    </#macro>

    <@test/>

    输出:

    Test text

    2.15 function ,return  指令

    2.15.1  概要

    <#function name param1 param2 ...paramN>

    ...

    <#return returnValue>

    </#function>

    这里:

      name :方法变量的名称(不是表达式)

      param1 , param2 等:局部变量的名称,存储参数的值(不是表达式),在 = 号

    后面和默认值(是表达式)是可选的。

      paramN ,最后一个参数,可以可选的包含一个尾部省略( ... ),这就意味着宏

    接受可变的参数数量。局部变量 paramN 将是额外参数的序列。

      returnValue :计算方法调用值的表达式。

    return 指令可以在 <#function...> 和 </#function> 之间被用在任意位置

    和任意次数。

    没有默认值的参数必须在有默认值参数( paramName=defaultValue )之前。

    2.15.2  描述

    创建一个方法变量(在当前命名空间中,如果你知道命名空间的特性)。这个指令和

    macro 指令的工作方式一样,除了 return 指令必须有一个参数来指定方法的返回值,

    而且视图写入输出的将会被忽略。如果到达 </#function> (也就是说没有 return

    returnValue ),那么方法的返回值就是未定义变量。

    示例 1:创建一个方法来计算两个树的平均值:

    <#function avg x y>

    <#return (x + y) / 2>

    </#function>

    ${avg(10, 20)}

    将会打印:

    15

    示例 2:创建一个方法来计算多个数字的平均值:

    <#function avg nums...>

    <#local sum = 0>

    <#list nums as num>

    <#local sum = sum + num>

    </#list>

    <#if nums?size != 0>

    <#return sum / nums?size>

    </#if>

    </#function>

    ${avg(10, 20)}

    ${avg(10, 20, 30, 40)}

    ${avg()!"N/A"}

    会打印:

    15

    25

    N/A

    2.16 flush  指令

    2.16.1  概要

    <#flush>

    2.16.2  描述

    当 FreeMarker 生成输出时,他/她通常存储生成的输出内容然后以一个或几个大片段送

    到客户端。这种发送的行为被称为冲洗(就像冲厕所)。尽管冲洗是自动发生的,有时你想

    在模板处理时的一点强制执行,这就是 flush 指令要做的。如果需要它,那么程序员会告

    诉你;通常你不必使用这个指令。自动冲洗的机制和 flush 指令的明确效果是在程序员的

    控制之下的。

    冲洗简单调用当前使用 java.io.Writer 实例的 flush方法。整体的缓冲和冲洗机制在 writer

    (就是传递给 Template.process 方法的参数)中已经实现了;FreeMarker 不用来处

    理它。

    2.17 stop  指令

    2.17.1  概要

    <#stop>

    <#stop reason>

    这里:

      reason :关于终端原因的信息化消息。表达式被算做是字符串。

    2.17.2  描述

    中止模板的处理。这是一种像紧急中断的机制:不要在普通情况下使用。抛出

    StopException 异常会发生中止,而且StopException 会持有 reason 参数的值。

    15

    25

    N/A

    2.18 ftl  指令

    2.18.1  概要

    <#ftl param1=value1 param2=value2 ...paramN=valueN>

    这里:

      param1 , param2 等:参数的名字,不是表达式。允许的参数有 encoding ,

    strip_whitespace , strip_text 等。参加下面。

      value1 , value2 等:参数的值。必须是一个常量表达式(如 true ,或

    "ISO-8859-5" ,或 {x:1, y:2})。它不能用变量。

    2.18.2  描述

    告诉 FreeMarker 和其他工具关于模板的信息,而且帮助程序员来自动检测一个文本文

    件是否是 FTL 文件。这个指令,如果存在,必须是模板的第一句代码。该指令前的任何空白

    将被忽略。这个指令的老式语法(#-less)格式是不被支持的。

    一些设置(编码方式,空白剥离等)在这里给定的话就有最高的优先级,也就是说,它

    们直接作用于模板而不管其他任何 FreeMarker 配置的设置。

    参数:

      encoding :使用这个你可以在模板文件中为模板指定编码方式(字符集) (也就

    是说 , 这 是 新 创 建 Template 的 encoding 设 置 , 而 且

    Configuration.getTemplate 中的 encoding 参数不能覆盖它。) 。要

    注意,FreeMarker 会尝试会和自动猜测的编码方式(这依赖于程序员对 FreeMarker

    的配置)找到 ftl 指令并解释它,然后就会发现 ftl 指令会让一些东西有所不

    同,之后以新的编码方式来读取模板。因此,直到 ftl 标记使用第一个编码方式

    读取到结尾,模板必须是合法的 FTL。这个参数的合法值是从 IANA 字符集注册表

    中参考 MIME 中的字符集名称。比如 ISO-8859-5,UTF-8 或 Shift_JIS。

      strip_whitespace :这将开启/关闭空白剥离。合法的值是布尔值常量 true

    和 flase (为了向下兼容,字符串 "yes" , "no" , "true" , "false" 也是

    可以的)。默认值(也就是当你不使用这个参数时)是依赖于程序员对 FreeMarker

    的配置,但是对新的项目还应该是 true 。

      strip_text :当开启它时,当模板被解析时模板中所有顶级文本被移除。这个

    不会影响宏,指令,或插值中的文本。合法值是布尔值常量 true 和 flase (为

    了向下兼容,字符串 "yes" , "no" , "true" , "false" 也是可以的)。默认

    值(也就是当你不使用这个参数时)是 flase 。

      strict_syntax :这会开启/关闭“严格的语法”。合法值是布尔值常量 true

    和 flase (为了向下兼容,字符串 "yes" , "no" , "true" , "false" 也是

    可以的)。默认值(也就是当你不使用这个参数时)是依赖于程序员对 FreeMarker

    的配 置 , 但 是 对 新 的 项 目 还 应 该 是 true ( 程 序 员 : 对 于

    config.setStrictSyntaxMode(true); ,你应该明确设置它为true )。

    要获取更多信息,可以参考:废弃的 FTL 结构/老式 FTL 语法部分。

      ns_prefixes : 这 是 关 联 节 点 命 名 空 间 前 缀 的 哈 希 表 。 比 如 :

    {"e":"http://example.com/ebook",

    "vg":"http://example.com/vektorGraphics"}。这通常是用于

    XML 处理的,前缀可以用于 XML 查询,但是它也影响visit 和 recurse 指令

    的工作。相同节点的命名空间只能注册一个前缀(否则会发生错误),所以在前缀

    和节点命名空间中是一对一的关系。前缀 D 和 N 是保留的。如果你注册前缀 D ,

    那么除了你可以使用前缀 D 来关联节点命名空间,你也可以设置默认的节点命名

    空间。前缀 N 就不能被注册;当且仅当前缀 D 被注册时, N 被用来表示在特定位

    置没有节点命名空间的节点。(要参考默认节点命名空间的用法, N ,一般的前缀,

    可以看 XML 处理中 visit 和 recurse 指令。) ns_prefixes 的作用限制在

    单独的 FTL 命名空间内,换句话说,就是为模板创建的 FTL 命名空间内。这也意味

    着 ns_prefixes 当一个 FTL 命名空间为包含它的模板所创建时才有作用,否则

    ns_prefixes 参数没有效果。FTL 命名空间当下列情况下为模板创建:(a)模

    板是“主”模板,也就是说它不是被 <#include ...> 来调用的模板,但是直

    接被调用的(和 process 一起的 Template 或 Environment 类的方法);

    (b)模板直接被 <#import ...> 调用。

      attributes :这是关联模板任意属性(名-值对)的哈希表。属性的值可以是

    任意类型(字符串,数字,序列等)。FreeMarker 不会尝试去理解属性的含义。它

    是由封装 FreeMarker(比如一个 Web 应用框架)的应用程序决定的。因此,允许

    的属性的设置是它们依赖的应用(Web 应用框架)的语义。程序员:你可以通过

    关联 Template 对象 的getCustomAttributeNames 和

    getCustomAttribute 方法(从freemarker.core.Configurable

    继承而来)获得属性。如当模板被解析时,关联 Template 对象的模板属性,属

    性可以在任意时间被读取,而模板不需要被执行。上面提到的方法返回未包装的属

    性值,也就是说,使用 FreeMarker 独立的类型,如 java.util.List 。

    这个指令也决定模板是否使用尖括号语法(比如 <#include 'foo.ftl'> )或方

    括号语法(如 [#include 'foo.ftl'] )。简单而言,这个指令使用的语法将会是整

    个模板使用的语法,而不管 FreeMarker 是如何配置的。

    2.19 t ,lt ,rt  指令

    2.19.1  概要

    <#t>

    <#lt>

    <#rt>

    <#nt>

    2.19.2  描述

    这些指令,指示 FreeMarker 去忽略标记中行的特定的空白

      t (整体削减):忽略本行中首和尾的所有空白。

      lt (左侧削减):忽略本行中首部所有的空白。

      rt (右侧削减):忽略本行中尾部所有的空白。

    这里:

      “首部空白”表示本行所有空格和制表符(和其他根据 UNICODE 中的空白字符,

    除了换行符)在第一个非空白字符之前。

      “尾部空白”表示本行所有的空格和制表符(和其他根据 UNICODE 中的空白字符,

    除了换行符)在最后一个非空白字符之后,还有行末尾的换行符。

    理解这些检查模板本身的指令是很重要的,而不是当你合并数据模型时,模板生成的输

    出。(也就是说,空白的移除发生在解析阶段)

    比如这个:

    --

    1 <#t>

    2<#t>

    3<#lt>

    4

    5<#rt>

    6

    --

    将会输出:

    --

    1 23

    4

    5 6

    --

    这些指令在行内的放置不重要。也就是说,不管你是将它们放在行的开头,或是行的末

    尾,或是在行的中间,效果都是一样的。

    2.20 nt  指令

    2.20.1  概要

    <#nt>

    2.20.2  描述

    “不要削减”。这个指令关闭行中出现的空白削减。它也关闭其他同一行中出现的削减

    指令( t , lt , rt 的效果)。

    2.21 attempt ,recover  指令

    2.21.1  概要

    <#attempt>

    attempt block

    <#recover>

    recover block

    </#attempt>

    这里:

      attempt block :任意内容的模板块。这是会被执行的,但是如果期间发生了

    错误,那么这块内容的输出将会回滚,之后 recover block 就会被执行。

      recover block : 任意内容的模板块。这个仅在attempt block 执行期

    间发生错误时被执行。你可以在这里打印错误信息或其他操作。

    recover 是命令的。 attempt/recover可以嵌套在其他 attempt s 或

    recover s 中。

    注意:

    上面 的 格 式 是 从 2.3.3 版 本 开 始 支 持 的 , 之 前 它 是

    <#attempt>...<#recover>...</#recover>,也支持向下兼容。此外,这些

    指令在 FreeMarker 2.3.1 版本时引入的,在 2.3 版本中是不存在的。

    2.12.2  描述

    如果你想让页面成功输出内容,尽管它在页面特定位置发生错误也这样,那么这些指令

    就是有用的。如果一个错误在 attempt block 执行期间发生,那么模板执行就会中止,

    但是 recover block 会代替 attempt block 执行。如果在 attempt block

    执行期间没有发生错误,那么 recover block 就会忽略。一个简单的示例如下:

    Primary content

    <#attempt>

    Optional content: ${thisMayFails}

    <#recover>

    Ops! The optional content is not available.

    </#attempt>

    Primary content continued

    如果 thisMayFails 变量不存在,那么输出:

    Primary content

    Ops! The optional content is not available.

    Primary content continued

    如果 thisMayFails 变量存在而且值为 123 ,那么输出:

    Primary content

    Optional content: 123

    Primary content continued

    attempt block 块有多或没有的语义:不管attempt block 块的完整内容是

    否输出(没有发生错误),或者在 attempt block (没有发生错误)块执行时没有输出

    结果。比如,上面的示例,发生在“Optional content”之后的失败被打印出来了,而没有在

    “Ops!”之前输出。( 这是在 attemptblock 块内,侵入的输出缓冲的实现,就连 flush

    指令也会送输出到客户端。)

    为了阻止来自上面示例的误解: attempt / recover 不仅仅是处理未定义变量(对

    于那个可以使用不存在变量控制符来处理)。它可以处理发生在块执行期间的各种类型的错

    误(而不是语法错误,这会在执行之前被检测到)。它的目的是包围更大的模板段,错误可

    能发生在很多地方。比如,你在模板中有一个部分,来处理打印广告,但是它不是页面的主

    要内容,所以你不想你的页面因为一些打印广告(也可能是短暂的数据库故障)的错误而挂

    掉。所以你将整个广告区域放在 attempt block 块中。

    在一些环境下,程序员配置 FreeMarker,所以对于特定的错误,它不会中止模板的执行,

    在打印一些错误提示信息到输出(请参考:程序开发指南/配置/错误处理部分)中之后,而

    是继续执行。 attempt 指令不会将这些抑制的错误视为错误。

    在 recover block 块中,错误的信息存在特殊变量 error 中。不要忘了以点开

    始引用特殊变量(比如: ${.error} )

    在模板执行期间发生的错误通常被被日志记录,不管是否发生在 attempt block

    块中。

    2.22 visit ,recurse ,fallback  指令

    2.22.1  概要

    <#visit node using namespace>

    <#visit node>

    <#recurse node using namespace>

    <#recurse node>

    <#recurse using namespace>

    <#recurse>

    Primary content

    Ops! The optional content is not available.

    Primary content continued

    Primary content

    Optional content: 123

    Primary content continued

    <#fallback>

    这里:

      node :算作节点变量的表达式。

      namespace :一个命名空间,或者是命名空间的序列。命名空间可以以命名空

    间哈希表(又称为根哈希表)给定,或者可以引入一个存储模板路径的字符串。代

    替命名空间哈希表,你也可以使用普通哈希表。

    2.22.2  描述

    visit 和 recurse 指令是用来递归处理树的。在实践中,这通常被用来处理 XML。

    2.22.2.1 visit  指令

    当你调用了 <#visit node> 时,它看上去像用户自定义指令(如宏)来调用从节点

    名称( node?node_name )和命名空间( node?node_namesoace )中有名称扣除

    的节点。名称扣除的规则:

      如果节点不支持节点命名空间(如 XML 中的文本节点),那么这个指令名仅仅是节

    点的名称( node?node_name )。如果 getNodeNamespace 方法返回 null

    时节点就不支持节点命名空间了。

      如果节点支持节点命名空间(如 XML 中的元素节点),那么从节点命名空间中的前

    缀扣除可能在节点名称前和一个做为分隔符(比如 e:book )的冒号追加上去。

    前缀,以及是否使用前缀,依赖于何种前缀在 FTL 命名空间中用 ftl 指令的

    ns_prefixes 参数注册的,那里 visit 寻找控制器指令(visit 调用的相

    同 FTL 命名空间不是重要的,后面你将会看到)。具体来说,如果没有用

    ns_prefixes 注册默认的命名空间,那么对于不属于任何命名空间( 当

    getNodeNamespace 返回"" )的节点 来说 就 不使 用前 缀。 如果 使 用

    ns_prefixes 给不属于任意命名空间的节点注册了默认命名空间,那么就使用

    前缀 N ,而对于属于默认节点命名空间的节点就不使用前缀了。否则,这两种情况

    下,用 ns_prefixes 关联节点命名空间的前缀已经被使用了。如果没有关联

    节点命名空间的节点前缀,那么 visit 仅仅就好像没有以合适的名称发现指令。

    自定义指令调用的节点对于特殊变量 .node 是可用的。比如:

    <#-- 假设nodeWithNameX?node_name是"x" -->

    <#visit nodeWithNameX>

    Done.

    <#macro x>

    Now I'm handling a node that has the name"x".

    Just to show how to access this node: thisnode has

    ${.node?children?size} children.

    </#macro>

    输出就像:

    Now I'm handling a node that has the name"x".

    Just to show how to access this node: thisnode has

    ${.node?children?size} children.

    </#macro>

    如果使用可选的 using 从句来指定一个或多个命名空间,那么 visit 就会在那么命

    名空间中寻找指令,和先前列表中指定的命名空间都获得优先级。如果指定 using 从句,

    对最后一个未完成的 visit 调用的用 using 从句指定命名空间的命名空间或序列被重用

    了。如果没有这样挂起的 visit 调用,那么当前的命名空间就被使用。比如,如果你执行

    这个模板:

    <#import "n1.ftl" as n1>

    <#import "n2.ftl" as n2>

    <#-- 这会调用 n2.x (因为没有 n1.x):-->

    <#visit nodeWithNameX using [n1, n2]>

    <#-- 这会调用当前命名空间中的 x:-->

    <#visit nodeWithNameX>

    <#macro x>

    Simply x

    </#macro>

    这是 n1.ftl :

    <#macro y>

    n1.y

    </#macro>

    这是 n2.ftl :

    <#macro x>

    n2.x

    <#-- 这会调用call n1.y,因为它 从等待访问调用中继承了"using[n1,

    n2]"-->

    <#visit nodeWithNameY>

    <#-- 这会调用n2.y: -->

    <#visit nodeWithNameY using.namespace>

    </#macro>

    <#macro y>

    n2.y

    </#macro>

    这会打印:

    n2.x

    n1.y

    n2.y

    Simply x

    如果 visit 既没有在和之前描述规则的名称扣除相同名字的FTL 命名空间发现自定义

    指令,那么它会尝试用名称 @node_type 来查找,又如果节点不支持节点类型属性(也

    就是 node?node_type 返回未定义变量),那么使用名称 @default 。对于查找来说,

    它使用和之前描述相同的机制。如果仍然没有找到处理节点的自定义指令,那么 visit 停

    止模板执行,并抛出错误。一些 XML 特定的节点类型在这方面有特殊的处理;参考:XML

    处理指南/声明的 XML 处理/详细内容部分。示例:

    <#-- 假设nodeWithNameX?node_name是"x" -->

    <#visit nodeWithNameX>

    <#-- 假设nodeWithNameY?node_type 是"foo" -->

    <#visit nodeWithNameY>

    <#macro x>

    Handling node x

    </#macro>

    <#macro @foo>

    There was no specific handler for node${node?node_name}

    </#macro>

    这会打印:

    Handling node x

    There was no specific handler for node y

    2.22.2.2 recurse  指令

    <#recurse> 指令是真正纯语义上的指令。它访问节点的所有子节点(而没有节点本

    身)。所以来写:

    <#recurse someNode using someLib>

    和这个是相等的:

    <#list someNode?children aschild><#visit child using

    someLib></#list>

    而目标节点在 recurse 指令中是可选的。如果目标节点没有指定,那就仅仅使

    用 .node 。因此, <#recurse> 这个精炼的指令和下面这个是相同的:

    <#list .node?children aschild><#visit child></#list>

    对于 熟 悉 XSLT 的 用 户 的 评 论 , <#recurse> 是 和 XSLT 中

    <xsl:apply-templates/> 指令相当类似的。

    2.22.2.3 fallback  指令

    正如前面所学的,在 visit 指令的文档中,自定义指令控制的节点也许在多个 FTL 命

    名空间中被搜索。 fallback 指令可以被用在自定义指令中被调用处理节点。它指挥

    FreeMarker 在更多的命名空间(也就是,在当前调用列表中自定义指令命名空间之后的命名

    空间)中来继续搜索自定义指令。如果节点处理器被发现,那么就被调用,否则 fallback

    不会做任何事情。

    这个指令的典型用法是在处理程序库之上写定制层,有时传递控制到定制的库中:

    <#import "/lib/docbook.ftl" asdocbook>

    <#-- 我们使用 docbook 类库,但是我们覆盖一些这个命名空间中的处理器-->

    <#visit document using [.namespace,docbook]>

    <#-- 覆盖"programlisting"处理器,但是要注意它的"role"属性是"java"

    -->

    <#macro programlisting>

    <#if .node.@role[0]!"" =="java">

    <#-- 在这里做一些特殊的事情... -->

    ...

    <#else>

    <#-- 仅仅使用原来的(覆盖的)处理器-->

    <#fallback>

    </#if>

    </#macro>

    展开全文
  • ARM汇编指令以及伪指令

    千次阅读 2020-10-26 17:21:05
    文章目录ARM寄存器ARM寻址方式数据处理指令转移指令程序状态寄存器访问指令加载/存储指令异常指令指令ARM的存储系统ATPCS介绍异常中断处理ARM链接器 ARM寄存器 R13:栈指针寄存器(SP) R14:连接寄存器(LR) R15:...

    文章目录

    ARM寄存器

    在这里插入图片描述

    • R13:栈指针寄存器(SP)
    • R14:连接寄存器(LR)
    • R15:程序计数器(PC)
    1. ARM采用流水线机制,当读取PC值时,该值为当前指令地址值+8个字节
    2. 当使用STR/STM保存R15时,保存可能是当前地址值+8字节,也有可能保存当前指令地址+12字节(取决芯片的具体设计)
    3. 由于ARM指令是字对齐,PC值的第0位和第1位总为0
    • CPSR:程序状态寄存器
    1. 可以在任何处理器模式下访问,包含条件标志位、中断禁止位、当前处理器模式标志,以及其他一些控制和状态位
    2. 每种模式下,都有一个专用的物理状态寄存器(SPSR),当异常发生时,SPSR存放当前的程序状态寄存器,异常退出时,可以用SPSR保存的值回复CPSR
    3. 由于用户模式和系统模式不是异常模式,所有没有SPSR,如果在用户模式或系统模式访问SPSR时,将发生不可预知的结果
    • CPSR和SPSR的格式
      在这里插入图片描述
    1. 条件标志位
    标志位 含义
    N 当两个补码表示的有符号整数运算时,N=1表示运算的结果为负数;N=0表示结果为整数或零
    Z Z=1表示运算的结果为0;Z=0表示运算的结果不为零,对应CMP指令,Z=1表示进行比较的两个数大小相等
    C 在加法指令中(包括比较指令CMP),当结果产生进位,则C=1,表示无符号数运算发生上溢,否则C=0;在减法指令中(包括比较指令CMP),当运算发生借位,则C=0,表示无符号数运算发生下溢,否则C=0;对于包含位移操作的非加/减法运算指令,C包含最后一次被溢出的位数值
    V 用于加/减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出

    以下指令会影响CPSR中的条件标志位
    1、比较指令,如CMP、CMN、TEQ、TST等
    2、当一些算术运算指令和逻辑指令的目标寄存器不是R15(PC)时,将会影响CPSR中的条件标志位
    3、MSR指令可以向CPSR/SPSR写入新值
    4、MRC指令将R15作为目标寄存器时,可以把写处理器产生的条件标志位的值传给ARM处理器
    5、一些LDM指令的变种指令可以将SPSR的值赋值到CPSR中,例如从异常中断程序返回
    6、一些带“位设置”的算术和逻辑指令的变种指令,也可以将SPSR的值复制到CPSR中,例如从异常中断程序返回

    1. Q标志位,在ARMv5的E系列处理器中,主要用于只是增强的DSP指令是否发生溢出
    2. CPSR的控制位,在特权的处理器模式下,软件可以修改这些控制位
      1、 中断禁止位,当I=1时禁止IRQ中断;当F=1时禁止FIQ中断
      2、T控制位,用于控制指令执行的状态,即说明本指令时ARM指令,还是Thumb指令
      3、M控制位,控制处理器模式
      在这里插入图片描述
    3. CPSR其他位用于将来ARM版本的扩展

    ARM指令的一般编码格式

    典型的ARM指令语法格式:
    <opcode>{<cond>}{S} <Rd>,<Rn>,<shifter_operand>
    在这里插入图片描述
    其中的符号及参数说明:

    • opcode:指令操作符编码
    • cond:指令执行的条件编码
    • S:决定指令的操作是否影响CPSR的条件标志位
    • Rd:目标寄存器编码
    • Rn:包含第1个操作数的寄存器编码
    • shifter_operand:表示第2个操作数

    ARM指令的条件域

    • 多数ARM指令都可以有条件执行,根据CPSR的条件标志位决定是否执行该指令。当条件满足时执行,条件不满足时改指令被当作一条NOP指令。
    • 可条件执行的指令可以在其指令助记符的扩展域加上条件码助记符,从而在特定的条件下执行。
    条件码 条件码助记符 含义 CPSR条件标志位
    0000 EQ 相等 Z=1
    0001 NE 不相等 Z=0
    0010 CS/HS 无符号数大于/等于 C=1
    0011 CC/LO 无符号数小于 C=0
    0100 MI 负数 N=1
    0101 PL 非负数 N=0
    0110 VS 上溢出 V=1
    0111 VC 没有上溢出 V=0
    1000 HI 无符号数大于 C=1 且 Z=0
    1001 LS 无符号数小于等于 C=0 或Z=1
    1010 GE 带符号数大于等于 N=1 且 V=1 或 N= 0 且 V=0
    1011 LT 带符号数小于 N=1 且 V=0 或 N= 0 且 V=1
    1100 GT 带符号数小于 Z=0 且 N=V
    1101 LE 带符号数小于/等于 Z=1 或 N !=V
    1110 AL 无条件执行
    1111 NV 该指令从不执行 ARMv3之前
    1111 未定义 该指令执行结果不可预知 ARMv3及ARMv4
    1111 AL 该指令无条件执行 ARMv5及以上版本

    ARM寻址方式

    数据处理指令的操作数寻址方式

    通常数据处理指令格式:
    <opcode>{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

    • opcode:指令操作符编码
    • cond:指令执行的条件编码
    • S:决定指令的操作是否影响CPSR的条件标志位
    • Rd:目标寄存器编码
    • Rn:包含第1个操作数的寄存器编码
    • shifter_operand:表示第2个操作数

    <shifter_operand>通常有3种格式

    1. 立即数方式。每个立即数由一个8位的常数循环右移偶数位得到,其中循环右移的位数由一个4位二进制的二倍表示。如果立即数记作<immediate>,8位常数记作immed_8,4位的循环右移值记作rotate_imm,则有:<immediate>=immed_8 循环右移 (2*rotate_imm)。并不是每一个32位的常数都是合法的立即数,只有通过构造方法得到的才是合法的立即数,如0x101、0x102、oxFF1不是合法的立即数。而且一个合法的立即数可能有多种编码方法,并且立即数构造包含循环移位操作,从而影响CPSR的条件标志位,因此ARM汇编编译器按照下面的规则生成立即数的编码:
      1、当立即数数值在0和0xFF范围时,令immed_8=<immediate>,rotate_imm=0
      2、其他情况下,汇编编译器选择使rotate_imm数值最小的编码方式
    2. 寄存器方式,在寄存器寻址方式下,操作数即为寄存器的数值,如:MOV R3,R2
    3. 寄存器移位方式,操作数位寄存器的数值做相应的移位而等到,移位的位数可以用立即数或寄存器方式表示
      1、ASR:算术右移
      2、LSL:逻辑左移
      3、LSR:逻辑右移
      4、ROR:循环右移
      5、RRX:扩展的循环右移
      MOV R0,R1,LSL #3 ;RO=R1<<3
      MOV R0,R1,ROR R2 ;RO=R1循环右移R2位

    立即数寻址方式

    指令编码格式
    在这里插入图片描述

    • 指令的操作数<shifter_operand>即为立即数#<immediate>
    • 当ratate_imm=0时,循环器的进位值为CPSR的C位条件标志位;当rotate_imm!=0时,循环器的进位值为操作数<shifter_operand>的最高位bit[31]
      示例:
      MOV R0,#0xFC0;令R0的数值为0xFC0

    寄存器寻址方式

    指令编码格式
    在这里插入图片描述

    寄存器移位寻址方式

    1. <Rm>,LSL #<shift_imm>的指令编码格式
      在这里插入图片描述
    2. <Rm>,LSL <Rs>的指令编码格式
      在这里插入图片描述
    3. <Rm>,LSR #<shift_imm>的指令编码格式
      在这里插入图片描述
    4. <Rm>,LSR <Rs>的指令编码格式
      在这里插入图片描述
    5. <Rm>,ASR #<shift_imm>的指令编码格式
      在这里插入图片描述
    6. <Rm>,ASR <Rs>的指令编码格式
      在这里插入图片描述
    7. <Rm>,ROR #<shift_imm>的指令编码格式
      在这里插入图片描述
    8. <Rm>,ROR <Rs>的指令编码格式
      在这里插入图片描述
    9. <Rm>,RRX的指令编码格式
      在这里插入图片描述

    字或无符号字节的Load/Store指令的寻址方式

    LDR指令格式
    在这里插入图片描述

    • cond为指令执行的条件编码
    • I、P、U、B、W、L标志位
    • Rd为目标寄存器编码
      1、I标志位指示address_mode是否使用基址寄存器,当I=1时,address_mode使用基址寄存器,当I=0时,则使用偏移量
      2、P标志位
      3、U标志位指示偏移量,当U=1时,正偏移,当U=0时,负偏移
      4、B标志位用于控制操作的数据类型,当B=1时,指令访问无符号字节数据,当B=0时,指令访问的是字数据
      5、W标志位用更新基址寄存器的值,当W=1时,访问完成后更新寄存器值,当W=0时,访问完成不更新
      6、L标志位用于控制内存操作的方向,当L=1时。指令执行Load操作,当L=0时,指令执行Store操作
    • Rn与Address_mode一起构成第二个操作数的内存地址
    • 当R15用作基址寄存器时,内存基地址为当前指令地址+8字节偏移量

    LDR指令的语法格式:
    LDR{<cond>}{B}{T} <Rd>,<address_mode>

    其中,<address_mode>表示第2个操作数的内存地址,共有9种格式

    1. [<Rn>, #+/-<offset_12>] 指令编码格式
      在这里插入图片描述
      内存地址address为寄存器的值加上/减去偏移量offset_12
      示例:LDR R0, [R1,#4];将内存单元R1+4中的字读取到R0寄存器中

    2. [<Rn>, #+/-<Rm>] 指令编码格式
      在这里插入图片描述
      内存地址address为寄存器的值加上/减去索引寄存器Rm的值

    3. [<Rn>, #+/-<Rm>, <shift>#<offset_imm>] 指令编码格式
      在这里插入图片描述
      内存地址address为基址寄存器Rn的值加上/减去一个地址偏移量
      示例:LDR R0, [R1,R2,LSL #2];将地址单元(R1+R2*4)中的数据读取到R0中

    4. [<Rn>, #+/-<offset_12>]! 指令编码格式
      在这里插入图片描述
      内存地址address为寄存器的值加上/减去偏移量offset_12,当指令执行的条件满足时,生成的地址值将跟新到基址寄存器Rn中
      示例:LDR R0, [R1,#4]!;将内存单元R1+4中的字读取到R0寄存器中,同时R1=R1+4

    5. [<Rn>, #+/-<Rm>]! 指令编码格式
      在这里插入图片描述
      1、内存地址address为寄存器的值加上/减去索引寄存器Rm的值,当指令执行的条件满足时,生成的地址值将跟新到基址寄存器Rn中
      2、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
      3、当Rn和Rm时同一个寄存器时,会产生不可预知的结果

    6. [<Rn>, #+/-<Rm>, <shift>#<offset_imm>]! 指令编码格式
      在这里插入图片描述
      1、内存地址address为基址寄存器Rn的值加上/减去一个地址偏移量,当指令执行的条件满足时,生成的地址值将跟新到基址寄存器Rn中
      2、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
      3、当Rn和Rm时同一个寄存器时,会产生不可预知的结果
      示例:LDR R0, [R1,R2,LSL #2];将地址单元(R1+R24)中的数据读取到R0中,同时R1=R1+R24

    7. [<Rn>], #+/-<offset_12> 指令编码格式
      在这里插入图片描述
      1、指令使用基址寄存器Rn的值作为实际内存访问的地址
      2、当指令执行的条件满足时,将基址寄存器的值加上/减去偏移量<offset_12>,生成新的地址值
      3、最后将新的地址值写入基址寄存器Rn中
      4、当R15用作基址寄存器Rn或Rm时,会产生不可预知的结果
      示例:LDR R0, [R1]+4;将地址为R1的内存单元数据读取到R0中,然后R1=R1+4

    8. [<Rn>], #+/-<Rm> 指令编码格式
      在这里插入图片描述
      1、指令使用基址寄存器Rn的值作为实际内存访问的地址
      2、当指令执行的条件满足时,将基址寄存器的值加上/减去偏移量Rm,生成新的地址值
      3、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
      4、当Rn和Rm时同一个寄存器时,会产生不可预知的结果

    9. [<Rn>], #+/-<Rm>, <shift>#<offset_imm> 指令编码格式
      在这里插入图片描述
      1、指令使用基址寄存器Rn的值作为实际内存访问的地址
      2、当指令执行的条件满足时,将基址寄存器的值加上/减去偏移量Rm,生成新的地址值
      3、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
      4、当Rn和Rm时同一个寄存器时,会产生不可预知的结果

    杂类Load/Store指令的寻址方式

    操作数为半字(无符号数或带符号数)数据的Load/Store指令,这类指令的语法格式为:
    LDR | STR{<cond>}H | SH | SB | D <Rd>,<address_mode>
    其中<address_mode>是指令内存单元的寻址方式,具体有6种格式:

    1. [<Rn>, #+/-<offset_8>]
    2. [<Rn>, #+/-<Rm>]
    3. [<Rn>, #+/-<offset_8>]!
    4. [<Rn>, #+/-<Rm>]!
    5. [<Rn>], #+/-<offset_8>
    6. [<Rn>], #+/-<Rm>]

    批量Load/Store指令的寻址方式

    批量Load/Store指令可以实现在一组寄存器和一块连续的内存单元之间传输数据,这类指令的语法格式为:
    LDM | STM{<cond>}<address_mode> <Rn>{!}, <registers>{^}

    • 指令中寄存器和内存单元的对应关系规则:编号低的寄存器对应与内存中的低地址单元,编号高的寄存器对应与内存中的高地址单元
    • <Rn>存放地址块的最低地址值
    • 栈指针指向栈顶元素(即最后一个入栈的数据元素)时,称为Full栈;栈指针指向与栈顶元素相邻的一个可用数据单元时称为Empty栈
    • 当数据栈向内存地址减少的方向增长时,称为Descending栈;当数据栈向内存地址增加的方向增长时,称为Ascending栈

    其中<address_mode>表示通常地址的变化方式,具体有4种格式:

    1. IA:事后递增方式
    2. IB:事先递增方式
    3. DA:事后递减方式
    4. DB:事先递减方式

    其中<address_mode>表示数据栈地址的变化方式,具体有4种格式

    1. FD:Full Descending
    2. ED:Empty Descending
    3. FA:Full Ascending
    4. EA:Empty Ascending

    协处理器Load/Store指令的寻址方式

    一条协处理器Load/Store指令可以在ARM处理器和协处理器之间传输批量数据,其语法格式如下:
    <opcode>{<cond>}{L} <coproc>,<CRd>,<addressing_mode>
    其中<addressing_mode>表示地址的变化方式有以下4种格式:

    1. [<Rn>, #+/-<offset_8>*4]
    2. [<Rn>, #+/-<offset_8>*4]!
    3. [<Rn>], #+/-<offset_8>*4
    4. [<Rn>], <option>

    ARM指令集

    跳转指令

    ARM跳转指令可以从当前指令向前或向后32MB的地址空间跳转

    1. B:跳转指令
    2. BL:带返回的跳转指令
    3. BX:带状态切换的跳转指令
    4. BLX:带返回和状态切换的跳转指令
    • BL指令的编码格式
      在这里插入图片描述
      指令的语法格式
      B{L}{<cond>} <target_address>

    • BLX(1)指令从ARM指令集跳转到指令中指定的目的地址,并将程序状态切换为Thumb状态,该指令同时将PC寄存器的内容复制到LR寄存器中,指令编码格式
      在这里插入图片描述
      指令的语法格式
      BLX <target_address>

    • BLX(2)指令从ARM指令集跳转到指令中指定的目的地址,目标地址的指令可以是ARM指令,也可以是Thumb指令,目标地址放在指令中的寄存器<Rm>,该地址的bit[0]为0,目标地址处的指令类型由CPSR中的T位决定,该指令同时将PC寄存器的内容复制到LR寄存器中,指令编码格式
      在这里插入图片描述
      指令的语法格式
      BLX{<cond>} <Rm>

    • BX指令从ARM指令集跳转到指令中指定的目的地址目标地址处的指令可以是ARM指令。也可以是Thumb指令,目标地址值为指令的值和0xFFFFFFFE与操作的结果,目标地址处的指令类型由寄存器<Rm>的bit[0]决定,当bit[0]=0时,目标地址处的指令为ARM指令,当bit[0]=1时,目标地址处的指令为Thumb指令,指令编码格式
      在这里插入图片描述
      指令的语法格式
      BX{<cond>} <Rm>

    数据处理指令

    MOV

    MOV指令将shifter_operand表示的数据传送到目标寄存器Rd中,并根据操作结果跟新CPSR
    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MOV{<cond>}{S} <Rd>, <shifter_operand>

    • 将数据从一个寄存器传送到另一个寄存器中
    • 将一个常数传送到一个寄存器中
    • 实现单纯的移位操作,左移操作可以实现将操作数乘以2^n
    • 当PC寄存器作为目标寄存器时,可以实现程序跳转
    • 当PC寄存器作为目标寄存器且指令中S位被设置时,指令在执行跳转操作的同时,将当前处理器模式的SPSR寄存器内容复制到CPSR中,这样指令“MOVS PC,LR”可以实现从某些中断中返回

    MVN

    MVN指令将shifter_operand表示的数据反码传送到目标寄存器Rd中,并根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MVN{<cond>}{S} <Rd>, <shifter_operand>

    • 向寄存器传送一个负数
    • 生成位掩码
    • 求一个数的反码

    ADD

    ADD指令将shifter_operand表示的数据与寄存器Rn中的值相加,并把结果保存到目标寄存器Rd中,同时根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    ADD{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • 实现两个操作数相加

    ADC

    ADC指令将shifter_operand表示的数据与寄存器Rn中的值相加,再加上CPSR中的C条件标志位的值,并把结果保存到目标寄存器Rd中,同时根据结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    ADC{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • ADC指令和ADD指令联合使用,可以实现两个64位的操作数相加

    SUB

    SUB指令从寄存器Rn中减去shifter_operand表示的数值,并把结果保存到目标寄存器Rd中,根据结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SUB{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • SUB指令实现两个操作数相减
    • 当SUBS指令与跳转指令联合使用时,可以实现程序中循环,就不需要CMP指令了
    • 在SUBS指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

    SBC

    SBC指令从寄存器Rn中减去shifter_operand表示的数值,再减去寄存器CPSR中C条件标志位的反码,并把结果保存到目标寄存器Rd中,同时根据操作的结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SBC{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • SBC指令和SUBS指令联合使用,可以实现两个64位的操作数相减
    • 在SBCS指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

    RSB

    RSB指令从shifter_operand表示的数值减去寄存器Rn值,并把结果保存到目标寄存器Rd中,同时根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    RSB{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • RSB指令实现两个操作数相减
    • 在RSB指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

    RSC

    RSC指令将shifter_operand表示的数值减去寄存器Rn的值,再减去寄存器CPSR中C条件标志位的反码,并把结果保存到目标寄存器Rd中,根据操作的结果跟新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    RSC{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • 在RSCS指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

    AND

    AND指令将shifter_operand表示的数值与寄存器Rn的值按位与操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    AND{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    ORR

    ORR指令将shifter_operand表示的数值与寄存器Rn的值按位或操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    ORR{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    EOR

    ORR指令将shifter_operand表示的数值与寄存器Rn的值按位异或操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    EOR{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    BIC

    BIC指令将shifter_operand表示的数值与寄存器Rn的值的反码按位与操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    BIC{<cond>}{S} <Rd>, <Rn>, <shifter_operand>

    • BIC指令可用于将寄存器中某些位的值设置成0,将某一位与1做BIC操作,该位值被设置成0;将某一位与0做BIC操作,该位不变

    CMP

    CMP指令从寄存器Rn中减去shifter_operand表示的数值,根据操作结果更新CPSR,后面的指令可以根据CPSR中的条件标志位判断是否执行

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    CMP<cond>} <Rn>, <shifter_operand>

    • CMP指令不保存操作结果

    CMN

    CMN指令将寄存器Rn中的值加上shifter_operand表示的数值,根据操作结果更新CPSR,后面的指令可以根据CPSR的条件标志位判断是否执行

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    CMN<cond>} <Rn>, <shifter_operand>

    • CMN根据加法操作结果设置CPSR条件标志位,不保存操作结果

    TST

    TST指令将shifter_operamd表示的数值与寄存器Rn的值按位与操作,根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    TST<cond>} <Rn>, <shifter_operand>

    • TST指令通常用于测试寄存器中某些位是1还是0

    TEQ

    TEQ指令将shifter_operand表示的数值与寄存器Rn的值按位异或操作,根据操作结果更新CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    TEQ<cond>} <Rn>, <shifter_operand>

    • TEQ指令通常用于比较两个数是否相等,这种操作通常不影响CPSR寄存器中的V位和C位
    • TEQ指令也可以用于比较两个操作数符号是否相同,该指令执行后,CPSR寄存器中的N位位两个操作数符号位异或操作的结果

    乘法指令

    MUL

    MUL指令实现两个32位的数(可以为无符号数,也可以是符号数)的乘积,并将结果放到一个32位的寄存器中,根据运算结果设置CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MUL<cond>}{S} <Rd> <Rm>, <Rs>

    • Rd寄存器为目标寄存器
    • Rm寄存器为第1个乘数所在的寄存器
    • Rs寄存器为第2个乘数所在的寄存器
    • 由于两个32位数相乘结果为64位,而MUL指令仅仅保存64位结果的低32位,对于带符号数和无符号的操作数来说,MUL指令执行的结果相同
    • 对于ARMv5以上,MULS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,MULS指令对CPSR的C标志位数值不确定
    • 寄存器Rm、Rn、Rd为R15时,指令执行的结果不可预测

    MLA

    MLA指令实现两个32位的数(可以为无符号数,也可以是符号数)的乘积,再将乘积加上第3个操作数,并把结果存放到一个32位的寄存器中,根据运算结果设置CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MLA<cond>}{S} <Rd>, <Rm>, <Rs>, <Rn>

    • Rd寄存器为目标寄存器
    • Rm寄存器为第1个乘数所在的寄存器
    • Rs寄存器为第2个乘数所在的寄存器
    • Rn寄存器为第3个操作数所在的寄存器,该操作数时一个加数
    • 由于两个32位数相乘结果为64位,而MLA指令仅仅保存64位结果的低32位,对于带符号数和无符号的操作数来说,MLA指令执行的结果相同
    • 对于ARMv5以上,MLAS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,MLAS指令对CPSR的C标志位数值不确定
    • 寄存器Rm、Rn、Rd为R15时,指令执行的结果不可预测

    SMULL

    SMULL指令实现两个32位的有符号数的乘积,乘积结果的高32位存放到一个32位的寄存器的RdHi中,乘积结果的低32位存放到另一个32位的寄存器RdLo中,根据运算结果设置CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SMULL<cond>}{S} <RdLo>, <RdHi>, <Rm>, <Rs>

    • RdHi寄存器存放乘积结果的高32位数据
    • RdLo寄存器存放乘积结果的低32位数据
    • 对于ARMv5以上,SMULLS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,SMULLS指令对CPSR的C标志位数值不确定
    • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

    SMLAL

    SMLAL指令将两个32位有符号数的64位乘积结果与RdHi和RdLo中的64位数相加,乘加法结果的高32位存放到RdHi,乘加法结果的低32位存放到RdLo中,根据运算结果设置CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SMLAL<cond>}{S} <RdLo>, <RdHi>, <Rm>, <Rs>

    • RdHi寄存器再指令执行前存放64位加数的高32位,指令执行后存放结果的高32位数据
    • RdLo寄存器再指令执行前存放64位加数的低32位,指令执行后存放结果的低32位数据
    • 对于ARMv5以上,SMLALS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,SMLALS指令对CPSR的C标志位数值不确定
    • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

    指令操作的伪代码

    if cond then
    	RdLo = (Rm * Rs)[31:0] + RdLo
    	RdHi = (Rm * Rs)[63:32 + RdHi + CPSR[C]
    	if S = 1 then
    		CPSR[N] = RdHi[31]
    		CPSR[Z] = if((RdHi == 0) and (RdLo == 0)) then 1 else 0
    		CPSR[C] = unaffected
    		CPSR[V] = unaffected
    

    UMULL

    UMULL指令实现两个32位的无符号数的乘积,乘积结果的高32位存放到一个32位的寄存器的RdHi中,乘积结果的低32位存放到另一个32位的寄存器RdLo中,根据运算结果设置CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    UMULL<cond>}{S} <RdLo>, <RdHi>, <Rm>, <Rs>

    • RdHi寄存器存放乘积结果的高32位数据
    • RdLo寄存器存放乘积结果的低32位数据
    • 对于ARMv5以上,UMULLS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,UMULLS指令对CPSR的C标志位数值不确定
    • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

    UMLAL

    UMLAL指令将两个32位有符号数的64位乘积结果与RdHi和RdLo中的64位数相加,乘加法结果的高32位存放到RdHi,乘加法结果的低32位存放到RdLo中,根据运算结果设置CPSR

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    UMLAL<cond>}{S} <RdLo> , <RdHi>, <Rm>, <Rs>

    • RdHi寄存器再指令执行前存放64位加数的高32位,指令执行后存放结果的高32位数据
    • RdLo寄存器再指令执行前存放64位加数的低32位,指令执行后存放结果的低32位数据
    • 对于ARMv5以上,UMLALS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,UMLALS指令对CPSR的C标志位数值不确定
    • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

    指令操作的伪代码

    if cond then
    	RdLo = (Rm * Rs)[31:0] + RdLo
    	RdHi = (Rm * Rs)[63:32 + RdHi + CPSR[C]
    	if S = 1 then
    		CPSR[N] = RdHi[31]
    		CPSR[Z] = if((RdHi == 0) and (RdLo == 0)) then 1 else 0
    		CPSR[C] = unaffected
    		CPSR[V] = unaffected
    

    杂类算术指令

    在ARMv5及以上的版本中,包含一条特殊的指令CLZ,用于计算操作数最高端0的个数

    CLZ

    CLZ指令用于计算寄存器中操作数最高端0的个数,如果操作数的bit[31]为1,则指令返回0;如果操作数为0,则指令返回32

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    CLZ<cond>}{S} <Rd> , <Rm>

    • cond为指令执行的条件码,当cond忽略时,指令为无条件执行
    • Rd为目标寄存器
    • Rm寄存器为第1个乘数所在的寄存器
    • Rs为源操作数寄存器,当Rd为R15时,指令执行结果不可预知

    指令操作的伪代码

    if Rm == 0
    	Rd = 32
    else
    	Rd = 31 - (bit position of most significant '1' in Rm)
    

    状态寄存器访问指令

    • 状态寄存器中,未定义位是当前未使用
    • 程序不能通过直接修改CPSR中的T控制位直接将程序状态切换到Thumb状态,必须通过BX等指令完成程序状态的切换
    • 通常修改状态寄存器是通过“读取-修改-写回”的操作序列来实现

    MRS

    MRS指令用于将状态寄存器的内容传送到通用寄存器中

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MRS{<cond>} <Rd>,CPSR
    MRS{<cond>} <Rd>,SPSR

    • cond为指令执行的条件码,当cond忽略时,指令为无条件执行
    • Rd为目标寄存器
    • MRS指令用于将状态寄存器的内容读取到通用寄存器中
    • 当异常中断允许嵌套时,需要在进入异常中断之后,嵌套中断发生之前保存当前处理器模式对应的SPSR,这时需要先通过MRS指令读取SPSR的值,然后用其他指令将SPSR的值保存起来
    • 在进程切换时也需要保存当前状态寄存器值

    MSR

    MSR指令用于将通用寄存器内容或一个立即数传送到状态寄存器中

    指令的编码格式
    (1) 指令的源操作数为通用寄存器时
    在这里插入图片描述
    (2) 指令的源操作数为立即数时
    在这里插入图片描述
    指令的语法格式
    MSR{<cond>} CPSR_<fields>, #<immediate>
    MSR{<cond>} CPSR_<fields>, <Rm>
    MSR{<cond>} SPSR_<fields>, #<immediate>
    MSR{<cond>} SPSR_<fields>, <Rm>

    • CPSR_<fields>(CPSR_fsxc、CPSR_f、CPSR_s、CPSR_x、CPSR_c)的fields是设置状态寄存器中需要操作的位。状态寄存器的32位可以分为4个8位的域:bits[31:24]为条件标志位域,用f表示;bits[23:16]为状态位域,用s表示;bits[15:8]为扩展位域,用x表示;bits[7:0]为控制位域,用c表示
    • immediate为将要传送到状态寄存器中的立即数
    • Rm寄存器包含将要传送到状态寄存器中的数据
    • MSR指令通常用于恢复状态寄存器的内容或者修改状态寄存器的内容

    指令操作的伪代码

    if cond then
    	if opcode[25] == 1
    		operand = 8_bit_immediate << (rotate_imm * 2)
    	else 
    		operand = Rm
    	
    	if R == 0 then
    		if field_mask[0] == 1 and InaprivilegeMode() then 
    			CPSR[7:0] = operand[7:0]
    		if field_mask[1] == 1 and InaprivilegeMode() then 
    			CPSR[15:8] = operand[15:8]
    		if field_mask[2] == 1 and InaprivilegeMode() then 
    			CPSR[23:16] = operand[23:16]
    		if field_mask[3] == 1 and InaprivilegeMode() then 
    			CPSR[31:24] = operand[31:24]
    	else 
    		if field_mask[0] == 1 and CurrentModeHasSPSR() then 
    			SPSR[7:0] = operand[7:0]
    		if field_mask[1] == 1 and urrentModeHasSPSR()then 
    			SPSR[15:8] = operand[15:8]
    		if field_mask[2] == 1 and urrentModeHasSPSR()then 
    			SPSR[23:16] = operand[23:16]
    		if field_mask[3] == 1 and urrentModeHasSPSR()then 
    			SPSR[31:24] = operand[31:24]
    

    内存访问指令

    LDR

    LDR指令用于从内存中将一个32位的字读取到指令中的目标寄存器中,如果指令中寻址方式确定的地址不是字对齐的,则从内存中读取的数值要进行循环右移操作,移位的位数为寻址方式确定的地址的bits[1:0]的8倍。

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>} <Rd>, <addressing_mode>

    • cond为指令执行的条件码,当cond忽略时,指令为无条件执行
    • Rd为目标寄存器
    • addressing_mode为指令的寻址方式
    • 用于从内存读取32位字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
    • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能,当PC被作为LDR指令的目标寄存器时,指令从内存中读取的字节数据将被当作目标地址值,指令执行后,程序将从目标地址处开始执行。在ARMv5及以上版本中,地址值的bit[0]用来确定目标地址处的程序状态,当bit[0]=1时,目标地址指令为Thumb指令;当bit[0]=0时,目标地址指令为ARM指令

    指令操作伪代码

    if cond then
    	if address[1:0] == 0b00 then
    		value = Memory[address,4]
    	else if address[1:0] == 0b01 then
    		value = Memory[address, 4] Rotate_Right 8
    	else if address[1:0] == 0b10 then
    		value = Memory[address, 4] Rotate_Right 16
    	else
    		value = Memory[address, 4] Rotate_Right 24
    if (Rd is R15) then
    	if (architecture version 5 or above) then
    		PC = value AND 0xFFFFFFFE
    		CPSR[T] = value[0]
    	else
    		PC = value AND 0xFFFFFFFC
    else 
    	Rd = value
    

    LDRB

    LDRB指令用于从内存中将一个8位的字节数据读取到指令中的目标寄存器中,并且将寄存器的高24位清零

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>}B <Rd>, <addressing_mode>

    • 用于从内存读取8位字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
    • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

    LDRBT

    LDRBT指令用于从内存中将一个8位的字节数据读取到指令中的目标寄存器中,并将寄存器的高24位清零,当在特权模式下使用本指令时,内存系统将该操作当作一般用户模式下的内存访问操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>}BT <Rd>, <addressing_mode>

    • 异常中断程序是在特权模式下执行的,此时如果需要按照用户模式的权限访问内存,可以使用LDRBT指令

    LDRH

    LDRH指令用于从内存中将一个16位的半字数据读取到指令中的目标寄存器中,并将寄存器的高16位清零,如果指令中的内存地址不是半字对齐的,指令会产生不可预知的结果

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>}H <Rd>, <addressing_mode>

    • 用于从内存中读取16位半字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
    • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

    LDRSB

    LDRSB指令用于从内存中将一个8位字节数据读取到指令中的目标寄存器中,并将寄存器的高24位设置成该字节数据的符号位的值(即将该8位字节数据进行符号位扩展,生成32位字数据)

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>}SB <Rd>, <addressing_mode>

    • 用于从内存中读取8位字节数据到通用寄存器中,然后可在该寄存器中对数据进行操作
    • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

    LDRSH

    LDRSH指令用于从内存中将一个16位的半字数据读取到指令中的目标寄存器中,并将寄存器的高12位设置成该半字数据发惹符号位的值,如果指令的内存地址不是半字对齐,指令产生不可预知的结果

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>}SH <Rd>, <addressing_mode>

    • 用于从内存中读取16位半字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
    • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

    LDRT

    LDR指令用于从内存中将一个32位的字读取到指令中的目标寄存器中,如果指令中寻址方式确定的地址不是字对齐的,则从内存中读取的数值要进行循环右移操作,移位的位数为寻址方式确定的地址的bits[1:0]的8倍。当在特权模式下使用本指令时,内存系统将该操作当作一般用户模式下的内存访问操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDR{<cond>}T <Rd>, <post_indexed_addressing_mode>

    • 异常中断程序在特权模式下执行时,如果需要按照用户模式的权限访问内存,可以使用LDRT指令

    STR

    STR指令用于将一个32位的字数据写入到指令中指定的内存单元

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STR{<cond>} <Rd>, <addressing_mode>

    • cond为指令执行的条件码,当cond忽略时,指令无条件执行
    • Rd为目标寄存器
    • addressing_mode为指令的寻址方式

    示例

    STR R0, [R1, #0x100];将R0中的字数据保存到内存单元[R1+0x100]中
    

    STRB

    STRB指令用于将一个8位字节数据写入到指令中指定的内存单元,该字节数据为指令中存放源操作数的寄存器的低8位

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STR{<cond>}B <Rd>, <addressing_mode>

    STRH

    STRH指令用于将一个16位的半字数据写入到指令中指定的内存单元,该半字数据为指令中存放源操作数的寄存器的低16位,如果指令中的内存地址不是半字对齐的,指令会产生不可预知的结果

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STR{<cond>}H <Rd>, <addressing_mode>

    STRT

    STRT指令用于将一个32位的字数据写入到指令中指定的内存单元,当在特权模式下执行该指令时,内存系统将该操作当作一般用户下的内存访问操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STR{<cond>}T <Rd>, <post_indexed_addressing_mode>

    STRBT

    STRBT指令用于将一个8位的字节数据写入到指令中指定的内存单元,当在特权模式下执行该指令时,内存系统将该操作当作一般用户模式下的内存访问操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STR{<cond>}BT <Rd>, <post_indexed_addressing_mode>

    批量Load/Store内存访问指令

    批量Load/Store内存访问指令的语法格式
    LDM | STM {<cond>} <addressing_mode> Rn{!}, <registers>{^}
    其中<address_mode>表示通常地址的变化方式,具体有4种格式:

    1. IA:事后递增方式
    2. IB:事先递增方式
    3. DA:事后递减方式
    4. DB:事先递减方式

    其中<address_mode>表示数据栈地址的变化方式,具体有4种格式

    1. FD:Full Descending
    2. ED:Empty Descending
    3. FA:Full Ascending
    4. EA:Empty Ascending

    LDM(1)(批量内存字数据读取指令)

    • LDM指令将数据从连续的内存单元中读取到指令中寄存器列表中的各寄存器中,主要用于块数据的读取、数据栈操作以及子程序中返回的操作
    • 当PC包含在LDM指令的寄存器列表时,指令从内存中读取的字数据将被当作目标地址值,指令执行后,程序将从目标地址处开始执行,即实现跳转操作
    • 在ARMv5及以上版本中,地址值的bit[0]用来确定目标地址处的程序状态,当bit[0]=1时,目标地址处的指令为Thumb指令;当bit[0]=0时,目标地址处的指令为ARM指令

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDM{<cond>} <addressing_mode> Rn{!}, <registers>

    • cond为指令执行的条件码
    • Rn为指令寻址模式中的基址寄存器,存放地址块的最低地址值
    • !设置指令中的W位,使指令执行后将操作数的内存地址写入基址寄存器Rn中
    • addressing_mode为指令的寻址方式
    • registers为寄存器列表,其中寄存器和内存单元的对应关系满足:编号低的寄存器对应于内存中的低地址单元,编号高的寄存器对应内存中的高地址单元
    • 如果指令中的基址寄存器Rn在寄存器列表register中,而且指令中寻址方式指定指令执行后跟新基址寄存器Rn值,则指令执行产生不可预知结果

    指令操作伪代码

    if cond then
    	address = start_address
    	for i = 0 to 14
    		if register_list[i] == 1 then
    			Ri = Memory[address, 4]
    			address = address + 4
    		if register_list[15] == 1 then
    			value = Memory[address, 4]
    			if (architecture version 5 or above) then
    				pc = value AND 0xFFFFFFFE
    				CPSR[T] = value[0]
    			else
    				pc  = value AND 0xFFFFFFFC
    			address = address + 4
    		assert end_address = address - 4
    

    LDM(2)(用户模式的批量内存字数据读取指令)

    • LDM指令将数据从连续的内存单元中读取到指令中寄存器列表中的各寄存器中,主要用于块数据的读取、数据栈操作以及子程序中返回的操作
    • PC寄存器不能包含在LDM指令的寄存器列表中
    • 当特权模式下执行该指令,内存系统将该操作当作一般用户模式下的内存访问操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDM{<cond>} <addressing_mode> <Rn>, <registers_without_pc>^

    • ^在寄存器列表中不含PC寄存器,指示指令中所用的寄存器为用户模式下的寄存器
    • 在本指令的后面不能紧跟访问备份寄存器的指令,最好跟一条NOP指令
    • 指令中的基址寄存器是指令执行时的当前处理器模式对应的物理寄存器,而不是用户模式对应的寄存器
    • 本指令忽略指令中内存地址的低2位
    • 异常中断程序在特权模式下执行,如果需要按照用户模式的权限访问内存,可以使用该指令
    • 在用户模式和系统模式下使用本指令会产生不可预知的结果

    指令操作的伪代码

    if cond then
    	address = start_address
    	for i = 0 to 14
    		if register_list[i] == 1 then
    			Ri = Memory[address, 4]
    			address = address + 4
    		assert end_address = address - 4
    

    LDM(3)(带状态寄存器的批量内存字数据读取指令)

    • LDM指令将数据从连续的内存单元中读取指令中寄存器列表中的各寄存器中,同时将当前处理器模式的SPSR寄存器内容复制到CPSR寄存器中
    • 当PC包含在LDM指令的寄存器列表时,指令从内存中读取的字数据将被当作目标地址值,指令执行后,程序将从目标地址处开始执行,实现跳转操作
    • 在ARMv5及以上版本和T系列的ARMv4版本中,SPSR寄存器的T位将复制到CPSR寄存器的T位,该位决定目标地址处的程序状态

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDM{<cond>} <addressing_mode> <Rn>{!}, <registers_and_pc>^

    • registers_and_pc为寄存器列表,在本格式指令中寄存器列表必须包含PC寄存器,其
    • ^指示指令执行时将当前处理器模式下的SPSR值赋值到CPSR中,若指令的寄存器列表不包含PC寄存器,则该指令为一条LDM(2)格式指令
    • 如果指令中基址寄存器Rn在寄存器列表registers中,而且指令中的寻址方式指定指令执行后更新寄存器Rn的值,则指令执行会产生不可预知的结果
    • 本指令主要用于从异常中断模式下返回,如果在用户模式或系统模式下使用该指令,会产生不可预知结果

    STM(1)(批量内存字数据写入指令)

    STM指令将指令中寄存器列表中的各寄存器数据写入到连续的内存单元中,主要用于块数据的写入、数据栈操作、以及进入子程序时保存相关的寄存器操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STM{<cond>} <addressing_mode> Rn{!}, <registers>

    • Rn为指令寻址模式中的基址寄存器,用于存放地址块的最低地址,如果R15被作为Rn,指令产生不可预知结果
    • 如果指令基址寄存器Rn在寄存器列表registers中,而且指令中寻址方式指定指令执行后更新基址寄存器Rn的值,则当Rn是register中编号最小的寄存器时,指令将Rn的初始值保存到内存中,否则,指令执行后产生不可预知的结果

    STM(2)(用户模式的批量内存字数据写入指令)

    STM指令将指令中寄存器列表中的各寄存器(用户模式对应的寄存器)数值写入连续的内存单元中,主要用于块数据的写入、数据栈操作以及进入子程序时保存相关的寄存器操作

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STM{<cond>} <addressing_mode> <Rn>, <registers_without_pc>^

    • Rn为指令寻址模式中的基址寄存器,存放地址块的最低地址值,如果R15被作为Rn,指令会产生不可预知的结果
    • ^指示指令中所用的寄存器为用户对应的寄存器
    • 本指令主要用于从异常中断模式下返回,如果在用户模型或系统模式下使用该指令,会产生不可预知的结果
    • 在本指令的后面不能紧跟访问备份寄存器的指令,最好跟一条NOP指令
    • 指令中的基址寄存器是指令执行时的当前处理器模式对应的物理寄存器,而不是用户模式对应的寄存器

    信号量操作指令

    信号量用于进程间的同步和互斥,对信号量的操作通常要求是一个原子操作,即在一条指令中完成信号量的读取和修改操作

    SWP(交换指令)

    SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中,当Rd和Rm为同一个寄存器时,指令交换该寄存器和内存单元的内容

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SWP{<cond>} <Rd>, <Rm>, [<Rn>]

    • cond为指令执行的条件码
    • Rd为目标寄存器
    • Rm寄存器包含将要保存到内存中的数值
    • Rn寄存器中包含将要访问的内存地址
    • 本指令主要用于实现信号量操作

    指令操作伪代码

    if cond then
    	if Rn[1:0] == 0b00 then
    		temp = Memory[Rn, 4]
    	else if Rn[1:0] == 0b01 then
    		temp = Memory[Rn, 4] Rotate_Right 8
    	else if Rn[1:0] == 0b10 then
    		temp = Memory[Rn, 4]  Rotate_Right 16
    	else
    		temp = Memory[Rn, 4]  Rotate_Right 24
    	Memory[Rn,4] = Rm
    	Rd = temp
    

    示例

    SWP R1, R2, [R3];将R3指示的内存单元字数据读取到R1寄存器中,同时将R2寄存器的数据写入R3指示的内存单元中
    

    SWPB(字节交换指令)

    SWPB指令用于将一个内存字节单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,寄存器Rd的高24位设置为0,同时将另一个寄存器Rm的低8位数值写入该内存单元中,当Rd和Rm为同一个寄存器时,指令交换该寄存器的低8位和内存字节单元的内容

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SWPB{<cond>} <Rd>, <Rm>, [<Rn>]

    异常中断产生指令

    ARM有两条异常中断产生指令

    • SWI:软中断指令,用于产生SWI异常中断,ARM通过该机制实现在用户模式中对操作系统特权模式的程序调用
    • BKPT:断点中断指令,在ARMv5及以上的版本引入,主要用于产生软件断点,供调试程序使用

    SWI(软中断指令)

    SWI指令用于产生软中断

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    SWI{<cond>} <immed_24>

    • cond为指令执行的条件码
    • immed_24为24位的立即数,该立即数被操作系统用来判断用户程序请求的服务类型
    • 用于用户程序调用操作系统服务,在SWI的异常中断处理程序中提供相关的系统服务,并定义参数传递方法:1、指令中24位的立即数指定用户请求的服务类型,参数通过通用寄存器传递;2、指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的数值决定,参数通过其他的通用寄存器传递

    指令操作的伪代码

    if cond then
    	R14_svc = address of next instruction after the SWI instruction
    	SPSR_svc = CPSR
    	CPSR[4:0] = 0b10011 #Enter Supervisor mode
    	CPSR[5] = 0 #Execute in ARM state
    	#CPSR[6] is unchange
    	CPSR[7] = 1 #Disable normal interrupts
    	if high vectors configured then
    		PC = 0xFFFF0008
    	else
    		PC = 0x00000008
    

    BKPT(断点中断指令)

    BKPT指令用于产生软件断点中断,软件调试程序可以使用该中断,当系统使用硬件调试部件时,可以忽略该中断

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    BKPT <immediate>

    • immediate为16位的立即数,这个立即数被调试软件用来保存额外的断点信息
    • 主要供软件调试程序使用

    指令操作的伪代码

    if (not overridden by debug hardware)
    	R14_abt = address of BKPT instruction + 4
    	SPSR_abt = CPSR
    	CPSR[4:0] = 0b10111
    	CPSR[5] = 0 #使程序处于ARM状态
    	#CPSR[6] is unchanged
    	CPSR[7] = 1 #禁止正常中断
    	if high vectors configured then
    		PC = 0xFFFF000C
    	else 
    		PC = 0x0000000C
    

    ARM协处理器指令

    • ARM支持16个协处理器,在程序执行的过程中,每个协处理器忽略属于ARM处理器和其他协处理器的指令
    • 当一个协处理器硬件不能执行属于它的协处理器指令时,将产生未定义指令异常中断,在该异常中断处理程序中,可以通过软件模拟该硬件操作,比如系统中不包含向量浮点运算器,则可以选择浮点软件模拟包,支持向量的浮点运算
    • ARM协处理器可以部分地执行一条指令,然后产生异常中断,如除法运算除数为0情况,所有操作都由ARM协处理器操作,ARM处理器不参与操作

    ARM协处理器指令包括以下3类:

    • 用于ARM处理器初始化ARM协处理器地数据处理操作
    • 用于ARM处理器地寄存器和ARM协处理器地寄存器地数据传送操作
    • 用于在ARM协处理器地寄存器和内存单元之间地传送数据

    协处理器指令

    • CDP:协处理器数据操作指令
    • LDC:协处理器数据读取指令
    • STC:协处理器数据写入指令
    • MCR:ARM寄存器到协处理器寄存器地数据传送指令
    • MRC:协处理器寄存器到ARM寄存器地数据传送指令

    CDP(协处理器数据操作指令)

    CDP指令让ARM处理器能够通知ARM协处理器执行特定地操作,该操作由协处理器完成,如果协处理器不能成功地执行该操作,将产生未定义地指令异常中断

    指令地编码格式
    在这里插入图片描述
    指令地语法格式
    CDP {<cond>} <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>, <opcode_2>
    CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>, <opcode_2>

    • cond为指令执行地条件码
    • CDP2格式中,cond为0b1111,指令为无条件执行指令
    • coproc为写处理器地编码
    • opcode_1为协处理器将执行地操作地操作码
    • CRd作为目标寄存器地协处理器寄存器
    • CRn为存放第1个操作数的协处理器寄存器
    • CRm为存放第2个操作数的协处理器寄存器
    • opcode_2为协处理器将执行的操作的操作码
    • 该指令让ARM处理器能够通知ARM协处理器执行特定的操作,且该操作不涉及ARM寄存器和内存单元

    示例

    CDP p5, 2, c12, c10, c3,4
    #协处理器p5的操作初始化
    #操作码1为2,操作码2为4
    #目标寄存器为C12
    #源操作数寄存器为C10和C3
    

    LDC(协处理器数据读取指令)

    LDC指令从一系列连续的内存单元将数据读取到协处理器的寄存器中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    LDC {<cond>}{L} <coproc>, <CRd>, <addressing_mode>
    LDC2{L} <coproc>, <CRd>, <addressing_mode>

    • LDC2格式中,cond为0b1111,指令为无条件执行指令
    • L指示指令为长读取操作,比如用于双精度的数据传送
    • addressing_mode为指令的寻址方式

    示例

    LDC p6, CR4, [R2, #4]
    #R2为ARM寄存器,指令读取内存单元(R2+4)的字数据,传送到协处理器p6的CR4寄存器中
    

    STC(协处理器数据写入指令)

    STC指令将协处理器的寄存器中的数据写入到一系列连续的内存单元中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    STC {<cond>}{L} <coproc>, <CRd>, <addressing_mode>
    STC2{L} <coproc>, <CRd>, <addressing_mode>

    • L指示指令为长读取操作,比如用于双精度的数据传送

    示例

    STC p8, CR4, [R2, #4]
    #R2为ARM寄存器,指令将协处理器p8的CR8寄存器中的字数据写入到内存单元(R2+4)中,执行后R2=R2+4
    

    MCR(ARM寄存器到协处理器寄存器地数据传送指令)

    MCR指令将ARM处理器的寄存器的数据传送到协处理器的寄存器中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MCR {<cond>}{L} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
    MCR2{L} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}

    • MCR2格式中,cond为0b1111,指令为无条件执行指令
    • Rd为ARM寄存器,其值将被传送到的协处理器的寄存器中
    • CRn为目标寄存器的协处理器寄存器
    • CRm为附加的目标寄存器或者源操作数寄存器

    示例

    MCR p14, 3, R7, c7, c11, 6
    #指令从ARM寄存器中将数据传送到协处理器p14的寄存器中
    #其中R7为ARM寄存器,存放源操作数
    #c7和c11为协处理器的寄存器,为目标寄存器
    #操作码1为3,操作码2为6
    

    MRC(协处理器寄存器到ARM寄存器地数据传送指令)

    MRC指令将协处理器寄存器中的数值传送到ARM处理器的寄存器中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断

    指令的编码格式
    在这里插入图片描述
    指令的语法格式
    MRC {<cond>}{L} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
    MRC2{L} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}

    • cond为0b1111,指令为无条件执行指令
    • Rd为目标寄存器的ARM寄存器
    • CRn为协处理器的寄存器,存放第1个源操作数
    • CRm为附加的目标寄存器或源操作数寄存器

    示例

    MRC p15, 2, R5, c0, c2, 6
    #指令将协处理器p15寄存器中的数据传送到ARM寄存器中
    #其中R5为ARM寄存器,是目标寄存器
    #c0和c2为协处理器的寄存器,存放源操作数
    #操作码1为2,操作码2为4
    

    汇编代码段示例

    长跳转

    通过直接向PC寄存器读取字数据,实现¥ GB的地址空间的任意跳转

    ADD LR, PC, #4;将子程序function的返回地址设置为当前指令地址后字节处,即return_here处
    LDR PC, [PC, #-4];从下一条指令(即DCD function)中读取跳转的目标地址,即function
    
    DCD function;DCD伪操作保存跳转的目的地址
    return_here;子程序function的返回地址
    

    多路跳转

    通过函数地址实现多路跳转,其中maxiindex为跳转的最大索引号,R0为跳转的索引号

    CMP R0, #maxiindex;判断索引号是否超出最大索引号
    LDRLO PC, [PC, R0, LSL #2] ;如果没有超过,跳转到相应的子程序处
    B IndexOutOfRange ;如果超过,跳转错误处理程序处
    DCD Handler0 ;子程序0的地址
    DCD Handler1 ;子程序1的地址
    DCD Handler2 ;子程序2的地址
    DCD Handler3 ;子程序3的地址
    ...
    

    简单的块复制

    程序一次将48个字节数据从R12首地址的一段连续的内存单元复制到仪R13为首地址的一段连续的内存单元中,R14为源数据区的末地址

    loop
    LDMIA R12!, {R0-R11};
    STMIA R13!, {R0-R11}
    CMP R12, R14;
    BLO loop;
    

    信号量指令的应用

    • 代码中用进程标识符标识各信号量的所有者,代码执行前进程的标识符保存在R1中,信号量的地址保存在R0中,当信号量[R0]==0时,表示与该信号量相关的临界区可用;当信号量[R0]==-1时,表示当前由进程正在查看该信号量的值
    • 如果当前进程查看的信号量正忙,当前进程将一直等待该信号量,为了避免当前进程的查询操作阻塞操作系统的进度调度,可以在下一次查询之前完成操作系统中的系统调度,使当前进程休眠一段时间
    MVN R2, #0;//将-1保存到R2中
    
    //lock
    spinin
    SWP R3, R2, [R0];//将信号量值读取到R3中,同时将其值设置成-1
    CMN R3, #1;//判断读取到的信号量值是否为-1,即是否有其他进程正在访问该信号量
    ...
    //如果有其他进程正在访问该信号量,则使当前进程休眠一段时间,以保证操作系统能够进行任务调度
    ...
    BEQ spinin;//如果有其他进程正在访问该信号量,跳转到spinin处执行,即循环查询信号量是否可用
    
    CMP R3, #0;//判断当前信号量是否可用,即内存单元[R0]是否为0,当不为0时,表示其他进程正拥有该信号量
    STRNE R3, [R0];//此时恢复该信号量的值,即该信号量拥有者的进程标识符
    ...
    //如果该信号量正在被别的进程占用,则使当前进程休眠一段时间,以保证操作系统能够完成任务调度
    ...
    BEN spinin;//进程重新尝试获取该信号量
    STR R1, [R0];//当前进程得到该信号量,将自己的进程标识符写入到内存单元[R0]处
    ...
    //该信号量所包保护的临界区数据
    ...
    
    //unlock
    spinout
    SWP R3, R2, [R0];//将信号量值读取到R3中,同时将其值设置成-1
    CMN R3, #1;//判断读取到的信号量值是否为-1,即是否有其他进程正在访问该信号量
    ...
    //如果有其他进程正在访问该信号量,则使当前进程休眠一段时间,以保证操作系统能够完成任务调度
    ...
    BEQ spinout;//如果有其他进程正在访问该信号量,跳转spinout处执行
    CMP R3, R1;//判断是否当前进程拥有该信号量
    BNE CorruptSemaphore;//如果当前进程不拥有该信号量,则系统出现错误
    MOV R2, #0;//如果当前进程拥有该信号量,重新将该信号量设置为可用,即0
    STR R2, [R0];
    

    SWI中断处理程序示例

    • 当SWI指令执行时,通常完成下面的工作
    if cond then
    	R14_svc = address of next instruction after the SWI instruction
    	SPSR_svc = CPSR
    	CPSR[4:0] = 0b10011 #Enter Supervisor mode
    	CPSR[5] = 0 #Execute in ARM state
    	CPSR[7] = 1 #Disable normal interrupts
    	if high vectors configured then
    		PC = 0xFFFF0008
    	else
    		PC = 0x00000008
    
    • SWI指令包含24位立即数,用于指定指令请求的具体SWI服务;对于Thumb指令而言。指令包含8位立即数指定指令请求的具体SWI服务
    • 当程序执行到SWI指令时,程序跳转到0x00000008处执行,由于该处是一条跳转指令,程序直接跳转到下面的代码段的首地址处开始执行,在代码段中,程序保存相关的寄存器,接着提取SWI指令中的立即数,以确认SWI指令请求的具体服务,跳转到相应的代码处执行
    • 下面代码段中,仅仅保存寄存器R0~R3、R12和LR,如果实际需要,可以修改代码中的保存和恢复指令中的寄存器列表
    SWIHandler //SWI中断服务程序
    STMFD sp!, {R0-R3,R12,LR}; //保存相关的寄存器
    MRS R0, SPSR;//将SPSR内容传送到R0中
    TST R0, #0x20;//判断程序状态是否为ARM状态
    /*
    通过获取返回地址的执行指令上一条执行指令,就可以获取SWI的执行指令数据
    通过SPSR[T]的判断确认立即数是24位,还是8位
    */
    LDRNEH R0, [LR, #-2];//如果是Thumb状态,获取[LR-2]地址处的执行指令,即SWI指令
    BICNE R0, R0, #0xFF00;//提取SWI指令中相应的8位立即数
    LDREQ R0, [LR, #-4]; //如果是ARM状态,获取[LR-4]地址处的执行指令,即SWI指令
    BICEQ R0, R0, #0xFF000000;//提取SWI指令中相应的8位立即数
    CMP R0, #MaxSWI;//判断指令请求的服务序号是否超出合法范围
    LDRLS pc, [pc, R0, LSL #2];//如果没有超出合法范围,跳转到相应的服务程序执行
    B SWIOutOfRange; //如果超出了合法范围,跳转到相应的服务程序执行
    Switable //服务程序的函数地址表
    DCD do_swi_0;//服务程序do_swi_0的首地址,该服务对于SWI指令中的立即数为0
    DCD do_swi_1;//服务程序do_swi_1的首地址,该服务对于SWI指令中的立即数为1
    ...
    
    do_SWI_0 //服务程序do_swi_0的代码
    ...
    //服务程序执行代码
    ...
    LDMFD sp!, {R0-R3, R12, pc}^; //从服务程序中返回
    
    do_swi_1 //服务程序do_swi_1的代码
    ...
    

    IRQ中断处理程序示例

    • 在ARM中,外部中断管理器或外设通过使能ARM处理器中的IRQ输入管脚产生IRQ异常中断
    • CPSR寄存器中的I控制位设置为1时,禁止ARM处理器响应IRQ中断请求;CPSR寄存器中的I控制位设置为0时,ARM处理器在指令边界处检查是否有IRQ中断请求
    • ARM处理器响应IRQ中断请求时,完成以下工作
    R14_irq = 当前指令地址 + 8
    SPSR_irq = CPSR
    CPSR[4:0] = 0b10010 #Enter Supervisor mode
    CPSR[5] = 0 #Execute in ARM state
    CPSR[7] = 1 #Disable normal interrupts
    if high vectors configured then
    	PC = 0xFFFF0018
    else
    	PC = 0x00000018
    
    • 通常IRQ中断向量存放内存单元0x00000018处,该地址处放一条跳转指令,其目标地址为下面代码段的首地址
    • 外围中断管理硬件将所有的IRQ异常中断请求按优先级排队,并把优先级最高的IRQ异常中断的相关信息保存到寄存器中,IRQ中断处理程序读取信息,并跳转响应的代码处执行
    //保存工作寄存器数据、返回地址和当前程序现场
    SUB R14, R14, #4; //调整R14值,使其指向发生IRQ中断的指令的下一条指令
    STMFD R13!, {R12, R14}; //保存返回地址和相关的寄存器数据,R13为栈指针
    
    MRS R12, SPSR; //保存SPSR
    STMFD R13!, {R12};//读取当前优先级最高的IRQ请求的相关信息
    MOV R12, #InterBase;//读取中断控制器的基地址
    LDR R12, [R12, #IntLevel];//读取优先级最高的IRQ的中断号IntBase[IntLevel]
    						//存放优先级最高的IRQ的中断号的寄存器的偏移地址修改CPSR中的控制位,重新允许IRQ中断
    
    MRS R14, CPSR; //读取CPSR
    BIC R14, R14, #0x80; //清除中断禁止位
    MSR CPSR_c, R14;//将R14的值写入CPSR
    
    LDR PC, [PC, R12, LSL #2];//跳转到当前IRQ对应的中断处理程序
    BOP;//插入空指令,保证跳转正确执行
    
    IntBase
    DCD Priority0Handler;//Priority0Handler的地址
    DCD Priority1Handler;//
    ...
    
    Priority0Handler
    STMFD R13!, {R0-R11};//保存工作寄存器组
    ...
    //中断处理程序主体
    ...
    MRS R12, CPSR;//修改CPSR的相关位,禁止响应中断
    ORR R12, R12, #0x80;
    MSR CPSR_c, R12; //注意这里不要使用R14,否则发生中断时,R14的内容会被破坏
    
    LDMFD R13!, {R0-R12};//恢复工作寄存器和SPSR
    MSR SPSR_cxsf, R12;
    LDMFD R13!, {R12, PC}^;//恢复所有寄存器,并返回
    
    Priority0Handler //Priority1Handler程序体
    ...
    

    进程切换

    • 进程是操作系统中任务调度的基本单位,每个进程由一个进程控制块PCB表示,进程控制块PCB中包含了进行相关的信息
    • 进程间切换就是通过某种方式保存当前进程的PCB,恢复新进程的PCB内容到处理器中
    • 这里讨论用户模式的进程间切换,切换过程是通过IRQ中断处理程序完成的
    • 比如在进程1执行到特定时机时,希望切换到进程2,此时系统产生IRQ中断,首先执行常规的中断处理操作,然后判断是返回被中断的进程1,还是切换到新的进程2执行,这里仅仅讨论用户模式的进程间切换,如果在特权模式下发生IRQ中断,中断处理程序一定返回被中断的进程
    • 这里假设IRQ中断处理程序仅仅保存寄存器R0~R3、R12及R14,使用R13作为栈指针,栈的类型为FD,其他寄存器保存不变,在中断处理程序中始终禁止中断,也不进行处理器模式切换
    • 这里假设进程控制块格式为从低地址到高地址依次为下列寄存器:CPSR、返回地址、R0~R4

    下面分3部分介绍进程间切换的过程

    1. 在进入IRQ中断处理程序时,首先计算返回地址,并保存相关的寄存器
    SUB R14, R14, #4;//使R4指向发生IRQ中断的指令的下面一条指令
    STMFD  R13!, {R0~R3, R12, R14}; //保存R0~R3,R123,R14
    
    1. 如果IRQ中断处理程序返回被中断的进程,则执行下面的指令,该指令从数据栈中恢复寄存器R0~R3及R12的值,将返回地址传送到PC中,并将SPSR_irq值赋值到CPSR中
    LDMFD R3!, {R0~R3, R12, PC}^;
    
    1. 如果IRQ切换到新的进程,则要保存被中断的进程的PCB,然后恢复新进程的PCB到处理器中
    //保存被中断的进程的PCB,该PCB存放在R0所指向的连续的内存单元中
    MRS R12, SPSR;//读取被中断的进程的CPSR
    STR R12, [R0], #8;//将其保存到R0指向的内存单元,并更新R0的值,R0=R0+8
    
    LDMFD R13!, {R2,R3};//读取被中断进程的R2和R3
    STMIA R0!, {R2,R3};//将其保存到R0指向的内存单元,并更新R0值
    LDMFD R13!, {R2, R3, R12, R14};//读取栈中的其他数据
    STR R14, [R0, #-12];//将返回地址值R14保存在PCB中的第2个字单元,即CPSR之后
    
    STMIA R0, {R2~R14}^; //保存其他所有的寄存器
    //将新进程的PCB中的内容恢复到处理器中,其中R1指向进程的PCB
    LDMIA R1!, {R12, R14};//恢复CPSR及R14
    MSR SPSR_fsxc, R12;
    LDMIA R1, {R0~R14}^;//恢复R0~R14
    NOP;//因为在用户模式的LDM指令后不能立即操作备份寄存器,故插入该指令
    
    MOVS PC, R14;//切换到新进程执行,同时更新CPSR
    

    伪操作

    伪操作不是在计算机运行期间由机器执行,是在汇编程序对源程序汇编期间由汇编程序处理的

    符号定义伪操作

    符号定义伪操作作用于定义ARM汇编程序中的变量,对变量进行赋值以及定义寄存器名称

    GBLA、GBLL、GBLS全局变量

    GBLA、GBLL、GBLS伪操作用于声明一个ARM程序中的全局变量,并且对其进行初始化,如果这些伪操作重新声明已经声明过的变量,则变量的值将被初始化成后一次声明语句中的值

    • GBLA伪操作声明一个全局的算术变量,并将其初始化成0
    • GBLL伪操作声明一个全局的逻辑变量,并将其初始化成{FALSE}
    • GBLS伪操作声明一个全局的串变量,并将其初始化成空串""
    GBLA objectsize;//声明一个全局的算术变量
    objectsize SETA 0xFF;//向该变量赋值
    SPACE objectsize;//引用该变量
    
    GBLL status;//声明一个全局的逻辑变量
    status SETL {TRUE};// 向该变量赋值
    

    LCLA、LCLL、LCLS局部变量

    LCLA、LCLL、LCLS伪操作用于声明一个ARM程序中的局部变量,并对其进行初始化,如果这些伪操作重新声明已经声明过的变量,则变量的值将被初始化成后一次声明语句中的值,局部变量的作用范围为包含该局部变量的宏代码

    • LCLA伪操作声明一个全局的算术变量,并将其初始化为0
    • LCLL伪操作声明一个局部的逻辑变量,并将其初始化成{FALSE}
    • LCLS伪操作声明一个局部的串变量,并将其初始化成空串""
    MACRO //声明一个宏
    $ label message $a //宏的原型
    LCLS err //声明一个局部变量
    err SETS "error no:" //向该变量赋值
    $label //代码
    INFO 0,"err":CC::STR:$a //使用该字符串
    MEND //宏定义结束
    

    SETA、SETL、SETS给变量赋值

    SETA、SETL、SETS伪操作用于给一个ARM程序中的变量赋值,在向变量赋值前,必须先声明该变量

    • SETA伪操作给一个算术变量赋值
    • SETL伪操作给一个逻辑变量赋值
    • SETS伪操作给一个串变量赋值

    语法格式
    variable <setx> expr

    • setx是以下伪操作之一:SETA、SETL或SETS
    • variable使用GBLA、GBLL、GBLS或LCLA、LCLL、LCLS声明的变量名称
    • expr为表达式,即赋予变量的值

    RLIST

    RLIST伪操作用于为一个通用寄存器列表定义名称,可以在LDM或STM指令中使用;在LDM或STM指令中,寄存器列表中的寄存器访问次序总是先访问编号低的寄存器,再访问编号高的寄存器,而不管寄存器列表中各寄存器的排列顺序

    语法格式
    name RLIST {list-of-registers}

    • name是寄存器列表的名称
    • {list-of-registers}为通用寄存器列表
    context RLIST {R0-R6, R8, R10-R12, R15} //将寄存器列表名称定义为context
    

    CN给协处理器的寄存器定义名称

    CN伪操作用来给一个协处理器的寄存器定义名称

    语法格式
    name CN expr

    • name是该寄存器的名称
    • expr为协处理器的寄存器的编号,数值范围为0~15

    CP给协处理器定义名称

    CP伪操作用来给一个协处理器定义名称

    语法格式
    name CP expr

    • name是该协处理器的名称
    • expr为协处理器的编号,数值范围0~15

    DN、SN为VFP的寄存器定义名称

    DN伪操作用来给一个双精度的VFP寄存器定义名称
    SN伪操作用来给一个单精度的VFP寄存器定义名称

    语法格式
    name DN expr
    name SN expr

    • name是该VFP寄存器的名称
    • expr为FVP双精度寄存器编号(0~15),或者VFP单精度寄存器编号(0~31)

    FN为FPA的浮点寄存器定义名称

    FN为一个FPA浮点寄存器定义名称

    语法格式
    name FN expr

    • name是该浮点寄存器的名称
    • expr为浮点寄存器的编号,数值范围为0~7

    数据定义伪操作

    • LTORG声明一个数据缓冲池的开始
    • MAP定义一个结构化的内存表的首地址
    • FIELD定义结构化的内存表中一个数据域
    • SPACE分配一块内存单元,并用0初始化
    • DCB分配一段字节的内存单元,并用指定的数据初始化
    • DCD及DCDU分配一段字的内存单元,并用指定的数据初始化
    • DCDO分配一段字的内存单元,并将各单元的内存初始化成该单元相对于静态基值寄存器的偏移量
    • DCFD及DCFDU分配一段双字的内存单元,并用双精度的浮点数据初始化
    • DCFS及DCFSU分配一段字的内存单元,并用单精度的浮点数据初始化
    • DCI分配一段字节的内存单元,用指定的数据初始化,指定内存单元中存放的是代码,而不是数据
    • DCQ及DCQU分配一段双字的内存单元,并用64位的整数数据初始化
    • DCW及DCWU分配一段半字的内存单元,并用指定的数据初始化
    • DATA在代码中使用数据,现不再使用,仅用于保持向前兼容

    LTORG

    LTORG用于声明一个数据缓冲区的开始

    AREA Example, CODE, READONLY
    start BL funcl
    funcl //子程序
    ...//子程序代码
    LDR R1, =0x55555555 //LDR R1, [PC, #offset to Literal Pool 1]
    ...//子程序代码
    MOV PC, LR //子程序结束
    
    LTORG //定义数据缓冲池&55555555
    data SPACE 4200 //从当前位置开始分配4200字节的内存单元
    END
    
    • 通常,ARM汇编编器器把数据缓冲池放在代码段的最后面,即下一个代码段开始之前,或END伪操作之前
    • 当程序中使用LDFD之类指令时,数据缓冲池的使用可能越界,这时可以使用LTORG伪操作定义数据缓冲池,防止越界发生,通常,大的代码段可以使用多个数据缓冲池
    • LTORG伪操作通常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误将数据缓冲池的数据当作指令来执行

    MAP

    MAP用于定义一个结构化的内存表的首地址,此时,内存表的位置计数器{VAR}设置成该地址,^是MAP的同义词

    语法格式
    MAP expr(, base-register}

    • expr位数字表达式或者程序的标号,当指令没有base-register时,expr即为结构化内存表的首地址,此时内存表的位置计数器{VAR}设置成该地址值,当expr位程序中的标号时,该标号必须是已经定义过的
    • base-register为一个寄存器,当指令包含这一项时,结构化内存表的首地址为expr和base-register寄存器值的和
    • MAP伪操作和FIELD伪操作配合使用,来定义结构化的内存表结构
    MAP 0x80, R9
    

    FIELD

    FIELD用于定义一个结构化内存表中的数据域,#是FILED的同义词

    语法格式
    {label} FIELD expr

    • {lable}为可选,当指令中包含这一项时,label的值为当前内存表的位置计数器{VAR}的值,汇编编译器处理该FIELD伪操作后,内存表计数器的值将加上expr
    • expr表示本数据域在内存表中所占的字节数
    • MAP伪操作和FILED伪操作配合使用,来定义结构化的内存表结构,MAP伪操作定义内存表的首地址,FIELD伪操作定义内存表中各数据域的字节长度,并可以为每一个数据域指定一个标号,其他指令可以引用该标号
    • MAP伪操作中的base-register寄存器值对于其后所有的FIELD伪操作定义的数据域时默认使用,直至遇到新的包含base-register项的MAP伪操作
    • MAP伪操作和FIELD伪操作仅仅定义数据结构,并不实际分配内存单元
    1. 基于绝对地址的内存表
    //首地址为固定地址0x1000
    //该内存表包含5各数据域
    //consta长度为4个字节
    //constb长度为4个字节
    //x的长度为8个字节
    //y的长度为8个字节
    //string的长度为256个字节
    //这种内存表称为基于绝对地址的内存表
    MAP 4096 //内存表的首地址为4096(0x1000)
    consta FIELD 4 //consta的长度为4个字节,相对位置为0
    constb FIELD 4 //constb的长度为4个字节,相对位置为5000
    x FIELD 8 //x的长度为8个字节,相对位置为5004
    y FIELD 8 //y的长度为8个字节,相对位置为5012
    string FIELD 256 //string的长度为256字节,相对位置为5020
    
    LDR R6,consta //在指令中引用内存表中的数据域
    
    1. 基于相对地址的内存表
    MAP 0//内存表的首地址为0
    consta FIELD 4 //consta的长度为4个字节,相对位置为0
    constb FIELD 4 //constb的长度为4个字节,相对位置为5000
    x FIELD 8 //x的长度为8个字节,相对位置为5004
    y FIELD 8 //y的长度为8个字节,相对位置为5012
    string FIELD 256 //string的长度为256字节,相对位置为5020
    
    MOV R9, #4096
    LDR R6, [R9, constb] //将内存表中的数据域constb读取到R5中
    

    内存表中个数据的实际内存地址不是基于一个固定的地址,而是基于LDR指令执行时R9寄存器的内容,通过上面方法定义内存表结构可以在程序中有多个实际(通过LDR指令中指定不同的基址寄存器实现)

    1. 基于相对地址的内存表
    MAP 0, R9 //内存表的首地址为0与R9寄存器值的和
    consta FIELD 4 //consta的长度为4个字节,相对位置为0
    constb FIELD 4 //constb的长度为4个字节,相对位置为5000
    x FIELD 8 //x的长度为8个字节,相对位置为5004
    y FIELD 8 //y的长度为8个字节,相对位置为5012
    string FIELD 256 //string的长度为256字节,相对位置为5020
    
    ADR R9, DATASTART
    LDR R5, constb //相当于LDR R5, [R9, #4]
    

    内存表中各数据域的实际内存地址不是基于一个固定的地址,而是基于LDR指令执行时R9寄存器中的内容,通过上面方法定义的内存表结构可以在程序中有多个实例(通过LDR指令前指定不同的基址寄存器R9值来实现)

    1. 基于PC的内存表
    DATASTUCT SPACE 280 //分配280字节的内存单元
    MAP DATASTUCT //内存表的首地址为DATASTUCT内存单元
    consta FIELD 4 //consta的长度为4个字节,相对位置为0
    constb FIELD 4 //constb的长度为4个字节,相对位置为5000
    x FIELD 8 //x的长度为8个字节,相对位置为5004
    y FIELD 8 //y的长度为8个字节,相对位置为5012
    string FIELD 256 //string的长度为256字节,相对位置为5020
    
    LDR R5, constb //相当于LDR R5, [PC, offset]
    

    内存表中各数据域的实际内存地址不是基于一个固定的地址,而是基于PC寄存器的值,在使用LDR指令访问内存表中的数据域时,不必使用基值寄存器

    1. 范围超出检查
      当FIELD伪操作中的操作数为0时,其中的标号即为当前内存单元的地址,由于其中操作数为0,汇编编译器处理该伪操作后,内存表的位置计数器的值并不改变,可以利用该技术判断当前内存的使用是否超过程序分配的可用内存
    startofmem EQU 0x1000 //分配的内存首地址
    endofmem EQU 0x2000 //分配的内存未地址
    
    MAP startofmem //内存表的首地址为DATASTUCT内存单元
    consta FIELD 4 //consta的长度为4个字节,相对位置为0
    constb FIELD 4 //constb的长度为4个字节,相对位置为5000
    x FIELD 8 //x的长度为8个字节,相对位置为5004
    y FIELD 8 //y的长度为8个字节,相对位置为5012
    string FIELD 256 //string的长度为256字节,相对位置为5020
    endofstru FIELD 0
    
    ASSERT endofstru <= endofmem
    

    SPACE

    SPACE伪操作用于分配一块内存单元,并用0初始化,%时SPACE的同义词
    语法格式
    {label} SPACE expr

    • {label}为可选的
    • expr表示本伪操作分配的内存字节数
    Datastruc SPACE 280
    

    DCB、DCD及DCDU、DCDO、DCFD及DCFDU、DCFS及DCFSU、DCI、DCQ及DCQU、DCW及DCWU

    1. DCB伪操作用于分配一段字节内存单元,并用语法格式中的expr初始化,=是DCB的同义词
      {label} DCB expr{,expr} …
      {label}为可选的
      expr可以为-128~255的数值或者字符串
    Nullstring DCB "Null string",0 //构造一个以NULL结尾的字符串
    
    1. DCD伪操作作用于分配一段内存单元(分配的内存都是字对齐的),并用伪操作中的expr初始化,&是DCD的同义词,DCDU与DCD的不同之处在于DCDU分配的内存并不严格字对齐
      {label} DCD expr{,expr} …
      {label}为可选的
      expr可以为数字表达式或者为程序中的标号
    • DCD伪操作可以在分配的第一个内存单元前插入填补字节以保证分配的内存是字对齐的
    • DCDU分配的内存单元则不需要字对齐
    data1 DCD 1, 5, 20 //其值分别为1、5和20
    data2 DCD memaddr + 4 //分配一个字单元,其值为程序中的标号memaddr加4个字节
    
    1. DCDO伪操作用于分配一段字内存单元(分配的内存都是字对齐的),并将各字单元的内容初始化为xexpr标号基于静态基址寄存器R9的偏移量
      {label} DCDO expr{,expr} …
    IMPORT externsym
    DCDO externsym //32位的字单元,其值位标号externsym基于R9的偏移量
    
    1. DCFD用于双精度的浮点数分配字对齐的内存单元,并将字单元的内容初始化为fpliteral表示的双精度浮点数,每个双精度的浮点数占据两个字单元,DCFD与DCFDU的不同之处在于DCFDU分配的内存单元并不严格字对齐
      {label} DCFD{U} fpliteral{,fpliteral} …
      {label}为可选的
      fpliteral为双精度的浮点数
    • DCFD伪操作可能在分配的第一个内存单元前插入填补字节,以保证分配的内存是字对齐的
    • DCFDU分配的内存单元则不需要字对齐
    • 如果将fpliteral转换内存单元的内部表示形式是由浮点数运算单元控制的
    DCFD 1E308, -4E-100
    DCFDU 10000, -.1, 3.1E26
    
    1. DCFS伪操作用来为单精度的浮点数分配字对齐的内存单元,并将字单元的内容初始化成fpliteral表示的单精度浮点数,每个单精度的浮点数占据1个字单元,DCFS与DCFSU的不同之处在于DCFSU分配的内存单元并不严格字对齐
      {label} DCFS{U} fpliteral{,fpliteral} …
    2. DCI,在ARM代码中,用于分配一段字内存单元(分配的内存都是字对齐的),并用伪操作中的expr将其初始化,在Thumb代码中,用于分配一段半字单元(分配的内存都是半字对齐的),并用伪操作中的expr将其初始化
      {label} DCI expr{,expr} …
    • DCI伪操作和DCD伪操作非常类似,不同之处在于DCI分配的内存中数据被标识为指令,可用于通过宏指令来定义处理器指令系统不支持的指令
    • 在ARM代码中,DCI可能在分配的第一个内存单元前插入最多3个字节的填补字节以保证分配的内存是字对齐的,在Thumb代码中,DCI可能在分配第一个内存单元前插入1个字节的填补字节以保证分配的内存是半字对齐的
    MACRO //这个宏指令将指令newinstr Rd, Rm定义为相应的机器指令
    newinstr $Rd,$Rm
    DCI oxe16f0f10 :OR: ($Rd:SHL:12) :OR: $Rm //这里存放的是指令MEMD
    
    1. DCQ伪操作用于分配一段以8个字节为单位的内存(分配的内存都是字对齐的),并用literal初始化,DCQU与DCQ的不同之处在于DCQU分配的内存单元并不严格字对齐
      {label} DCQ{U} {-}literal{, {-}literal} …
      {label}为可选的
      literal为64位的数字表达式,其取值范围为0~2^64 -1,当在literal前加上"-"符号时,literal的取值范围为-2^63-1。在内存中,2^64-n与-n具有相同的表达形式
    • DCQ伪操作可能在分配的第一个内存单元前插入多达3个字节的填补字节以保证分配的内存是字对齐的
    • DCQU分配的内存单元则不需要字对齐
    AREA MiscData, DATA, READWRITE
    data DCQ -225, 2_101 //2_1-1指的是二进制的101
    DCQU number + 4 //number必须是已经定义过的数字表达式
    
    1. DCW伪操作用于分配一段半字内存单元(分配的内存都是半字对齐的),并且使用expr初始化,DCWU与DCW的不同之处在于DCWU分配的内存单元并不严格半字对齐
      {label} DCW expr{,expr} …
      {label}为可选的
      expr为数字表达式,其取值范围为-32768~65535
    • DCW伪操作可能在分配的第一个内存单元前插入1字节的填补字节以保证分配的内存是半字对齐的
    • DCWU分配的内存单元则不需要半字对齐

    汇编控制伪操作

    IF、ELSE及ENDIF

    IF、ELSE及ENDIF伪操作能够根据条件把一段源代码包括在汇编语言程序内或者将其排除在程序之外。"[“是IF伪操作的同义词,”|“是ELSE伪操作的同义词,”]"是ENDIF伪操作的同义词,其中ELSE伪操作可选的,也可以嵌套使用。

    语法格式

    IF logical expression
    	insttructions or directives
    ELSE
    {
    	insttructions or directives
    }
    ENDIF
    

    示例

    IF Version = "1.0"
    	//指令
    	//伪指令
    ELSE
    	//指令
    	//伪指令
    ENDIF
    

    WHILE及WEND

    WHILE及WEND伪操作能够根据条件重复汇编相同的或者几乎相同的一段源代码,WHILE及WEND伪操作可以嵌套使用

    语法格式

    WHILE logical expression
    	insttructions or directives
    WEND
    

    示例

    count SETA 1
    WHILE count <= 4
    	count SETA count+1
    WEND
    

    MACRO及MEND

    MACRO及MEND伪操作标识宏定义的开始,MEND标识宏定义的结束,用MACRO及MEND定义的一段代码,称为宏定义体,可以在程序中通过宏指令多次调用该代码段,宏定义可以嵌套。

    语法格式

    MACRO
    {$label} macroname {$parameter{,$parameter}...}
    ...
    //code
    ...
    MEND
    
    • $label在宏指令被展开时,label可被替换成相应的符号,通常是一个标号,在一个符号前使用$表示程序被汇编时将使用相应的值替代$后的符号
    • macroname为所定义的宏的名称
    • $parameter为宏指令的参数,当宏指令被展开时,将被替换成相应的值,类似于函数中的形式参数,可以在宏定义时为参数指定相应的默认值
    • 宏定义中的$label是一个可选参数,当宏定义体中用到多个标号时,可以使用类似$label.$internallabel的标号命名规则使程序易读
    • 对于ARM程序中的局部变量,如果该变量在宏定义中被定义,其作用范围为该宏定义体

    示例

    MACRO //宏定义的开始
    $label xmac $p1,$p2 //宏的名称为xmac,有两个参数$p1$p2,宏的标号$label可用于构造宏定义体内的其他标号名称
    ...
    //code
    ...
    $label.loop1 //$label.loop1为宏定义体的内部标号
    ...
    //code
    ...
    BGE $label.loop1
    
    $lable.loop2 //$label.loop1为宏定义体的内部标号
    ...
    //code
    ...
    BL $p1 //参数$p1为一个子程序的名称
    BGT $label.loop2 
    ...
    //code
    ...
    ADR $p2
    ...
    //code
    ...
    MEND //宏定义结束
    
    //在程序中调用该宏
    abc xmac subr1, de //通过宏的名称xmac调用宏,其中宏的标号为abc,参数为subr1,de
    

    MEXIT

    MEXIT用于从宏中跳转出去

    示例

    MACRO
    {$label} macroname {$parameter{,$parameter}...}
    ...
    //code
    ...
    MEXIT
    ...
    //code
    ...
    MEND
    

    信息报告伪操作

    ASSERT

    ASSERT在汇编编译器对编译程序的第二遍扫描中,如果其中的条件不成立,ASSERT伪操作将报告该错误信息

    语法格式

    ASSERT logical expression
    
    • 其中logical expression为一个逻辑表达式
    • 用于保证源程序被汇编时满足相关的条件,如果条件不满足,ASSERT伪操作报告错误类型,并终止汇编

    示例

    ASSERT a>0x10
    

    INFO

    INFO伪操作支持汇编处理过程的第一遍扫描或者第二遍扫描时报告诊断信息

    语法格式

    INFO numeric-expression, string-expression
    
    • string-expression为一个串表达式
    • numeric-expression为一个数字表达式
    • 如果numeric-expression的值为0,则在汇编处理中,第二扫描时,伪操作打印string-expression;如果numeric-expression的值不为0,则在汇编处理中,第一遍扫描时,伪操作打印string-expression,并终止汇编
    • INFO伪操作用于用户定义的错误信息

    示例

    INFO 0, "Version 1.0" //在第二遍扫描时,报告版本信息
    IF endofdata <= label1
    	INFO 4, "Data overrun at label1" //如果endofdata<=label1成立,在第一遍扫描时报告错误信息,并终止汇编
    ENDIF
    

    OPT

    OPT伪操作,可以在源程序中设置列表选项

    语法格式

    OPT n
    
    • 使用编译选项-list将使编译器产生列表文件
    • 默认情况下,-list选项生成常规的列表文件,包括变量声明、宏展开、条件编译伪操作以及MEND伪操作,而且列表文件只是在第二遍扫描时给出,通过OPT伪操作,可以在源程序中改变默认的选项
    • n为所设置的选项的编码
    选项编码n 选项的含义
    1 设置常规列表选项
    2 关闭常规列表选项
    4 设置分页符,在新的一页开始显示
    8 将行号重新设置为0
    16 设置选项,显示SET、GBL、LCL伪操作
    32 设置选项,不显示SET、GBL、LCL伪操作
    64 设置选项,显示宏展开
    128 设置选项,不显示宏展开
    256 设置选项,显示宏调用
    512 设置选项,不显示宏调用
    1024 设置选项,显示第一遍扫描列表
    2048 设置选项,不显示第一遍扫描列表
    4096 设置选项,显示条件汇编伪操作
    8192 设置选项,不显示条件汇编伪操作
    16384 设置选项,显示MEND伪操作
    21768 设置选项,不显示MEND伪操作

    示例
    在func1前插入"OPT 4"伪操作,func1将在新的一页中显示

    AREA Example, CODE, READONLY
    start
    ...
    //code
    ...
    BL func1
    ...
    //code
    ...
    OPT 4
    func1
    ...
    //code
    ...
    

    TTL及SUBT

    • TTL伪操作在列表文件的每一页的开头插入一个标题,该TTL伪操作将作用在其后的每一页,直到遇到新的TTL伪操作
    • SUBT伪操作在列表文件的每一页的开头插入一个子标题,该SUBT伪操作将作用在其后的每一页,直到遇到新的SUBT伪操作

    语法格式
    TTL title
    SUBT subtitle

    • TTL伪操作在列表文件的页顶部显示一个标题,如果要在列表文件的第一页显示标题,TTL伪操作要放在源程序的第一行
    • 当使用TTL伪操作改变标题时,新的标题将在下一页开始起作用
    • SUBT伪操作在列表文件的页标题的下面显示一个子标题,如果要在列表文件的第一页显示子标题,TTL伪操作要放在源程序的第一行
    • 当使用SUBT伪操作改变页标题时,新的标题将在下一页开始起作用

    示例

    TTL Fisrt Title
    SUBT Fisrt Subtitle
    

    其他伪操作

    CODE16及CODE32

    CODE16伪操作告诉汇编编译器后面的指令序列为16位的Thumb指令
    CODE32伪操作告诉汇编编译器后面的指令序列为32为的ARM指令

    • CODE16何CODE32只是告诉编译器后面的指令的类型,伪操作本身不进行程序状态的切换

    示例
    程序现在ARM状态下执行,然后通过BX指令切换到Thumb状态,并跳转到相应的Thumb指令处执行,在Thumb程序入口处用CODE16伪操作标识下面的指令为Thumb指令

    AREA ChangeState, CODE, READONLY
    CODE32
    LDR R0, =start + 1
    BX R0
    
    CODE16
    start MOV R0, #10
    

    EQU

    EQU伪操作为数字常量,基于寄存器的值和程序中的标号(基于PC的值)定义一个字符名称,*是EQU的同义词

    语法格式
    name EQU expr{, type}

    • expr为基于寄存器的地址值、程序中的标号、32位的地址常量或者32位的常量
    • name为EQU伪操作为expr定义的字符名称
    • type当expr为32位常量时,可以使用type指示expr表示的数据的类型,type有3中取值:CODE16、CODE32、DATA
    • EQU伪操作的作用类似于C语言的#define,用于位一个常量定义字符名称

    AREA

    AREA伪操作用于定义一个代码段或者数据段

    语法格式
    AREA sectionName {, attr}{, attr}

    • sectionName为所定义的代码段或者数据段的名称,如果该名称以数字开头,则名称必须用"|"括起来,如|1_datasec|,还要一些代码段具有约定的名称,如|.text|
    • attr是该代码段(或程序段)的属性,在AREA伪操作中,各属性间用逗号隔开
    1. ALIGN=expression:默认情况下,ELF的代码段和数据段是4字节对齐的,Expression可以取0~31的数值,相应的对齐方式为2^expression字节对齐
    2. ASSOC=section:指定与本段相关的ELF段,任何时候链接section段页必须包括section段
    3. CODE:定义代码段,默认属性为READONLY
    4. COMDEF:定义一个通用的段,该段可以包含代码或者数据,在各源文件中,同名的COMDEF段必须相同
    5. COMMON:定义一个通用的段,该段不包含任何用于代码和数据,链接器将其初始化为0,各源文件中同名的COMMON段公用同样的内存单元,链接器为其分配合适的尺寸
    6. DATA:定义数据段,默认属性为READWRITE
    7. NOINIT:指定本数据段仅仅保留内存单元,而没有将各初始值写入内存单元,或者将各内存单元值初始化为0
    8. READONLY:指定本地为只读,代码段的默认属性为READONLY
    9. READWRITE:指定本地为可读可写,是数据段的默认属性
    • 可以用AREA伪操作将程序分成多个ELF格式段,同名的段会被放在同一ELF段中

    ENTRY

    ENTRY伪操作指定程序的入口点

    AREA example CODE, READONLY
    ENTRY
    
    • 一个程序(可以包含多个源文件)中只是要有一个ENTRY(可以有多个ENTRY),但是一个源文件中最多只能有一个ENTRY,也可以没有

    END

    END伪操作告诉编译器已经到源程序结尾

    ALIGN

    ALIGN伪操作通过添加补丁使当前位置满足一定的对齐方式

    语法格式
    ALIGN {expr{, offset}}

    • expr为数字表达式,用于指定对齐方式,可能的取值为2的n次幂,如果伪操作中没有指定expr,则当前位置对齐到下一个字边界处
    • offset为数字表达式
    • Thumb的宏指令ADR要求地址是字对齐的,而Thumb代码中地址标号可能不是字对齐的,这就需要伪操作ALIGN 4使Thumb代码中的地址标号字对齐
    • 由于有些ARM处理器的Cache采用其他对齐方式,如16字节的对齐方式,此时使用ALIGN伪操作指定合适的对齐方式可以充分使用Cache的性能
    • LDRD及STRD指令要求内存单元使8字节对齐的,这样为LDRD/STRD指令分配的内存单元前,要使用ALIGN 8实现8字节对齐方式
    • 地址标号通常自身没有对齐要求,而在ARM代码中要求地址标号是字对齐的,在Thumb代码中要求字节对齐

    示例
    将两个字节数据放在同一个字的第一个字节和第4各字节中

    AREA offsetExample, CODE
    DCB 1
    ALIGN 4, 3
    DCB 1
    

    EXPORT及GLOBAL

    EXPORT声明一个符号可以被其他文件引用,相当于声明一个全局变量
    FLOBAL是EXPORT的同义词

    语法格式
    EXPORT symbol{[WEAK]}

    • symbol为声明的符号的名称,区分大小写
    • [WEAK]选项声明其他的同名符号优先于本符号被引用
    • 使用EXPORT伪操作,可以声明一个源文件的符号,使得该符号能够被其他源文件引用

    IMPORT

    IMPORT伪操作告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件可能引用该符号,而且不论本源文件是否实际引用该符号,该符号都被加入本源文件的符号表中

    语法格式
    IMPORT symbol{[WEAK]}

    • symbol为声明的符号的名称,区分大小写
    • 指定[WEAK],如果symbol在所有的源文件都没有定义,编译器也不会产生任何错误信息,同时编译器也不会到当前被INCLUDE进来的库中去查找该符号
    • 如果IMPORT伪操作声明一个符号是其他源文件定义时,如果链接器链接处理时不能解析该符号,且没有指定[WEAK]选项,则链接器将会报告错误
    • 如果该符号被B或者BL指令引用,则符号被设置成下一条指令的地址

    EXTERN

    EXTERN伪操作告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号,如果本源文件没有实际引用该符号,该符号将不会加入到本源文件的符号表中

    语法格式
    EXTERN symbol{[WEAK]}

    • 如果该符号被B或者BL指令引用,则符号被设置成下一条指令的地址

    GET及INCLUDE

    GET伪操作将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理,INCLUDE是GET的同义词

    语法格式
    GET filename

    • filename为被包含的源文件名称,可以使用路径信息
    • 被包含的源文件也可以使用GET伪操作

    INCBIN

    INCBIN伪操作将一个文件包含到当前源文件中,被包含的文件不进行汇编处理

    语法格式
    INCBIN filename

    • filename为被包含的源文件名称,可以使用路径信息
    • 可以使用INCBIN将一个可执行文件或任意的数据包含到当前文件中,被包含的执行文件或者数据将原封不动放到当前文件中,编译器从INCBIN伪操作后面开始继续处理

    KEEP

    KEEP伪操作告诉编译器将局部符号包含在目标文件的符号表中

    语法格式
    KEEP {symbol}

    • symbol为被包含在目标文件的符号表中的符号,如果没有指定symbol,则除了基于寄存器外的所有符号将被包含在目标文件的符号表中
    • 默认情况下,编译器仅将如下符号包含到目标文件的符号表中:被输出的符号、将被重定位的符号
    • 使用KEEP伪操作可以将局部符号包含到目标文件的符号表中,从而使的调试工作更加方便

    NOFP

    使用NOFP伪操作可禁止源程序中包含浮点运算指令

    REQUIRE

    REQUIRE伪操作指定段之间的相互依赖关系

    语法格式
    REQUIRE label

    • label为所需的标号的名称
    • 当进行连接处理时,若遇到包含有REQUIRE label伪操作的源文件,则定义label的源文件也将被包含

    REQUIRE8及PRESERVE8

    REQUIRE8伪操作指示当前代码中要求数据栈8字节对齐
    PRESERVE8伪操作指示当前代码中数据栈是8字节对齐的

    • LDRD和STRD指令要求内存单元地址是8字节对齐的,当在程序中使用这些指令在数据栈中传送数据时,要求该数据栈是8字节对齐的
    • 链接器要保证要求8字节对齐的数据栈代码只能被数据栈是8字节对齐的代码调用

    RN

    RN伪操作为一个特定的寄存器定义名称

    语法格式
    name RN expr

    • expr为某个寄存器的编码
    • name为本伪操作给寄存器expr定义的名称

    ROUT

    ROUT伪操作用于定义局部变量的有效范围

    语法格式
    {name} ROUT
    -name为所定义的作用范围名称
    当没有使用ROUT伪操作定义局部变量的作用范围时,局部变量的作用范围为其所在的段
    ROUT伪操作作用的范围为本ROUT伪操作和下一个ROUT伪操作(指同一个段的ROUT伪操作)之间

    ARM汇编语言伪指令

    ADR

    ADRL

    LDR

    NOP

    ARM的存储系统

    ATPCS介绍

    异常中断处理

    实际上,当异常中断发生时,程序计数器PC所指的位置对于各种不同的异常中断时不同的,因此,返回地址对于各种不同的异常中断也是不同。

    • 异常中断的响应过程
    1. 保存处理器当前状态、中断屏蔽位以及各条件标志位,通过将当前程序状态寄存器CPSR的内容保存将要执行的异常中断对应的SPSR寄存器中。
    2. 设置当前程序状态寄存器CPSR中相应的位,包括设置CPSR中的位,使处理器进入相应的执行模式,设置CPSR中的位。
    3. 将寄存器lr_mode设置成返回地址
    4. 将程序计数器(PC)设置成改异常中断的中断向量地址,从而跳转到相应的异常中断处理程序执行。
    • 异常中断处理程序返回
    1. 恢复中断的程序的处理器状态,将SPSR_mode寄存器内容复制到CPSR中。
    2. 返回发生异常中断的指令的下一条指令处执行,即把lr_mode寄存器的内容复制到程序计数器PC中。

    ARM链接器

    展开全文
  • ZPL指令

    万次阅读 2015-03-11 16:14:38
    ^CC,~CC指令格式  ^CCx,~CCx ^CC,~CC = 改变脱字符 x = 任何ASCII字符     缺省值:要求有参数。如不用参数,下一字符接收后作为新的前缀字符。
  • JSP指令标签

    千次阅读 2012-07-17 00:27:39
    指令元素 1.page指令 import session contentType buffer isTreadSafe info errorPage isErrorPage 2.include指令 3.taglib指令 二.脚本元素 1.声明元素 2.表达式元素 3.脚本元素 4.注释元素 三....
  • ARM指令集 和 条件执行 详解

    千次阅读 2014-04-07 10:57:19
    算术和逻辑指令 ADC : 带进位的加法 (Addition with Carry) ADC{条件}{S} , , dest = op_1 + op_2 + carry ADC 将把两个操作数加起来,并把结果放置到目的寄存器中。它使用一个进位标志位,这样就...
  • <br />宏指令是由用户按照宏定义格式编写的一段程序,其中语句可以是指令、伪指令,甚至是已定义的宏指令。 伪指令指令的区别在于,每一条指令必须生成机器代码,然后在程序运行期间由CPU来执行其操作...
  • 什么是AT指令

    千次阅读 2009-08-03 17:11:00
    AT指令介绍及用法 AT 指令AT 即Attention,AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter, TA)或数据电路终端设备(Data ...
  • AT指令

    千次阅读 2014-03-07 14:17:36
    AT指令 AT 即Attention,AT指令一般应用于终端设备与PC应用之间的连接与通信...PDU数据格式分析 ▪ 短消息编码 ▪ 短消息的发送与接收案例 ▪ 注意事项 1简介 AT指令集是从终端设备(Terminal Equip
  • x86汇编指令详解

    千次阅读 2016-07-27 11:30:24
    x86汇编指令详解 80x86指令系统  80x86指令系统,指令按功能可分为以下七个部分。  (1) 数据传送指令。  (2) 算术运算指令。  (3) 逻辑运算指令。  (4) 串操作指令。  (5) 控制转移指令。  (6) ...
  • 2 指令系统体系结构题库

    千次阅读 2019-02-17 11:16:43
    1、下列关于CISC和RISC的描述错误的是? A、CISC指令长度是不固定的 B、CISC指令的操作数必须预存于寄存器中 C、RISC指令长度是固定的 D、RISC指令的操作数必须预存于寄存器中 E、RISC架构的指令种类通常比CISC...
  • 指令错误,原因:立即数不能作为目标操作数 2. MOV BX , CL ;指令错误,原因:字长不一致 3. INC [BX] ;指令错误,原因:需指定操作数存储器操作字 4. MOV [BX] , [6014H] ;指令错误,原因:不能同时为存储器...
  • (一)指令格式 1.指令的基本格式 2.定长操作码指令格式 3.扩展操作码指令格式 (二)指令的寻址方式 1.有效地址的概念 2.数据寻址和指令寻址 3.常见寻址方式 (三)CISC和RISC的基本概念 4.1 指令格式  ...
  • 汇编指令之算术指令

    千次阅读 2007-05-18 20:52:00
    算术指令 IBMPC机的算术运算指令包括二进制运算及十进制运算指令。算术指令用来执行算术运算,它们中有双操作数指令,也有单操作数指令。如前所述,双操作数指令的两个操作数中除源操作数为立即数的情况外,必须有...
  • 下列关于CISC和RISC的描述错误的是? RISC指令的操作数必须预存于寄存器中 RISC架构的指令种类通常比CISC架构更少 RISC指令长度是固定的 CISC指令长度是不固定的 CISC指令的操作数必须预存于寄存器中 2.第 2...
  • 8086汇编指令全集

    万次阅读 多人点赞 2016-04-07 15:08:39
    8086汇编指令全集 学习汇编语言,最关键的就在于汇编指令集的掌握以及计算机工作方式的理解,以下是80X86汇编过程中经常用到的一些汇编指令。 从功能分类上来说,一共可分为 一、 数据传送指令:MOV、XCHG、LEA、...
  • APDU指令小记

    万次阅读 2019-01-05 23:39:35
    APDU: Application Protocol data unit,,是智能卡与智能卡读卡器之间传送的信息单元, (向智能卡发送的命令)指令(ISO 7816-4规范有定义):CLA INS P1 P2 Lc Data Le。 CLA:指令类别;INS:指令码;P1、P2:...
  • 汇编指令

    千次阅读 2015-10-16 10:44:29
    80x86指令系统  80x86指令系统,指令按功能可分为以下七个部分。  (1) 数据传送指令。  (2) 算术运算指令。  (3) 逻辑运算指令。  (4) 串操作指令。  (5) 控制转移指令。  (6) 处理器控制指令。 ...
  • AT指令介绍及用法

    千次阅读 2008-04-30 14:30:00
    AT 指令 AT 即Attention,AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备 (Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter, TA)或数据电路终端设备 (Data Circuit Terminal ...
  • Docker-DockerFile指令详解

    千次阅读 2018-07-30 17:42:32
    供了十多个指令。下面我们继续讲解其他的指令 FROM 所谓定制镜像,那么就一定是以一个镜像为基础,在其上进行修改定制。就像我们之前运行了一个Nginx的容器,在其上面修改一样,基础容器是必需指定的。而FROM...
  • 汇编伪指令

    千次阅读 2012-05-04 21:32:46
    段定义伪指令    段定义伪指令是表示一个段开始和结束的命令,80x86有... 完整段定义伪指令格式如下:  段名 SEGMENT  .  .  .  段名 ENDS  段名由用户命名。对于数据段、附加段和堆栈段来说,段内一般
  • 数据格式 mov指令 关于操作数指示符的理解 mov后缀b、w、l、q的确定方法总结 mov中的数据转换 关于本文的说明 本文主要记录的是本人对于《深入理解计算机系统》一书中第三章部分章节个人理解和总结分析,其中...
  • AT 指令集详解

    万次阅读 多人点赞 2019-03-11 11:41:20
    AT指令格式:AT指令都以”AT”开头,以(即\r,回车符)结束,模块运行后,串口默认的设置为:8位数据位、1位停止位、无奇偶校验位、硬件流控制(CTS/RTS). 注意为了发送AT命令,最后还要加上 (即\n,换行符)这是串口终端...
  • MIPS32指令

    千次阅读 2021-01-15 11:01:53
    MIPS32指令集 更全的MIPS指令
  • 汇编指令

    千次阅读 2007-11-09 17:10:00
    汇编指令大全 网络安全 2006-3-7 14:59 8088汇编指令表 一、数据传输指令 ─────────────────────────────────────── 它们在存贮器和寄存器、寄存器和输入输出端口之间传送...
  • 《ARM 指令集》IEEE浮点指令

    千次阅读 2016-10-23 21:17:24
    ARM 可以与最多 16 个协处理器相接口(interface...定义了一个标准的 ARM 浮点指令集,所以编码可以跨越所有 RISC OS 机器。如果不存在实际的硬件,则这些指令被截获并由浮点模拟器模块(FPEmulator)来执行。程序不需要知
  • Powershell指令集_1

    千次阅读 2015-12-04 11:51:01
    :两个时间戳相减,能显示下列比较信息: $dateObject1 = get-date dateObject1 .Subtract (dateObject2) Days : 0 Hours : 0 Minutes : - 1 Seconds : - 19 Milliseconds : - 732 Ticks : - ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,418
精华内容 14,967
关键字:

下列指令格式错误的是