Fixed numerous bitfield problems.
authorepetrich <epetrich@4a8a32a2-be11-0410-ad9d-d568d2c75423>
Thu, 14 Aug 2003 03:15:23 +0000 (03:15 +0000)
committerepetrich <epetrich@4a8a32a2-be11-0410-ad9d-d568d2c75423>
Thu, 14 Aug 2003 03:15:23 +0000 (03:15 +0000)
* src/SDCC.y: More bitfield related error checking
* src/SDCCsymt.h,
* src/SDCCsymt.c (compStructSize): fixed bitfield offset calc
* support/Util/SDCCerr.h,
* support/Util/SDCCerr.c: Added & edited some bitfield err msgs
* src/mcs51/gen.c (genPackBits, genUnpackBits): fixed mask bugs
* src/ds390/gen.c (genPackBits, genUnpackBits): fixed mask bugs
* support/regression/tests/bitfields.c: tests added

git-svn-id: https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc@2826 4a8a32a2-be11-0410-ad9d-d568d2c75423

ChangeLog
src/SDCC.y
src/SDCCsymt.c
src/SDCCsymt.h
src/ds390/gen.c
src/mcs51/gen.c
support/Util/SDCCerr.c
support/Util/SDCCerr.h
support/regression/tests/bitfields.c

index cb3c009fef9e3ad66f8226613bc10907abf77475..0b42a0fa3ef49d7f864af859a72aefe18cd65eb8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2003-08-14  Erik Petrich <epetrich@ivorytower.norman.ok.us>
+
+       Fixed numerous bitfield problems.
+
+       * src/SDCC.y: More bitfield related error checking
+       * src/SDCCsymt.h,
+       * src/SDCCsymt.c (compStructSize): fixed bitfield offset calc
+       * support/Util/SDCCerr.h,
+       * support/Util/SDCCerr.c: Added & edited some bitfield err msgs
+       * src/mcs51/gen.c (genPackBits, genUnpackBits): fixed mask bugs
+       * src/ds390/gen.c (genPackBits, genUnpackBits): fixed mask bugs
+       * support/regression/tests/bitfields.c: tests added
+       
+2003-08-13  Erik Petrich <epetrich@ivorytower.norman.ok.us>
+
+       Made the constant following the "interrupt" keyword optional. If
+       omitted, the function will not automatically be given an entry
+       in the interrupt vector table (similar to #pragma NOIV, but
+       less syntacticly kludgy). The interrupt number is also now
+       range checked. Also fixed a bug in the high order bit example
+       in the manual.
+
+       * src/SDCC.y
+       * src/SDCCmem.c
+       * src/SDCCglue.c
+       * src/SDCCsymt.h
+       * support/Util/SDCCerr.c
+       * support/Util/SDCCerr.h
+       * doc/sdccman.lyx
+
 2003-08-13  Bernhard Held <bernhard@bernhardheld.de>
 
        * src/SDCCcse.c (algebraicOpts): fix bug converting op from value to type
index 564837930411bdd6b7d8739ea231284dd3f10c76..8107bd016ce0ef957f53a4c07d99cc09f5587798 100644 (file)
@@ -788,13 +788,33 @@ struct_declarator_list
 
 struct_declarator
    : declarator 
-   | ':' constant_expr  {  
+   | ':' constant_expr  {
+                           int bitsize;
                            $$ = newSymbol (genSymName(NestLevel),NestLevel) ; 
-                           $$->bitVar = (int) floatFromVal(constExprValue($2,TRUE));
+                           bitsize= (int) floatFromVal(constExprValue($2,TRUE));
+                           if (bitsize > (port->s.int_size * 8)) {
+                             bitsize = port->s.int_size * 8;
+                             werror(E_BITFLD_SIZE, bitsize);
+                           }
+                           if (!bitsize)
+                             bitsize = BITVAR_PAD;
+                           $$->bitVar = bitsize;
                         }                        
    | declarator ':' constant_expr 
-                        { 
-                         $1->bitVar = (int) floatFromVal(constExprValue($3,TRUE));                     
+                        {
+                          int bitsize;
+                          bitsize= (int) floatFromVal(constExprValue($3,TRUE));
+                          if (bitsize > (port->s.int_size * 8)) {
+                            bitsize = port->s.int_size * 8;
+                            werror(E_BITFLD_SIZE, bitsize);
+                          }
+                          if (!bitsize) {
+                            $$ = newSymbol (genSymName(NestLevel),NestLevel) ; 
+                            $$->bitVar = BITVAR_PAD;
+                            werror(W_BITFLD_NAMED);
+                          }
+                         else
+                           $1->bitVar = bitsize;
                         }
    ;
 
index 7b3b6f584df7c4e99041aa8523d06b735667f87a..3ba40fc609554b9689f7dd5a34a1738eec757cf0 100644 (file)
@@ -1066,7 +1066,10 @@ compStructSize (int su, structdef * sdef)
 
        /* create the internal name for this variable */
        SNPRINTF (loop->rname, sizeof(loop->rname), "_%s", loop->name);
-       loop->offset = (su == UNION ? sum = 0 : sum);
+       if (su == UNION) {
+           sum = 0;
+           bitOffset = 0;
+       }
        SPEC_VOLATILE (loop->etype) |= (su == UNION ? 1 : 0);
 
        /* if this is a bit field  */
@@ -1075,46 +1078,76 @@ compStructSize (int su, structdef * sdef)
            /* change it to a unsigned bit */
            SPEC_NOUN (loop->etype) = V_BIT;
            SPEC_USIGN (loop->etype) = 1;
-           /* check if this fit into the remaining   */
-           /* bits of this byte else align it to the */
-           /* next byte boundary                     */
-           if ((SPEC_BLEN (loop->etype) = loop->bitVar) <= (8 - bitOffset)) {
-               SPEC_BSTR (loop->etype) = bitOffset;
-               if ((bitOffset += (loop->bitVar % 8)) == 8)
-                   sum++;
-           }
-           else /* does not fit */ {
-               bitOffset = 0;
-               SPEC_BSTR (loop->etype) = bitOffset;
-               sum += (loop->bitVar / 8);
-               bitOffset += (loop->bitVar % 8);
+           SPEC_BLEN (loop->etype) = loop->bitVar;
+
+           if (loop->bitVar == BITVAR_PAD) {
+               /* A zero length bitfield forces padding */
+               SPEC_BSTR (loop->etype) = bitOffset;
+               SPEC_BLEN (loop->etype) = 0;
+               bitOffset = 8;
+               loop->offset = sum;
            }
-           /* if this is the last field then pad */
-           if (!loop->next && bitOffset && bitOffset != 8) {
-               bitOffset = 0;
-               sum++;
+           else {
+               if (bitOffset == 8) {
+                   bitOffset = 0;
+                   sum++;
+               }
+               /* check if this fit into the remaining   */
+               /* bits of this byte else align it to the */
+               /* next byte boundary                     */
+               if (loop->bitVar <= (8 - bitOffset)) {
+                   /* fits into current byte */
+                   loop->offset = sum;
+                   SPEC_BSTR (loop->etype) = bitOffset;
+                   bitOffset += loop->bitVar;
+               }
+               else if (!bitOffset) {
+                   /* does not fit, but is already byte aligned */
+                   loop->offset = sum;
+                   SPEC_BSTR (loop->etype) = bitOffset;
+                   bitOffset += loop->bitVar;
+               } 
+               else {
+                   /* does not fit; need to realign first */
+                   sum++;
+                   loop->offset = (su == UNION ? sum = 0 : sum);
+                   bitOffset = 0;
+                   SPEC_BSTR (loop->etype) = bitOffset;
+                   bitOffset += loop->bitVar;
+               }
+               while (bitOffset>8) {
+                   bitOffset -= 8;
+                   sum++;
+               } 
            }
        }
        else {
+           /* This is a non-bit field. Make sure we are */
+           /* byte aligned first */
+           if (bitOffset) {
+               sum++;
+               loop->offset = (su == UNION ? sum = 0 : sum);
+               bitOffset = 0;
+           }
+           loop->offset = sum;
            checkDecl (loop, 1);
            sum += getSize (loop->type);
        }
 
        loop = loop->next;
 
-       /* if this is not a bitfield but the */
-       /* previous one was and did not take */
-       /* the whole byte then pad the rest  */
-       if ((loop && !loop->bitVar) && bitOffset) {
-           bitOffset = 0;
-           sum++;
-       }
-
        /* if union then size = sizeof larget field */
-       if (su == UNION)
+       if (su == UNION) {
+           /* For UNION, round up after each field */
+           sum += ((bitOffset+7)/8);
            usum = max (usum, sum);
+       }
 
     }
+    
+    /* For STRUCT, round up after all fields processed */
+    if (su != UNION)
+       sum += ((bitOffset+7)/8);
 
     return (su == UNION ? usum : sum);
 }
index cbedb3aae910495b959af6bb96f59c8e4f763447..3c8bf447b7cc153a85b4d282a72e1739e46c21fe 100644 (file)
@@ -33,6 +33,8 @@
 #define INTNO_MAX 255                  /* maximum allowed interrupt number */
 #define INTNO_UNSPEC (INTNO_MAX+1)     /* interrupt number unspecified */
 
+#define BITVAR_PAD -1
+
 enum {
     TYPEOF_INT=1,
     TYPEOF_SHORT,
index 1686e093e38fa7383285b84f672376671b5ff31a..a5b88a727074fcfea5f1604816c9bd147b00dcf8 100644 (file)
@@ -8979,13 +8979,15 @@ static void
 genUnpackBits (operand * result, char *rname, int ptype)
 {
   int shCnt;
-  int rlen;
+  int rlen = 0;
   sym_link *etype;
   int offset = 0;
+  int rsize;
 
   D (emitcode (";", "genUnpackBits "););
 
   etype = getSpec (operandType (result));
+  rsize = getSize (operandType (result));
 
   /* read the first byte  */
   switch (ptype)
@@ -9025,12 +9027,12 @@ genUnpackBits (operand * result, char *rname, int ptype)
 
       emitcode ("anl", "a,#!constbyte",
                ((unsigned char) -1) >> (8 - SPEC_BLEN (etype)));
-      aopPut (AOP (result), "a", offset);
-      return;
+      aopPut (AOP (result), "a", offset++);
+      goto finish;
     }
 
   /* bit field did not fit in a byte  */
-  rlen = SPEC_BLEN (etype) - 8;
+  rlen = SPEC_BLEN (etype);
   aopPut (AOP (result), "a", offset++);
 
   while (1)
@@ -9077,11 +9079,17 @@ genUnpackBits (operand * result, char *rname, int ptype)
 
   if (rlen)
     {
-      emitcode ("anl", "a,#!constbyte", ((unsigned char) -1) >> (rlen));
-      aopPut (AOP (result), "a", offset);
+      emitcode ("anl", "a,#!constbyte", ((unsigned char) -1) >> (8-rlen));
+      aopPut (AOP (result), "a", offset++);
     }
 
-  return;
+finish:
+  if (offset < rsize)
+    {
+      rsize -=offset;
+      while (rsize--)
+        aopPut (AOP (result), zero, offset++);
+    }
 }
 
 
@@ -9760,6 +9768,7 @@ genPackBits (sym_link * etype,
             operand * right,
             char *rname, int p_type)
 {
+  int shCount = 0;
   int offset = 0;
   int rLen;
   int blen, bstr;
@@ -9774,12 +9783,17 @@ genPackBits (sym_link * etype,
   /* it exactly fits a byte then         */
   if (SPEC_BLEN (etype) <= 8)
     {
+      unsigned char mask = ((unsigned char) (0xFF << (blen + bstr)) |
+                            (unsigned char) (0xFF >> (8 - bstr)));
+      shCount = SPEC_BSTR (etype);
+      
       /* shift left acc */
-      AccLsh (SPEC_BSTR (etype));
+      AccLsh (shCount);
 
       if (SPEC_BLEN (etype) < 8)
        {                       /* if smaller than a byte */
 
+          emitcode ("anl", "a,#0x%02x", (~mask) & 0xff);
 
          switch (p_type)
            {
@@ -9801,9 +9815,7 @@ genPackBits (sym_link * etype,
              break;
            }
 
-         emitcode ("anl", "a,#!constbyte", (unsigned char)
-                   ((unsigned char) (0xFF << (blen + bstr)) |
-                    (unsigned char) (0xFF >> (8 - bstr))));
+         emitcode ("anl", "a,#!constbyte", mask);
          emitcode ("orl", "a,b");
          if (p_type == GPOINTER)
            emitcode ("pop", "b");
@@ -9872,6 +9884,9 @@ genPackBits (sym_link * etype,
   /* last last was not complete */
   if (rLen)
     {
+      emitcode ("anl", "a,#!constbyte",
+                (~(((unsigned char) -1 << rLen) & 0xff)) & 0xff);
+
       /* save the byte & read byte */
       switch (p_type)
        {
@@ -9893,7 +9908,7 @@ genPackBits (sym_link * etype,
          break;
        }
 
-      emitcode ("anl", "a,#!constbyte", ((unsigned char) -1 << rLen));
+      emitcode ("anl", "a,#!constbyte", (((unsigned char) -1 << rLen) & 0xff));
       emitcode ("orl", "a,b");
     }
 
index 6825ec2eda447731b3ecc62beeaa6e979f385901..983c5a1d0419d38e990c8d7dc9331e21f74a4df3 100644 (file)
@@ -7220,8 +7220,7 @@ genUnpackBits (operand * result, char *rname, int ptype)
 
   if (rlen)
     {
-      //  emitcode("anl","a,#0x%02x",((unsigned char)-1)>>(rlen));
-      AccLsh (8 - rlen);
+      emitcode ("anl", "a,#0x%02x", ((unsigned char) -1) >> (8-rlen));
       aopPut (AOP (result), "a", offset++, isOperandVolatile (result, FALSE));
     }
 
@@ -7232,7 +7231,6 @@ finish:
       while (rsize--)
        aopPut (AOP (result), zero, offset++, isOperandVolatile (result, FALSE));
     }
-  return;
 }
 
 
@@ -7748,6 +7746,8 @@ genPackBits (sym_link * etype,
   /* it exactly fits a byte then         */
   if (SPEC_BLEN (etype) <= 8)
     {
+      unsigned char mask = ((unsigned char) (0xFF << (blen + bstr)) |
+                           (unsigned char) (0xFF >> (8 - bstr)));
       shCount = SPEC_BSTR (etype);
 
       /* shift left acc */
@@ -7756,6 +7756,7 @@ genPackBits (sym_link * etype,
       if (SPEC_BLEN (etype) < 8)
        {                       /* if smaller than a byte */
 
+         emitcode ("anl", "a,#0x%02x", (~mask) & 0xff);
 
          switch (p_type)
            {
@@ -7777,9 +7778,7 @@ genPackBits (sym_link * etype,
              break;
            }
 
-         emitcode ("anl", "a,#0x%02x", (unsigned char)
-                   ((unsigned char) (0xFF << (blen + bstr)) |
-                    (unsigned char) (0xFF >> (8 - bstr))));
+         emitcode ("anl", "a,#0x%02x", mask);
          emitcode ("orl", "a,b");
          if (p_type == GPOINTER)
            emitcode ("pop", "b");
@@ -7848,6 +7847,9 @@ genPackBits (sym_link * etype,
   /* last last was not complete */
   if (rLen)
     {
+      emitcode ("anl", "a,#0x%02x",
+                (~(((unsigned char) -1 << rLen) & 0xff)) &0xff);
+
       /* save the byte & read byte */
       switch (p_type)
        {
@@ -8633,8 +8635,8 @@ genCast (iCode * ic)
   aopOp (right, ic, FALSE);
   aopOp (result, ic, FALSE);
 
-  /* if the result is a bit */
-  if (IS_BITVAR(OP_SYMBOL(result)->type))
+  /* if the result is a bit (and not a bitfield) */
+  if (AOP_TYPE (result) == AOP_CRY)
     {
       /* if the right size is a literal then
          we know what the value is */
@@ -8662,6 +8664,7 @@ genCast (iCode * ic)
       goto release;
     }
 
+
   /* if they are the same size : or less */
   if (AOP_SIZE (result) <= AOP_SIZE (right))
     {
index 925b81c2b9f83b729d8f138580b1fb1be9eacea5..c48877bbf811486cc0ef18556699488ac72948ee 100644 (file)
@@ -115,7 +115,7 @@ struct
 { E_INT_REQD, ERROR_LEVEL_ERROR,
    "type must be INT for bit field definition" },
 { E_BITFLD_SIZE, ERROR_LEVEL_ERROR,
-   "bit field size greater than 16. assuming 16" },
+   "bit field size cannot be greater than int (%d bits)" },
 { W_TRUNCATION, ERROR_LEVEL_WARNING,
    "high order truncation might occur" },
 { E_CODE_WRITE, ERROR_LEVEL_ERROR,
@@ -391,6 +391,8 @@ struct
     "useless declaration (possible use of keyword as variable name)" },
 { E_INT_BAD_INTNO, ERROR_LEVEL_ERROR,
     "interrupt number '%u' is not valid" },
+{ W_BITFLD_NAMED, ERROR_LEVEL_WARNING,
+    "ignoring declarator of 0 length bitfield" },
 };
 
 /*
index 221ca0b20af6b288f7b86be1a170dfb6044d847a..30bb73ea64e6006cd42cdb70a27f12c509177aaa 100644 (file)
@@ -183,6 +183,7 @@ SDCCERR - SDCC Standard error handler
 #define W_INT_OVL 165               /* integer overflow in expression */
 #define W_USELESS_DECL 166         /* useless declaration */
 #define E_INT_BAD_INTNO 167         /* invalid interrupt number */
+#define W_BITFLD_NAMED 168          /* declarator used with 0 length bitfield */
 
 /** Describes the maximum error level that will be logged.  Any level
  *  includes all of the levels listed after it.
index 13f81b9ae9570c6b43e409fe79ac45499ea81ff3..fb106afd0d2774f050d71176f6741729c92ae2d3 100644 (file)
 struct {
   char c0_3 : 3;
   char c3_5 : 5;
-} c_bitfield;
+} c_bf;
 
 struct {
   int i0_7 : 7;
   int i7_9 : 9;
-} i_bitfield;
+} i_bf;
 
 struct {
   long l0_7 : 7;
   long l7_10 : 10;
   long l17_15 : 15;
-} l_bitfield;
+} l_bf;
+
+
+struct {
+  unsigned int b0 : 1;
+  unsigned int b1 : 1;
+  unsigned int b2 : 1;
+  unsigned int b3 : 1;
+  unsigned int b4 : 1;
+  unsigned int b5 : 1;
+  unsigned int b6 : 1;
+  unsigned int b7 : 1;
+  unsigned int b8 : 1;
+  unsigned int b9 : 1;
+} sb_bf;
+
+struct {
+  unsigned int b0 : 1;
+  unsigned int b2 : 1;
+} size1a_bf;
+
+struct {
+  unsigned int b0 : 1;
+  unsigned int b1 : 1;
+  unsigned int    : 0;
+} size1b_bf;
+
+struct {
+  unsigned int b0 : 1;
+  unsigned int b1 : 1;
+  unsigned int b2 : 6;
+} size1c_bf;
+
+struct {
+  unsigned int b0 : 1;
+  unsigned int    : 0;
+  unsigned int b1 : 1;
+} size2a_bf;
+
+struct {
+  unsigned int b0 : 1;
+  unsigned int b1 : 1;
+  unsigned int b2 : 1;
+  unsigned int b3 : 1;
+  unsigned int b4 : 1;
+  unsigned int b5 : 1;
+  unsigned int b6 : 1;
+  unsigned int b7 : 1;
+  unsigned int b8 : 1;
+  unsigned int b9 : 1;
+} size2b_bf;
+
+struct {
+  unsigned int b0 : 4;
+  unsigned int b1 : 5;
+} size2c_bf;
+
+struct {
+  unsigned int b0 : 12;
+  unsigned int b1 : 3;
+} size2d_bf;
+
+struct {
+  unsigned int b0 : 3;
+  unsigned int b1 : 12;
+} size3a_bf;
+
+
+void
+testBitfieldSizeof(void)
+{
+  /* Although bitfields are extremely implementation dependant, these
+     assertions should hold for all implementations with storage units
+     of 8 bits or larger (nearly universal).
+  */
+  ASSERT( sizeof(size1a_bf) >= 1);
+  ASSERT( sizeof(size1b_bf) >= 1);
+  ASSERT( sizeof(size1c_bf) >= 1);
+  ASSERT( sizeof(size2a_bf) >= 2);
+  ASSERT( sizeof(size2b_bf) >= 2);
+  ASSERT( sizeof(size2c_bf) >= 2);
+  ASSERT( sizeof(size2d_bf) >= 2);
+  ASSERT( sizeof(size3a_bf) >= 2);
+  ASSERT( sizeof(size1a_bf) == sizeof(size1b_bf));
+  ASSERT( sizeof(size1a_bf) < sizeof(size2a_bf));
+
+  /* Some SDCC specific assertions. SDCC uses 8 bit storage units.
+     Bitfields that are less than 8 bits, but would (due to earlier
+     bitfield declarations) span a storage unit boundary are
+     realigned to the next storage unit boundary. Bitfields of
+     8 or greater bits are always aligned to start on a storage
+     unit boundary.
+  */
+#ifdef SDCC
+  ASSERT( sizeof(size1a_bf) == 1);
+  ASSERT( sizeof(size1b_bf) == 1);
+  ASSERT( sizeof(size1c_bf) == 1);
+  ASSERT( sizeof(size2a_bf) == 2);
+  ASSERT( sizeof(size2b_bf) == 2);
+  ASSERT( sizeof(size2c_bf) == 2);
+  ASSERT( sizeof(size2d_bf) == 2);
+  ASSERT( sizeof(size3a_bf) == 3);
+#endif
+}
+
+
+void
+testBitfieldsSingleBitLiteral(void)
+{
+#if !defined(SDCC_z80) && !defined(SDCC_gbz80)
+  size2b_bf.b0 = 0; 
+  size2b_bf.b1 = 0; 
+  size2b_bf.b2 = 0; 
+  size2b_bf.b3 = 0; 
+  size2b_bf.b4 = 0; 
+  size2b_bf.b5 = 0; 
+  size2b_bf.b6 = 0; 
+  size2b_bf.b7 = 0; 
+  size2b_bf.b8 = 0; 
+  size2b_bf.b9 = 0; 
+
+  /* make sure modulo 2 truncation works */
+  size2b_bf.b0 = 0x3fe;
+  ASSERT(size2b_bf.b0==0);
+  ASSERT(size2b_bf.b1==0);
+  ASSERT(size2b_bf.b2==0);
+  ASSERT(size2b_bf.b3==0);
+  ASSERT(size2b_bf.b4==0);
+  ASSERT(size2b_bf.b5==0);
+  ASSERT(size2b_bf.b6==0);
+  ASSERT(size2b_bf.b7==0);
+  ASSERT(size2b_bf.b8==0);
+  ASSERT(size2b_bf.b9==0);
+  size2b_bf.b0 = 0x3ff;
+  ASSERT(size2b_bf.b0==1);
+  ASSERT(size2b_bf.b1==0);
+  ASSERT(size2b_bf.b2==0);
+  ASSERT(size2b_bf.b3==0);
+  ASSERT(size2b_bf.b4==0);
+  ASSERT(size2b_bf.b5==0);
+  ASSERT(size2b_bf.b6==0);
+  ASSERT(size2b_bf.b7==0);
+  ASSERT(size2b_bf.b8==0);
+  ASSERT(size2b_bf.b9==0);
+
+  /* make sure both bytes work */
+  size2b_bf.b9 = 0x3ff;
+  ASSERT(size2b_bf.b0==1);
+  ASSERT(size2b_bf.b1==0);
+  ASSERT(size2b_bf.b2==0);
+  ASSERT(size2b_bf.b3==0);
+  ASSERT(size2b_bf.b4==0);
+  ASSERT(size2b_bf.b5==0);
+  ASSERT(size2b_bf.b6==0);
+  ASSERT(size2b_bf.b7==0);
+  ASSERT(size2b_bf.b8==0);
+  ASSERT(size2b_bf.b9==1);
+#endif
+}
+
+void
+testBitfieldsSingleBit(void)
+{
+#if !defined(SDCC_z80) && !defined(SDCC_gbz80)
+  volatile unsigned char c;
+
+  c = 0;
+  size2b_bf.b0 = c; 
+  size2b_bf.b1 = c; 
+  size2b_bf.b2 = c; 
+  size2b_bf.b3 = c; 
+  size2b_bf.b4 = c; 
+  size2b_bf.b5 = c; 
+  size2b_bf.b6 = c; 
+  size2b_bf.b7 = c; 
+  size2b_bf.b8 = c; 
+  size2b_bf.b9 = c; 
+
+  /* make sure modulo 2 truncation works */
+  c = 0xfe;
+  size2b_bf.b0 = c;
+  ASSERT(size2b_bf.b0==0);
+  ASSERT(size2b_bf.b1==0);
+  ASSERT(size2b_bf.b2==0);
+  ASSERT(size2b_bf.b3==0);
+  ASSERT(size2b_bf.b4==0);
+  ASSERT(size2b_bf.b5==0);
+  ASSERT(size2b_bf.b6==0);
+  ASSERT(size2b_bf.b7==0);
+  ASSERT(size2b_bf.b8==0);
+  ASSERT(size2b_bf.b9==0);
+  c++;
+  size2b_bf.b0 = c;
+  ASSERT(size2b_bf.b0==1);
+  ASSERT(size2b_bf.b1==0);
+  ASSERT(size2b_bf.b2==0);
+  ASSERT(size2b_bf.b3==0);
+  ASSERT(size2b_bf.b4==0);
+  ASSERT(size2b_bf.b5==0);
+  ASSERT(size2b_bf.b6==0);
+  ASSERT(size2b_bf.b7==0);
+  ASSERT(size2b_bf.b8==0);
+  ASSERT(size2b_bf.b9==0);
+
+  /* make sure both bytes work */
+  size2b_bf.b9 = c;
+  ASSERT(size2b_bf.b0==1);
+  ASSERT(size2b_bf.b1==0);
+  ASSERT(size2b_bf.b2==0);
+  ASSERT(size2b_bf.b3==0);
+  ASSERT(size2b_bf.b4==0);
+  ASSERT(size2b_bf.b5==0);
+  ASSERT(size2b_bf.b6==0);
+  ASSERT(size2b_bf.b7==0);
+  ASSERT(size2b_bf.b8==0);
+  ASSERT(size2b_bf.b9==1);
+#endif
+}
+
+void
+testBitfieldsMultibit(void)
+{
+#if !defined(SDCC_z80) && !defined(SDCC_gbz80)
+  size2c_bf.b0 = 0xff; /* should truncate to 0x0f */
+  size2c_bf.b1 = 0;
+  ASSERT(size2c_bf.b0==0x0f);
+  ASSERT(size2c_bf.b1==0);
+
+  size2c_bf.b1 = 0xff; /* should truncate to 0x1f */
+  size2c_bf.b0 = 0;
+  ASSERT(size2c_bf.b0==0);
+  ASSERT(size2c_bf.b1==0x1f);
+
+  size2d_bf.b0 = 0xffff; /* should truncate to 0x0fff */
+  size2d_bf.b1 = 0;
+  ASSERT(size2d_bf.b0==0x0fff);
+  ASSERT(size2d_bf.b1==0);
+
+  size2d_bf.b1 = 0xffff; /* should truncate to 0x07 */
+  size2d_bf.b0 = 0;
+  ASSERT(size2d_bf.b0==0);
+  ASSERT(size2d_bf.b1==0x07);
+
+  size2d_bf.b0 = 0x0321;
+  size2d_bf.b1 = 1;
+  ASSERT(size2d_bf.b0==0x0321);
+  ASSERT(size2d_bf.b1==1);
+
+  size2d_bf.b0 = 0x0a46;
+  size2d_bf.b1 = 5;
+  ASSERT(size2d_bf.b0==0x0a46);
+  ASSERT(size2d_bf.b1==5);
+#endif
+}
 
 void
 testBitfields(void)