如何在字符串定义中将 ASCII 字符大写?
How to capitalise ASCII character in string define?
对于我创建的脚本 ,我必须在字符串定义中将传递给 NASM 的文件名大写。我使用 %substr 在 %rep 循环中提取单个字符,重复计数来自 %strlen。我尝试了以下方式来将字符大写:
$ cat test.asm
%define cc 'a'
%if cc >= 'a' && cc <= 'z'
%strcat cc cc - 'a' + 'A'
%endif
但失败如下:
$ nasm test.asm
test.asm:3: error: non-string passed to `%strcat' (9)
$
错误消息是由 NASM 预处理器源中的此位生成的:https://repo.or.cz/nasm.git/blob/437e0ffa01505d173a8b9cfe2decf74f2e9795a5:/asm/preproc.c#l3472
使用https://repo.or.cz/nasm.git/blob/437e0ffa01505d173a8b9cfe2decf74f2e9795a5:/asm/preproc.c#l207定义的token类型(9好像是TOK_OTHER。)
我最终用这种方法解决了这个问题,虽然它看起来很奇怪,可能会有更好的方法。
%if cc >= 'a' && cc <= 'z'
%substr cc "ABCDEFGHIJKLMNOPQRSTUVWXYZ" (cc - 'a' + 1)
%endif
来自我在 https://hg.ulukai.org/ecm/bootimg/file/f8c890f92116f2593c1e2016a71f95dfd7bbcd36/bootimg.asm#l314
的消息来源
我认为cc - 'a' + 'A'
是一个数值常量,不再是一个字符串。它不能作为多字符字符串的一部分使用。不幸的是 %strcat
不会将数字转回字符串?
对于单个字符,您不需要字符串内容,只需按位运算符即可生成正确的 ASCII 代码。 (What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?)
;; %if cc >= 'a' && cc <= 'z' ; if cc might not be alphabetic
%define upper_cc (cc & ~0x20)
;; %endif
;; %if ...
%define lower_cc (cc | 0x20)
;; %endif
带有 nasm -felf64 -l/dev/stdout test.asm
的测试用例确认我们得到了正确的数字常量,高位没有垃圾。
1 %define cc 'a'
2
3 %define upper_cc (cc & ~0x20)
4 %define lower_cc (cc | 0x20)
5
6 00000000 B8 61000000 mov eax, lower_cc
7 00000005 B8 41000000 mov eax, upper_cc
不过,如果您想稍后对结果进行字符串连接和填充,这可能没有用。
我确实找到了另一种方法,它允许我们在任何地方都使用 %xdefine 而不是 %strcat。 (x 很重要,否则会导致错误的递归宏。)然而,这需要一个特殊的类似“strlen”的宏来计算字符串片段和数字,以计算出复合字符串列表的长度与 db
一起使用时向上。这是完整的示例,基于 from my script 已在其他答案中链接的那部分。
%include "lmacros1.mac"
%macro checkchar 2-3.nolist 0
%if %2 <= ' ' || %2 >= 128 || \
%2 == '/' || %2 == '\' || \
%2 == '"' || %2 == "'" || \
%2 == %3
%error Invalid character (%2) in name (%1)
%endif
%endmacro
%macro strlen_ll 1-*.nolist
%assign ll 0
%rep %0
%ifstr %1
%strlen ll2 %1
%assign ll ll + ll2
%else
%assign ll ll + 1
%endif
%rotate 1
%endrep
%endmacro
%macro test 1
%define string %1
%strlen length string
%assign ii 1
%rep length
%substr cc string length - ii + 1
%ifidn cc,"/"
%substr string string length -ii + 2, -1
%exitrep
%endif
%ifidn cc,"\"
%substr string string length -ii + 2, -1
%exitrep
%endif
%assign ii ii + 1
%endrep
%strlen length string
%assign ii 1
%define name ""
%define ext ""
%assign dotyet 0
%rep length
%substr cc string ii
%assign ii ii + 1
%if cc >= 'a' && cc <= 'z'
%xdefine cc (cc - 'a' + 'A')
%endif
%ifn dotyet
%ifidn cc,"."
%assign dotyet 1
%else
strlen_ll name
%if ll >= 8
%error Too long name part in %1
%exitrep
%endif
checkchar %1,cc
%xdefine name name,cc
%endif
%else
strlen_ll ext
%if ll >= 3
%error Too long ext part in %1
%exitrep
%else
checkchar %1,cc,"."
%xdefine ext ext,cc
%endif
%endif
%endrep
%ifidn name,""
%error Invalid empty name part in %1
%endif
fill 8,32,db name
fill 3,32,db ext
%endmacro
test "../foo/bar/baz.bin"
test "quux"
对于我创建的脚本
$ cat test.asm
%define cc 'a'
%if cc >= 'a' && cc <= 'z'
%strcat cc cc - 'a' + 'A'
%endif
但失败如下:
$ nasm test.asm
test.asm:3: error: non-string passed to `%strcat' (9)
$
错误消息是由 NASM 预处理器源中的此位生成的:https://repo.or.cz/nasm.git/blob/437e0ffa01505d173a8b9cfe2decf74f2e9795a5:/asm/preproc.c#l3472
使用https://repo.or.cz/nasm.git/blob/437e0ffa01505d173a8b9cfe2decf74f2e9795a5:/asm/preproc.c#l207定义的token类型(9好像是TOK_OTHER。)
我最终用这种方法解决了这个问题,虽然它看起来很奇怪,可能会有更好的方法。
%if cc >= 'a' && cc <= 'z'
%substr cc "ABCDEFGHIJKLMNOPQRSTUVWXYZ" (cc - 'a' + 1)
%endif
来自我在 https://hg.ulukai.org/ecm/bootimg/file/f8c890f92116f2593c1e2016a71f95dfd7bbcd36/bootimg.asm#l314
的消息来源我认为cc - 'a' + 'A'
是一个数值常量,不再是一个字符串。它不能作为多字符字符串的一部分使用。不幸的是 %strcat
不会将数字转回字符串?
对于单个字符,您不需要字符串内容,只需按位运算符即可生成正确的 ASCII 代码。 (What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?)
;; %if cc >= 'a' && cc <= 'z' ; if cc might not be alphabetic
%define upper_cc (cc & ~0x20)
;; %endif
;; %if ...
%define lower_cc (cc | 0x20)
;; %endif
带有 nasm -felf64 -l/dev/stdout test.asm
的测试用例确认我们得到了正确的数字常量,高位没有垃圾。
1 %define cc 'a'
2
3 %define upper_cc (cc & ~0x20)
4 %define lower_cc (cc | 0x20)
5
6 00000000 B8 61000000 mov eax, lower_cc
7 00000005 B8 41000000 mov eax, upper_cc
不过,如果您想稍后对结果进行字符串连接和填充,这可能没有用。
我确实找到了另一种方法,它允许我们在任何地方都使用 %xdefine 而不是 %strcat。 (x 很重要,否则会导致错误的递归宏。)然而,这需要一个特殊的类似“strlen”的宏来计算字符串片段和数字,以计算出复合字符串列表的长度与 db
一起使用时向上。这是完整的示例,基于 from my script 已在其他答案中链接的那部分。
%include "lmacros1.mac"
%macro checkchar 2-3.nolist 0
%if %2 <= ' ' || %2 >= 128 || \
%2 == '/' || %2 == '\' || \
%2 == '"' || %2 == "'" || \
%2 == %3
%error Invalid character (%2) in name (%1)
%endif
%endmacro
%macro strlen_ll 1-*.nolist
%assign ll 0
%rep %0
%ifstr %1
%strlen ll2 %1
%assign ll ll + ll2
%else
%assign ll ll + 1
%endif
%rotate 1
%endrep
%endmacro
%macro test 1
%define string %1
%strlen length string
%assign ii 1
%rep length
%substr cc string length - ii + 1
%ifidn cc,"/"
%substr string string length -ii + 2, -1
%exitrep
%endif
%ifidn cc,"\"
%substr string string length -ii + 2, -1
%exitrep
%endif
%assign ii ii + 1
%endrep
%strlen length string
%assign ii 1
%define name ""
%define ext ""
%assign dotyet 0
%rep length
%substr cc string ii
%assign ii ii + 1
%if cc >= 'a' && cc <= 'z'
%xdefine cc (cc - 'a' + 'A')
%endif
%ifn dotyet
%ifidn cc,"."
%assign dotyet 1
%else
strlen_ll name
%if ll >= 8
%error Too long name part in %1
%exitrep
%endif
checkchar %1,cc
%xdefine name name,cc
%endif
%else
strlen_ll ext
%if ll >= 3
%error Too long ext part in %1
%exitrep
%else
checkchar %1,cc,"."
%xdefine ext ext,cc
%endif
%endif
%endrep
%ifidn name,""
%error Invalid empty name part in %1
%endif
fill 8,32,db name
fill 3,32,db ext
%endmacro
test "../foo/bar/baz.bin"
test "quux"