open-watcom / open-watcom-v2

Open Watcom V2.0 - Source code repository, Wiki, Latest Binary build, Archived builds including all installers for download.
Other
948 stars 155 forks source link

Line terminator and mutlple statements in __asm block #1184

Open crazii opened 7 months ago

crazii commented 7 months ago

Line terminator and mutlple statements in __asm block

Main purpose: to use macros. Macros are not expanded in multiple lines, there need to be an terminator of a statement.

BC uses ; instead of using it as an comment token. GCC uses \n\t in the assembly string. I was guessing ; would work for open watcom, but it turns out it is a comment token after I checked with wdis

jmalak commented 7 months ago

asm block use C preprocessor tokenizing and C language tokens that ';' character should be end of statement. Comment should be standard C comment. If it doesn't work as expected then give here sample code.

crazii commented 7 months ago

Testing code:

#define TEST(reg) \
    push reg; \
    pop reg;

int main()
{
    __asm {
        xchg ax, bx; //tag
        TEST(ax);
        xchg ax, bx; //tag
    }
    return 0;
}

WDIS output:

Segment: _TEXT BYTE USE16 00000016 bytes
0000                main_:
0000  B8 0C 00              mov     ax,0x000c
0003  E8 00 00              call        __STK
0006  53                push        bx
0007  51                push        cx
0008  52                push        dx
0009  56                push        si
000A  57                push        di
000B  93                xchg        ax,bx
000C  50                push        ax
000D  93                xchg        ax,bx
000E  31 C0             xor     ax,ax
0010  5F                pop     di
0011  5E                pop     si
0012  5A                pop     dx
0013  59                pop     cx
0014  5B                pop     bx
0015  C3                ret
crazii commented 7 months ago

Also __asm { pop ds; xchg ax, bx } will get an error, __asm { pop ds xchg ax, bx } will generate only the LAST instruction.

Only (with a line break)

    __asm { pop ds [;]
     xchg ax, bx  }

Works.

This also makes the define including __asm keyword not working. (with or without ;)

#define TEST() __asm {\
  push ax[;] \
  pop ax [;] \
}
jmalak commented 7 months ago

Many Thanks for your detailed bug report info. The asm statement is not much used in OW source code. It uses #pragma aux. asm statement processing must be reviewed.

jmalak commented 7 months ago

I checked how the __asm statement is processed by OW compilers. there are following limitation, it explains behaviour which you are reporting.

If you need using macros and multi statements line then best selection is to use #pragma aux. pragma aux process each string token as simple instruction that you can serialize many instruction as you want in single or multiple lines. pragma aux can combine bytes and instruction together and can use macros for any part of declaration.

crazii commented 7 months ago

Can it be used in another inline assembly? like it's nested - call/embed another #praga aux block in the existing #pragma aux or __asm{}

Here's my original thoughts: I want reuse the hack for the offset, offered by @joncampbell123 from: here, for better maintenance, so that there will be only one copy in the original source and avoid typos/bugs among multiple copies.

#define HACK_OFFSET(reg, label) \
call hack; \
jmp label; \
hack: \
pop reg; \
push bx; \
mov bx, reg; \
add reg, word ptr cs:[bx+1]; \
add reg 3; \
pop bx

...

__asm {
...
#if defined(__BC__)
mov ax, offset LABEL
#else
HACK_OFFSET(ax, LABEL)
#endif
...
LABEL:

}

I don't have much experience with #pragma aux, so currently I don't think it's possible to do it with #pragma aux. Very much appreciated if you have any ideas/advices. Otherwise I think I have to stick with multiple copies among the source. - not perfect, but at least it's working.

joncampbell123 commented 7 months ago

DOSLIB also uses pragma aux if you want some examples:

https://github.com/joncampbell123/doslib/blob/master/hw/dos/dos.h#L133

Pragma aux has a lot more than just assembly language and is documented here: http://downloads.openwatcom.org/ftp/manuals/current/cguide.pdf

crazii commented 7 months ago

Thanks, I understood that it can tell the compiler what input registers it use, what it returns, and what it modifies. I've written some of them when wrap up 32bit mode dpmi functions in a DJGPP style https://github.com/crazii/USBDDOS/blob/master/USBDDOS/DPMI/dpmi_wc.c (a couple of years ago but now deprecated),
But that's the limit of my experience. I read the cguide.pdf just now that #pragma aux indeed has much more options, mostly ABI related.

crazii commented 7 months ago

Anyways with hacks/workaounds my project of Open watcom build is OK and I just wanted to give some feedback to open watcom, hope it might help.

I'll leave this issue as a feature request, and with a question mark, whether/when to improve is up to you.

jmalak commented 7 months ago

OK. The OW concept for pragma aux is that it is template of code, single definition and multiple use. Any code described by pragma aux represent C like function. You can define multiple parameters and single return value. power of pragma aux is in use of registers, stack can be used for passing parameters but is not eficient as registers. Any addressing is done in C code, you have no access to member of structure etc, but you can pass address by register to in-line code. You can see into bld/watcom/h subdirectory, there are many header files with in-line assembly they is included to C source code and code is used only if you instantized it by call. By example there is tinyio.h to direct access to DOS API calls from C source without int86 overhead. You can look on several file to better understand of use pragma aux. int10.h int33.h xfloat.h for FPU related code there are also lot assembly in-line code in C run-time library next example is C API for RDOS released by #pragma aux construct, it is in bld/watcom/h files owcomp.h, owflat.h and rdosdev.h. generaly it is used for small piece of assembly code which should be more efficient. Generaly OW concept is to do as much as possible in C and only improve code where it has sense by in-line assembly or if you need some interaction with HW from C code.

Generaly OW preferred method for in-line assembly is #pragma aux. __asm statement is not used in OW code base that it has low priority to do any enhancement. it is rather for compatibility with similar toolchains which use this statement, but only basic support.

rdos314 commented 7 months ago

pragma aux is used in RDOS much in the same way as int10 and int21 is used to interface DOS calls. It defines a C interface that is inlined where parameters are put in the proper registers. The actual call is not an interrupt, rather a special invalid instruction that is modified to a far call on usage. The calls are generated with a tool and are emitted as series of bytes.

I think #pragma aux is a lot more powerful than similar features in GCC.

crazii commented 7 months ago

Agreed. _asm block is used base on my current situation. I'll try #pragma aux first when I write some from the start.