Search code examples
assemblystructx86nasm

Error "non-constant argument supplied to TIMES" from istruc use of a structure


I have recently started back up on my project and am getting an error when I compile my nasm program. I am using nasm 2.15.05 and am compiling in DOSBox, I have also tried FreeDOS in VirtualBox but I suspect it has nothing to do with the environment. The error that is produced is:

non-constant argument supplied to TIMES.

I am not using times in the source file which threw me but I looked at the listing and it's the istruc macro. Here is the relevant block from the listing file:

     0                              <1> times %$strucname_size-($-%$strucstart) db 0
     0 00000066 <len 1>             <1>  ;;; times COORD_size-([email protected]) db 0
     0          ******************  <1>  error: non-constant argument supplied to TIMES

And here is the source of the structure:

    struc COORD
    .TopLeft:                   resw    2
    .BottomRight:               resw    2
    endstruc

And here is the implementation of the structure:

section data
.draw_data:
    istruc  COORD
        at COORD.TopLeft,           dw  10, 20
        at COORD.BottomRight,       dw  50, 150
    iend

Any help would be greatly appreciated. I did see another post about the same error but that was caused by a case-sensitivity issue which I do not see in this case.


Solution

  • This error is caused by defining the struc structure after its use by the istruc macro.

    First, look at this test to see how it works correctly when using your example, with the struc occurring in the source before its use by istruc:

    $ nasm -v
    NASM version 2.15.03 compiled on Dec 28 2020
    $ cat testpass.asm
    
    struc COORD
     .TopLeft:      resw 2
     .BottomRight:  resw 2
    endstruc
    
    section data
    .draw_data:
    istruc COORD
     at COORD.TopLeft,      dw 10, 20
     at COORD.BottomRight,  dw 50, 150
    iend
    
    $ nasm testpass.asm
    $
    

    Next, we can reproduce your error if we swap the two blocks of code:

    $ cat testfail.asm
    
    section data
    .draw_data:
    istruc COORD
     at COORD.TopLeft,      dw 10, 20
     at COORD.BottomRight,  dw 50, 150
    iend
    
    struc COORD
     .TopLeft:      resw 2
     .BottomRight:  resw 2
    endstruc
    
    $ nasm testfail.asm
    testfail.asm:7: error: non-constant argument supplied to TIMES
    $
    

    Further, let's look at the listing file. Recent NASM provides the -Lp switch to keep the listing file even in the case of errors. Use this and this is the result:

    $ nasm testfail.asm -l testfail.lst -Lp
    testfail.asm:7: error: non-constant argument supplied to TIMES
    $ cat testfail.lst
         1
         2                                  section data
         3                                  .draw_data:
         4                                  istruc COORD
         5 00000000 <len 4h>                 at COORD.TopLeft,      dw 10, 20
         6 00000004 <len 4h>                 at COORD.BottomRight,  dw 50, 150
         6          ******************       error: TIMES value -4 is negative
         7 00000008 <len 1h>                iend
         7          ******************       error: non-constant argument supplied to TIMES
         8
         9                                  struc COORD
        10 00000000 <len 4h>                 .TopLeft:      resw 2
        11 00000004 <len 4h>                 .BottomRight:  resw 2
        12                                  endstruc
        13
    $
    

    We can see there's actually two errors. The "non-constant" error appears to take precedence over the "is negative" error however. Let's see what the istruc macros expand to:

    $ nasm testfail.asm -E > efail.asm
    $ nasm efail.asm -l efail.lst -Lp
    testfail.asm:8: error: non-constant argument supplied to TIMES
    $ cat efail.lst
         1                                  %line 2+1 testfail.asm
         3                                  [section data]
         4                                  .draw_data:
         5                                  [email protected]:
         6                                  times (COORD.TopLeft-COORD)-([email protected]) db 0
         7                                  %line 5+0 testfail.asm
         5 00000000 <len 4h>                dw 10, 20
         5                                  %line 6+1 testfail.asm
         7                                  times (COORD.BottomRight-COORD)-([email protected]) db 0
         7          ******************       error: TIMES value -4 is negative
         8                                  %line 6+0 testfail.asm
         6 00000004 <len 4h>                dw 50, 150
         6                                  %line 7+1 testfail.asm
         8 00000008 <len 1h>                times COORD_size-([email protected]) db 0
         8          ******************       error: non-constant argument supplied to TIMES
         9
        10                                  [absolute 0]
        11                                  %line 9+0 testfail.asm
         9                                  COORD:
         9                                  %line 10+1 testfail.asm
        11 00000000 <len 4h>                 .TopLeft: resw 2
        12 00000004 <len 4h>                 .BottomRight: resw 2
        13                                  COORD_size equ ($-COORD)
        14                                  %line 12+0 testfail.asm
        12                                  [section data]
        12                                  %line 13+1 testfail.asm
        14
    $
    

    The times repetition causing the error is emited from iend, and is intended to pad the instance of the structure up to the full size of the structure. NASM structure labels, like labels in NASM generally, have no concept of a "label size", so after the last structure field's start label there could be an arbitrary amount of space reserved. To honour this space, whatever its size, NASM records the size of the structure in a label named like the structure with _size appended. The times directive evidently wants the COORD_size label to be defined before its use.

    In the manual it says that "[t]he operand to TIMES is a critical expression", and links to the section on critical expressions:

    defined to be an expression whose value is required to be computable in the first pass, and which must therefore depend only on symbols defined before it. The argument to the TIMES prefix is a critical expression.

    So, using COORD_size before it is defined makes the critical expression not be computable.

    Digging deeper

    Just for fun, let's look at what happens when we comment out the offending directive:

    $ cat e2.asm
    [section data]
    .draw_data:
    [email protected]:
    times (COORD.TopLeft-COORD)-([email protected]) db 0
    dw 10, 20
    times (COORD.BottomRight-COORD)-([email protected]) db 0
    dw 50, 150
    ; times COORD_size-([email protected]) db 0
    
    [absolute 0]
    COORD:
     .TopLeft: resw 2
     .BottomRight: resw 2
    COORD_size equ ($-COORD)
    [section data]
    $ nasm e2.asm
    $
    

    It passes! Why is that? It appears that the deltas of the form COORD.TopLeft - COORD are actually allowed in the critical expression for times. I assume this is an artefact of NASM actually being a multi-pass assembler. It (correctly) guesses that the delta will evaluate to a scalar number. On an earlier pass, this is evaluated as zero, resulting in the "TIMES value -4 is negative" error. However, on a subsequent pass NASM knows the label values and inserts them so no error remains. I'm honestly not sure why this doesn't happen to the COORD_size use, but I assume it is related to NASM not knowing it will be equated to a scalar number later.

    Just to make sure, here's the same test with the offending directive not commented out; it fails as before:

    $ cat e3.asm
    [section data]
    .draw_data:
    [email protected]:
    times (COORD.TopLeft-COORD)-([email protected]) db 0
    dw 10, 20
    times (COORD.BottomRight-COORD)-([email protected]) db 0
    dw 50, 150
    times COORD_size-([email protected]) db 0
    
    [absolute 0]
    COORD:
     .TopLeft: resw 2
     .BottomRight: resw 2
    COORD_size equ ($-COORD)
    [section data]
    $ nasm e3.asm
    e3.asm:8: error: non-constant argument supplied to TIMES
    $
    

    Finally, modifying the example so as to use a delta for the size, helped by an additional label at the end of the structure, makes all times directives work as intended:

    $ cat e4.asm
    [section data]
    .draw_data:
    [email protected]:
    times (COORD.TopLeft-COORD)-([email protected]) db 0
    dw 10, 20
    times (COORD.BottomRight-COORD)-([email protected]) db 0
    dw 50, 150
    times (COORD_end-COORD)-([email protected]) db 0
    
    [absolute 0]
    COORD:
     .TopLeft: resw 2
     .BottomRight: resw 2
    COORD_end:
    COORD_size equ ($-COORD)
    [section data]
    $ nasm e4.asm
    $