2

For a script I created for another question, I had to capitalise filenames passed to NASM in string defines. I used %substr to extract single characters in a %rep loop, with a repetition count from %strlen. I tried the following way to capitalise a character:

$ cat test.asm
 %define cc 'a'
 %if cc >= 'a' && cc <= 'z'
  %strcat cc cc - 'a' + 'A'
 %endif

But that failed as follows:

$ nasm test.asm
test.asm:3: error: non-string passed to `%strcat' (9)
$

The error message is generated by this bit in the NASM preprocessor source: https://repo.or.cz/nasm.git/blob/437e0ffa01505d173a8b9cfe2decf74f2e9795a5:/asm/preproc.c#l3472

Which uses the token type the definition of which is in https://repo.or.cz/nasm.git/blob/437e0ffa01505d173a8b9cfe2decf74f2e9795a5:/asm/preproc.c#l207 (9 seems to be TOK_OTHER.)

Rob
  • 14,746
  • 28
  • 47
  • 65
ecm
  • 2,583
  • 4
  • 21
  • 29
  • @Rob: Sorry, my mistake for putting tags in the title. I didn't understand that you'd edited it the first time around. – ecm Jul 27 '19 at 15:41
  • Another failed attempt: `%assign cc cc - 'a' + 'A'` \ `%strcat cc cc` This fails with `error: non-string passed to '%strcat' (6)` – ecm Jul 27 '19 at 15:58
  • To aid web searches, let me mention that we want to *uppercase* characters with the NASM preprocessor here. – ecm Jul 27 '19 at 21:53

3 Answers3

2

I eventually ended up with this way of solving the problem, though it seems weird enough that there might be a better one.

 %if cc >= 'a' && cc <= 'z'
  %substr cc "ABCDEFGHIJKLMNOPQRSTUVWXYZ" (cc - 'a' + 1)
 %endif

From my source at https://hg.ulukai.org/ecm/bootimg/file/f8c890f92116f2593c1e2016a71f95dfd7bbcd36/bootimg.asm#l314

ecm
  • 2,583
  • 4
  • 21
  • 29
1

I did find another way, which allows us to use %xdefine instead of %strcat everywhere. (The x is important, else it would lead to wrongly recursive macros.) This however requires a special "strlen"-like macro that counts string fragments as well as numbers, to figure out the length that the compound string-like list would take up when used with db. Here's the full example, based on that part from my script already linked in the other answer.

%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"
ecm
  • 2,583
  • 4
  • 21
  • 29
0

I think cc - 'a' + 'A' is a numeric constant, no longer a string. It's not something that would work as part of a multi-character string. Unfortunately %strcat doesn't turn numbers back into strings?


For a single character, you don't need string stuff, just bitwise operators to make the right ASCII code. (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

Test case with nasm -felf64 -l/dev/stdout test.asm which confirms that we get the right numeric constant with no garbage in the high bits.

     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

This might not be useful if you want to later do string concatenation and stuff on the result, though.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Two problems with this: 1. As you mention, I want to do operations on a string made from these. 2. cc can be a non-alphabetic character, some of which your upper/lower_cc macros would fail for. – ecm Jul 27 '19 at 16:47
  • 1
    @ecm: for 2: you can wrap the re-define inside a `%if` range-check. For 1: yeah I already upvoted your answer. :P Leaving this here in case it's useful for other future readers who are mentally stuck on strings when this simple way *would* work. – Peter Cordes Jul 27 '19 at 16:49