2

I'm compiling this C source with OpenWatcom V2 wcc:

/* Writes a '\0'-terminated string to the file descriptor. */
static void fdputs(int fd, const char *s);
#pragma aux fdputs = \
"push si" \
"mov cx, -1" \
"repz scasb" \
"neg cx" \
"inc cx"  /* now cx is the number of bytes to write */ \
"pop dx"  /* now dx points to the buffer (s argument) */ \
"mov ah, 0x40" /* WRITE */ \
"int 0x21" \
parm [ bx si ] \
modify [ ax cx dx si ];  /* Also modifies cf */

int myfunc(void) {
  fdputs(1, "Hello!");
  return 0;
}

In the disassebly of the .obj file generated by wcc the 6 pushes and the 5 pops don't balance out. (The code, when run, crashes because of this.)

$ wcc -bt=dos -ms -s -os -W -w4 -wx -we -wcd=202 -0 -fr -fo=t.obj t.c
$ wdis -a -fi -i=@ t.obj
.387
                PUBLIC  myfunc_
                EXTRN   _small_code_:BYTE
DGROUP          GROUP   CONST,CONST2,_DATA
_TEXT           SEGMENT BYTE PUBLIC USE16 'CODE'
                ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
myfunc_:
        push            bx
        push            cx
        push            dx
        push            si
        mov             ax,offset DGROUP:@$1
        push            ax
        mov             bx,1
        xor             si,si
        push            si
        mov             cx,0ffffH
        repe scasb
        neg             cx
        inc             cx
        pop             dx
        mov             ah,40H
        int             21H
        xor             ax,ax
        pop             si
        pop             dx
        pop             cx
        pop             bx
        ret
_TEXT           ENDS
CONST           SEGMENT WORD PUBLIC USE16 'DATA'
@$1:
    DB  48H, 65H, 6cH, 6cH, 6fH, 21H, 0

CONST           ENDS
CONST2          SEGMENT WORD PUBLIC USE16 'DATA'
CONST2          ENDS
_DATA           SEGMENT WORD PUBLIC USE16 'DATA'
_DATA           ENDS
                END

Am I using the wcc inline assembly correctly? Is this likely a bug in wcc?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
pts
  • 80,836
  • 20
  • 110
  • 183
  • Hmmm. Your macro includes `parm [ bx si ]`, which I think means to expect `si` as an input parameter, but also includes that same register in the `modify`; this feels like a mismatch? – Steve Friedl Jul 12 '20 at 22:18
  • If it's not OK to pass the same register in both `parm` and `modify`, then it's reasonable to expect wcc to fail with an error message. My intention was putting `si` in both `parm` and `modify` was to express that one of the arguments are passed in `si`, and during execution, the code modifies `si`. – pts Jul 12 '20 at 22:41
  • Removing `si` from `modify` still keeps the `push` and `pop` instructions unbalanced. – pts Jul 12 '20 at 22:42
  • Oh well, thanks for checking it. – Steve Friedl Jul 12 '20 at 22:42

1 Answers1

3

I believe this is a bug with OpenWatcom C/C++, as I have observed this behaviour in the past. To get around it I list each parameter separately between their own [ and ] . Try modifying:

parm [ bx si ] \

to be:

parm [ bx ] [ si ] \

The resulting code should look like:

.387
                PUBLIC  myfunc_
                EXTRN   _small_code_:BYTE
DGROUP          GROUP   CONST,CONST2,_DATA
_TEXT           SEGMENT BYTE PUBLIC USE16 'CODE'
                ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
myfunc_:
        push            bx
        push            cx
        push            dx
        push            si
        mov             si,offset DGROUP:@$1
        mov             bx,1
        push            si
        mov             cx,0ffffH
        repe scasb
        neg             cx
        inc             cx
        pop             dx
        mov             ah,40H
        int             21H
        xor             ax,ax
        pop             si
        pop             dx
        pop             cx
        pop             bx
        ret
_TEXT           ENDS
CONST           SEGMENT WORD PUBLIC USE16 'DATA'
@$1:
    DB  48H, 65H, 6cH, 6cH, 6fH, 21H, 0

CONST           ENDS
CONST2          SEGMENT WORD PUBLIC USE16 'DATA'
CONST2          ENDS
_DATA           SEGMENT WORD PUBLIC USE16 'DATA'
_DATA           ENDS
                END

The use of the AX register to transfer the address of the const char *s is removed as well as the extra push ax that doesn't have a corresponding pop associated with it.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 1
    This indeed solved the problem. I had some other bugs in my code, e.g. it should use es:di instead of ds:si for scasb. After applying the fix in this answer it was straightforward to fix my code. – pts Jul 12 '20 at 23:30
  • @pts : Yeah, I didn't even look at the actual code other than the bug causing the unbalanced stack, but yes SCASB should be using ES:DI instead of DS:SI. – Michael Petch Jul 12 '20 at 23:43