From 6230b6a81397ce9e9b1fa67f8da5adc7fa9fbc5a Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Mon, 9 Mar 2015 18:53:45 -0400 Subject: [PATCH 01/14] Completed the cc65 code that recognizes __CDECL__ as a calling convention qualifier. --- src/cc65/datatype.c | 16 ++++++------ src/cc65/datatype.h | 12 ++++++++- src/cc65/declare.c | 59 +++++++++++++++++++++++++++------------------ 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 053810b50..8d17d2851 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2012, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -290,18 +290,18 @@ void PrintType (FILE* F, const Type* T) fprintf (F, "union %s", ((SymEntry*) T->A.P)->Name); break; case T_TYPE_ARRAY: + if (T->A.L == UNSPECIFIED) { + fprintf (F, "[] "); + } else { + fprintf (F, "[%ld] ", T->A.L); + } /* Recursive call */ PrintType (F, T + 1); - if (T->A.L == UNSPECIFIED) { - fprintf (F, "[]"); - } else { - fprintf (F, "[%ld]", T->A.L); - } return; case T_TYPE_PTR: + fprintf (F, "* "); /* Recursive call */ PrintType (F, T + 1); - fprintf (F, "*"); return; case T_TYPE_FUNC: fprintf (F, "function returning "); @@ -659,7 +659,7 @@ Type* GetBaseElementType (Type* T) ** will return. Otherwise it will return the base element type, which means ** the element type that is not an array. */ -{ +{ while (IsTypeArray (T)) { ++T; } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 598d0a228..92b3d0122 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2012, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -603,6 +603,16 @@ INLINE int IsQualCDecl (const Type* T) # define IsQualCDecl(T) (((T)->C & T_QUAL_CDECL) != 0) #endif +#if defined(HAVE_INLINE) +INLINE int IsQualCConv (const Type* T) +/* Return true if the given type has a calling convention qualifier */ +{ + return (T->C & T_QUAL_CCONV) != 0; +} +#else +# define IsQualCConv(T) (((T)->C & T_QUAL_CCONV) != 0) +#endif + int IsVariadicFunc (const Type* T) attribute ((const)); /* Return true if this is a function type or pointer to function type with ** variable parameter list diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 693c2116e..e6417c6a8 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2013, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -85,7 +85,7 @@ struct StructInitData { static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers); -/* Parse a type specificier */ +/* Parse a type specifier */ static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers); /* Parse initialization of variables. Return the number of data bytes. */ @@ -336,17 +336,28 @@ static void FixQualifiers (Type* DataType) while (T->C != T_END) { if (IsTypePtr (T)) { - /* Fastcall qualifier on the pointer? */ - if (IsQualFastcall (T)) { - /* Pointer to function which is not fastcall? */ - if (IsTypeFunc (T+1) && !IsQualFastcall (T+1)) { - /* Move the fastcall qualifier from the pointer to - ** the function. - */ - T[0].C &= ~T_QUAL_FASTCALL; - T[1].C |= T_QUAL_FASTCALL; + /* Calling convention qualifier on the pointer? */ + if (IsQualCConv (T)) { + /* Pull the convention off of the pointer */ + Q = T[0].C & T_QUAL_CCONV; + T[0].C &= ~T_QUAL_CCONV; + /* Pointer to a function which doesn't have an explicit convention? */ + if (IsTypeFunc (T + 1)) { + if (IsQualCConv (T + 1)) { + if (T[1].C == Q) { + /* TODO: The end of Declarator() catches this error. + ** Try to make it let the error be caught here, instead. + */ + Warning ("Pointer duplicates function's calling convention"); + } else { + Error ("Mismatch between pointer's and function's calling conventions"); + } + } else { + /* Move the qualifier from the pointer to the function. */ + T[1].C |= Q; + } } else { - Error ("Invalid `_fastcall__' qualifier for pointer"); + Error ("Not pointer to a function; can't use a calling convention"); } } @@ -355,8 +366,8 @@ static void FixQualifiers (Type* DataType) if (Q == T_QUAL_NONE) { /* No address size qualifiers specified */ if (IsTypeFunc (T+1)) { - /* Pointer to function. Use the qualifier from the function - ** or the default if the function don't has one. + /* Pointer to function. Use the qualifier from the function, + ** or the default if the function doesn't have one. */ Q = (T[1].C & T_QUAL_ADDRSIZE); if (Q == T_QUAL_NONE) { @@ -368,7 +379,7 @@ static void FixQualifiers (Type* DataType) T[0].C |= Q; } else { /* We have address size qualifiers. If followed by a function, - ** apply these also to the function. + ** apply them to the function also. */ if (IsTypeFunc (T+1)) { TypeCode FQ = (T[1].C & T_QUAL_ADDRSIZE); @@ -489,7 +500,7 @@ static void ParseEnumDecl (void) static int ParseFieldWidth (Declaration* Decl) -/* Parse an optional field width. Returns -1 if no field width is speficied, +/* Parse an optional field width. Returns -1 if no field width is specified, ** otherwise the width of the field. */ { @@ -862,7 +873,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) -/* Parse a type specificier */ +/* Parse a type specifier */ { ident Ident; SymEntry* Entry; @@ -1376,13 +1387,13 @@ static FuncDesc* ParseFuncDecl (void) static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Recursively process declarators. Build a type array in reverse order. */ { - /* Read optional function or pointer qualifiers. These modify the - ** identifier or token to the right. For convenience, we allow the fastcall - ** qualifier also for pointers here. If it is a pointer-to-function, the - ** qualifier will later be transfered to the function itself. If it's a + /* Read optional function or pointer qualifiers. They modify the + ** identifier or token to the right. For convenience, we allow a calling + ** convention also for pointers here. If it's a pointer-to-function, the + ** qualifier later will be transfered to the function itself. If it's a ** pointer to something else, it will be flagged as an error. */ - TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_FASTCALL); + TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_CCONV); /* Pointer to something */ if (CurTok.Tok == TOK_STAR) { @@ -1390,10 +1401,10 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Skip the star */ NextToken (); - /* Allow const, restrict and volatile qualifiers */ + /* Allow const, restrict, and volatile qualifiers */ Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT); - /* Parse the type, the pointer points to */ + /* Parse the type that the pointer points to */ Declarator (Spec, D, Mode); /* Add the type */ From a798b1d6487c38345cf136617543671e4c85adbf Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Tue, 10 Mar 2015 05:53:52 -0400 Subject: [PATCH 02/14] Made __fastcall__ be the default calling convention for non-variadic functions. --- include/ace.h | 38 +++++++++++++++++++------------------- include/assert.h | 10 +++++----- include/cbm.h | 4 ++-- include/dbg.h | 10 +++++----- include/lynx.h | 12 ++++++------ include/zlib.h | 4 ++-- libsrc/cbm/cbm_load.c | 8 ++++---- src/cc65/codeinfo.c | 39 +++++++++++++++++++-------------------- src/cc65/expr.c | 9 +++++---- src/cc65/function.c | 11 +++++------ 10 files changed, 72 insertions(+), 73 deletions(-) diff --git a/include/ace.h b/include/ace.h index 8d9130085..fba672227 100644 --- a/include/ace.h +++ b/include/ace.h @@ -2,14 +2,14 @@ /* */ /* ace.h */ /* */ -/* ACE system specific definitions */ +/* ACE system-specific definitions */ /* */ /* */ /* */ -/* (C) 1998-2001 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 1998-2015, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -61,9 +61,9 @@ struct aceDirentBuf { char ad_name [17]; /* Name itself, ASCIIZ */ }; -int aceDirOpen (char* dir); -int aceDirClose (int handle); -int aceDirRead (int handle, struct aceDirentBuf* buf); +int __cdecl__ aceDirOpen (char* dir); +int __cdecl__ aceDirClose (int handle); +int __cdecl__ aceDirRead (int handle, struct aceDirentBuf* buf); /* Type of an ACE key. Key in low byte, shift mask in high byte */ typedef unsigned int aceKey; @@ -92,23 +92,23 @@ typedef unsigned int aceKey; #define aceOP_RPTRATE 11 /* Key repeat rate */ /* Console functions */ -void aceConWrite (char* buf, size_t count); -void aceConPutLit (int c); -void aceConPos (unsigned x, unsigned y); -void aceConGetPos (unsigned* x, unsigned* y); +void __cdecl__ aceConWrite (char* buf, size_t count); +void __cdecl__ aceConPutLit (int c); +void __cdecl__ aceConPos (unsigned x, unsigned y); +void __cdecl__ aceConGetPos (unsigned* x, unsigned* y); unsigned aceConGetX (void); unsigned aceConGetY (void); -char* aceConInput (char* buf, unsigned initial); +char __cdecl__* aceConInput (char* buf, unsigned initial); int aceConStopKey (void); aceKey aceConGetKey (void); -int aceConKeyAvail (aceKey* key); -void aceConKeyMat (char* matrix); -void aceConSetOpt (unsigned char opt, unsigned char val); -int aceConGetOpt (unsigned char opt); +int __cdecl__ aceConKeyAvail (aceKey* key); +void __cdecl__ aceConKeyMat (char* matrix); +void __cdecl__ aceConSetOpt (unsigned char opt, unsigned char val); +int __cdecl__ aceConGetOpt (unsigned char opt); /* Misc stuff */ -int aceMiscIoPeek (unsigned addr); -void aceMiscIoPoke (unsigned addr, unsigned char val); +int __cdecl__ aceMiscIoPeek (unsigned addr); +void __cdecl__ aceMiscIoPoke (unsigned addr, unsigned char val); diff --git a/include/assert.h b/include/assert.h index 87bc4ff02..ceabd2482 100644 --- a/include/assert.h +++ b/include/assert.h @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1998-2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 1998-2000, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -42,7 +42,7 @@ #ifdef NDEBUG # define assert(expr) #else -extern void _afailed (const char*, unsigned); +extern void __cdecl__ _afailed (const char*, unsigned); # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) #endif diff --git a/include/cbm.h b/include/cbm.h index 730b0b49e..1bc29bf4c 100644 --- a/include/cbm.h +++ b/include/cbm.h @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2012, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -222,7 +222,7 @@ void cbm_k_unlsn (void); -unsigned int cbm_load (const char* name, unsigned char device, void* data); +unsigned int __cdecl__ cbm_load (const char* name, unsigned char device, void* data); /* Loads file "name", from given device, to given address -- or, to the load ** address of the file if "data" is the null pointer (like load"name",8,1 ** in BASIC). diff --git a/include/dbg.h b/include/dbg.h index 734ca06b8..7b4f67e31 100644 --- a/include/dbg.h +++ b/include/dbg.h @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1998-2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 1998-2000, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -88,7 +88,7 @@ unsigned __fastcall__ DbgDisAsmLen (unsigned Addr); int __fastcall__ DbgIsRAM (unsigned Addr); /* Return true if we can read and write the given address */ -char* DbgMemDump (unsigned Addr, char* Buf, unsigned char Len); +char* __cdecl__ DbgMemDump (unsigned Addr, char* Buf, unsigned char Len); /* Create a line of a memory dump in the given buffer. The buffer contains ** the starting address (4 digits hex), then Len bytes in this format: ** "AAAA__XX_YY_ZZ_...". The passed char buffer must hold Len*3+5 bytes diff --git a/include/lynx.h b/include/lynx.h index e9c702994..72f3d5bfd 100644 --- a/include/lynx.h +++ b/include/lynx.h @@ -2,7 +2,7 @@ /* */ /* lynx.h */ /* */ -/* Lynx system specific definitions */ +/* Lynx system-specific definitions */ /* */ /* */ /* */ @@ -109,25 +109,25 @@ extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */ /* Sound support */ /*****************************************************************************/ -void lynx_snd_init (); +void lynx_snd_init (void); /* Initialize the sound driver */ -void lynx_snd_pause (); +void lynx_snd_pause (void); /* Pause sound */ -void lynx_snd_continue (); +void lynx_snd_continue (void); /* Continue sound after pause */ void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music); /* Play tune on channel */ -void lynx_snd_stop (); +void lynx_snd_stop (void); /* Stop sound on all channels */ void __fastcall__ lynx_snd_stop_channel (unsigned char channel); /* Stop sound on all channels */ -unsigned char lynx_snd_active(); +unsigned char lynx_snd_active(void); /* Show which channels are active */ /*****************************************************************************/ diff --git a/include/zlib.h b/include/zlib.h index 9bc5dcb27..971095cd3 100644 --- a/include/zlib.h +++ b/include/zlib.h @@ -83,8 +83,8 @@ unsigned __fastcall__ inflatemem (char* dest, const char* source); */ -int uncompress (char* dest, unsigned* destLen, - const char* source, unsigned sourceLen); +int __cdecl__ uncompress (char* dest, unsigned* destLen, + const char* source, unsigned sourceLen); /* Original zlib description: diff --git a/libsrc/cbm/cbm_load.c b/libsrc/cbm/cbm_load.c index 695af504b..7d4a5086b 100644 --- a/libsrc/cbm/cbm_load.c +++ b/libsrc/cbm/cbm_load.c @@ -1,9 +1,9 @@ /* ** Marc 'BlackJack' Rintsch, 06.03.2001 ** -** unsigned int cbm_load(const char* name, -** unsigned char device, -** const unsigned char* data); +** unsigned int __cdecl__ cbm_load(const char* name, +** unsigned char device, +** const unsigned char* data); */ #include <cbm.h> @@ -11,7 +11,7 @@ /* loads file "name" from given device to given address or to the load address ** of the file if "data" is 0 */ -unsigned int cbm_load(const char* name, unsigned char device, void* data) +unsigned int __cdecl__ cbm_load(const char* name, unsigned char device, void* data) { /* LFN is set to 0; but, it's not needed for loading ** (BASIC V2 sets it to the value of the SA for LOAD). diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index be80319e7..ae264dce4 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 2001-2012, Ullrich von Bassewitz */ +/* (C) 2001-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -386,33 +386,32 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) ** Search for it in the list of builtin functions. */ if (Name[0] == '_') { - /* Search in the symbol table, skip the leading underscore */ SymEntry* E = FindGlobalSym (Name+1); - /* Did we find it in the top level table? */ + /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { - FuncDesc* D = E->V.F.Func; - /* A function may use the A or A/X registers if it is a fastcall - ** function. If it is not a fastcall function but a variadic one, - ** it will use the Y register (the parameter size is passed here). - ** In all other cases, no registers are used. However, we assume - ** that any function will destroy all registers. + /* A variadic function will use the Y register (the parameter list + ** size is passed there). A fastcall function will use the A or A/X + ** registers. In all other cases, no registers are used. However, + ** we assume that any function will destroy all registers. */ - if (IsQualFastcall (E->Type) && D->ParamCount > 0) { - /* Will use registers depending on the last param */ - unsigned LastParamSize = CheckedSizeOf (D->LastParam->Type); - if (LastParamSize == 1) { - *Use = REG_A; - } else if (LastParamSize == 2) { - *Use = REG_AX; - } else { - *Use = REG_EAX; - } - } else if ((D->Flags & FD_VARIADIC) != 0) { + if ((D->Flags & FD_VARIADIC) != 0) { *Use = REG_Y; + } else if (!IsQualCDecl (E->Type) && D->ParamCount > 0) { + /* Will use registers depending on the last param. */ + switch (CheckedSizeOf (D->LastParam->Type)) { + case 1u: + *Use = REG_A; + break; + case 2u: + *Use = REG_AX; + break; + default: + *Use = REG_EAX; + } } else { /* Will not use any registers */ *Use = REG_NONE; diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 03374a521..c8c47f6aa 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1,6 +1,7 @@ /* expr.c ** -** Ullrich von Bassewitz, 21.06.1998 +** 1998-06-21, Ullrich von Bassewitz +** 2015-03-10, Greg King */ @@ -471,8 +472,8 @@ static void FunctionCall (ExprDesc* Expr) IsFuncPtr = IsTypeFuncPtr (Expr->Type); if (IsFuncPtr) { - /* Check wether it's a fastcall function that has parameters */ - IsFastcall = IsQualFastcall (Expr->Type + 1) && (Func->ParamCount > 0); + /* Check whether it's a fastcall function that has parameters */ + IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && !IsQualCDecl (Expr->Type + 1) && (Func->ParamCount > 0); /* Things may be difficult, depending on where the function pointer ** resides. If the function pointer is an expression of some sort @@ -517,7 +518,7 @@ static void FunctionCall (ExprDesc* Expr) } /* If we didn't inline the function, get fastcall info */ - IsFastcall = IsQualFastcall (Expr->Type); + IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && !IsQualCDecl (Expr->Type); } /* Parse the parameter list */ diff --git a/src/cc65/function.c b/src/cc65/function.c index d9f1eeac3..d80ba916a 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 2000-2012, Ullrich von Bassewitz */ +/* (C) 2000-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -460,6 +460,9 @@ void NewFunc (SymEntry* Func) */ if (D->ParamCount > 0 || (D->Flags & FD_VARIADIC) != 0) { g_importmainargs (); + + /* The start-up code doesn't fast-call main(). */ + Func->Type->C |= T_QUAL_CDECL; } /* Determine if this is a main function in a C99 environment that @@ -478,13 +481,9 @@ void NewFunc (SymEntry* Func) PushLiteralPool (Func); /* If this is a fastcall function, push the last parameter onto the stack */ - if (IsQualFastcall (Func->Type) && D->ParamCount > 0) { - + if ((D->Flags & FD_VARIADIC) == 0 && !IsQualCDecl (Func->Type) && D->ParamCount > 0) { unsigned Flags; - /* Fastcall functions may never have an ellipsis or the compiler is buggy */ - CHECK ((D->Flags & FD_VARIADIC) == 0); - /* Generate the push */ if (IsTypeFunc (D->LastParam->Type)) { /* Pointer to function */ From 604a5b4447d7a8c17411380fdf03c024a05587f5 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Fri, 13 Mar 2015 05:40:55 -0400 Subject: [PATCH 03/14] Changed the documentation to reflect the new fastcall/cdecl reality. --- doc/cc65.sgml | 75 +++++++++++++++++++++++++++++--------------- doc/customizing.sgml | 19 +++++------ 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index a6b4a07ee..bf7b1e336 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -3,7 +3,7 @@ <article> <title>cc65 Users Guide <author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> -<date>2000-09-03, 2001-10-02, 2005-08-01 +<date>2015-03-13 <abstract> cc65 is a C compiler for 6502 targets. It supports several 6502 based home @@ -549,9 +549,9 @@ and the one defined by the ISO standard: be passed as parameters by value. However, struct assignment *is* possible. <p> -<item> Part of the C library is available only with fastcall calling - conventions (see below). It means that you must not mix pointers to - those functions with pointers to user-written, not-fastcall functions. +<item> Most of the C library is available only with the fastcall calling + convention (see below). It means that you must not mix pointers to + those functions with pointers to user-written, cdecl functions. <p> <item> The <tt/volatile/ keyword doesn't have an effect. This is not as bad as it sounds, since the 6502 has so few registers that it isn't @@ -589,29 +589,54 @@ This cc65 version has some extensions to the ISO C standard. <ref id="inline-asm" name="see there">. <p> -<item> There is a special calling convention named "fastcall". - The syntax for a function declaration using fastcall is +<item> The normal calling convention -- for non-variadic functions -- is + named "fastcall". The syntax for a function declaration that + <em/explicitly/ uses fastcall is - <tscreen><verb> - <return type> fastcall <function name> (<parameter list>) - </verb></tscreen> - or - <tscreen><verb> - <return type> __fastcall__ <function name> (<parameter list>) - </verb></tscreen> - An example would be - <tscreen><verb> - void __fastcall__ f (unsigned char c) - </verb></tscreen> - The first form of the fastcall keyword is in the user namespace and can - therefore be disabled with the <tt><ref id="option--standard" + <tscreen><verb> + <return type> fastcall <function name> (<parameter list>) + </verb></tscreen> + or + <tscreen><verb> + <return type> __fastcall__ <function name> (<parameter list>) + </verb></tscreen> + An example would be + <tscreen><verb> + void __fastcall__ f (unsigned char c) + </verb></tscreen> + The first form of the fastcall keyword is in the user namespace and can + therefore be disabled with the <tt><ref id="option--standard" name="--standard"></tt> command line option. - For functions declared as <tt/fastcall/, the rightmost parameter is not - pushed on the stack but left in the primary register when the function - is called. This will reduce the cost when calling assembler functions - significantly, especially when the function itself is rather small. - <p> + For functions that are <tt/fastcall/, the rightmost parameter is not + pushed on the stack but left in the primary register when the function + is called. That significantly reduces the cost of calling functions. + <p> + +<item> There is another calling convention named "cdecl". Variadic functions + (their prototypes have an ellipsis [<tt/.../]) always use this + convention. The syntax for a function declaration using cdecl is + + <tscreen><verb> + <return type> cdecl <function name> (<parameter list>) + </verb></tscreen> + or + <tscreen><verb> + <return type> __cdecl__ <function name> (<parameter list>) + </verb></tscreen> + An example would be + <tscreen><verb> + void __cdecl__ f (unsigned char c) + </verb></tscreen> + The first form of the cdecl keyword is in the user namespace and can + therefore be disabled with the <tt><ref id="option--standard" + name="--standard"></tt> command line option. + + For functions that are <tt/cdecl/, the rightmost parameter is pushed + onto the stack before the function is called. That increases the cost + of calling those functions, especially when they are called from many + places. + <p> <item> There are two pseudo variables named <tt/__AX__/ and <tt/__EAX__/. Both refer to the primary register that is used by the compiler to @@ -663,7 +688,7 @@ This cc65 version has some extensions to the ISO C standard. </verb></tscreen> Since the variable is of type <tt/void/ you may not use it as is. - However, taking the address of the variable results in a <tt/void*/ + However, taking the address of the variable results in a <tt/void */ which may be passed to any function expecting a pointer. See the <url url="geos.html" name="GEOS library document"> for examples diff --git a/doc/customizing.sgml b/doc/customizing.sgml index 23cf8c5e8..a54821c34 100644 --- a/doc/customizing.sgml +++ b/doc/customizing.sgml @@ -3,7 +3,7 @@ <article> <title>Defining a Custom cc65 Target <author>Bruce Reidenbach -<date>2010-02-22 +<date>2015-03-13 <abstract> This section provides step-by-step instructions on how to use the cc65 @@ -525,15 +525,16 @@ The first step in creating the assembly language code for the driver is to determine how to pass the C arguments to the assembly language routine. The cc65 toolset allows the user to specify whether the data is passed to a subroutine via the stack or by the processor registers by -using the <tt>__fastcall__</tt> function declaration (note that there -are two underscore characters in front of and two behind the -<tt>fastcall</tt> declaration). When <tt>__fastcall__</tt> is -specified, the rightmost argument in the function call is passed to the +using the <tt/__fastcall__/ and <tt/__cdecl__/ function qualifiers (note that +there are two underscore characters in front of and two behind each +qualifier). <tt/__fastcall__/ is the default. When <tt/__cdecl__/ <em/isn't/ +specified, and the function isn't variadic (i.e., its prototype doesn't have +an ellipsis), the rightmost argument in the function call is passed to the subroutine using the 6502 registers instead of the stack. Note that if there is only one argument in the function call, the execution overhead required by the stack interface routines is completely avoided. -Without <tt>__fastcall__</tt>, the argument is loaded in the A and X +With <tt/__cdecl__</tt>, the last argument is loaded into the A and X registers and then pushed onto the stack via a call to <tt>pushax</tt>. The first thing the subroutine does is retrieve the argument from the stack via a call to <tt>ldax0sp</tt>, which copies the values into the A @@ -561,7 +562,7 @@ _foo: jsr ldax0sp ; Retrieve A and X from the stack jmp incsp2 ; Pop A and X from the stack (includes return) </verb></tscreen> -If <tt>__fastcall__</tt> is specified, the argument is loaded into the A +If <tt/__cdecl__/ isn't specified, then the argument is loaded into the A and X registers as before, but the subroutine is then called immediately. The subroutine does not need to retrieve the argument since the value is already available in the A and X registers. @@ -596,7 +597,7 @@ variable which is stored in the zero page memory space in order to allow for retrieval of each character in the string via the indirect indexed addressing mode. -The assembly language routine is stored in a file names +The assembly language routine is stored in a file named "rs232_tx.s" and is shown below: <tscreen><code> @@ -680,7 +681,7 @@ all of the behind-the-scene work is transparent to the user. #define TX_FIFO_FULL (FIFO_STATUS & 0x01) #define RX_FIFO_EMPTY (FIFO_STATUS & 0x02) -extern void wait (); +extern void wait (void); extern void __fastcall__ rs232_tx (char *str); int main () { From 66d79da3c2d96d5eafa879776971085c13f9ab01 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Fri, 13 Mar 2015 05:46:40 -0400 Subject: [PATCH 04/14] Made cbm_load() be a fastcall function. --- include/cbm.h | 2 +- libsrc/cbm/cbm_load.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/cbm.h b/include/cbm.h index 1bc29bf4c..701924d57 100644 --- a/include/cbm.h +++ b/include/cbm.h @@ -222,7 +222,7 @@ void cbm_k_unlsn (void); -unsigned int __cdecl__ cbm_load (const char* name, unsigned char device, void* data); +unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void* data); /* Loads file "name", from given device, to given address -- or, to the load ** address of the file if "data" is the null pointer (like load"name",8,1 ** in BASIC). diff --git a/libsrc/cbm/cbm_load.c b/libsrc/cbm/cbm_load.c index 7d4a5086b..eba864fcc 100644 --- a/libsrc/cbm/cbm_load.c +++ b/libsrc/cbm/cbm_load.c @@ -11,7 +11,7 @@ /* loads file "name" from given device to given address or to the load address ** of the file if "data" is 0 */ -unsigned int __cdecl__ cbm_load(const char* name, unsigned char device, void* data) +unsigned int __fastcall__ cbm_load(const char* name, unsigned char device, void* data) { /* LFN is set to 0; but, it's not needed for loading ** (BASIC V2 sets it to the value of the SA for LOAD). From 38231a5cc69a8d31e0fabc7e3bf47b685a107548 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Fri, 13 Mar 2015 07:35:47 -0400 Subject: [PATCH 05/14] Made _afailed() and uncompress() be fastcall functions. --- include/assert.h | 4 ++-- include/zlib.h | 6 +++--- libsrc/cbm/cbm_load.c | 6 +++--- libsrc/common/_afailed.c | 5 +++-- libsrc/dbg/dbgdump.s | 2 +- libsrc/zlib/uncompress.c | 4 ++-- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/assert.h b/include/assert.h index ceabd2482..504964dc2 100644 --- a/include/assert.h +++ b/include/assert.h @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2000, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -42,7 +42,7 @@ #ifdef NDEBUG # define assert(expr) #else -extern void __cdecl__ _afailed (const char*, unsigned); +extern void __fastcall__ _afailed (const char*, unsigned); # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) #endif diff --git a/include/zlib.h b/include/zlib.h index 971095cd3..8fa6a2bd1 100644 --- a/include/zlib.h +++ b/include/zlib.h @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 2000-2001 Piotr Fusik <fox@scene.pl> */ +/* (C) 2000-2015 Piotr Fusik <fox@scene.pl> */ /* */ /* This file is based on the zlib.h from 'zlib' general purpose compression */ /* library, version 1.1.3, (C) 1995-1998 Jean-loup Gailly and Mark Adler. */ @@ -83,8 +83,8 @@ unsigned __fastcall__ inflatemem (char* dest, const char* source); */ -int __cdecl__ uncompress (char* dest, unsigned* destLen, - const char* source, unsigned sourceLen); +int __fastcall__ uncompress (char* dest, unsigned* destLen, + const char* source, unsigned sourceLen); /* Original zlib description: diff --git a/libsrc/cbm/cbm_load.c b/libsrc/cbm/cbm_load.c index eba864fcc..c1c6f568a 100644 --- a/libsrc/cbm/cbm_load.c +++ b/libsrc/cbm/cbm_load.c @@ -1,9 +1,9 @@ /* ** Marc 'BlackJack' Rintsch, 06.03.2001 ** -** unsigned int __cdecl__ cbm_load(const char* name, -** unsigned char device, -** const unsigned char* data); +** unsigned int __fastcall__ cbm_load(const char* name, +** unsigned char device, +** const unsigned char* data); */ #include <cbm.h> diff --git a/libsrc/common/_afailed.c b/libsrc/common/_afailed.c index 4e56b93f2..7c6df4a2c 100644 --- a/libsrc/common/_afailed.c +++ b/libsrc/common/_afailed.c @@ -1,7 +1,8 @@ /* ** _afailed.c ** -** Ullrich von Bassewitz, 06.06.1998 +** 1998-06-06, Ullrich von Bassewitz +** 2015-03-13, Greg King */ @@ -11,7 +12,7 @@ -void _afailed (char* file, unsigned line) +void __fastcall__ _afailed (char* file, unsigned line) { fprintf (stderr, "ASSERTION FAILED IN %s(%u)\n", file, line); exit (2); diff --git a/libsrc/dbg/dbgdump.s b/libsrc/dbg/dbgdump.s index b2045872f..8ab646d21 100644 --- a/libsrc/dbg/dbgdump.s +++ b/libsrc/dbg/dbgdump.s @@ -1,7 +1,7 @@ ; ; Ullrich von Bassewitz, 11.08.1998 ; -; char* DbgMemDump (unsigend Addr, char* Buf, unsigned char Length); +; char* __cdecl__ DbgMemDump (unsigend Addr, char* Buf, unsigned char Length); ; .export _DbgMemDump diff --git a/libsrc/zlib/uncompress.c b/libsrc/zlib/uncompress.c index 7810eb4f8..4e449a3ef 100644 --- a/libsrc/zlib/uncompress.c +++ b/libsrc/zlib/uncompress.c @@ -6,8 +6,8 @@ #include <zlib.h> -int uncompress (char* dest, unsigned* destLen, - const char* source, unsigned sourceLen) +int __fastcall__ uncompress (char* dest, unsigned* destLen, + const char* source, unsigned sourceLen) { unsigned len; unsigned char* ptr; From b452bdc5e46ce6626293ba8ac1f8a4a9f37f73b6 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Fri, 13 Mar 2015 12:18:43 -0400 Subject: [PATCH 06/14] Catch fastcall pointers to variadic functions. --- src/cc65/declare.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index e6417c6a8..4fab3ea0a 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -353,8 +353,12 @@ static void FixQualifiers (Type* DataType) Error ("Mismatch between pointer's and function's calling conventions"); } } else { - /* Move the qualifier from the pointer to the function. */ - T[1].C |= Q; + if (Q == T_QUAL_FASTCALL && IsVariadicFunc (T + 1)) { + Error ("Variadic-function pointers cannot be `__fastcall__'"); + } else { + /* Move the qualifier from the pointer to the function. */ + T[1].C |= Q; + } } } else { Error ("Not pointer to a function; can't use a calling convention"); From 52f5854813697246e58e3f3b6c234248984e5521 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Sat, 14 Mar 2015 16:50:08 -0400 Subject: [PATCH 07/14] Fixed a typo. --- doc/cc65.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index bf7b1e336..842ba061a 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -3,7 +3,7 @@ <article> <title>cc65 Users Guide <author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> -<date>2015-03-13 +<date>2015-03-14 <abstract> cc65 is a C compiler for 6502 targets. It supports several 6502 based home @@ -614,7 +614,7 @@ This cc65 version has some extensions to the ISO C standard. <p> <item> There is another calling convention named "cdecl". Variadic functions - (their prototypes have an ellipsis [<tt/.../]) always use this + (their prototypes have an ellipsis [<tt/.../]) always use this convention. The syntax for a function declaration using cdecl is <tscreen><verb> From 2842b68a04c024c479baacfafa3752bd78f0c675 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Thu, 19 Mar 2015 06:27:25 -0400 Subject: [PATCH 08/14] Reverted one of the changes in how function prototypes look, in cc65's assembly output. --- src/cc65/datatype.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 8d17d2851..9971a9569 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -290,18 +290,18 @@ void PrintType (FILE* F, const Type* T) fprintf (F, "union %s", ((SymEntry*) T->A.P)->Name); break; case T_TYPE_ARRAY: - if (T->A.L == UNSPECIFIED) { - fprintf (F, "[] "); - } else { - fprintf (F, "[%ld] ", T->A.L); - } /* Recursive call */ PrintType (F, T + 1); + if (T->A.L == UNSPECIFIED) { + fprintf (F, " []"); + } else { + fprintf (F, " [%ld]", T->A.L); + } return; case T_TYPE_PTR: - fprintf (F, "* "); /* Recursive call */ PrintType (F, T + 1); + fprintf (F, " *"); return; case T_TYPE_FUNC: fprintf (F, "function returning "); From 8743e9911d33ca6aa01f8dcb2424644e6e06721e Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Wed, 22 Apr 2015 09:59:23 -0400 Subject: [PATCH 09/14] Added a command-line option to compile a program, with __cdecl__ as the default calling convention. --- doc/cc65.sgml | 31 ++++++++++++++++++++++--------- src/cc65/codeinfo.c | 6 +++++- src/cc65/expr.c | 13 +++++++++---- src/cc65/function.c | 5 ++++- src/cc65/global.c | 3 ++- src/cc65/global.h | 3 ++- src/cc65/main.c | 13 ++++++++++++- 7 files changed, 56 insertions(+), 18 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 842ba061a..fa6b824c6 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -3,7 +3,7 @@ <article> <title>cc65 Users Guide <author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> -<date>2015-03-14 +<date>2015-04-21 <abstract> cc65 is a C compiler for 6502 targets. It supports several 6502 based home @@ -74,6 +74,7 @@ Short options: Long options: --add-source Include source as comment + --all-cdecl Make functions default to __cdecl__ --bss-name seg Set the name of the BSS segment --check-stack Generate stack overflow checks --code-name seg Set the name of the CODE segment @@ -114,6 +115,14 @@ Here is a description of all the command line options: <descrip> + <tag><tt>--all-cdecl</tt></tag> + + Tells the compiler that functions which aren't declared explicitly with + either the <tt/__cdecl__/ or <tt/__fastcall__/ calling conventions should + have the cdecl convention. (Normally, functions that aren't variadic are + fast-called.) + + <label id="option-bss-name"> <tag><tt>--bss-name seg</tt></tag> @@ -550,8 +559,9 @@ and the one defined by the ISO standard: possible. <p> <item> Most of the C library is available only with the fastcall calling - convention (see below). It means that you must not mix pointers to - those functions with pointers to user-written, cdecl functions. + convention (<ref id="extension-fastcall" name="see below">). It means + that you must not mix pointers to those functions with pointers to + user-written, cdecl functions. <p> <item> The <tt/volatile/ keyword doesn't have an effect. This is not as bad as it sounds, since the 6502 has so few registers that it isn't @@ -589,6 +599,7 @@ This cc65 version has some extensions to the ISO C standard. <ref id="inline-asm" name="see there">. <p> +<label id="extension-fastcall"> <item> The normal calling convention -- for non-variadic functions -- is named "fastcall". The syntax for a function declaration that <em/explicitly/ uses fastcall is @@ -611,10 +622,11 @@ This cc65 version has some extensions to the ISO C standard. For functions that are <tt/fastcall/, the rightmost parameter is not pushed on the stack but left in the primary register when the function is called. That significantly reduces the cost of calling functions. + <newline><newline> <p> <item> There is another calling convention named "cdecl". Variadic functions - (their prototypes have an ellipsis [<tt/.../]) always use this + (their prototypes have an ellipsis [<tt/.../]) always use that convention. The syntax for a function declaration using cdecl is <tscreen><verb> @@ -626,16 +638,17 @@ This cc65 version has some extensions to the ISO C standard. </verb></tscreen> An example would be <tscreen><verb> - void __cdecl__ f (unsigned char c) + int * __cdecl__ f (unsigned char c) </verb></tscreen> - The first form of the cdecl keyword is in the user namespace and can - therefore be disabled with the <tt><ref id="option--standard" - name="--standard"></tt> command line option. + + The first form of the cdecl keyword is in the user namespace; + and therefore, can be disabled with the <tt><ref id="option--standard" + name="--standard"></tt> command-line option. For functions that are <tt/cdecl/, the rightmost parameter is pushed onto the stack before the function is called. That increases the cost of calling those functions, especially when they are called from many - places. + places.<newline><newline> <p> <item> There are two pseudo variables named <tt/__AX__/ and <tt/__EAX__/. diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index ae264dce4..de51781a6 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -46,6 +46,7 @@ #include "codeseg.h" #include "datatype.h" #include "error.h" +#include "global.h" #include "reginfo.h" #include "symtab.h" #include "codeinfo.h" @@ -400,7 +401,10 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) */ if ((D->Flags & FD_VARIADIC) != 0) { *Use = REG_Y; - } else if (!IsQualCDecl (E->Type) && D->ParamCount > 0) { + } else if (D->ParamCount > 0 && + (AutoCDecl ? + IsQualFastcall (E->Type) : + !IsQualCDecl (E->Type))) { /* Will use registers depending on the last param. */ switch (CheckedSizeOf (D->LastParam->Type)) { case 1u: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index c8c47f6aa..d615993c4 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1,7 +1,7 @@ /* expr.c ** ** 1998-06-21, Ullrich von Bassewitz -** 2015-03-10, Greg King +** 2015-04-19, Greg King */ @@ -471,9 +471,11 @@ static void FunctionCall (ExprDesc* Expr) /* Handle function pointers transparently */ IsFuncPtr = IsTypeFuncPtr (Expr->Type); if (IsFuncPtr) { - /* Check whether it's a fastcall function that has parameters */ - IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && !IsQualCDecl (Expr->Type + 1) && (Func->ParamCount > 0); + IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && Func->ParamCount > 0 && + (AutoCDecl ? + IsQualFastcall (Expr->Type + 1) : + !IsQualCDecl (Expr->Type + 1)); /* Things may be difficult, depending on where the function pointer ** resides. If the function pointer is an expression of some sort @@ -518,7 +520,10 @@ static void FunctionCall (ExprDesc* Expr) } /* If we didn't inline the function, get fastcall info */ - IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && !IsQualCDecl (Expr->Type); + IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && + (AutoCDecl ? + IsQualFastcall (Expr->Type) : + !IsQualCDecl (Expr->Type)); } /* Parse the parameter list */ diff --git a/src/cc65/function.c b/src/cc65/function.c index d80ba916a..22b305739 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -481,7 +481,10 @@ void NewFunc (SymEntry* Func) PushLiteralPool (Func); /* If this is a fastcall function, push the last parameter onto the stack */ - if ((D->Flags & FD_VARIADIC) == 0 && !IsQualCDecl (Func->Type) && D->ParamCount > 0) { + if ((D->Flags & FD_VARIADIC) == 0 && D->ParamCount > 0 && + (AutoCDecl ? + IsQualFastcall (Func->Type) : + !IsQualCDecl (Func->Type))) { unsigned Flags; /* Generate the push */ diff --git a/src/cc65/global.c b/src/cc65/global.c index e2800a65e..dbdd72f3c 100644 --- a/src/cc65/global.c +++ b/src/cc65/global.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2012, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -44,6 +44,7 @@ unsigned char AddSource = 0; /* Add source lines as comments */ +unsigned char AutoCDecl = 0; /* Make functions default to __cdecl__ */ unsigned char DebugInfo = 0; /* Add debug info to the obj */ unsigned char PreprocessOnly = 0; /* Just preprocess the input */ unsigned char DebugOptOutput = 0; /* Output debug stuff */ diff --git a/src/cc65/global.h b/src/cc65/global.h index 2abd78601..8b0af5a83 100644 --- a/src/cc65/global.h +++ b/src/cc65/global.h @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 1998-2012, Ullrich von Bassewitz */ +/* (C) 1998-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -52,6 +52,7 @@ /* Options */ extern unsigned char AddSource; /* Add source lines as comments */ +extern unsigned char AutoCDecl; /* Make functions default to __cdecl__ */ extern unsigned char DebugInfo; /* Add debug info to the obj */ extern unsigned char PreprocessOnly; /* Just preprocess the input */ extern unsigned char DebugOptOutput; /* Output debug stuff */ diff --git a/src/cc65/main.c b/src/cc65/main.c index ece22041a..6b42c4db1 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 2000-2013, Ullrich von Bassewitz */ +/* (C) 2000-2015, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ @@ -104,6 +104,7 @@ static void Usage (void) "\n" "Long options:\n" " --add-source\t\t\tInclude source as comment\n" + " --all-cdecl\t\t\tMake functions default to __cdecl__\n" " --bss-name seg\t\tSet the name of the BSS segment\n" " --check-stack\t\t\tGenerate stack overflow checks\n" " --code-name seg\t\tSet the name of the CODE segment\n" @@ -350,6 +351,15 @@ static void OptAddSource (const char* Opt attribute ((unused)), +static void OptAllCDecl (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Make functions default to cdecl instead of fastcall. */ +{ + AutoCDecl = 1; +} + + + static void OptBssName (const char* Opt attribute ((unused)), const char* Arg) /* Handle the --bss-name option */ { @@ -790,6 +800,7 @@ int main (int argc, char* argv[]) /* Program long options */ static const LongOpt OptTab[] = { { "--add-source", 0, OptAddSource }, + { "--all-cdecl", 0, OptAllCDecl }, { "--bss-name", 1, OptBssName }, { "--check-stack", 0, OptCheckStack }, { "--code-name", 1, OptCodeName }, From b24c87e61fd2c625ecb0b8db357cc04d631f5633 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Wed, 22 Apr 2015 10:05:07 -0400 Subject: [PATCH 10/14] Changed the compiler test-suite to work with the fastcall-default version of cc65. --- test/ref/otccex.c | 2 +- test/val/Makefile | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/ref/otccex.c b/test/ref/otccex.c index f3d6c71ec..aa5df158f 100644 --- a/test/ref/otccex.c +++ b/test/ref/otccex.c @@ -126,7 +126,7 @@ mymain(int argc,char **argv) } else { /* why not using a function pointer ? */ f = &fact; - print_num((*(long (*)())f)(n), base); + print_num((*(long (*)(int))f)(n), base); } printf("\n"); return 0; diff --git a/test/val/Makefile b/test/val/Makefile index 2efcbd0de..cb954a307 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -26,6 +26,13 @@ TESTS := $(foreach option,. .o. .os. .osi. .osir. .oi. .oir. .or.,$(SOURCES:%.c= all: $(TESTS) +# cq71.c and cq84.c have "K & R"-style syntax. And, some local forward +# function-declarations don't match the later global function definitions. +# Those programs fail when fastcall is used; but, the cdecl calling convention +# tolerates those conflicts. Therefore, make their functions default to cdecl. +# +$(WORKDIR)/cq71%prg $(WORKDIR)/cq84%prg: CC65FLAGS += -Wc --all-cdecl + $(WORKDIR)/%.prg: %.c $(CL65) $(CC65FLAGS) $< -o $@ $(SIM65) $(SIM65FLAGS) $@ From 0bb3bafb3edaf9f86510e145b99352dd1801a042 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Tue, 12 May 2015 04:15:00 -0400 Subject: [PATCH 11/14] Made cc65 catch an assignment of a function pointer to a pointer with a different calling convention. --- src/cc65/typecmp.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 67941026b..b01cd3f51 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1998-2008 Ullrich von Bassewitz */ -/* Roemerstrasse 52 */ -/* D-70794 Filderstadt */ -/* EMail: uz@cc65.org */ +/* (C) 1998-2015, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -37,6 +37,7 @@ /* cc65 */ #include "funcdesc.h" +#include "global.h" #include "symtab.h" #include "typecmp.h" @@ -247,21 +248,21 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } if (LeftQual != RightQual) { /* On the first indirection level, different qualifiers mean - ** that the types are still compatible. On the second level, - ** this is a (maybe minor) error, so we create a special - ** return code, since a qualifier is dropped from a pointer. - ** Starting from the next level, the types are incompatible - ** if the qualifiers differ. + ** that the types still are compatible. On the second level, + ** that is a (maybe minor) error. We create a special return code + ** if a qualifier is dropped from a pointer. But, different calling + ** conventions are incompatible. Starting from the next level, + ** the types are incompatible if the qualifiers differ. */ + /* (Debugging statement) */ /* printf ("Ind = %d %06X != %06X\n", Indirections, LeftQual, RightQual); */ switch (Indirections) { - case 0: SetResult (Result, TC_STRICT_COMPATIBLE); break; case 1: - /* A non const value on the right is compatible to a + /* A non-const value on the right is compatible to a ** const one to the left, same for volatile. */ if ((LeftQual & T_QUAL_CONST) < (RightQual & T_QUAL_CONST) || @@ -270,7 +271,27 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } else { SetResult (Result, TC_STRICT_COMPATIBLE); } - break; + + /* If a calling convention wasn't set explicitly, + ** then assume the default one. + */ + LeftQual &= T_QUAL_CCONV; + if (LeftQual == 0) { + LeftQual = AutoCDecl ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + RightQual &= T_QUAL_CCONV; + if (RightQual == 0) { + RightQual = AutoCDecl ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + + /* (If the objects actually aren't pointers to functions, + ** then this test will pass anyway; and, more appropriate + ** tests will be performed.) + */ + if (LeftQual == RightQual) { + break; + } + /* else fall through */ default: SetResult (Result, TC_INCOMPATIBLE); @@ -280,7 +301,6 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check for special type elements */ switch (LeftType) { - case T_TYPE_PTR: ++Indirections; break; From e72132c8ae54c543a0ede95fe486d6f4cbf33bfc Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Sun, 24 May 2015 08:32:15 -0400 Subject: [PATCH 12/14] Made cc65 properly test variadic-function pointer assignments. Improved some error messages. --- src/cc65/declare.c | 11 ++++------- src/cc65/typecmp.c | 18 +++++++++--------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4fab3ea0a..060a6756d 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -335,26 +335,23 @@ static void FixQualifiers (Type* DataType) T = DataType; while (T->C != T_END) { if (IsTypePtr (T)) { - /* Calling convention qualifier on the pointer? */ if (IsQualCConv (T)) { /* Pull the convention off of the pointer */ Q = T[0].C & T_QUAL_CCONV; T[0].C &= ~T_QUAL_CCONV; + /* Pointer to a function which doesn't have an explicit convention? */ if (IsTypeFunc (T + 1)) { if (IsQualCConv (T + 1)) { - if (T[1].C == Q) { - /* TODO: The end of Declarator() catches this error. - ** Try to make it let the error be caught here, instead. - */ + if ((T[1].C & T_QUAL_CCONV) == Q) { Warning ("Pointer duplicates function's calling convention"); } else { - Error ("Mismatch between pointer's and function's calling conventions"); + Error ("Function's and pointer's calling conventions are different"); } } else { if (Q == T_QUAL_FASTCALL && IsVariadicFunc (T + 1)) { - Error ("Variadic-function pointers cannot be `__fastcall__'"); + Error ("Variadic-function pointers cannot be __fastcall__"); } else { /* Move the qualifier from the pointer to the function. */ T[1].C |= Q; diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index b01cd3f51..8025f4efe 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -249,7 +249,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) if (LeftQual != RightQual) { /* On the first indirection level, different qualifiers mean ** that the types still are compatible. On the second level, - ** that is a (maybe minor) error. We create a special return code + ** that is a (maybe minor) error. We create a special return-code ** if a qualifier is dropped from a pointer. But, different calling ** conventions are incompatible. Starting from the next level, ** the types are incompatible if the qualifiers differ. @@ -272,22 +272,22 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SetResult (Result, TC_STRICT_COMPATIBLE); } + if (LeftType != T_TYPE_FUNC) { + break; + } + /* If a calling convention wasn't set explicitly, ** then assume the default one. */ LeftQual &= T_QUAL_CCONV; - if (LeftQual == 0) { - LeftQual = AutoCDecl ? T_QUAL_CDECL : T_QUAL_FASTCALL; + if (LeftQual == T_QUAL_NONE) { + LeftQual = (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; } RightQual &= T_QUAL_CCONV; - if (RightQual == 0) { - RightQual = AutoCDecl ? T_QUAL_CDECL : T_QUAL_FASTCALL; + if (RightQual == T_QUAL_NONE) { + RightQual = (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; } - /* (If the objects actually aren't pointers to functions, - ** then this test will pass anyway; and, more appropriate - ** tests will be performed.) - */ if (LeftQual == RightQual) { break; } From bbb6f89731ccc54bffcc811e9405192f16cbbd89 Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Sun, 24 May 2015 18:31:50 -0400 Subject: [PATCH 13/14] Made cc65 properly test calling conventions when it compares forward declarations to function definitions. --- src/cc65/declare.c | 2 +- src/cc65/typecmp.c | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 060a6756d..163084835 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1455,7 +1455,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* We cannot specify fastcall for variadic functions */ if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { - Error ("Variadic functions cannot be `__fastcall__'"); + Error ("Variadic functions cannot be __fastcall__"); Qualifiers &= ~T_QUAL_FASTCALL; } diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 8025f4efe..673dfa163 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -246,6 +246,19 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) return; } } + + if (LeftType == T_TYPE_FUNC) { + /* If a calling convention wasn't set explicitly, + ** then assume the default one. + */ + if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) { + LeftQual |= (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) { + RightQual |= (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + } + if (LeftQual != RightQual) { /* On the first indirection level, different qualifiers mean ** that the types still are compatible. On the second level, @@ -272,23 +285,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SetResult (Result, TC_STRICT_COMPATIBLE); } - if (LeftType != T_TYPE_FUNC) { - break; - } - - /* If a calling convention wasn't set explicitly, - ** then assume the default one. - */ - LeftQual &= T_QUAL_CCONV; - if (LeftQual == T_QUAL_NONE) { - LeftQual = (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; - } - RightQual &= T_QUAL_CCONV; - if (RightQual == T_QUAL_NONE) { - RightQual = (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; - } - - if (LeftQual == RightQual) { + if (LeftType != T_TYPE_FUNC || (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) { break; } /* else fall through */ From b49fd26d1683df6cfc669faa853b44f08ccf6cac Mon Sep 17 00:00:00 2001 From: Greg King <gregdk@users.sf.net> Date: Tue, 26 May 2015 11:23:54 -0400 Subject: [PATCH 14/14] Improved the compiler documentation, a little bit. --- doc/cc65.sgml | 62 ++++++++++++++++++++++---------------------- doc/customizing.sgml | 4 +-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index fa6b824c6..ac03c53b2 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -3,7 +3,7 @@ <article> <title>cc65 Users Guide <author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> -<date>2015-04-21 +<date>2015-05-26 <abstract> cc65 is a C compiler for 6502 targets. It supports several 6502 based home @@ -558,10 +558,10 @@ and the one defined by the ISO standard: be passed as parameters by value. However, struct assignment *is* possible. <p> -<item> Most of the C library is available only with the fastcall calling - convention (<ref id="extension-fastcall" name="see below">). It means - that you must not mix pointers to those functions with pointers to - user-written, cdecl functions. +<item> Most of the C library is available with only the fastcall calling + convention (<ref id="extension-fastcall" name="see below">). It means + that you must not mix pointers to those functions with pointers to + user-written, cdecl functions (the calling conventions are incompatible). <p> <item> The <tt/volatile/ keyword doesn't have an effect. This is not as bad as it sounds, since the 6502 has so few registers that it isn't @@ -600,30 +600,30 @@ This cc65 version has some extensions to the ISO C standard. <p> <label id="extension-fastcall"> -<item> The normal calling convention -- for non-variadic functions -- is - named "fastcall". The syntax for a function declaration that - <em/explicitly/ uses fastcall is +<item> The normal calling convention -- for non-variadic functions -- is + named "fastcall". The syntax for a function declaration that + <em/explicitly/ uses fastcall is - <tscreen><verb> - <return type> fastcall <function name> (<parameter list>) - </verb></tscreen> - or - <tscreen><verb> - <return type> __fastcall__ <function name> (<parameter list>) - </verb></tscreen> - An example would be - <tscreen><verb> - void __fastcall__ f (unsigned char c) - </verb></tscreen> - The first form of the fastcall keyword is in the user namespace and can - therefore be disabled with the <tt><ref id="option--standard" + <tscreen><verb> + <return type> fastcall <function name> (<parameter list>) + </verb></tscreen> + or + <tscreen><verb> + <return type> __fastcall__ <function name> (<parameter list>) + </verb></tscreen> + An example is + <tscreen><verb> + void __fastcall__ f (unsigned char c) + </verb></tscreen> + The first form of the fastcall keyword is in the user namespace and can + therefore be disabled with the <tt><ref id="option--standard" name="--standard"></tt> command line option. - For functions that are <tt/fastcall/, the rightmost parameter is not - pushed on the stack but left in the primary register when the function - is called. That significantly reduces the cost of calling functions. - <newline><newline> - <p> + For functions that are <tt/fastcall/, the rightmost parameter is not + pushed on the stack but left in the primary register when the function + is called. That significantly reduces the cost of calling those functions. + <newline><newline> + <p> <item> There is another calling convention named "cdecl". Variadic functions (their prototypes have an ellipsis [<tt/.../]) always use that @@ -636,14 +636,14 @@ This cc65 version has some extensions to the ISO C standard. <tscreen><verb> <return type> __cdecl__ <function name> (<parameter list>) </verb></tscreen> - An example would be + An example is <tscreen><verb> - int * __cdecl__ f (unsigned char c) + int* __cdecl__ f (unsigned char c) </verb></tscreen> The first form of the cdecl keyword is in the user namespace; - and therefore, can be disabled with the <tt><ref id="option--standard" - name="--standard"></tt> command-line option. + and therefore, can be disabled with the <tt/<ref id="option--standard" + name="--standard">/ command-line option. For functions that are <tt/cdecl/, the rightmost parameter is pushed onto the stack before the function is called. That increases the cost @@ -701,7 +701,7 @@ This cc65 version has some extensions to the ISO C standard. </verb></tscreen> Since the variable is of type <tt/void/ you may not use it as is. - However, taking the address of the variable results in a <tt/void */ + However, taking the address of the variable results in a <tt/void*/ which may be passed to any function expecting a pointer. See the <url url="geos.html" name="GEOS library document"> for examples diff --git a/doc/customizing.sgml b/doc/customizing.sgml index a54821c34..0a0b8c87e 100644 --- a/doc/customizing.sgml +++ b/doc/customizing.sgml @@ -597,7 +597,7 @@ variable which is stored in the zero page memory space in order to allow for retrieval of each character in the string via the indirect indexed addressing mode. -The assembly language routine is stored in a file named +The assembly language routine is stored in a file names "rs232_tx.s" and is shown below: <tscreen><code> @@ -681,7 +681,7 @@ all of the behind-the-scene work is transparent to the user. #define TX_FIFO_FULL (FIFO_STATUS & 0x01) #define RX_FIFO_EMPTY (FIFO_STATUS & 0x02) -extern void wait (void); +extern void wait (); extern void __fastcall__ rs232_tx (char *str); int main () {