Documentation Archive Developer
Search

ADC Home > Reference Library > Technical Notes > Legacy Documents > Tools >

Legacy Documentclose button

Important: This document is part of the Legacy section of the ADC Reference Library. This information should not be used for new development.

Current information on this Reference Library topic can be found here:

MPW 2.0.2 Bugs

CONTENTS

This Technical Note describes latest information about bugs or unexpected "features" in the MPW C, Pascal, and Assembler products and the Toolbox and OS Interface Libraries. We intend this Note to be a complete list of all known bugs in these products, which will be updated as old bugs are fixed, or new ones appear. If you have encountered a bug or unexpected feature which is not described here, be sure to let us know. Specific code examples are useful.

[Aug 01 1988]






Introduction

The bugs described in the October 1 revision of this Note will be fixed in the 3.0 release of MPW scheduled for Fall 1988.

Back to top

C Language

The following information applies to the C compiler and its associated libraries shipped with the 2.0.2 version of MPW.

  1. A series of bugs involving floating point array elements and the +=, *=, and = operators. A similar bug was reported as fixed in MPW 2.0.2, unfortunately the fix did not apply to array elements. This bug ONLY occurs when using SANE in combination with float or double variables, it does not occur if the -mc68881 compiler option is specified or if extended variables are used. The following fragment illustrates the bugs:
    main()
    {
        double x[2],y;  /* Also fails if x,y are declared float
                    but succeeds if declared extended */
        x[0] = 0.5;
        y = 5.0;
        x[0] += 2.0*y;
        printf("x[0] = %f\n", x[0]);

        x[0] = 0.5;
        y = 5.0;
        x[0] = x[0] + 2.0*y;
        printf("x[0] = %f\n", x[0]);

        x[0] = 0.5;
        y = 5.0;
        x[0] *= 2.0*y;
        printf("x[0] = %f\n", x[0]);

        x[0] = 0.5;
        y = 5.0;
        x[0] = x[0]*(2.0*y);    /* Succeeds if parenthesis are removed */
        printf("x[0] = %f\n", x[0]);

        exit(0);

This code fragment returns the erroneous values 0.5, 0.5, 0.5, 0.5 (the correct values are 10.5,10.5, 5.0 and 5.0).

Workaround:

If using SANE, use extended variables in these situations.
  1. Taking the address of a floating point formal parameter (function argument) to a function fails. This bug occurs when using either SANE or the -mc68881 compiler option in combination with float or double function arguments, it does not occur if the function arguments are declared extended. The following fragment illustrates two instances of this bug:
    #include <Types.h>
    #include <Math.h>
    #include <stdio.h>

    #define real float    /* Fails with either float or double */

    main()
    {
        Bug1(1.0, 2.0, 3.0);
        Bug2(1.0, 2.0, 3.0);
    }

    Bug1 (x, y, z)
        real x, y, z;
    {
        real *p, *q, *r;

        /* Take address of arguments, assign directly */
        p = &x; q = &y; r = &z;

        fprintf(stderr, "Example 1: Before: %g, %g, %g\n", x, y, z);
        *p = 11.0;
        *q = 12.0;
        *r = 13.0;
        fprintf(stderr, "Example 1: After:  %g, %g, %g\n", x, y, z);
    }

    Bug2(x, y, z)
        real x, y, z;
    {
        fprintf(stderr, "Example 2: Bug2 Before: %g, %g, %g\n",
             x, y, z);

        /* Take address of arguments, assign indirectly */
        foo(&x, &y, &z);
        fprintf(stderr, "Example 2: Bug2 After:  %g, %g, %g\n",
             x, y, z);
    }
    foo(x, y, z)
    real *x, *y, *z;
    {
        fprintf(stderr, "Example 2: foo Before: %g, %g, %g\n",
            *x, *y, *z);
        *x = 11.0;
        *y = 12.0;
        *z = 13.0;
        fprintf(stderr, "Example 2: foo After:  %g, %g, %g\n",
            *x, *y, *z);

This is, in fact, a general problem with C compilers. The underlying reason for this problem is related to the automatic widening and narrowing of basic types performed by C compilers. For instance in C, as defined by K&R (The C Programming Language, Kernighan & Ritchie, 1978, Appendix A Sec. 7.1, p. 186 and Sec. 10.1, P. 206), variables of type char and short are widened to int before being passed as function arguments and narrowed before use inside the function. Similarly, the floating point type float is automatically widened to double before being passed. K&R notes, however, that "C converts all float actual parameters to double, so formal parameters declared float have their declarations adjusted to read double." The value of such a formal parameter is not narrowed to the declared type of the parameter, instead the declared type is adjusted to match that of the widened value. So, in fact, the sample code above will fail if real is defined as float, even on a bug free K&R conforming compiler.

In MPW C, where float and double are widened to extended, the sample code fails for either float or double formal parameters. This can, of course, lead to additional problems if you are porting code from an environment where double was the widened floating point type (where taking the address of a double formal parameter would work as expected).

Workaround:

Taking the address of a function argument is not recommended; you should make a local copy in a temporary variable and take the address of the copy. If you must take the address of a floating point function argument, make sure it is declared as type extended, and the pointer is of type extended*.

  1. The shift operators >> and << can sometimes produce unexpected results when applied to unsigned short and unsigned char operands. The anomaly lies in the fact that the 680x0 LSR.L and LSL.L instructions are used instead of the LSR.W and LSL.W or the LSR.B and LSL.B instructions. The following example illustrates this anomaly:
    main()
    {
        unsigned short u, c;
        short i, k;

        u = 0xFFFF;
        k = 8;

        i = (u << k)/256;
        printf("unsigned short: i = %d, %#x\n", i, i);

        c = 256;
        i = (u << k)/c;
        printf("unsigned short: i = %d, %#x\n", i, i);

This code fragment returns the values -1, 0xFFFFFFFF and -1, 0xFFFFFFFF, which are the correct values as defined by the C language, however, you might be expecting 255, 0xFF and 255, 0xFF.

  1. The compiler optimization flags -q (sacrifice code size for speed) and -q2 (memory locations won't change except by explicit stores) can produce incorrect code. We have several vague descriptions of problems, and are looking for more specific examples. The following example illustrates a specific bug that occurs when using enumerated types:
    badfunc(bool, i)
        Boolean *bool;
        int i;
    {
        while (true) {
            if(i < 3) {
                *bool  = true;
                if (func(1)) return;
            }
             if (func(i)) return;
        }

The enumerated type here is the type used to define the values true and false in the header file Types.h. The optimizer is apparently confused by the fact that true has the char value 1, which it thinks is the same as the int value 1 to be passed to the function func(). The object code produced for the two calls to the function func() is:

    ...
    MOVEQ    #$01,D3    ; Move the constant 1 into a register
    ...
    MOVE.B    D3,(A2)     ; Assign "true" to variable bool
    MOVE.B    D3,-(A7)    ; Attempt to push integer 1 onto stack
            ; ERROR should be MOVE.L !!!!!
    JSR    *+$0002    ; JSR to func
    ...
    MOVE.L    D4,-(A7)     ; Correctly push integer variable i onto stack
    JSR    *+$0002     ; JSR to func

In the first function call, the int constant 1 is passed as a byte value! Since the stack is correctly adjusted after the first call this error may go undetected (except that the called function may spot the resulting nonsensical parameter). This problem is, of course, not limited to the enumerated type defining true and false, but can occur as a side effect of any of the many enumerated types defined in the Toolbox header files.

Workaround:

The best solution, for now, is to avoid using the optimization flags -q and -q2 altogether.

  1. The compiler flag -mc68020 (generate MC68020 instructions) generates inconsistent code when passing structures by value. Specifically, structures larger than 4 bytes that are not a multiple of 4 bytes are padded out to the nearest 4 byte multiple before being pushed onto the stack by the calling routine. Unfortunately, the called routine does not take this padding into account when accessing its function arguments. The following example illustrates this bug:
    #include <Types.h>
    #include <strings.h>

    typedef struct         /* 6 byte long structure */
    {
      short Temp1;
      short Temp2;
      short Temp3;
    } TestStruct;

    main()
    {
      TestStruct reply;

      foo(reply,"Hello world.\n");
    }


    foo(tst ,str)
      TestStruct tst;
      char *str;
    {
      Debugger();    /* So we can look, before stepping off the cliff */
      printf("%s",str);

Since function arguments are pushed onto the stack in left to right order in C, the pointer to the string constant "Hello world.\n" is pushed onto the stack before the padded contents of the reply structure. Thus when the called function, foo(), goes to fetch the argument str, it looks at the location just beyond where the argument tst is located. Unfortunately, since the called function does not know the structure argument was padded, it will not find the correct value for the second argument (or in the general case, any arguments following the structure).

Workaround:

When using the -mc68020 compiler option, either don't pass structures larger than 4 bytes by value, or make sure all structures larger than 4 bytes are padded out to a multiple of 4 bytes.
  1. Switch statements with non-integer controlling expressions can fail. The following example illustrates the problem:
    #include <stdio.h>
    #include <strings.h>
    main()
    {
        unsigned short w = 1;

        printf("The following test fails in MPW\n");
        printf("Should switch to 1; actually got to ");

        switch (w) {
            case 1:
                printf("1\n");
                break;
            case 5:
                printf("5\n");
                break;
            case 32771:
                printf("32771\n");
                break;
            case 32773:
                printf("32773\n");
                break;
            default:
                printf("default\n");
                break;
        }

In this example, instead of reaching case 1: in the switch statement, the default: case is reached due to the fact that the compiler generates comparison instructions based on word length values. The 1st Edition of K&R (The C Programming Language, Kernighan & Ritchie, 1978, Appendix A Sec. 9.7, p202) requires all case constants to have integer type and the controlling expression to be coerced to integer type. The 2nd Edition (ANSI) of K&R (The C Programming Language, Kernighan & Ritchie, 1988, Appendix A Sec. 9.4, p223) requires that the controlling expression and all case constants undergo "integral promotion"--promotion to int (or to unsigned int if necessary). MPW 2.0.2 C fails to promote either the controlling expression or the case constants to type int.

Workaround:

If the controlling expression is manually coerced to type int this example functions correctly.

  1. Variable declarations inside the body of switch statements, when combined with the -g compiler flag, can cause the compiler's code generator to abort with the message: "Code generator abort code 615." The following example illustrates the problem:
    foo(i)
        int i;
    {
        switch (i) {
            int j;   /* VARIABLE DECLARATION INSIDE BODY OF SWITCH */


            case 0:
                j = 22 +i;
                printf("INSIDE: i=%d, j=%d\n", i, j);
                break;
            case 1:
                j = 57 -i;
                printf("INSIDE: i=%d, j=%d\n", i, j);
                break;
            default:
                j = i;
                printf("INSIDE: i=%d, j=%d\n", i, j);
                break;
        }

While such a declaration is perfectly legal C, it is a bad practice (The C Programming Language, Kernighan & Ritchie, 1978, Appendix A Sec. 9.2, p201). K&R go on to point out that if the declaration of the variable contains an initializer, the initialization will NOT take place. This is also the ANSI draft C standard's interpretation.

Workaround:

Since the -g option is a very useful debugging option, move the variable declaration outside the body of the switch statement.

  1. Compatibility Note: Local variable declarations of the form char s[]; (as an array of unspecified length), may not behave as expected. Pre-ANSI compilers often expand such declarations into the equivalent of an extern declaration. MPW 2.0.2 C, and most modern compilers, treat this construct as the declaration of a new array, thus overriding any global declaration with the same name. In the following example:
    /* From file foo.c */
    char s[255];

    main()
    {
        strcpy(s, "This is one heck of a mess");
        otherfunc();
    }


    /* From file bar.c */
    otherfunc()
    {
        char s[];

        printf("%s\n", s);

garbage is printed. As a local variable declaration, this declaration is incomplete since no length is specified or implied, so an ANSI C compiler will of course fail. This is obviously not a recommended programming practice, but if you are porting old C code you may encounter this usage.

Workaround:

ALWAYS use the declaration extern char s[]; instead.
  1. Another instance where declarations of the form type s[]; (as an array of unspecified length) may not behave as expected, is as a member of a struct or union. Pre ANSI compilers often expand such declarations into the equivalent of a pointer declaration. This construct is explicitly prohibited in ANSI C structures and unions. MPW 2.0.2 C on the other hand, issues no warning and treats this construct as the declaration of an array of length zero, which occupies no space in the struct. In the following example:
    typedef struct  ST1  {
            int array1[];    /* Zero length array or Ptr to array? */
            int array2[];
            int array3[];
    }ST1;

    main()
    {
        ST1 s1;
        int i1, i2, i3;

        i1 = s1.array1[0];
        i2 = s1.array2[0];
        i3 = s1.array3[0];

the three fields of the struct ST1 are located at the same memory location, and the assignments shown will actually copy garbage into the integers, since no space was allocated for even the first element of the arrays. Unfortunately, structures containing an array of unspecified or zero length as the final member are sometimes used to indicate a structure containing a variable length array. While this may be useful, it is not tolerated by ANSI C and thus is not a recommended programming practice. However, if you are porting old C code you may encounter this usage.

Workaround:

ALWAYS use the declarations of the form type *s;, type (*s)[]; , or type s[1] (depending on the intended meaning) in structures and unions instead.
  1. The routines _SetUpA5 and _RestoreA5 described in the OS Utilities Chapter of Inside Macintosh, Volume II, are missing. Refer to M.OV.A5 for two new routines which solve this problem.
  2. Hint: Switch statements with large numbers of cases (over 100 or so) can trigger the appearance of the MPW Bulldozer cursor (signaling heap purging and compacting in progress), can cause "Out of memory" errors from the compiler, or at least take a very long time to compile.

Workaround:

Large switch statements can be split up into smaller ones to avoid these symptoms.

Back to top

Pascal Language

The following information applies to the Pascal compiler and its associated libraries shipped with the 2.0.2 version of MPW.

  1. The Pascal compiler generates incorrect code when accessing the elements of packed Boolean arrays with more than 32767 elements. The generated code contains an ASR.W instruction to compute the byte offset into the array, this of course fails for numbers larger than 32767 ($7FFF). The following example, which zeroes bits far beyond the end of the array itself, illustrates the error:
    PROGRAM PackArrayBug;

        VAR
          MyBits:    packed array[0..33000] of Boolean;

          PROCEDURE BadCode;
          VAR
            i: longint;
          BEGIN
            for i := 0 to 33000 do
              MyBits[i] := false;
          END;

        BEGIN
            BadCode

Workaround:

Don't use packed Boolean arrays with more than 32767 elements.

  1. The Pascal compiler fails to detect the situation where a procedure name is passed as an argument to a routine expecting a function as an argument. The following example illustrates the error:
    PROGRAM FuncArgBug;

          PROCEDURE ExpectsFunc(x: integer; function Visit(y1: longint;
                 y2: char;): Boolean);
          VAR
            result: Boolean;
            yc: char;
          BEGIN
            yc := 'A';
            result := Visit(x, yc);
          END;

          PROCEDURE FormalProc(y1: longint; y2: char;);
          BEGIN
            writeln(y1: 1, ' ', y2);
          END;

        BEGIN
            ExpectsFunc(5, FormalProc);

This type of problem typically leads to stack misalignment and spectacular crashes.

Workaround:

Make certain that Pascal routines expecting functions as arguments are indeed passed functions.

  1. The -mc68881 option causes the Pascal compiler to generate incorrect code when calling external C language routines with floating point extended arguments. Apparently the compiler miscalculates the size of the extended argument so that it incorrectly removes the arguments from the stack. The following example, which can corrupt the local variables of its caller, illustrates the error:
    FUNCTION E2S(f: Str255; x: extended): Str255;
       VAR
        dummy: integer;
        i:     integer;
        t:     Str255;

      FUNCTION sprintf(str: Ptr; fmt: Ptr; x: extended):
            integer; C; EXTERNAL;

      BEGIN
        t[0] := chr(0);
        f[ord(f[0]) +1] := chr(0);

        dummy := sprintf(@t[1], @f[1], x);

        i := 0;
        repeat
          i := i+1;
        until ((t[i] = chr(0)) or (i > 254));
        t[0] := chr(i);

        E2S := t;

The relevant portions of the generated code are:

    LINK       A6,#$FDF0    ; LINK for local vars
    MOVEM.L    D6/D7,-(A7)    ; Save Registers used here
    ...
    ...
    LEA        $FF00(A6),A0    ; Get address of extended var
    MOVE.L     -(A0),-(A7)    ; Push extended onto stack
    MOVE.L     -(A0),-(A7)
    MOVE.L     -(A0),-(A7)
    PEA        $FF01(A6)    ; Push address of format str
    PEA        $FDF1(A6)    ; Push address of target str
    JSR        *+$0002    ; JSR to sprintf
    LEA        $0012(A7),A7    ; Pop args off stack
        ; (SHOULD pop off $14 bytes!)
    MOVE.W     D0,D6    ; Save function result
    ...
    ...
    MOVEM.L    (A7)+,D6/D7    ; Restore registers used here
        ;  (Now they've been corrupted!)
    UNLK       A6    ; Correctly restores A7
    MOVEA.L    (A7)+,A0
    ADDQ.W     #$8,A7

Notice that this code would have succeeded if the routine had not used the D6 and D7 registers for storage, and then restored them (incorrectly) before returning.

Workaround:

When calling such a routine with the -mc68881 option, isolate the call in a small subroutine or function that has no local variables, so that registers will not need to be saved and restored.

  1. The {$SC+} compiler flag for short circuiting AND and OR operators can sometimes produce incorrect code. The following example does not work if {$SC+} has been used:
    USES
      Memtypes,QuickDraw,OSIntf,ToolIntf,PackIntf;

    VAR
      b1,b2,b3 : BOOLEAN;

    Begin
      b1 := false;
      b2 := true;
      b3 := true;

      if not (b1 and b2) and b3 then
          SysBeep(40);

Workaround:

Don't use the {$SC+} compiler flag.
  1. The Pascal compiler generates incorrect overflow checking code for longint valued arguments to the Odd function. The generated code contains a signed divide (DIVS) by 1 followed by a TRAPV, thus the overflow flag is set for values greater than $7FFF. The following example will fail with a CHK exception, unless the {$OV+} directive is removed:
    {$OV+}
    PROGRAM PascalOdd;

        VAR
            IsOdd:    Boolean;
            longval: LONGINT;

        BEGIN
            longval := 123456;
            IsOdd := Odd(longval);

Workaround:

Don't use the {$OV+} compiler flag if you pass longint values to the Odd function.

  1. The Pascal compiler generates incorrect code when functions with RECORD type are used as the object of a WITH statement. This will only occur if the function is called at a level below the main program, and if the length of the RECORD type is 4 bytes or less. The generated code often contains an LEA (A7)+,an instruction, which is of course illegal. The following example demonstrates this unusual situation:
    PROGRAM PasRecordValFuncBug;

      TYPE
        OurRecord   =
          RECORD
            a:    Integer;
          END;

      FUNCTION RecordValuedFunction: OurRecord;
      BEGIN
      END;

      PROCEDURE ContainsBadCode;

      BEGIN
        WITH RecordValuedFunction DO BEGIN  { This usage bad code. }
          a:=a;
        END;
      END;

    BEGIN { PasRecordValFuncBug }
      ContainsBadCode;
      WITH RecordValuedFunction DO BEGIN  { This usage is okay. }
        a:=a;
      END;

Workaround:

Don't use RECORD valued functions as the object of WITH statements.

Back to top

Assembly Language

The following information applies to the Assembler and its associated libraries shipped with the 2.0.2 version of MPW.

  1. There are no known outstanding bugs in the MPW Assembler.

Back to top

Interface Libraries

The following information applies to the Toolbox and OS interface libraries shipped with the 2.0.2 version of MPW.

  1. The glue for the Device Manager call GetDCtlValue [Not in ROM] described in Inside Macintosh, Volume III, is incorrect and will return an incorrect value for the handle to the driver's device control entry. The following is a corrected version of the erroneous glue found in the library file Interface.o:
    ;-----------------------------------------------------
    ;FUNCTION GetDCtlEntry(refNum: Integer) : DCtlHandle;
    ;-----------------------------------------------------
    GetDCtlEntry PROC EXPORT
        MOVEA.L    (SP)+,A0        ;Get the return address
        MOVE.W    (SP)+,D0        ;Get the refNum
        ADDQ.W    #$1,D0        ;Change to a
        NEG.W    D0            ;  Unit Number
    ;===>    LSR.W    #$2,D0        ;==Shift in wrong direction!
        LSL.W    #$2,D0        ;Times 4 bytes/entry
        MOVEA.L    UTableBase,A1    ;Get address of unit table
        MOVE.L    (A1,D0.W),(SP)    ;Get the DCtlHandle

This error will affect C, Pascal, and assembly language users.

Workaround:

Use the corrected glue for GetDCtlValue.

  1. The glue for the register-based Vertical Retrace Manager calls _SlotVInstall and _SlotVRemove described in Inside Macintosh, Volume V, is incorrect. The following are corrected versions of the erroneous glue for these routines found in the library file Interface.o:
    ;-----------------------------------------------------------------------
    ;FUNCTION SlotVInstall(vblTaskPtr: QElemPtr; theSlot: Integer): OSErr;
    ;-----------------------------------------------------------------------
    SlotVInstall PROC EXPORT
        MOVEA.L    (A7)+,A1    ; save return address
        MOVE.W    (A7)+,D0    ; the slot number
        MOVEA.L    (A7)+,A0    ; the VBL task ptr
        _SlotVInstall
        MOVE.W    D0,(A7)    ; save result code on stack
        JMP     (A1)    ; return to caller
        ENDPROC

    ;----------------------------------------------------------------------
    ;FUNCTION SlotVRemove(vblTaskPtr: QElemPtr; theSlot: Integer): OSErr;
    ;----------------------------------------------------------------------
    SlotVRemove PROC EXPORT
        MOVEA.L    (A7)+,A1    ; save return address
        MOVE.W    (A7)+,D0    ; the slot number
        MOVEA.L    (A7)+,A0    ; the VBL task ptr
        _SlotVRemove
        MOVE.W    D0,(A7)    ; save result code on stack
        JMP    (A1)    ; return to caller

These errors will affect C, Pascal, and assembly language users.

Workaround:

Use the corrected glue for _SlotVInstall and _SlotVRemove.

  1. The glue for the register based Start Manager calls _GetTimeout and _SetTimeout described in Inside Macintosh, Volume V, is incorrect . The following are corrected versions of the erroneous glue for these routines found in the library file Interface.o:
    ;-----------------------------------------------------------------------
    ;PROCEDURE GetTimeout(VAR count: INTEGER);
    ;-----------------------------------------------------------------------
    GetTimeout PROC EXPORT
    ;===>    CLR.W      -(A7)        ;===OOPS, selector in A0 not on stack
        SUBA.L     A0,A0        ;Put selector in A0, i.e. 0
        _InternalWait
        MOVEA.L    (A7)+,A1    ;Pop return address into A1
        MOVEA.L    (A7)+,A0    ;Pop location for VAR count
        MOVE.W     D0,(A0)    ;Stuff returned value into count
        JMP        (A1)        ;And go home

    ;----------------------------------------------------------------------
    ; PROCEDURE SetTimeout(count: INTEGER);
    ;----------------------------------------------------------------------
    SetTimeout PROC EXPORT
        MOVEA.L    (A7)+,A1     ;Pop return address into A1
        MOVE.W     (A7),D0    ;Move count parameter into D0
    ;===>    MOVE.W     #$0001,(A7) ;===OOPS, selector in A0 not on stack
        MOVEA.W    #$0001,A0     ;Put selector in A0
        _InternalWait

These errors will affect C, Pascal, and assembly language users.

Workaround:

Use the corrected glue for _GetTimeout and _SetTimeout.

Back to top

References

M.OV.A5

Back to top

Downloadables

Acrobat gif

Acrobat version of this Note (K).

Download