From 016b03e3562666db24e5364b8034ad07314a96fc Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 7 Mar 2023 17:00:38 -0500 Subject: [PATCH 01/11] ca65 jmp (abs) wrap warning only applies to 6502, later CPUs do not have this bug --- src/ca65/instr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index b72be6711..7cbefaecf 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1617,11 +1617,12 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if (A.AddrModeBit & AM65_ABS_IND) { + if (A.AddrModeBit & AM65_ABS_IND && CPU < CPU_65SC02) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise - ** things will go weird later. + ** things will go weird later. This only affects the 6502 CPU, + ** and was corrected in 65C02 and later CPUs in this family. */ ExprNode* E = GenNE (GenByteExpr (CloneExpr (A.Expr)), 0xFF); From 08223360d539aede5719696be9d287747950500b Mon Sep 17 00:00:00 2001 From: Bob Andrews Date: Tue, 2 May 2023 12:43:50 +0200 Subject: [PATCH 02/11] Update instr.c --- src/ca65/instr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 7cbefaecf..4f95ec75c 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1617,7 +1617,7 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if (A.AddrModeBit & AM65_ABS_IND && CPU < CPU_65SC02) { + if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02)) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise From 2c47ea45af0ad91a611c866b9f99a5c7eae4e5f1 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Tue, 2 May 2023 18:18:10 -0400 Subject: [PATCH 03/11] bug895.c compliant token pasting syntax to remove warnings Gets rid of some unnecessary warning spam in the test log of lines like this: ``` bug895.c:95: Warning: Pasting formed "unsigned_long_14(", an invalid preprocessing token ``` --- test/val/bug895.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/val/bug895.c b/test/val/bug895.c index c4892d7b1..3c0331a6d 100644 --- a/test/val/bug895.c +++ b/test/val/bug895.c @@ -21,7 +21,7 @@ unsigned int uia, uib; unsigned long ula, ulb; #define OPTCMP8TEST_SINGLE(num,cmpop,asmprefix,vara,varb,b0,b1,a0,a1,typename,name) \ - typename name ## _ ## num ## (void) { \ + typename name ## _ ## num(void) { \ varb = b0; \ asm( asmprefix ); \ vara = a0; \ @@ -30,7 +30,7 @@ unsigned long ula, ulb; } #define OPTCMP8TEST_VERIFY(num,b,desc,printterm,name) \ - ASSERT_AreEqual(name ## _ ## num ##(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ")."); + ASSERT_AreEqual(name ## _ ## num(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ")."); /* Generates a set of comparison tests for one type and set of test values. ** name = a name for this test (no spaces) From e7046a02ff392d793647fccd76245ce1bc00c4cc Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 2 May 2023 19:00:34 -0400 Subject: [PATCH 04/11] Disallow pass/return of 3-byte struct (#2022), document capability added in #1102. --- doc/cc65.sgml | 5 ++--- src/cc65/datatype.c | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 683249bda..2e99f3201 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -806,9 +806,8 @@ and the one defined by the ISO standard: The datatypes "float" and "double" are not available.

- C Functions may not return structs (or unions), and structs may not - be passed as parameters by value. However, struct assignment *is* - possible. + C Functions may return structs (or unions) by value, but only of + 1, 2 or 4 byte sizes.

Most of the C library is available with only the fastcall calling convention (). It means diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6907ee099..caa41a7a4 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -803,7 +803,6 @@ const Type* GetStructReplacementType (const Type* SType) switch (SizeOf (SType)) { case 1: NewType = type_uchar; break; case 2: NewType = type_uint; break; - case 3: /* FALLTHROUGH */ case 4: NewType = type_ulong; break; default: NewType = SType; break; } From af11d4d947d090419827907604d8751a3eb01411 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 2 May 2023 19:21:21 -0400 Subject: [PATCH 05/11] Document that struct-param is default off since: 3129266 --- doc/cc65.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 2e99f3201..023204f4d 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -741,7 +741,7 @@ Here is a description of all the command line options: Warn about no return statement in function returning non-void. - Warn when passing structs by value. + Warn when passing structs by value. (Disabled by default.) Warn about #pragmas that aren't recognized by cc65. From bf22f94a436345334e81fe70d196a8e805b7a778 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 2 May 2023 20:02:58 -0400 Subject: [PATCH 06/11] struct pass and return by value test --- test/val/struct-by-value.c | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 test/val/struct-by-value.c diff --git a/test/val/struct-by-value.c b/test/val/struct-by-value.c new file mode 100644 index 000000000..0e846c117 --- /dev/null +++ b/test/val/struct-by-value.c @@ -0,0 +1,144 @@ +/* Test of passing and returning structs by value. + Structs of 1, 2 and 4 bytes are supported. + Note that structs of 3 bytes are disabled, see: + https://github.com/cc65/cc65/issues/2022 +*/ + +int fail = 0; + +struct s1 { char a; }; +struct s2 { char a, b; }; +struct s3 { char a, b, c; }; +struct s4 { char a, b, c, d; }; + +const struct s1 c1 = { 1 }; +const struct s2 c2 = { 2, 3 }; +const struct s3 c3 = { 4, 5, 6 }; +const struct s4 c4 = { 7, 8, 9, 10 }; + +struct s1 return1() { return c1; } +struct s2 return2() { return c2; } +/*struct s3 return3() { return c3; }*/ +struct s4 return4() { return c4; } + +int compare1(struct s1 a, struct s1 b) +{ + if (a.a != b.a) return 1; + return 0; +} + +int compare2(struct s2 a, struct s2 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + return 0; +} + +/*int compare3(struct s3 a, struct s3 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + return 0; +}*/ + +int compare4(struct s4 a, struct s4 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + if (a.d != b.d) return 1; + return 0; +} + +int pass1(struct s1 p1) +{ + struct s1 a1; + a1 = p1; + if (a1.a != c1.a) return 1; + return 0; +} + +int pass2(struct s2 p2) +{ + struct s2 a2; + a2 = p2; + if (a2.a != c2.a) return 1; + if (a2.b != c2.b) return 1; + return 0; +} + +/*int pass3(struct s3 p3) +{ + struct s3 a3; + a3 = p3; + if (a3.a != c3.a) return 1; + if (a3.b != c3.b) return 1; + if (a3.c != c3.c) return 1; + return 0; +}*/ + +int pass4(struct s4 p4) +{ + struct s4 a4; + a4 = p4; + if (a4.a != c4.a) return 1; + if (a4.b != c4.b) return 1; + if (a4.c != c4.c) return 1; + if (a4.d != c4.d) return 1; + return 0; +} + +void reset(char* gg) +{ + char i; + for (i=0;i<5;++i) gg[i] = 128+i; +} + +int test(char* gg, char start) +{ + char i; + for (i=start;i<5;++i) + if (gg[i] != 128+i) return 1; + return 0; +} + +int main() +{ + /* Used to check #2022 bug condition of extra bytes being overwritten. */ + union + { + char gg[5]; + struct s1 g1; + struct s2 g2; + struct s3 g3; + struct s4 g4; + } guard; + + reset(guard.gg); + guard.g1 = return1(); + fail += compare1(guard.g1,c1); + fail += test(guard.gg,1); + + reset(guard.gg); + guard.g2 = return2(); + fail += compare2(guard.g2,c2); + fail += test(guard.gg,2); + + /*reset(guard.gg); + guard.g3 = return3(); + fail += compare3(guard.g3,c3); + fail += test(guard.gg,3);*/ + + reset(guard.gg); + guard.g4 = return4(); + fail += compare4(guard.g4,c4); + fail += test(guard.gg,4); + + fail += pass1(c1); + fail += pass2(c2); + /*fail += pass3(c3);*/ + fail += pass4(c4); + + return fail; +} From 65f773f5ee96d3d872fe6fd06fba079fa6b64c6a Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 01:01:21 -0400 Subject: [PATCH 07/11] Explicit z: should suppress "Suspicious address expression" warning #194 --- src/ca65/ea.h | 5 +++++ src/ca65/ea65.c | 2 ++ src/ca65/instr.c | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ca65/ea.h b/src/ca65/ea.h index d861e9a6c..487027c02 100644 --- a/src/ca65/ea.h +++ b/src/ca65/ea.h @@ -43,6 +43,10 @@ /*****************************************************************************/ +/* EffAddr Flags */ +#define EFFADDR_OVERRIDE_ZP 0x00000001UL + + /* GetEA result struct */ typedef struct EffAddr EffAddr; @@ -51,6 +55,7 @@ struct EffAddr { unsigned long AddrModeSet; /* Possible addressing modes */ struct ExprNode* Expr; /* Expression if any (NULL otherwise) */ unsigned Reg; /* Register number in sweet16 mode */ + unsigned long Flags; /* Other properties */ /* The following fields are used inside instr.c */ unsigned AddrMode; /* Actual addressing mode used */ diff --git a/src/ca65/ea65.c b/src/ca65/ea65.c index 275d90b56..5bd2ba82b 100644 --- a/src/ca65/ea65.c +++ b/src/ca65/ea65.c @@ -72,11 +72,13 @@ void GetEA (EffAddr* A) /* Clear the output struct */ A->AddrModeSet = 0; A->Expr = 0; + A->Flags = 0; /* Handle an addressing size override */ switch (CurTok.Tok) { case TOK_OVERRIDE_ZP: Restrictions = AM65_DIR | AM65_DIR_X | AM65_DIR_Y; + A->Flags |= EFFADDR_OVERRIDE_ZP; NextTok (); break; diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 4f95ec75c..89162c3c6 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1269,7 +1269,8 @@ static int EvalEA (const InsDesc* Ins, EffAddr* A) ExprNode* Left = A->Expr->Left; if ((A->Expr->Op == EXPR_BYTE0 || A->Expr->Op == EXPR_BYTE1) && Left->Op == EXPR_SYMBOL && - GetSymAddrSize (Left->V.Sym) != ADDR_SIZE_ZP) { + GetSymAddrSize (Left->V.Sym) != ADDR_SIZE_ZP && + !(A->Flags & EFFADDR_OVERRIDE_ZP)) { /* Output a warning */ Warning (1, "Suspicious address expression"); From 4d698bf18c89e8426e4ba8a37e4b72d1d51ee7ba Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 03:05:14 -0400 Subject: [PATCH 08/11] Don't use a,x,y in macro parameter example, document why not. #392 --- doc/ca65.sgml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 360b0bab2..2f95a032e 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -4262,8 +4262,13 @@ macro actually takes in the definition. You may also leave intermediate parameters empty. Empty parameters are replaced by empty space (that is, they are removed when the macro is expanded). If you have a look at our macro definition above, you will see, that replacing the "addr" parameter -by nothing will lead to wrong code in most lines. To help you, writing -macros with a variable parameter list, there are some control commands: +by nothing will lead to wrong code in most lines. + +The names "a", "x" and "y" should be avoided for macro parameters, as these +will usually conflict with the 6502 registers. + +For writing macros with a variable parameter list, control commands are +available: tests the rest of the line and returns true, if there are any tokens on the remainder of the line. Since @@ -4274,15 +4279,15 @@ opposite. Look at this example: -.macro ldaxy a, x, y -.ifnblank a - lda #a +.macro ldaxy i, j, k +.ifnblank i + lda #i .endif -.ifnblank x - ldx #x +.ifnblank j + ldx #j .endif -.ifnblank y - ldy #y +.ifnblank k + ldy #k .endif .endmacro From 2b60adfa11ad6b348b70cc224337f1cac6b4c125 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 03:26:10 -0400 Subject: [PATCH 09/11] Document directives that use SetBoolOption "can" use +/- rather than "must" #1772 --- doc/ca65.sgml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 360b0bab2..72a133299 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2283,7 +2283,7 @@ See: ,,,,

- Enable output to the listing. The command must be followed by a boolean + Enable output to the listing. The command can be followed by a boolean switch ("on", "off", "+" or "-") and will enable or disable listing output. The option has no effect if the listing is not enabled by the command line @@ -4040,7 +4040,7 @@ See: ,.SMART

- Switch on or off smart mode. The command must be followed by a '+' or '-' + Switch on or off smart mode. The command can be followed by a '+' or '-' character to switch the option on or off respectively. The default is off (that is, the assembler doesn't try to be smart), but this default may be changed by the -s switch on the command line. From 456fa9f963dcba838a135416a5d52957b54c3ffb Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 05:36:37 -0400 Subject: [PATCH 10/11] cc65 document: both pass and return of structs are allowed --- doc/cc65.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 023204f4d..1476b40a3 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -806,8 +806,8 @@ and the one defined by the ISO standard: The datatypes "float" and "double" are not available.

- C Functions may return structs (or unions) by value, but only of - 1, 2 or 4 byte sizes. + C Functions may pass and return structs (or unions) by value, but only + of 1, 2 or 4 byte sizes.

Most of the C library is available with only the fastcall calling convention (). It means From 6ffc4004d7d7bbae4ba48aba53263545d651d750 Mon Sep 17 00:00:00 2001 From: Bob Andrews Date: Wed, 3 May 2023 14:24:13 +0200 Subject: [PATCH 11/11] Revert "Forbid 3-byte struct pass/return by value, document struct value pass/return" --- doc/cc65.sgml | 7 +- src/cc65/datatype.c | 1 + test/val/struct-by-value.c | 144 ------------------------------------- 3 files changed, 5 insertions(+), 147 deletions(-) delete mode 100644 test/val/struct-by-value.c diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 1476b40a3..683249bda 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -741,7 +741,7 @@ Here is a description of all the command line options: Warn about no return statement in function returning non-void. - Warn when passing structs by value. (Disabled by default.) + Warn when passing structs by value. Warn about #pragmas that aren't recognized by cc65. @@ -806,8 +806,9 @@ and the one defined by the ISO standard: The datatypes "float" and "double" are not available.

- C Functions may pass and return structs (or unions) by value, but only - of 1, 2 or 4 byte sizes. + C Functions may not return structs (or unions), and structs may not + be passed as parameters by value. However, struct assignment *is* + possible.

Most of the C library is available with only the fastcall calling convention (). It means diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index caa41a7a4..6907ee099 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -803,6 +803,7 @@ const Type* GetStructReplacementType (const Type* SType) switch (SizeOf (SType)) { case 1: NewType = type_uchar; break; case 2: NewType = type_uint; break; + case 3: /* FALLTHROUGH */ case 4: NewType = type_ulong; break; default: NewType = SType; break; } diff --git a/test/val/struct-by-value.c b/test/val/struct-by-value.c deleted file mode 100644 index 0e846c117..000000000 --- a/test/val/struct-by-value.c +++ /dev/null @@ -1,144 +0,0 @@ -/* Test of passing and returning structs by value. - Structs of 1, 2 and 4 bytes are supported. - Note that structs of 3 bytes are disabled, see: - https://github.com/cc65/cc65/issues/2022 -*/ - -int fail = 0; - -struct s1 { char a; }; -struct s2 { char a, b; }; -struct s3 { char a, b, c; }; -struct s4 { char a, b, c, d; }; - -const struct s1 c1 = { 1 }; -const struct s2 c2 = { 2, 3 }; -const struct s3 c3 = { 4, 5, 6 }; -const struct s4 c4 = { 7, 8, 9, 10 }; - -struct s1 return1() { return c1; } -struct s2 return2() { return c2; } -/*struct s3 return3() { return c3; }*/ -struct s4 return4() { return c4; } - -int compare1(struct s1 a, struct s1 b) -{ - if (a.a != b.a) return 1; - return 0; -} - -int compare2(struct s2 a, struct s2 b) -{ - if (a.a != b.a) return 1; - if (a.b != b.b) return 1; - return 0; -} - -/*int compare3(struct s3 a, struct s3 b) -{ - if (a.a != b.a) return 1; - if (a.b != b.b) return 1; - if (a.c != b.c) return 1; - return 0; -}*/ - -int compare4(struct s4 a, struct s4 b) -{ - if (a.a != b.a) return 1; - if (a.b != b.b) return 1; - if (a.c != b.c) return 1; - if (a.d != b.d) return 1; - return 0; -} - -int pass1(struct s1 p1) -{ - struct s1 a1; - a1 = p1; - if (a1.a != c1.a) return 1; - return 0; -} - -int pass2(struct s2 p2) -{ - struct s2 a2; - a2 = p2; - if (a2.a != c2.a) return 1; - if (a2.b != c2.b) return 1; - return 0; -} - -/*int pass3(struct s3 p3) -{ - struct s3 a3; - a3 = p3; - if (a3.a != c3.a) return 1; - if (a3.b != c3.b) return 1; - if (a3.c != c3.c) return 1; - return 0; -}*/ - -int pass4(struct s4 p4) -{ - struct s4 a4; - a4 = p4; - if (a4.a != c4.a) return 1; - if (a4.b != c4.b) return 1; - if (a4.c != c4.c) return 1; - if (a4.d != c4.d) return 1; - return 0; -} - -void reset(char* gg) -{ - char i; - for (i=0;i<5;++i) gg[i] = 128+i; -} - -int test(char* gg, char start) -{ - char i; - for (i=start;i<5;++i) - if (gg[i] != 128+i) return 1; - return 0; -} - -int main() -{ - /* Used to check #2022 bug condition of extra bytes being overwritten. */ - union - { - char gg[5]; - struct s1 g1; - struct s2 g2; - struct s3 g3; - struct s4 g4; - } guard; - - reset(guard.gg); - guard.g1 = return1(); - fail += compare1(guard.g1,c1); - fail += test(guard.gg,1); - - reset(guard.gg); - guard.g2 = return2(); - fail += compare2(guard.g2,c2); - fail += test(guard.gg,2); - - /*reset(guard.gg); - guard.g3 = return3(); - fail += compare3(guard.g3,c3); - fail += test(guard.gg,3);*/ - - reset(guard.gg); - guard.g4 = return4(); - fail += compare4(guard.g4,c4); - fail += test(guard.gg,4); - - fail += pass1(c1); - fail += pass2(c2); - /*fail += pass3(c3);*/ - fail += pass4(c4); - - return fail; -}