5

I am fairly certain I have seen this in code before, but I am unable to find any references on how to do it. I do expect this to be compiler or assembler specific.

I want to define an function pointer array of (compile-time) fixed length for use as an interrupt vector table on an embedded device. Each handler will push its interrupt number before jumping to a common handler. Creating a macro for these simpler functions is straight forward:

.macro irq number
    .global irq\number
    irq\number:
    pushd $\number
    jmp irq_handler_common
.endm

These can then be defined manually like this:

irq 0
irq 1
irq 2
...

However, I would rather not clutter my ASM file by manually defining 256 of these. Thus, what I would like to do is use an for-loop like preprocessor/macro that would allow me to do something like this:

for i in 0 ... 255
    irq i

Can this be done in using macros?

sherrellbc
  • 4,650
  • 9
  • 48
  • 77
  • How about [rept](https://sourceware.org/binutils/docs/as/Rept.html). Note that this and what you showed are assembler directives for the gnu assembler (`gas`) and as such not related to `gcc` or preprocessor. – Jester Jan 08 '18 at 22:40
  • Although @Jester is more or less correct, there is a relationship with GCC and assembly since GCC will act as a front end to running `as` (gnu assembler). Some people do falsely assume that means GCC is an assembler too. – Michael Petch Jan 08 '18 at 22:50
  • 2
    You can do something like `.altmacro` `.set i, 0` `.rept 256` `irq %i` `.set i, i + 1` .`endr` . The `.altmacro` directive will be required here for this to work – Michael Petch Jan 08 '18 at 23:44
  • @MichaelPetch This _seemed_ to do the trick. I'll report back. Is .altmacro uncomon – sherrellbc Jan 09 '18 at 00:27
  • 1
    `.altmacro` usage isn't uncommon among GAS developers (One could probably argue that GAS isn't as common for assembly development in general). It is [documented](https://sourceware.org/binutils/docs-2.29/as/Altmacro.html) and used by developers using GAS. I have used it in my own projects. We use `.almacro` here so we can take advantage of _Expression results as strings You can write ‘%expr’ to evaluate the expression expr and use the result as a string._ – Michael Petch Jan 09 '18 at 00:32
  • @MichaelPetch, Indeed this worked. I am now trying to create a vector of these new labels using a very similar macro (except using `.long irq %i`) but I observe the following error: `Error: invalid operands (*UND* and *ABS* sections) for %`. Do you know what might be the problem? Example uses of `altmacro` seem to be hard to come by. I've a few of the string incantations (using `'` and `<`) but none seemed to work. – sherrellbc Jan 09 '18 at 13:52

1 Answers1

8

Using suggestions by @MichaelPetch and @Jester I've compiled the following working solution (requires use of the GNU Assembler for altmacro, or other supporting assembler). Unfortunately I had to create two macros, one to instantiate the interrupt entry point stubs and the other to help create the vector of these entry addresses. I was observing Error: invalid operands (*UND* and *ABS* sections) for % if I tried to use .long irq %i from within the .rept that creates default_handlers. If anyone has a smaller/simpler solution please post!

Define the entry point specific to each handler entry

.macro irq_stubX number
    irq\number:
    pushd $\number
    jmp irq_handler_common
.endm

Using this macro (and altmacro), create 256 instances

.altmacro

.section .text
.set i,0
.rept 256
    irq_stubX %i
    .set i,i+1
.endr

Finally, using another macro, create a vector of the labels we just created above.

.section .data
.macro irq_labelX number
    .long irq\number
.endm

default_handlers:
.set i,0
.rept 256
    irq_labelX %i
    .set i, i+1
.endr

For reference, this does not work and reports the error mentioned above:

default_handlers:
.set i,0
.rept 256
    .long irq %i
    .set i, i+1
.endr

EDIT. The solution can be made smaller and more clear per @RossRidge's suggestion below.

.altmacro

.macro irq_insertX number
    .section .text
    irq_stubX \number

    .section .data
    .long irq\number
.endm

.section .data
default_handlers:
.set i,0
.rept 256
    irq_insertX %i
    .set i, i+1
.endr
sherrellbc
  • 4,650
  • 9
  • 48
  • 77
  • 1
    You can merge `irq_stubX` and `irq_labelX` into one macro and only use one `.rept` loop, by moving the section directives into the unified macro. – Ross Ridge Jan 09 '18 at 17:30
  • @RossRidge Updated the answer. Nice addition! It works well. – sherrellbc Jan 09 '18 at 18:50
  • IDK why you thought `.long irq %i` would work. A `.long` directive takes a list of numeric expressions and assembles those bytes directly into the output file. It doesn't accept instruction mnemonics. That part of your answer seems like a distraction. You might want to move it to a separate section at the bottom. And BTW, re: the `*UND* and *ABS*` section stuff, see https://stackoverflow.com/a/46870205/224132. All symbols have an associated section. I'm not sure exactly what `.long irq %i` would be trying to evaluate, though, without a comma. – Peter Cordes Jan 10 '18 at 04:34
  • @PeterCordes My use of `.long irq %i` was an attempt evaluate an expression that represented the label of a previously inserted code. As shown it is a little confusing due to the name sharing between the stub labels and the macro name. This is why I renamed the stub macro `irq_stubX` and tried to use `.long irq %i` to produce `.long irq0` (or `irq1`, etc), but was not able to get it working. The `default_handlers` symbol is meant to reflect an array of pointers to these stub functions. – sherrellbc Jan 10 '18 at 12:51