From 53f0552fe71f1aed5534c8e04f99fb0fa75256cf Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 16 May 2021 17:12:50 +0200 Subject: [PATCH 01/74] fix warnings --- samples/nachtm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/nachtm.c b/samples/nachtm.c index 0b962fa5d..02801da24 100644 --- a/samples/nachtm.c +++ b/samples/nachtm.c @@ -972,12 +972,12 @@ static void MakeNiceScreen (void) /* Clear the screen hide the cursor, set colors */ #ifdef __CBM610__ - textcolor (COLOR_WHITE); + (void)textcolor (COLOR_WHITE); #else - textcolor (COLOR_GRAY3); + (void)textcolor (COLOR_GRAY3); #endif - bordercolor (COLOR_BLACK); - bgcolor (COLOR_BLACK); + (void)bordercolor (COLOR_BLACK); + (void)bgcolor (COLOR_BLACK); clrscr (); cursor (0); From 4ba3ff3048e0baffac77f671c6947afdeee775df Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 16 May 2021 18:23:23 +0200 Subject: [PATCH 02/74] redirect c64 to geos-cbm and apple2enh to geos-apple when given with SYS= on the command line, as suggested by oliver --- samples/geos/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 9ad555565..a894c5d01 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -3,6 +3,17 @@ # var. to build for another target system. SYS ?= geos-cbm +# If SYS was given on the commandline, redirect "c64" to "geos-cbm" and +# "apple2enh" to "geos-apple" +ifeq "$(origin SYS)" "command line" +ifeq "$(SYS)" "c64" + override SYS = geos-cbm +endif +ifeq "$(SYS)" "apple2enh" + override SYS = geos-apple +endif +endif + # Just the usual way to find out if we're # using cmd.exe to execute make rules. ifneq ($(shell echo),) From a9af6aa74334483c09fd367ab01c5d4358ab7120 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 16 May 2021 18:29:45 +0200 Subject: [PATCH 03/74] fix warnings --- samples/geos/geosver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/geos/geosver.c b/samples/geos/geosver.c index 1402d148e..3d68798a2 100644 --- a/samples/geos/geosver.c +++ b/samples/geos/geosver.c @@ -8,8 +8,8 @@ struct window wholeScreen = {0, SC_PIX_HEIGHT-1, 0, SC_PIX_WIDTH-1}; void main (void) { unsigned char os = get_ostype(); - unsigned char *machine = NULL; - unsigned char *version = NULL; + char *machine = NULL; + char *version = NULL; unsigned char good = 1; SetPattern(0); From 43ca88726344b82065bf83dd5b693f7b6006c94e Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 16 May 2021 16:06:52 +0800 Subject: [PATCH 04/74] Fixed 'case'/'default' labels in non-compound 'switch' body statement. --- src/cc65/function.c | 2 +- src/cc65/stmt.c | 177 ++++++++++++++++++++++++++------------------ src/cc65/stmt.h | 2 +- src/cc65/swstmt.c | 2 +- 4 files changed, 106 insertions(+), 77 deletions(-) diff --git a/src/cc65/function.c b/src/cc65/function.c index 4e61cc1d3..452181af9 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -644,7 +644,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Now process statements in this block */ while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { - Statement (0); + AnyStatement (0); } /* If this is not a void function, and not the main function in a C99 diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index ccb113978..022a8475c 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -163,7 +163,7 @@ static int IfStatement (void) TestResult = TestInParens (Label1, 0); /* Parse the if body */ - GotBreak = Statement (0); + GotBreak = AnyStatement (0); /* Else clause present? */ if (CurTok.Tok != TOK_ELSE) { @@ -195,7 +195,7 @@ static int IfStatement (void) g_defcodelabel (Label1); /* Total break only if both branches had a break. */ - GotBreak &= Statement (0); + GotBreak &= AnyStatement (0); /* Generate the label for the else clause */ g_defcodelabel (Label2); @@ -225,7 +225,7 @@ static void DoStatement (void) g_defcodelabel (LoopLabel); /* Parse the loop body */ - Statement (0); + AnyStatement (0); /* Output the label for a continue */ g_defcodelabel (ContinueLabel); @@ -283,7 +283,7 @@ static void WhileStatement (void) g_defcodelabel (LoopLabel); /* Loop body */ - Statement (&PendingToken); + AnyStatement (&PendingToken); /* Emit the while condition label */ g_defcodelabel (CondLabel); @@ -509,7 +509,7 @@ static void ForStatement (void) /* Loop body */ g_defcodelabel (BodyLabel); - Statement (&PendingToken); + AnyStatement (&PendingToken); /* If we had an increment expression, move the code to the bottom of ** the loop. In this case we don't need to jump there at the end of @@ -536,17 +536,20 @@ static void ForStatement (void) -static int CompoundStatement (void) +static int CompoundStatement (int* PendingToken) /* Compound statement. Allow any number of statements inside braces. The ** function returns true if the last statement was a break or return. */ { - int GotBreak; + int GotBreak = 0; /* Remember the stack at block entry */ int OldStack = StackPtr; unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); + /* Skip '{' */ + NextToken (); + /* Enter a new lexical level */ EnterBlockLevel (); @@ -554,16 +557,15 @@ static int CompoundStatement (void) DeclareLocals (); /* Now process statements in this block */ - GotBreak = 0; while (CurTok.Tok != TOK_RCURLY) { if (CurTok.Tok != TOK_CEOF) { - GotBreak = Statement (0); + GotBreak = AnyStatement (0); } else { break; } } - /* Clean up the stack. */ + /* Clean up the stack if the codeflow may reach the end */ if (!GotBreak) { g_space (StackPtr - OldStack); } @@ -583,12 +585,80 @@ static int CompoundStatement (void) /* Leave the lexical level */ LeaveBlockLevel (); + /* Skip '}' */ + CheckTok (TOK_RCURLY, "'}' expected", PendingToken); + return GotBreak; } -int Statement (int* PendingToken) +static void Statement (int* PendingToken) +/* Single-line statement */ +{ + ExprDesc Expr; + unsigned PrevErrorCount; + CodeMark Start, End; + + /* Remember the current error count and code position */ + PrevErrorCount = ErrorCount; + GetCodePos (&Start); + + /* Actual statement */ + ED_Init (&Expr); + Expr.Flags |= E_NEED_NONE; + Expression0 (&Expr); + + /* If the statement didn't generate code, and is not of type + ** void, emit a warning. + */ + GetCodePos (&End); + if (!ED_YetToLoad (&Expr) && + !ED_MayHaveNoEffect (&Expr) && + CodeRangeIsEmpty (&Start, &End) && + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { + Warning ("Expression result unused"); + } + CheckSemi (PendingToken); +} + + + +static int ParseAnyLabels (void) +/* Return -1 if there are any labels with a statement */ +{ + unsigned PrevErrorCount = ErrorCount; + int HasLabels = 0; + for (;;) { + if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { + /* C 'goto' label */ + DoLabel (); + } else if (CurTok.Tok == TOK_CASE) { + /* C 'case' label */ + CaseLabel (); + } else if (CurTok.Tok == TOK_DEFAULT) { + /* C 'default' label */ + DefaultLabel (); + } else { + /* No labels */ + break; + } + HasLabels = 1; + } + + if (HasLabels) { + if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) { + return -1; + } + } + + return 0; +} + + + +int AnyStatement (int* PendingToken) /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or @@ -598,40 +668,27 @@ int Statement (int* PendingToken) ** NULL, the function will skip the token. */ { - ExprDesc Expr; - int GotBreak; - unsigned PrevErrorCount; - CodeMark Start, End; - - ED_Init (&Expr); - /* Assume no pending token */ if (PendingToken) { *PendingToken = 0; } - /* Check for a label. A label is always part of a statement, it does not + /* Handle any labels. A label is always part of a statement, it does not ** replace one. */ - while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { - /* Handle the label */ - DoLabel (); - if (CheckLabelWithoutStatement ()) { - return 0; - } + if (ParseAnyLabels ()) { + return 0; } switch (CurTok.Tok) { - case TOK_LCURLY: - NextToken (); - GotBreak = CompoundStatement (); - CheckTok (TOK_RCURLY, "'{' expected", PendingToken); - return GotBreak; - case TOK_IF: return IfStatement (); + case TOK_SWITCH: + SwitchStatement (); + break; + case TOK_WHILE: WhileStatement (); break; @@ -640,10 +697,15 @@ int Statement (int* PendingToken) DoStatement (); break; - case TOK_SWITCH: - SwitchStatement (); + case TOK_FOR: + ForStatement (); break; + case TOK_GOTO: + GotoStatement (); + CheckSemi (PendingToken); + return 1; + case TOK_RETURN: ReturnStatement (); CheckSemi (PendingToken); @@ -659,55 +721,22 @@ int Statement (int* PendingToken) CheckSemi (PendingToken); return 1; - case TOK_FOR: - ForStatement (); - break; - - case TOK_GOTO: - GotoStatement (); - CheckSemi (PendingToken); - return 1; - - case TOK_SEMI: - /* Ignore it */ - CheckSemi (PendingToken); - break; - case TOK_PRAGMA: DoPragma (); break; - case TOK_CASE: - CaseLabel (); - CheckLabelWithoutStatement (); + case TOK_SEMI: + /* Empty statement. Ignore it */ + CheckSemi (PendingToken); break; - case TOK_DEFAULT: - DefaultLabel (); - CheckLabelWithoutStatement (); - break; + case TOK_LCURLY: + return CompoundStatement (PendingToken); default: - /* Remember the current error count and code position */ - PrevErrorCount = ErrorCount; - GetCodePos (&Start); - - /* Actual statement */ - Expr.Flags |= E_NEED_NONE; - Expression0 (&Expr); - - /* If the statement didn't generate code, and is not of type - ** void, emit a warning. - */ - GetCodePos (&End); - if (!ED_YetToLoad (&Expr) && - !ED_MayHaveNoEffect (&Expr) && - CodeRangeIsEmpty (&Start, &End) && - IS_Get (&WarnNoEffect) && - PrevErrorCount == ErrorCount) { - Warning ("Expression result unused"); - } - CheckSemi (PendingToken); + /* Simple statement */ + Statement (PendingToken); + break; } return 0; } diff --git a/src/cc65/stmt.h b/src/cc65/stmt.h index 04ef728a3..8cff99d92 100644 --- a/src/cc65/stmt.h +++ b/src/cc65/stmt.h @@ -44,7 +44,7 @@ -int Statement (int* PendingToken); +int AnyStatement (int* PendingToken); /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 3878f7b67..ee0bd1a85 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -148,7 +148,7 @@ void SwitchStatement (void) /* Parse the following statement, which may actually be a compound ** statement if there is a curly brace at the current input position */ - HaveBreak = Statement (&RCurlyBrace); + HaveBreak = AnyStatement (&RCurlyBrace); /* Check if we had any labels */ if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { From 1450f146a50ac257eea145607801d4f6b1cb4bfb Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 16 May 2021 16:09:45 +0800 Subject: [PATCH 05/74] Fixed '[]', '()' '.' and '->' operators following a postfix increment/decrement. --- src/cc65/expr.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a63214f49..b68b3abbb 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -80,6 +80,8 @@ static GenDesc GenOASGN = { TOK_OR_ASSIGN, GEN_NOPUSH, g_or }; static void parseadd (ExprDesc* Expr, int DoArrayRef); +static void PostInc (ExprDesc* Expr); +static void PostDec (ExprDesc* Expr); @@ -88,6 +90,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef); /*****************************************************************************/ + static unsigned GlobalModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { @@ -1510,7 +1513,8 @@ static void hie11 (ExprDesc *Expr) Primary (Expr); /* Check for a rhs */ - while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || + while (CurTok.Tok == TOK_INC || CurTok.Tok == TOK_DEC || + CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) { switch (CurTok.Tok) { @@ -1554,6 +1558,14 @@ static void hie11 (ExprDesc *Expr) StructRef (Expr); break; + case TOK_INC: + PostInc (Expr); + break; + + case TOK_DEC: + PostDec (Expr); + break; + default: Internal ("Invalid token in hie11: %d", CurTok.Tok); @@ -2106,13 +2118,6 @@ void hie10 (ExprDesc* Expr) /* An expression */ hie11 (Expr); - /* Handle post increment */ - switch (CurTok.Tok) { - case TOK_INC: PostInc (Expr); break; - case TOK_DEC: PostDec (Expr); break; - default: break; - } - } break; } From ce487651b09901fbd69183e69425d830505900e8 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 16 May 2021 22:24:35 +0200 Subject: [PATCH 06/74] as suggested by Oliver: - only output messages if MAKELEVEL is 0 - indent nested ifeq - use if (,) syntax --- samples/Makefile | 4 +++- samples/geos/Makefile | 20 ++++++++++++-------- samples/geos/grc/Makefile | 4 +++- samples/tutorial/Makefile | 5 ++++- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/samples/Makefile b/samples/Makefile index 90fba50c9..f1e7a1e0b 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -344,7 +344,9 @@ samples: $(EXELIST_$(SYS)) # empty target used to skip systems that will not work with any program in this dir notavailable: - @echo "warning: generic samples not available for" $(SYS) +ifeq ($(MAKELEVEL),0) + @echo "info: generic samples not available for" $(SYS) +endif disk: $(DISK_$(SYS)) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index a894c5d01..0bf862ca7 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -5,13 +5,15 @@ SYS ?= geos-cbm # If SYS was given on the commandline, redirect "c64" to "geos-cbm" and # "apple2enh" to "geos-apple" -ifeq "$(origin SYS)" "command line" -ifeq "$(SYS)" "c64" - override SYS = geos-cbm -endif -ifeq "$(SYS)" "apple2enh" - override SYS = geos-apple -endif +ifeq ($(origin SYS),command line) + ifeq ($(SYS),c64) + override SYS = geos-cbm + $(info GEOS: c64 -> geos-cbm) + endif + ifeq ($(SYS),apple2enh) + override SYS = geos-apple + $(info GEOS: apple2enh -> geos-apple) + endif endif # Just the usual way to find out if we're @@ -80,7 +82,9 @@ samples: $(EXELIST_$(SYS)) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) else samples: - @echo "warning: geos samples not available for" $(SYS) + ifeq ($(MAKELEVEL),0) + @echo "info: geos samples not available for" $(SYS) + endif endif diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 0a432fd89..4ad4f00a6 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -41,7 +41,9 @@ ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) else samples: - @echo "warning: grc sample not available for" $(SYS) + ifeq ($(MAKELEVEL),0) + @echo "info: grc sample not available for" $(SYS) + endif endif test.s: test.grc diff --git a/samples/tutorial/Makefile b/samples/tutorial/Makefile index 39b2d6e8c..911988db5 100644 --- a/samples/tutorial/Makefile +++ b/samples/tutorial/Makefile @@ -81,6 +81,9 @@ hello: hello.c text.s # empty target used to skip systems that will not work with any program in this dir notavailable: - @echo "warning: tutorial sample not available for" $(SYS) +ifeq ($(MAKELEVEL),0) + @echo "info: tutorial sample not available for" $(SYS) +endif + clean: @$(DEL) hello 2>$(NULLDEV) From 5fc9d3f048f8897bce1c56a4359af1221f6141b9 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 16 May 2021 22:57:28 +0200 Subject: [PATCH 07/74] change driver _install calls from taking "void*" to "const void*" --- include/em.h | 2 +- include/joystick.h | 2 +- include/serial.h | 2 +- include/tgi.h | 2 +- libsrc/em/em-kernel.s | 2 +- libsrc/joystick/joy-kernel.s | 2 +- libsrc/serial/ser-kernel.s | 2 +- libsrc/tgi/tgi-kernel.s | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/em.h b/include/em.h index b151800ae..47bd23222 100644 --- a/include/em.h +++ b/include/em.h @@ -82,7 +82,7 @@ unsigned char __fastcall__ em_load_driver (const char* driver); unsigned char em_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ em_install (void* driver); +unsigned char __fastcall__ em_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char em_uninstall (void); diff --git a/include/joystick.h b/include/joystick.h index b6771c381..963c9ba95 100644 --- a/include/joystick.h +++ b/include/joystick.h @@ -89,7 +89,7 @@ unsigned char __fastcall__ joy_load_driver (const char* driver); unsigned char joy_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ joy_install (void* driver); +unsigned char __fastcall__ joy_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char joy_uninstall (void); diff --git a/include/serial.h b/include/serial.h index 990f66521..58943d507 100644 --- a/include/serial.h +++ b/include/serial.h @@ -136,7 +136,7 @@ unsigned char __fastcall__ ser_load_driver (const char* driver); unsigned char ser_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ ser_install (void* driver); +unsigned char __fastcall__ ser_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char ser_uninstall (void); diff --git a/include/tgi.h b/include/tgi.h index 135ef63f1..2ef65b856 100644 --- a/include/tgi.h +++ b/include/tgi.h @@ -82,7 +82,7 @@ void tgi_unload (void); ** necessary. */ -void __fastcall__ tgi_install (void* driver); +void __fastcall__ tgi_install (const void* driver); /* Install an already loaded driver. */ void tgi_uninstall (void); diff --git a/libsrc/em/em-kernel.s b/libsrc/em/em-kernel.s index c982dac88..d5be0ae23 100644 --- a/libsrc/em/em-kernel.s +++ b/libsrc/em/em-kernel.s @@ -36,7 +36,7 @@ emd_sig: .byte $65, $6d, $64, EMD_API_VERSION ; "emd", version ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ em_install (void* driver); +; unsigned char __fastcall__ em_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/joystick/joy-kernel.s b/libsrc/joystick/joy-kernel.s index c2d50c8d8..53d475c57 100644 --- a/libsrc/joystick/joy-kernel.s +++ b/libsrc/joystick/joy-kernel.s @@ -33,7 +33,7 @@ joy_sig: .byte $6A, $6F, $79, JOY_API_VERSION ; "joy", version .code ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ joy_install (void* driver); +; unsigned char __fastcall__ joy_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/serial/ser-kernel.s b/libsrc/serial/ser-kernel.s index 4c5b455b6..b6c57a3b5 100644 --- a/libsrc/serial/ser-kernel.s +++ b/libsrc/serial/ser-kernel.s @@ -39,7 +39,7 @@ ser_sig: .byte $73, $65, $72, SER_API_VERSION ; "ser", version .code ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ ser_install (void* driver); +; unsigned char __fastcall__ ser_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/tgi/tgi-kernel.s b/libsrc/tgi/tgi-kernel.s index 3a388b6dc..cbd655279 100644 --- a/libsrc/tgi/tgi-kernel.s +++ b/libsrc/tgi/tgi-kernel.s @@ -88,7 +88,7 @@ tgi_sig: .byte $74, $67, $69, TGI_API_VERSION ; "tgi", version .code ;---------------------------------------------------------------------------- -; void __fastcall__ tgi_install (void* driver); +; void __fastcall__ tgi_install (const void* driver); ; /* Install an already loaded driver. */ From d2da30a7e25cb6f918af10b6d69dbabdc369512a Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 16 May 2021 23:38:17 +0200 Subject: [PATCH 08/74] give the "samples" target something to do to supress the "nothing to be done for 'samples'" message --- samples/geos/Makefile | 4 +++- samples/geos/grc/Makefile | 3 +++ samples/tutorial/Makefile | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 0bf862ca7..e792c52f1 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -84,10 +84,12 @@ else samples: ifeq ($(MAKELEVEL),0) @echo "info: geos samples not available for" $(SYS) + else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) endif endif - bitmap.c: logo.pcx $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 4ad4f00a6..9dd9ebc5e 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -43,6 +43,9 @@ else samples: ifeq ($(MAKELEVEL),0) @echo "info: grc sample not available for" $(SYS) + else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) endif endif diff --git a/samples/tutorial/Makefile b/samples/tutorial/Makefile index 911988db5..af5062588 100644 --- a/samples/tutorial/Makefile +++ b/samples/tutorial/Makefile @@ -83,6 +83,9 @@ hello: hello.c text.s notavailable: ifeq ($(MAKELEVEL),0) @echo "info: tutorial sample not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) endif clean: From c53059468e8a96e4ade3781aa87a0faa5f1abc4a Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 17 May 2021 14:25:18 +0200 Subject: [PATCH 09/74] fix documentation for the driver _init calls --- doc/funcref.sgml | 6 +++--- doc/tgi.sgml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 4d1a278b0..2cb8bbf44 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -3410,7 +3410,7 @@ loaded. / - / - / - / - Date: Mon, 17 May 2021 14:25:33 +0200 Subject: [PATCH 10/74] added a missing comment --- test/misc/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/test/misc/Makefile b/test/misc/Makefile index 397635afd..b17c69f5c 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -64,6 +64,7 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) $(if $(QUIET),echo misc/bug760.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error $(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1437.$1.$2.prg) From 4a57656f692c5fb08fe80e0c665d852d8e32388b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 17 May 2021 14:40:09 +0200 Subject: [PATCH 11/74] add test for issue #1504 --- test/val/bug1504.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/val/bug1504.c diff --git a/test/val/bug1504.c b/test/val/bug1504.c new file mode 100644 index 000000000..bd93c7387 --- /dev/null +++ b/test/val/bug1504.c @@ -0,0 +1,13 @@ + +/* bug #1504 - Some compilation failures */ + +#include + +int main(void) +{ + int i = 0, *p = &i; + switch (i) case 0: case 1: i = 21; /* Should be OK but fails */ + p++[0] += 21; /* Should be OK but fails */ + printf("%d\n", i); + return i != 42; +} From d14148ab4f9d8d1d52025eac31bfc09d977fbaa7 Mon Sep 17 00:00:00 2001 From: Greg King Date: Mon, 17 May 2021 19:48:47 -0400 Subject: [PATCH 12/74] Restricted commit b9a3c7888822732a0de92741cfe1a3e1b6bb272f to classic-style Assembly macros. .include will work at expansion-time for .define macros. --- src/ca65/macro.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 34d87e65f..d6c807035 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -438,9 +438,7 @@ void MacDef (unsigned Style) /* Parse the parameter list */ if (HaveParams) { - while (CurTok.Tok == TOK_IDENT) { - /* Create a struct holding the identifier */ IdDesc* I = NewIdDesc (&CurTok.SVal); @@ -449,6 +447,7 @@ void MacDef (unsigned Style) M->Params = I; } else { IdDesc* List = M->Params; + while (1) { if (SB_Compare (&List->Id, &CurTok.SVal) == 0) { Error ("Duplicate symbol '%m%p'", &CurTok.SVal); @@ -490,9 +489,8 @@ void MacDef (unsigned Style) ** the .LOCAL command is detected and removed, at this time. */ while (1) { - /* Check for include */ - if (CurTok.Tok == TOK_INCLUDE) { + if (CurTok.Tok == TOK_INCLUDE && Style == MAC_STYLE_CLASSIC) { /* Include another file */ NextTok (); /* Name must follow */ @@ -529,9 +527,7 @@ void MacDef (unsigned Style) /* Check for a .LOCAL declaration */ if (CurTok.Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) { - while (1) { - IdDesc* I; /* Skip .local or comma */ @@ -570,6 +566,7 @@ void MacDef (unsigned Style) if (CurTok.Tok == TOK_IDENT) { unsigned Count = 0; IdDesc* I = M->Params; + while (I) { if (SB_Compare (&I->Id, &CurTok.SVal) == 0) { /* Local param name, replace it */ From 6d560f42366ecbd12f05f6ce121ffa7685497114 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 May 2021 15:16:14 +0200 Subject: [PATCH 13/74] change prototype for GraphicsString() to void __fastcall__ GraphicsString(const void *myGfxString); --- doc/geos.sgml | 2 +- include/geos/ggraph.h | 2 +- libsrc/geos-common/graph/graphicsstring.s | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/geos.sgml b/doc/geos.sgml index 11ff9c534..f7943f8cd 100644 --- a/doc/geos.sgml +++ b/doc/geos.sgml @@ -205,7 +205,7 @@ see them together in the filling box in GeoPaint. GraphicsString

- One of the more powerfull routines of GEOS. This function calls other graphic functions depending on the given command string. See the structures chapter for a more detailed description. diff --git a/include/geos/ggraph.h b/include/geos/ggraph.h index ec9fb0fa1..b550d6ca1 100644 --- a/include/geos/ggraph.h +++ b/include/geos/ggraph.h @@ -43,7 +43,7 @@ void __fastcall__ BitOtherClip(void *proc1, void *proc2, char skipl, char skipr, unsigned skiptop, struct iconpic *myIcon); -void __fastcall__ GraphicsString(char *myGfxString); +void __fastcall__ GraphicsString(const void *myGfxString); #ifdef __GEOS_CBM__ void SetNewMode(void); diff --git a/libsrc/geos-common/graph/graphicsstring.s b/libsrc/geos-common/graph/graphicsstring.s index 86c74f1a6..282649284 100644 --- a/libsrc/geos-common/graph/graphicsstring.s +++ b/libsrc/geos-common/graph/graphicsstring.s @@ -3,7 +3,7 @@ ; ; 25.12.99 -; void GraphicsString (char *myString); +; void GraphicsString (const void *myString); .export _GraphicsString From 0ae1aad3d3705b5cbae0d0ff74fae711bc505f52 Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Wed, 19 May 2021 21:08:43 +0300 Subject: [PATCH 14/74] Fix typo: VIAx_CR -> VIAx_ACR --- asminc/vic20.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asminc/vic20.inc b/asminc/vic20.inc index 41a935238..7184ab6ee 100644 --- a/asminc/vic20.inc +++ b/asminc/vic20.inc @@ -90,7 +90,7 @@ VIA1_T1LH := VIA1+$7 ; Timer 1 latch, high byte VIA1_T2CL := VIA1+$8 ; Timer 2, low byte VIA1_T2CH := VIA1+$9 ; Timer 2, high byte VIA1_SR := VIA1+$A ; Shift register -VIA1_CR := VIA1+$B ; Auxiliary control register +VIA1_ACR := VIA1+$B ; Auxiliary control register VIA1_PCR := VIA1+$C ; Peripheral control register VIA1_IFR := VIA1+$D ; Interrupt flag register VIA1_IER := VIA1+$E ; Interrupt enable register @@ -112,7 +112,7 @@ VIA2_T1LH := VIA2+$7 ; Timer 1 latch, high byte VIA2_T2CL := VIA2+$8 ; Timer 2, low byte VIA2_T2CH := VIA2+$9 ; Timer 2, high byte VIA2_SR := VIA2+$A ; Shift register -VIA2_CR := VIA2+$B ; Auxiliary control register +VIA2_ACR := VIA2+$B ; Auxiliary control register VIA2_PCR := VIA2+$C ; Peripheral control register VIA2_IFR := VIA2+$D ; Interrupt flag register VIA2_IER := VIA2+$E ; Interrupt enable register From 681c9594cc37581c50d48b67364398bdedeae4bf Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Fri, 21 May 2021 01:45:55 +0200 Subject: [PATCH 15/74] libsrc/atari5200/crt0.s: fix formatting --- libsrc/atari5200/crt0.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari5200/crt0.s b/libsrc/atari5200/crt0.s index 9538d3bb4..8f6e02273 100644 --- a/libsrc/atari5200/crt0.s +++ b/libsrc/atari5200/crt0.s @@ -44,4 +44,4 @@ _exit: jsr donelib ; Run module destructors ; A 5200 program isn't supposed to exit. -halt: jmp halt +halt: jmp halt From 663268dca98856a0280a49cc0582a77cd511a05f Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 21 May 2021 03:32:43 -0400 Subject: [PATCH 16/74] Syncronize the Supervision crt0.s with its ld65 config files. .segment "VECTOR" -> "VECTORS". Fixes #1506. --- libsrc/supervision/crt0.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/supervision/crt0.s b/libsrc/supervision/crt0.s index 203c681b8..fbae1fc46 100644 --- a/libsrc/supervision/crt0.s +++ b/libsrc/supervision/crt0.s @@ -67,13 +67,13 @@ not_dma: ; Removing this segment gives only a warning. .segment "FFF0" .proc reset32kcode - lda #(6<<5) | SV_LCD_ON | SV_NMI_ENABLE_ON + lda #(6<<5) | SV_LCD_ON | SV_NMI_ENABLE_ON sta sv_bank ; Now, the 32Kbyte image can reside in the top of 64Kbyte and 128Kbyte ROMs. jmp reset .endproc - .segment "VECTOR" + .segment "VECTORS" .word nmi .word reset32kcode From e13f57e86c37ae2601bed1d980fc1fa73915ff3b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 21 May 2021 16:09:10 +0200 Subject: [PATCH 17/74] added another testcase for issue #1462 --- test/todo/bug1462-2.c | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/todo/bug1462-2.c diff --git a/test/todo/bug1462-2.c b/test/todo/bug1462-2.c new file mode 100644 index 000000000..df94cfc59 --- /dev/null +++ b/test/todo/bug1462-2.c @@ -0,0 +1,51 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* even the = operation is buggy in certain ways */ + +#include + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; +} T; + +int failures = 0; + +T *f(T *t) +{ + t->a = 0; + t->c = 0; + return t; +} + +void test(void) +{ + T a = { 7, 0, 7 }; + T *p = &a; + + a.b = f(p)->a; + + if (a.a != 0) { + ++failures; + } + printf("%d\n", a.a); + + if (p->b != 0) { + ++failures; + } + printf("%d\n", p->b); + + if ((&a)->c != 0) { + ++failures; + } + printf("%d\n", (&a)->c); + + printf("Failures: %d\n", failures); +} + +int main(void) +{ + test(); + return failures; +} From b08dc28cc1cb2241515d69ccdd89a3ac31afc788 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sat, 22 May 2021 22:34:52 +0200 Subject: [PATCH 18/74] another testcase related to issue #1462 --- test/todo/bug1462-3.c | 95 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 test/todo/bug1462-3.c diff --git a/test/todo/bug1462-3.c b/test/todo/bug1462-3.c new file mode 100644 index 000000000..b75d568b9 --- /dev/null +++ b/test/todo/bug1462-3.c @@ -0,0 +1,95 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* More testson "op= expression result value" that a naive fix might fail with */ + +#include + +typedef struct { + signed int a : 3; + unsigned int b : 3; + signed int c : 3; + unsigned int d : 3; +} T1; + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; + signed int d : 3; +} T2; + + +int failures1 = 0; +int failures2 = 0; + +void test1(void) +{ + T1 a = { 3, 3, 3, 3 }; + int i; + + i = a.a += a.b + a.c; + if (i != 1) { + ++failures1; + } + printf("i = %d, a.a = %d\n", i, a.a); + + i = a.b *= -1; + if (i != 5 || a.b != 5) { + ++failures1; + } + printf("i = %d, a.b = %d\n", i, a.b); + + i = a.c * -1; + if (i != -3) { + ++failures1; + } + printf("i = %d, a.c = %d\n", i, a.c); + + i = a.d ^= -1; + if (i != 4 || a.d != 4) { + ++failures1; + } + printf("i = %d, a.d = %d\n", i, a.d); + + printf("Failures: %d\n", failures1); +} + +void test2(void) +{ + T2 b = { 3, 3, 4, 4 }; + int i; + + i = b.a++; + if (i != 3 || b.a != -4) { + ++failures2; + } + printf("i = %d, b.a = %d\n", i, b.a); + + i = ++b.b; + if (i != -4 || b.b != -4) { + ++failures2; + } + printf("i = %d, b.b = %d\n", i, b.b); + + i = b.c--; + if (i != -4 || b.c != 3) { + ++failures2; + } + printf("i = %d, b.c = %d\n", i, b.c); + + i = --b.d; + if (i != 3 || b.d != 3) { + ++failures2; + } + printf("i = %d, b.d = %d\n", i, b.d); + + printf("Failures: %d\n", failures2); +} + +int main(void) +{ + test1(); + test2(); + return failures1 + failures2; +} + From 0db23a8951ee43e8fdcb4377a58b045af6142983 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 23 May 2021 18:55:06 +0200 Subject: [PATCH 19/74] testcase for issue #263 --- test/val/bug263.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/val/bug263.c diff --git a/test/val/bug263.c b/test/val/bug263.c new file mode 100644 index 000000000..6f3f8f57e --- /dev/null +++ b/test/val/bug263.c @@ -0,0 +1,49 @@ + +/* issue #263 - cc65 miscompiles w/ a static variable and -O */ + +#include +#include + +int failures = 0; + +void __fastcall__ set_vram_update(unsigned char *ptr) +{ + printf("set_vram_update: %04x\n", ptr); + if (ptr != NULL) { + failures++; + } +} + +unsigned char __fastcall__ ppu_wait_nmi(void) +{ + // we need to make sure somehow the akku is not zero before the break + return 0x1234; +} + +unsigned char ctrl, ret, i; + +unsigned char gameloop (void) +{ + ctrl = 0; + ret = 0; + while(1) { + if (ctrl & 1) { + while (--i) { + ppu_wait_nmi(); + } + break; + } + ctrl = 1; + } + // This will pass garbage, not NULL. + set_vram_update(NULL); + return ret; +} + +int main(void) +{ + gameloop(); + printf("failures: %d\n", failures); + return failures; +} + From adda9438d25212740e169443936ba3a79e2b6745 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 24 May 2021 13:53:14 +0200 Subject: [PATCH 20/74] testcase for issue #1357 --- test/misc/Makefile | 6 ++++++ test/misc/bug1357.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/misc/bug1357.c diff --git a/test/misc/Makefile b/test/misc/Makefile index b17c69f5c..c8b130ec4 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -106,6 +106,12 @@ $(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error +$(WORKDIR)/bug1357.$1.$2.prg: bug1357.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1357.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # this one requires --std=c89, it fails with --std=c99 $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1265.$1.$2.prg) diff --git a/test/misc/bug1357.c b/test/misc/bug1357.c new file mode 100644 index 000000000..40415f868 --- /dev/null +++ b/test/misc/bug1357.c @@ -0,0 +1,30 @@ + +/* issue #1357 - X Macros don't work with C preprocessor */ + +#define OPCODES(X) \ + X(PUSHNIL) \ + X(PUSHTRUE) \ + X(PUSHFALSE) + +enum { +#define X(op) op, +OPCODES(X) +#undef X + N_OPS +}; + +/* cc65 -E bug1357.c -o bug1357.c.pre + should produce something like this: + +enum { +PUSHNIL, +PUSHTRUE, +PUSHFALSE, + N_OPS +}; +*/ + +int main(void) +{ + return 0; +} From ae3d3a4b5dd499406713e42254dc4b1626fd10dd Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 24 May 2021 13:53:44 +0200 Subject: [PATCH 21/74] make readme a bit more clear (hopefully) --- test/readme.txt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/readme.txt b/test/readme.txt index 0523482fd..49ae363cc 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -1,6 +1,13 @@ This directory contains test code for automatic regression testing of the CC65 -compiler. +compiler and tools. +/asm - contains the assembler regression tests + +/dasm - contains the disassembler regression tests + + +/val, /ref and /err generally contain the tests that are used to verify that the +compiler is working as expected (when the tests behave as described): /val - The bulk of tests are contained here, individual tests should exit with an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error. @@ -9,6 +16,9 @@ compiler. /err - contains tests that MUST NOT compile + +/todo and /misc generally contain the tests that fail because of known bugs: + /todo - These tests fail due to open compiler issues. The makefile in this directory _expects_ the tests to fail, because of @@ -16,9 +26,6 @@ compiler. moved to /val in the PR fixing the issue, which will make CI pass again. No changes to makefiles are required! -/asm - contains the assembler regression tests - -/dasm - contains the disassembler regression tests /misc - a few tests that need special care of some sort From 022935320c61ac491cf20afe887b30f1534d0117 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 24 May 2021 14:30:10 +0200 Subject: [PATCH 22/74] test for issue #897 --- test/val/bug897.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/val/bug897.c diff --git a/test/val/bug897.c b/test/val/bug897.c new file mode 100644 index 000000000..eaf751441 --- /dev/null +++ b/test/val/bug897.c @@ -0,0 +1,52 @@ + +/* issue #897 - __asm__()-referenced code-labels are generated for only branches and jumps */ + +#include +#include + +static unsigned char *srcptr, *dstptr; + +#define COPY_LEN 16 + +void test(void) +{ + asm("lda %v", srcptr); + asm("sta %g+1", s2b_copy_from); + asm("lda %v+1", srcptr); + asm("sta %g+2", s2b_copy_from); + + asm("lda %v", dstptr); + asm("sta %g+1", s2b_copy_to); + asm("lda %v+1", dstptr); + asm("sta %g+2", s2b_copy_to); + + asm("ldy #%b", COPY_LEN-1); +s2b_copy_from: + asm("lda $FFFF,y"); +s2b_copy_to: + asm("sta $FFFF,y"); + asm("dey"); + asm("bpl %g", s2b_copy_from); +} + +unsigned char src[16] = "0123456789abcdef"; +unsigned char dest[16]; + +int failures = 0; + +unsigned char i; + +int main(void) +{ + srcptr = src; + dstptr = dest; + test(); + for (i = 0; i < COPY_LEN; i++) { + printf("%d %02x %02x\n", i, src[i], dest[i]); + if (src[i] != dest[i]) { + failures++; + } + } + printf("failures: %d\n", failures); + return failures; +} From 65c640d2cf1059e97e518feb3a3a348a70a95636 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 24 May 2021 15:15:07 +0200 Subject: [PATCH 23/74] added missing atari5200 target --- samples/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/Makefile b/samples/Makefile index f1e7a1e0b..7e5c1934d 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -190,6 +190,9 @@ EXELIST_atarixl = $(EXELIST_atari) EXELIST_atari2600 = \ atari2600hello +EXELIST_atari5200 = \ + notavailable + EXELIST_atmos = \ ascii \ hello \ From 30830e1348f516626aab482b311e741428f411bc Mon Sep 17 00:00:00 2001 From: Polluks Date: Sun, 23 May 2021 10:19:00 +0200 Subject: [PATCH 24/74] Added missing Creativision functions --- doc/funcref.sgml | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 2cb8bbf44..792a51741 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -298,6 +298,16 @@ function. +

+ + + + + + + + +

@@ -1659,6 +1669,41 @@ used in presence of a prototype. +bios_playsound

+ + + +/ + +BASIC has a fixed tempo of 18. +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + +static const unsigned char notes[] = { + 0x77, 0x4F, 0x37, + 0x4B, 0x05, 0xBB, + 0x4F, 0x27, 0x83, + 0x93, 0x9B, 0x93, + 0x17, 0x4F, 0x96, + 0xAB, 0x17, 0x4F, + 0x0E +}; +bios_playsound (notes, sizeof notes); + + + + + bgcolor

@@ -5769,6 +5814,73 @@ be used in presence of a prototype. +psg_delay

+ + + +/ + +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + + + + +psg_outb

+ + + +/ + +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + +psg_outb (0x80); // Latch frequency +psg_outb (0x07); // Frequency byte 2 +psg_outb (0x90); // Channel 0 full volume + + + + + +psg_silence

+ + + +/ + + +, + + + + + qsort

From 010eea12a2fe03d6fd41aca307076aed8d1299c0 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 25 May 2021 13:38:06 +0200 Subject: [PATCH 25/74] move test for issue #1211 into misc --- test/misc/Makefile | 6 ++++++ test/{err => misc}/bug1211-ice-move-refs-2.c | 0 2 files changed, 6 insertions(+) rename test/{err => misc}/bug1211-ice-move-refs-2.c (100%) diff --git a/test/misc/Makefile b/test/misc/Makefile index c8b130ec4..e6c58c5a4 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -112,6 +112,12 @@ $(WORKDIR)/bug1357.$1.$2.prg: bug1357.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1357.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but compiler exits with internal error +$(WORKDIR)/bug1211-ice-move-refs-2.$1.$2.prg: bug1211-ice-move-refs-2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1211-ice-move-refs-2.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # this one requires --std=c89, it fails with --std=c99 $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1265.$1.$2.prg) diff --git a/test/err/bug1211-ice-move-refs-2.c b/test/misc/bug1211-ice-move-refs-2.c similarity index 100% rename from test/err/bug1211-ice-move-refs-2.c rename to test/misc/bug1211-ice-move-refs-2.c From 6bedade593e2b34fae4f86e11579ca92d18d00f6 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sat, 29 May 2021 08:37:38 -0400 Subject: [PATCH 26/74] Fixed the creativision function prototypes. Added const to a pointer parameter. --- include/creativision.h | 8 ++++---- libsrc/creativision/psg.s | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/creativision.h b/include/creativision.h index 40b9ee03a..6910ee0cf 100644 --- a/include/creativision.h +++ b/include/creativision.h @@ -70,9 +70,9 @@ #define COLOR_WHITE 15 /* Protos */ -void __fastcall__ psg_outb(unsigned char b); -void __fastcall__ psg_delay(unsigned char b); -void psg_silence(void); -void __fastcall__ bios_playsound(void *a, unsigned char b); +void __fastcall__ psg_outb (unsigned char b); +void __fastcall__ psg_delay (unsigned char b); +void psg_silence (void); +void __fastcall__ bios_playsound (const void *a, unsigned char b); #endif /* #ifndef _CVISION_H */ diff --git a/libsrc/creativision/psg.s b/libsrc/creativision/psg.s index a32e87f02..18f4ffe2e 100644 --- a/libsrc/creativision/psg.s +++ b/libsrc/creativision/psg.s @@ -1,7 +1,7 @@ -; void __fastcall__ psg_outb( unsigned char b ); -; void __fastcall__ psg_delayms( unsigned char c); -; void __fastcall__ bios_playsound( void *b, unsigned char c); -; void psg_silence( void ); +; void __fastcall__ psg_outb (unsigned char b); +; void __fastcall__ psg_delay (unsigned char c); +; void __fastcall__ bios_playsound (const void *b, unsigned char c); +; void psg_silence (void); .export _psg_outb, _psg_silence, _psg_delay .export _bios_playsound From c0f29993e06c1fbe81f7367e8445f51db05d5fc9 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sat, 29 May 2021 08:53:13 -0400 Subject: [PATCH 27/74] Fixed the descriptions of the Creativision's functions. Fixed bios_playsound()'s position in the alphabetical list of functions. --- doc/funcref.sgml | 112 ++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 792a51741..28faa068a 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -1669,41 +1669,6 @@ used in presence of a prototype. -bios_playsound

- - - -/ - -BASIC has a fixed tempo of 18. -The function is only available as fastcall function, so it may only be -used in presence of a prototype. - -, - - -static const unsigned char notes[] = { - 0x77, 0x4F, 0x37, - 0x4B, 0x05, 0xBB, - 0x4F, 0x27, 0x83, - 0x93, 0x9B, 0x93, - 0x17, 0x4F, 0x96, - 0xAB, 0x17, 0x4F, - 0x0E -}; -bios_playsound (notes, sizeof notes); - - - - - bgcolor

@@ -1729,6 +1694,44 @@ used in presence of a prototype. +bios_playsound

+ + + +/ + +BASIC has a fixed tempo of 18. +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + +#include +void main (void) +{ + static const unsigned char notes[] = { + 0x77, 0x4F, 0x37, + 0x4B, 0x05, 0xBB, + 0x4F, 0x27, 0x83, + 0x93, 0x9B, 0x93, + 0x17, 0x4F, 0x96, // played backwards + 0xAB, 0x17, 0x4F, // three-note chords + 0x0E // tempo + }; + bios_playsound (notes, sizeof notes); +} + + + + + bordercolor

@@ -1823,7 +1826,7 @@ be used in presence of a prototype. The function is specific to the C128. The function will not return to the caller. - @@ -5820,14 +5823,14 @@ be used in presence of a prototype. / - The function is only available as fastcall function, so it may only be used in presence of a prototype. -, @@ -5840,23 +5843,29 @@ used in presence of a prototype. -/ - The function is only available as fastcall function, so it may only be used in presence of a prototype. -, - -psg_outb (0x80); // Latch frequency -psg_outb (0x07); // Frequency byte 2 -psg_outb (0x90); // Channel 0 full volume + +#include +void main (void) +{ + psg_outb (0x80); // Latch frequency + psg_outb (0x07); // Frequency byte 2 + psg_outb (0x90); // Channel 0 full volume + psg_delay (100); + psg_silence (); +} @@ -5866,13 +5875,14 @@ psg_outb (0x90); // Channel 0 full volume -/ - -, From ee5014c5952cb0a50f0e870911941694fee291d6 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sat, 29 May 2021 09:04:47 -0400 Subject: [PATCH 28/74] Simplified the Supervision "hello world" sample program. --- samples/supervisionhello.c | 134 +++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/samples/supervisionhello.c b/samples/supervisionhello.c index db2b5f66c..ed03e82f3 100644 --- a/samples/supervisionhello.c +++ b/samples/supervisionhello.c @@ -3,89 +3,93 @@ /* Watara Supervision sample C program */ /* */ /* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */ +/* Greg King (greg.king5@verizon.net), 2021 */ /* */ /*****************************************************************************/ #include -#include +#include -// Number of bytes per screen line (Remark: Last 8 bytes are not displayed) -#define BYTES_PER_LINE 48 +/* Number of words per screen line (Remark: Last 4 words aren't displayed) */ +#define WORDS_PER_LINE (160/8+4) -// Character definitions in 8x8 format -const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; -const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}; -const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}; -const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}; -const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}; -const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}; -const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}; +struct sv_vram { + unsigned int v[160/8][8][WORDS_PER_LINE]; +}; +#define SV_VRAM ((*(struct sv_vram *)0x4000).v) -void clear_screen(void) +/* Character definitions in 8x8 format */ +/* That format gives us a screen of 20 columns and 20 rows */ +static const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; +static const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}; +static const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}; +static const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}; +static const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}; +static const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}; +static const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}; + +static void clear_screen(void) { - unsigned short i; - - for(i=0;i<0x2000;++i) + memset(SV_VIDEO, 0, 0x2000); +} + +/* Necessary conversion to have 2 bits per pixel with darkest hue */ +/* Remark: The Supervision uses 2 bits per pixel, and bits are mapped into pixels in reversed order */ +static unsigned int __fastcall__ double_reversed_bits(unsigned char) +{ + __asm__("stz ptr2"); + __asm__("stz ptr2+1"); + __asm__("ldy #$08"); +L1: __asm__("lsr a"); + __asm__("php"); + __asm__("rol ptr2"); + __asm__("rol ptr2+1"); + __asm__("plp"); + __asm__("rol ptr2"); + __asm__("rol ptr2+1"); + __asm__("dey"); + __asm__("bne %g", L1); + __asm__("lda ptr2"); + __asm__("ldx ptr2+1"); + return __AX__; +} + +static void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch) +{ + unsigned char k; + + for(k=0;k<8;++k) { - POKE(SV_VIDEO+i,0); + SV_VRAM[y][k][x] = double_reversed_bits(ch[k]); } } -// Necessary conversion to have 2 bits per pixel with darkest hue -// Remark: The Supervision uses 2 bits per pixel and bits are mapped into pixel in reversed order -unsigned char reversed_map_one_to_two_lookup[16] = -{ - 0x00, 0xC0, 0x30, 0xF0, 0x0C, 0xCC, 0x3C, 0xFC, - 0x03, 0xC3, 0x33, 0xF3, 0x0F, 0xCF, 0x3F, 0xFF -}; - -unsigned char left_map_one_to_two(unsigned char n) -{ - return reversed_map_one_to_two_lookup[n >> 4]; -} - -unsigned char right_map_one_to_two(unsigned char n) -{ - return reversed_map_one_to_two_lookup[n&0x0F]; -} - -void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch) -{ - unsigned char k; - - for(k=0;k<8;++k) - { \ - SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)] = left_map_one_to_two(ch[k]); - SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)+1] = right_map_one_to_two(ch[k]); - } -} - -void init_lcd(void) +static void init_lcd(void) { SV_LCD.width = 160; SV_LCD.height = 160; } -int main() -{ - init_lcd(); - clear_screen(); - - display_char(3,2, h_char); - display_char(3,3, e_char); - display_char(3,4, l_char); - display_char(3,5, l_char); - display_char(3,6, o_char); - - display_char(3,8, w_char); - display_char(3,9, o_char); - display_char(3,10,r_char); - display_char(3,11,l_char); - display_char(3,12,d_char); +static void hello(unsigned char x, unsigned char y) +{ + display_char(x+ 0,y,h_char); + display_char(x+ 1,y,e_char); + display_char(x+ 2,y,l_char); + display_char(x+ 3,y,l_char); + display_char(x+ 4,y,o_char); - while(1) {}; - - return 0; + display_char(x+ 6,y,w_char); + display_char(x+ 7,y,o_char); + display_char(x+ 8,y,r_char); + display_char(x+ 9,y,l_char); + display_char(x+10,y,d_char); } +void main(void) +{ + init_lcd(); + clear_screen(); + hello(2,3); + hello(7,16); +} From 0d3c827d803c573c9aa4a361658665a912e27402 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 30 May 2021 03:51:11 -0400 Subject: [PATCH 29/74] Made the C preprocessor #if nesting stack have 256 levels. Closes #1523. --- src/cc65/preproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 37073e784..a607e3217 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -71,7 +71,7 @@ unsigned char Preprocessing = 0; /* Management data for #if */ -#define MAX_IFS 64 +#define MAX_IFS 256 #define IFCOND_NONE 0x00U #define IFCOND_SKIP 0x01U #define IFCOND_ELSE 0x02U From fe003eedd4e3554ec12ad4a234ce885f96e39c99 Mon Sep 17 00:00:00 2001 From: IrgendwerA8 Date: Mon, 31 May 2021 00:24:46 +0200 Subject: [PATCH 30/74] Fix Atari keyboard code for cursor up key --- asminc/atari.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asminc/atari.inc b/asminc/atari.inc index 7c46b3252..c2a210ea0 100644 --- a/asminc/atari.inc +++ b/asminc/atari.inc @@ -924,7 +924,7 @@ KEY_QUESTIONMARK = KEY_SLASH | KEY_SHIFT KEY_CLEAR = KEY_LESSTHAN | KEY_SHIFT KEY_INSERT = KEY_GREATERTHAN | KEY_SHIFT -KEY_UP = KEY_UNDERLINE | KEY_CTRL +KEY_UP = KEY_DASH | KEY_CTRL KEY_DOWN = KEY_EQUALS | KEY_CTRL KEY_LEFT = KEY_PLUS | KEY_CTRL KEY_RIGHT = KEY_ASTERISK | KEY_CTRL From 0bfa13722bb72d8e8f99606f3ecddeedf360149c Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:23:02 +0800 Subject: [PATCH 31/74] More funcinfo on register usage fixes. --- src/cc65/codeent.c | 4 + src/cc65/codeinfo.c | 586 ++++++++++++++++++++++------------------- src/cc65/codeinfo.h | 3 + src/cc65/codeoptutil.c | 105 +++++--- 4 files changed, 386 insertions(+), 312 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index dd2000db0..4f257a22e 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -172,6 +172,10 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) if (Info && Info->ByteUse != REG_NONE) { /* These addressing modes will never change the zp loc */ E->Use |= Info->WordUse; + + if ((E->Use & REG_SP) != 0) { + E->Use |= SLV_IND; + } } break; diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 46a7d76c6..88f8a5138 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -82,269 +82,296 @@ struct FuncInfo { unsigned Chg; /* Changed/destroyed registers */ }; -/* Note for the shift functions: Shifts are done modulo 32, so all shift +/* Functions that change the SP are regarded as using the SP as well. +** The callax/jmpvec functions may call a function that uses/changes more +** registers, so we should further check the info of the called function +** or just play it safe. +** Note for the shift functions: Shifts are done modulo 32, so all shift ** routines are marked to use only the A register. The remainder is ignored ** anyway. */ static const FuncInfo FuncInfoTable[] = { - { "addeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, - { "addeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, - { "addysp", REG_Y, PSTATE_ALL | REG_NONE }, - { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "bcasta", REG_A, PSTATE_ALL | REG_AX }, - { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, - { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "bnega", REG_A, PSTATE_ALL | REG_AX }, - { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, - { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, - { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, - { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, - { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, - { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, - { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, - { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, - { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, - { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, - { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, - { "callax", REG_AX, PSTATE_ALL | REG_ALL }, - { "complax", REG_AX, PSTATE_ALL | REG_AX }, - { "decax1", REG_AX, PSTATE_ALL | REG_AX }, - { "decax2", REG_AX, PSTATE_ALL | REG_AX }, - { "decax3", REG_AX, PSTATE_ALL | REG_AX }, - { "decax4", REG_AX, PSTATE_ALL | REG_AX }, - { "decax5", REG_AX, PSTATE_ALL | REG_AX }, - { "decax6", REG_AX, PSTATE_ALL | REG_AX }, - { "decax7", REG_AX, PSTATE_ALL | REG_AX }, - { "decax8", REG_AX, PSTATE_ALL | REG_AX }, - { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, - { "decsp1", REG_NONE, PSTATE_ALL | REG_Y }, - { "decsp2", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp3", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp4", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp5", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp6", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp7", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp8", REG_NONE, PSTATE_ALL | REG_A }, - { "incax1", REG_AX, PSTATE_ALL | REG_AX }, - { "incax2", REG_AX, PSTATE_ALL | REG_AX }, - { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incsp1", REG_NONE, PSTATE_ALL | REG_NONE }, - { "incsp2", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp3", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp4", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp5", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp6", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp7", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp8", REG_NONE, PSTATE_ALL | REG_Y }, - { "laddeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "laddeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, - { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "laddeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, - { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "ldax0sp", REG_NONE, PSTATE_ALL | REG_AXY }, - { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "ldaxysp", REG_Y, PSTATE_ALL | REG_AXY }, - { "ldeax0sp", REG_NONE, PSTATE_ALL | REG_EAXY }, - { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, - { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, - { "ldeaxysp", REG_Y, PSTATE_ALL | REG_EAXY }, - { "leaa0sp", REG_A, PSTATE_ALL | REG_AX }, - { "leaaxsp", REG_AX, PSTATE_ALL | REG_AX }, - { "lsubeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "lsubeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, - { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "lsubeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, - { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "negax", REG_AX, PSTATE_ALL | REG_AX }, - { "push0", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push0ax", REG_AX, PSTATE_ALL | REG_Y | REG_SREG }, - { "push1", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push2", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push3", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push4", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push5", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push6", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push7", REG_NONE, PSTATE_ALL | REG_AXY }, - { "pusha", REG_A, PSTATE_ALL | REG_Y }, - { "pusha0", REG_A, PSTATE_ALL | REG_XY }, - { "pusha0sp", REG_NONE, PSTATE_ALL | REG_AY }, - { "pushaFF", REG_A, PSTATE_ALL | REG_Y }, - { "pushax", REG_AX, PSTATE_ALL | REG_Y }, - { "pushaysp", REG_Y, PSTATE_ALL | REG_AY }, - { "pushc0", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, - { "pushc1", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, - { "pushc2", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, - { "pusheax", REG_EAX, PSTATE_ALL | REG_Y }, - { "pushl0", REG_NONE, PSTATE_ALL | REG_AXY }, - { "pushw", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "pushw0sp", REG_NONE, PSTATE_ALL | REG_AXY }, - { "pushwidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "pushwysp", REG_Y, PSTATE_ALL | REG_AXY }, - { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "regswap1", REG_XY, PSTATE_ALL | REG_A }, - { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, - { "return0", REG_NONE, PSTATE_ALL | REG_AX }, - { "return1", REG_NONE, PSTATE_ALL | REG_AX }, - { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "staspidx", REG_A | REG_Y, PSTATE_ALL | REG_Y | REG_TMP1 | REG_PTR1 }, - { "stax0sp", REG_AX, PSTATE_ALL | REG_Y }, - { "staxspidx", REG_AXY, PSTATE_ALL | REG_TMP1 | REG_PTR1 }, - { "staxysp", REG_AXY, PSTATE_ALL | REG_Y }, - { "steax0sp", REG_EAX, PSTATE_ALL | REG_Y }, - { "steaxysp", REG_EAXY, PSTATE_ALL | REG_Y }, - { "subeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, - { "subeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, - { "subysp", REG_Y, PSTATE_ALL | REG_AY }, - { "tosadd0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosadda0", REG_A, PSTATE_ALL | REG_AXY }, - { "tosaddax", REG_AX, PSTATE_ALL | REG_AXY }, - { "tosaddeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosand0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosanda0", REG_A, PSTATE_ALL | REG_AXY }, - { "tosandax", REG_AX, PSTATE_ALL | REG_AXY }, - { "tosandeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosaslax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosasleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosasrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosasreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosdiv0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosdiva0", REG_A, PSTATE_ALL | REG_ALL }, - { "tosdivax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosdiveax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "toseq00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toseqa0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toseqax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toseqeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosgt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosicmp", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosicmp0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslcmp", REG_EAX, PSTATE_ALL | REG_A | REG_Y | REG_PTR1 }, - { "tosle00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "toslt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosltax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosmod0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosmodeax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "tosmul0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosmula0", REG_A, PSTATE_ALL | REG_ALL }, - { "tosmulax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosmuleax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "tosne00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosnea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosneax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosneeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosrsub0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosrsuba0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosrsubax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosrsubeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosshlax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosshleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosshrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosshreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tossub0ax", REG_AX, PSTATE_ALL | REG_EAXY }, - { "tossuba0", REG_A, PSTATE_ALL | REG_AXY }, - { "tossubax", REG_AX, PSTATE_ALL | REG_AXY }, - { "tossubeax", REG_EAX, PSTATE_ALL | REG_EAXY }, - { "tosudiv0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosudiva0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudivax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudiveax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosuge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosugt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosule00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosulea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosuleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosuleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosult00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosulta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosultax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosulteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosumod0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosumoda0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodeax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosumul0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosumula0", REG_A, PSTATE_ALL | REG_ALL }, - { "tosumulax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosumuleax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "tosxor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosxora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosxorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosxoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, - { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "addeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "addeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "addysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP }, + { "along", REG_A, PSTATE_ALL | REG_X | REG_SREG }, + { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "aulong", REG_NONE, PSTATE_ALL | REG_X | REG_SREG }, + { "axlong", REG_X, PSTATE_ALL | REG_Y | REG_SREG }, + { "axulong", REG_NONE, PSTATE_ALL | REG_Y | REG_SREG }, + { "bcasta", REG_A, PSTATE_ALL | REG_AX }, + { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, + { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "bnega", REG_A, PSTATE_ALL | REG_AX }, + { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, + { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, + { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, + { "callax", REG_AX, PSTATE_ALL | REG_ALL }, /* PSTATE_ZN | REG_PTR1 */ + { "complax", REG_AX, PSTATE_ALL | REG_AX }, + { "decax1", REG_AX, PSTATE_ALL | REG_AX }, + { "decax2", REG_AX, PSTATE_ALL | REG_AX }, + { "decax3", REG_AX, PSTATE_ALL | REG_AX }, + { "decax4", REG_AX, PSTATE_ALL | REG_AX }, + { "decax5", REG_AX, PSTATE_ALL | REG_AX }, + { "decax6", REG_AX, PSTATE_ALL | REG_AX }, + { "decax7", REG_AX, PSTATE_ALL | REG_AX }, + { "decax8", REG_AX, PSTATE_ALL | REG_AX }, + { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, + { "decsp1", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "decsp2", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp3", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp4", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp5", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp6", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp7", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp8", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "enter", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "incax1", REG_AX, PSTATE_ALL | REG_AX }, + { "incax2", REG_AX, PSTATE_ALL | REG_AX }, + { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incsp1", REG_SP, PSTATE_ALL | REG_SP }, + { "incsp2", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp3", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp4", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp5", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp6", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp7", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp8", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "jmpvec", REG_EVERYTHING, PSTATE_ALL | REG_ALL }, /* NONE */ + { "laddeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldax0sp", SLV_TOP, PSTATE_ALL | REG_AXY }, + { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_AXY }, + { "ldeax0sp", SLV_TOP, PSTATE_ALL | REG_EAXY }, + { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_EAXY }, + { "leaa0sp", REG_SP | REG_A, PSTATE_ALL | REG_AX }, + { "leaaxsp", REG_SP | REG_AX, PSTATE_ALL | REG_AX }, + { "leave00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leave0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leave", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "leavey00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leavey0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leavey", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_Y }, + { "lsubeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "negax", REG_AX, PSTATE_ALL | REG_AX }, + { "negeax", REG_EAX, PSTATE_ALL | REG_EAX }, + { "popa", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "popax", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "popeax", SLV_TOP, PSTATE_ALL | REG_SP | REG_EAXY }, + { "push0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push0ax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y | REG_SREG }, + { "push1", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push2", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push3", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push4", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push5", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push6", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push7", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pusha", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pusha0", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_XY }, + { "pusha0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "pushaFF", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pushax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushaysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "pushc0", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc1", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc2", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pusheax", REG_SP | REG_EAX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushl0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushw", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushw0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushwidx", REG_SP | REG_AXY, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushwysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AXY }, + { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "regswap1", REG_XY, PSTATE_ALL | REG_A }, + { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, + { "resteax", REG_SAVE, PSTATE_ZN | REG_EAX }, /* also uses regsave+2/+3 */ + { "return0", REG_NONE, PSTATE_ALL | REG_AX }, + { "return1", REG_NONE, PSTATE_ALL | REG_AX }, + { "saveeax", REG_EAX, PSTATE_ZN | REG_Y | REG_SAVE }, /* also regsave+2/+3 */ + { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "staspidx", SLV_TOP | REG_AY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, + { "stax0sp", REG_SP | REG_AX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "staxspidx", SLV_TOP | REG_AXY, PSTATE_ALL | REG_SP | REG_TMP1 | REG_PTR1 }, + { "staxysp", REG_SP | REG_AXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "steax0sp", REG_SP | REG_EAX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "steaxspidx", SLV_TOP | REG_EAXY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, /* also tmp2, tmp3 */ + { "steaxysp", REG_SP | REG_EAXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "subeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "subeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "subysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "swapstk", SLV_TOP | REG_AX, PSTATE_ALL | SLV_TOP | REG_AXY }, /* also ptr4 */ + { "tosadd0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosadda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosand0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosanda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosaslax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosasrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosdiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiveax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_ALL }, + { "toseq00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqa0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosgt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosicmp", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosicmp0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosint", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslcmp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_A | REG_Y | REG_PTR1 }, + { "tosle00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "toslong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosltax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosmod0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmodeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosmul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosmulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosne00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosnea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshlax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tossub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tossuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tosudiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosudiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudiveax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosuge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosugt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosule00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosulong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "tosult00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosultax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosumod0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumoda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodeax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosumulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosxor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosxora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, }; #define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0])) @@ -481,6 +508,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { FuncDesc* D = GetFuncDesc (E->Type); + *Use = REG_NONE; /* 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 @@ -488,31 +516,40 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** we assume that any function will destroy all registers. */ if ((D->Flags & FD_VARIADIC) != 0) { - *Use = REG_Y; + *Use = REG_Y | REG_SP | SLV_TOP; } else if (D->Flags & FD_CALL_WRAPPER) { /* Wrappers may go to any functions, so mark them as using all ** registers. */ *Use = REG_EAXY; - } else if ((D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) && - IsFastcallFunc (E->Type)) { + } else if (D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) { /* Will use registers depending on the last param. If the last ** param has incomplete type, or if the function has not been ** prototyped yet, just assume __EAX__. */ - if (D->LastParam != 0) { - switch (SizeOf (D->LastParam->Type)) { - case 1u: - *Use = REG_A; - break; - case 2u: - *Use = REG_AX; - break; - default: - *Use = REG_EAX; + if (IsFastcallFunc (E->Type)) { + if (D->LastParam != 0) { + switch (SizeOf (D->LastParam->Type)) { + case 1u: + *Use = REG_A; + break; + case 2u: + *Use = REG_AX; + break; + default: + *Use = REG_EAX; + } + if (D->ParamCount > 1) { + /* Passes other params on the stack */ + *Use |= REG_SP | SLV_TOP; + } + } else { + /* We'll assume all */ + *Use = REG_EAX | REG_SP | SLV_TOP; } } else { - *Use = REG_EAX; + /* Passes all params on the stack */ + *Use = REG_SP | SLV_TOP; } } else { /* Will not use any registers */ @@ -551,6 +588,9 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* Use the information we have */ *Use = Info->Use; *Chg = Info->Chg; + if ((*Use & (SLV_TOP | SLV_IND)) != 0) { + *Use |= REG_SP; + } } else { /* It's an internal function we have no information for. If in ** debug mode, output an additional warning, so we have a chance diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 88e26cdf4..14ef54d8f 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -75,6 +75,8 @@ struct RegContents; #define REG_SP_HI 0x2000U /* Defines for some special register usage */ +#define SLV_IND 0x00010000U /* Accesses (sp),y */ +#define SLV_TOP 0x00020000U /* Accesses (sp),0 */ #define SLV_SP65 0x00200000U /* Accesses 6502 stack pointer */ #define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */ #define SLV_PL65 0x00800000U /* Pops from 6502 stack */ @@ -104,6 +106,7 @@ struct RegContents; #define REG_EAXY (REG_EAX | REG_Y) #define REG_ZP 0xFFF8U #define REG_ALL 0xFFFFU + #define PSTATE_CZ (PSTATE_C | PSTATE_Z) #define PSTATE_CZN (PSTATE_C | PSTATE_Z | PSTATE_N) #define PSTATE_CZVN (PSTATE_C | PSTATE_Z | PSTATE_V | PSTATE_N) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 16c41162a..a4980aa3a 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -827,21 +827,28 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) CodeEntry* E = CS_GetEntry (D->Code, I); + /* Check against some things that should not happen */ + CHECK ((E->Use & SLV_TOP) != SLV_TOP); + /* Check if this entry does a stack access, and if so, if it's a plain ** load from stack, since this is needed later. */ int Correction = 0; - if ((E->Use & REG_SP) != 0) { + if ((E->Use & SLV_IND) == SLV_IND) { - /* Check for some things that should not happen */ - CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); - CHECK (strcmp (E->Arg, "sp") == 0); - /* We need to correct this one */ - Correction = (E->OPC == OP65_LDA)? 2 : 1; + if (E->OPC != OP65_JSR) { + /* Check against some things that should not happen */ + CHECK (E->AM == AM65_ZP_INDY && E->RI->In.RegY >= (short) Offs); + CHECK (strcmp (E->Arg, "sp") == 0); + + /* We need to correct this one */ + Correction = 2; + + } else { + /* We need to correct this one */ + Correction = 1; + } - } else if (CE_IsCallTo (E, "ldaxysp")) { - /* We need to correct this one */ - Correction = 1; } if (Correction) { @@ -849,7 +856,7 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) ** value. */ CodeEntry* P = CS_GetPrevEntry (D->Code, I); - if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) { + if (P && P->OPC == OP65_LDY && CE_IsConstImm (P) && !CE_HasLabel (E)) { /* The Y load is just before the stack access, adjust it */ CE_SetNumArg (P, P->Num - Offs); } else { @@ -860,39 +867,59 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) } /* If we need the value of Y later, be sure to reload it */ - if (RegYUsed (D->Code, I+1)) { - CodeEntry* N; + unsigned R = REG_Y | (E->Chg & ~REG_A); + R = GetRegInfo (D->Code, I + 1, R) & R; + if ((R & REG_Y) != 0) { const char* Arg = MakeHexArg (E->RI->In.RegY); - if (Correction == 2 && (N = CS_GetNextEntry(D->Code, I)) != 0 && - ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { - /* The Y register is used but the load instruction loads A - ** and is followed by a branch that evaluates the zero flag. - ** This means that we cannot just insert the load insn - ** for the Y register at this place, because it would - ** destroy the Z flag. Instead place load insns at the - ** target of the branch and after it. - ** Note: There is a chance that this code won't work. The - ** jump may be a backwards jump (in which case the stack - ** offset has already been adjusted) or there may be other - ** instructions between the load and the conditional jump. - ** Currently the compiler does not generate such code, but - ** it is possible to force the optimizer into something - ** invalid by use of inline assembler. - */ + if ((R & PSTATE_ZN) != 0 && (R & ~(REG_Y | PSTATE_ZN)) == 0) { + CodeEntry* N; + if ((N = CS_GetNextEntry (D->Code, I)) != 0 && + ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { + /* The Y register is used but the load instruction loads A + ** and is followed by a branch that evaluates the zero flag. + ** This means that we cannot just insert the load insn + ** for the Y register at this place, because it would + ** destroy the Z flag. Instead place load insns at the + ** target of the branch and after it. + ** Note: There is a chance that this code won't work. The + ** jump may be a backwards jump (in which case the stack + ** offset has already been adjusted) or there may be other + ** instructions between the load and the conditional jump. + ** Currently the compiler does not generate such code, but + ** it is possible to force the optimizer into something + ** invalid by use of inline assembler. + ** Note: In reality, this route is never taken as all + ** callers of this function will just give up with + ** optimization whenever they detect a branch. + */ - /* Add load insn after the branch */ - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+2); + /* Add load insn after the branch */ + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I+2); - /* Add load insn before branch target */ - CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); - CHECK (J > I); /* Must not happen */ - InsertEntry (D, Y, J); + /* Add load insn before branch target */ + CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); + CHECK (J > I); /* Must not happen */ + InsertEntry (D, Y, J); - /* Move the label to the new insn */ - CodeLabel* L = CS_GenLabel (D->Code, Y); - CS_MoveLabelRef (D->Code, N, L); + /* Move the label to the new insn */ + CodeLabel* L = CS_GenLabel (D->Code, Y); + CS_MoveLabelRef (D->Code, N, L); + + /* Skip the next two instructions in the next round */ + I += 2; + } else { + /* This could be suboptimal but it will always work (unless stack overflows) */ + CodeEntry* X = NewCodeEntry (OP65_PHP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, 0, 0, E->LI); + InsertEntry (D, X, I+2); + X = NewCodeEntry (OP65_PLP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+3); + /* Skip the three inserted instructions in the next round */ + I += 3; + } } else { CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); InsertEntry (D, X, I+1); From 79be6dec1625ca962ec01755d273f833c583b12c Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:23:02 +0800 Subject: [PATCH 32/74] More quick hack for CE_GenRegInfo. --- src/cc65/codeent.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 4f257a22e..0a1b917db 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -1781,6 +1781,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX ^ 0xFF); } + } else if (strncmp (E->Arg, "asrax", 5) == 0 || + strncmp (E->Arg, "shrax", 5) == 0) { + if (RegValIsKnown (In->RegX)) { + if (In->RegX == 0x00 || In->RegX == 0xFF) { + Out->RegX = In->RegX; + } + } } else if (strcmp (E->Arg, "tosandax") == 0) { if (RegValIsKnown (In->RegA) && In->RegA == 0) { Out->RegA = 0; From 4d5fe3854033b1b24d20fa4eb40e38dbf34c7cc9 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:23:03 +0800 Subject: [PATCH 33/74] Fixed OptStackOps when the stuff pushed on stack top is accessed before the op. --- src/cc65/codeoptutil.c | 87 ++++++++++++++++-------------------------- src/cc65/codeoptutil.h | 2 +- src/cc65/coptstop.c | 27 +++++++------ 3 files changed, 48 insertions(+), 68 deletions(-) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index a4980aa3a..173d5185f 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -1224,66 +1224,43 @@ static int CmpHarmless (const void* Key, const void* Entry) -int HarmlessCall (const char* Name) +int HarmlessCall (const CodeEntry* E, int PushedBytes) /* Check if this is a call to a harmless subroutine that will not interrupt ** the pushax/op sequence when encountered. */ { - static const char* const Tab[] = { - "aslax1", - "aslax2", - "aslax3", - "aslax4", - "aslaxy", - "asrax1", - "asrax2", - "asrax3", - "asrax4", - "asraxy", - "bcastax", - "bnegax", - "complax", - "decax1", - "decax2", - "decax3", - "decax4", - "decax5", - "decax6", - "decax7", - "decax8", - "decaxy", - "incax1", - "incax2", - "incax3", - "incax4", - "incax5", - "incax6", - "incax7", - "incax8", - "incaxy", - "ldaidx", - "ldauidx", - "ldaxidx", - "ldaxysp", - "negax", - "shlax1", - "shlax2", - "shlax3", - "shlax4", - "shlaxy", - "shrax1", - "shrax2", - "shrax3", - "shrax4", - "shraxy", - }; + unsigned Use = 0, Chg = 0; + if (GetFuncInfo (E->Arg, &Use, &Chg) == FNCLS_BUILTIN) { + if ((Chg & REG_SP) != 0) { + return 0; + } + if ((Use & REG_SP) != 0 && + ((Use & (SLV_IND | SLV_TOP)) != SLV_IND || + RegValIsUnknown (E->RI->In.RegY) || + E->RI->In.RegY < PushedBytes)) { + /* If we are using the stack, and we don't have "indirect" + ** addressing mode, or the value of Y is unknown, or less + ** than two, we cannot cope with this piece of code. Having + ** an unknown value of Y means that we cannot correct the + ** stack offset, while having an offset less than PushedBytes + ** means that the code works with the value on stack which + ** is to be removed. + */ + return 0; + } + return 1; + } else { + static const char* const Tab[] = { + "_abs", + }; - void* R = bsearch (Name, - Tab, - sizeof (Tab) / sizeof (Tab[0]), - sizeof (Tab[0]), - CmpHarmless); - return (R != 0); + void* R = bsearch (E->Arg, + Tab, + sizeof (Tab) / sizeof (Tab[0]), + sizeof (Tab[0]), + CmpHarmless); + return (R != 0); + } } diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h index 70aa5f462..140b11236 100644 --- a/src/cc65/codeoptutil.h +++ b/src/cc65/codeoptutil.h @@ -261,7 +261,7 @@ void RemoveRegLoads (StackOpData* D, LoadInfo* LI); void RemoveRemainders (StackOpData* D); /* Remove the code that is unnecessary after translation of the sequence */ -int HarmlessCall (const char* Name); +int HarmlessCall (const CodeEntry* E, int PushedBytes); /* Check if this is a call to a harmless subroutine that will not interrupt ** the pushax/op sequence when encountered. */ diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 01d0b039c..23636e533 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1820,20 +1820,18 @@ unsigned OptStackOps (CodeSeg* S) Data.OpEntry = E; State = FoundOp; break; - } else if (!HarmlessCall (E->Arg)) { - /* A call to an unkown subroutine: We need to start - ** over after the last pushax. Note: This will also - ** happen if we encounter a call to pushax! + } else if (!HarmlessCall (E, 2)) { + /* The call might use or change the content that we are + ** going to access later via the stack pointer. In any + ** case, we need to start over after the last pushax. + ** Note: This will also happen if we encounter a call + ** to pushax! */ I = Data.PushIndex; State = Initialize; break; } - - } else if ((E->Use & REG_SP) != 0 && - (E->AM != AM65_ZP_INDY || - RegValIsUnknown (E->RI->In.RegY) || - E->RI->In.RegY < 2)) { + } else if (((E->Chg | E->Use) & REG_SP) != 0) { /* If we are using the stack, and we don't have "indirect Y" ** addressing mode, or the value of Y is unknown, or less @@ -1843,9 +1841,14 @@ unsigned OptStackOps (CodeSeg* S) ** that the code works with the value on stack which is to ** be removed. */ - I = Data.PushIndex; - State = Initialize; - break; + if (E->AM == AM65_ZPX_IND || + ((E->Chg | E->Use) & SLV_IND) == 0 || + (RegValIsUnknown (E->RI->In.RegY) || + E->RI->In.RegY < 2)) { + I = Data.PushIndex; + State = Initialize; + break; + } } From 3584c4c87fceb3771daaa054de1eed75ebc46b86 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Mon, 22 Feb 2021 11:57:32 -0500 Subject: [PATCH 34/74] fix crash when a NULL ExprNode is checked for circular references --- src/ld65/expr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ld65/expr.c b/src/ld65/expr.c index bc3d7941c..e106e09d0 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -464,6 +464,12 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) { Export* E; + if (Expr == 0) + { + D->TooComplex = 1; + return; + } + switch (Expr->Op) { case EXPR_LITERAL: From 6ebe551919ba9acecd5d9caf6d8668d9fc11d09d Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Mon, 22 Feb 2021 18:14:27 -0500 Subject: [PATCH 35/74] return is sufficient, TooComplex flag is unnecessary to resolve this crash case suggested by greg-king5 in #1409 --- src/ld65/expr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ld65/expr.c b/src/ld65/expr.c index e106e09d0..23f52a16f 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -466,7 +466,6 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) if (Expr == 0) { - D->TooComplex = 1; return; } From d5d9183ccfde833296a3e2d3f4d44fd7468b3a56 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Mon, 22 Feb 2021 18:17:11 -0500 Subject: [PATCH 36/74] conform to prevailing if bracket style --- src/ld65/expr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ld65/expr.c b/src/ld65/expr.c index 23f52a16f..5d6b56917 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -464,8 +464,7 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) { Export* E; - if (Expr == 0) - { + if (Expr == 0) { return; } From 39ef63cbbc0f27968fdd044a0af22630969324e6 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 4 Jun 2021 08:49:10 -0400 Subject: [PATCH 37/74] Don't check for circular references of imports that don't have matching exports. This fix will avoid referring to a struct member through a null pointer. --- src/ld65/expr.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ld65/expr.c b/src/ld65/expr.c index 5d6b56917..41db2326c 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -457,16 +457,14 @@ long GetExprVal (ExprNode* Expr) static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) /* Check if the given expression consists of a segment reference and only -** constant values, additions and subtractions. If anything else is found, +** constant values, additions, and subtractions. If anything else is found, ** set D->TooComplex to true. ** Internal, recursive routine. */ { Export* E; - if (Expr == 0) { - return; - } + CHECK (Expr != 0); switch (Expr->Op) { @@ -483,7 +481,7 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) */ if (ExportHasMark (E)) { CircularRefError (E); - } else { + } else if (E->Expr != 0) { MarkExport (E); GetSegExprValInternal (E->Expr, D, Sign); UnmarkExport (E); From 1c16e46f2369306fa7397dbde8750aad3b824816 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sat, 5 Jun 2021 11:31:28 -0400 Subject: [PATCH 38/74] Improved ld65's error messages about ca65's .BANK() function. * Split a message into two more specific messages. --- src/ld65/expr.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ld65/expr.c b/src/ld65/expr.c index 41db2326c..7a2f37d4a 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -403,17 +403,20 @@ long GetExprVal (ExprNode* Expr) case EXPR_BANK: GetSegExprVal (Expr->Left, &D); - if (D.TooComplex || D.Seg == 0) { - Error ("Argument for .BANK is not segment relative or too complex"); + if (D.TooComplex) { + Error ("Argument of .BANK() is too complex"); + } + if (D.Seg == 0) { + Error ("Argument of .BANK() isn't a label attached to a segment"); } if (D.Seg->MemArea == 0) { - Error ("Segment '%s' is referenced by .BANK but " - "not assigned to a memory area", + Error ("Segment '%s' is referenced by .BANK()," + " but not assigned to a memory area", GetString (D.Seg->Name)); } if (D.Seg->MemArea->BankExpr == 0) { - Error ("Memory area '%s' is referenced by .BANK but " - "has no BANK attribute", + Error ("Memory area '%s' is referenced by .BANK()," + " but has no BANK attribute", GetString (D.Seg->MemArea->Name)); } return GetExprVal (D.Seg->MemArea->BankExpr); From ed9f9ccbabea58a2e2a688434290d0ad026acd13 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Mon, 7 Jun 2021 20:56:24 +0000 Subject: [PATCH 39/74] Fix #1536 --- src/cc65/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/main.c b/src/cc65/main.c index 0b156fb74..5d1fd7487 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -291,7 +291,7 @@ static void SetSys (const char* Sys) break; default: - AbEnd ("Unknown target system type %d", Target); + AbEnd ("Unknown target system '%s'", Sys); } /* Initialize the translation tables for the target system */ From 09862e7ce908d7915e6341f626f2930e0cc62118 Mon Sep 17 00:00:00 2001 From: Filip Golewski Date: Tue, 8 Jun 2021 20:05:22 +0200 Subject: [PATCH 40/74] Update src/msbuild.cmd script to optionally include Visual Studio 2019 Community build tools --- src/msbuild.cmd | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/msbuild.cmd b/src/msbuild.cmd index 5736846da..2e1821f0a 100644 --- a/src/msbuild.cmd +++ b/src/msbuild.cmd @@ -1,2 +1,18 @@ +@echo off + +if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" goto vs2017 +if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" goto vs2019 + +echo Error: VsDevCmd.bat not found! +goto:eof + +:vs2017 call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" +goto run + +:vs2019 +call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" +goto run + +:run msbuild.exe %* From dcacba472ae69c27146a7767bc7835e5b239d8ff Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 5 Apr 2021 16:40:33 +0800 Subject: [PATCH 41/74] Moved ArithmeticConvert() from cc65/expr.c to cc65/datatype.c. Reorganized a few functions in cc65/datatype.c. Added SignedType() and UnsignedType() for future usage. Made LimitExprValue() external so that it can be used more often. --- src/cc65/datatype.c | 480 ++++++++++++++++++++++++++++---------------- src/cc65/datatype.h | 90 +++++---- src/cc65/expr.c | 61 +----- src/cc65/expr.h | 3 + 4 files changed, 358 insertions(+), 276 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 90bf892ba..6465c27a6 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -546,14 +546,14 @@ unsigned long GetIntegerTypeMax (const Type* Type) -static unsigned TypeOfBySize (const Type* Type) +static unsigned TypeOfBySize (unsigned Size) /* Get the code generator replacement type of the object by its size */ { unsigned NewType; /* If the size is less than or equal to that of a a long, we will copy ** the struct using the primary register, otherwise we use memcpy. */ - switch (SizeOf (Type)) { + switch (Size) { case 1: NewType = CF_CHAR; break; case 2: NewType = CF_INT; break; case 3: /* FALLTHROUGH */ @@ -566,125 +566,6 @@ static unsigned TypeOfBySize (const Type* Type) -Type* NewPointerTo (const Type* T) -/* Return a type string that is "pointer to T". The type string is allocated -** on the heap and may be freed after use. -*/ -{ - /* Get the size of the type string including the terminator */ - unsigned Size = TypeLen (T) + 1; - - /* Allocate the new type string */ - Type* P = TypeAlloc (Size + 1); - - /* Create the return type... */ - P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); - memcpy (P+1, T, Size * sizeof (Type)); - - /* ...and return it */ - return P; -} - - - -void PrintType (FILE* F, const Type* T) -/* Print fulle name of the type */ -{ - StrBuf Buf = AUTO_STRBUF_INITIALIZER; - fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T))); - SB_Done (&Buf); -} - - - -void PrintFuncSig (FILE* F, const char* Name, const Type* T) -/* Print a function signature */ -{ - StrBuf Buf = AUTO_STRBUF_INITIALIZER; - StrBuf ParamList = AUTO_STRBUF_INITIALIZER; - StrBuf East = AUTO_STRBUF_INITIALIZER; - StrBuf West = AUTO_STRBUF_INITIALIZER; - - /* Get the function descriptor used in definition */ - const FuncDesc* D = GetFuncDefinitionDesc (T); - - /* Get the parameter list string. Start from the first parameter */ - SymEntry* Param = D->SymTab->SymHead; - unsigned I; - for (I = 0; I < D->ParamCount; ++I) { - CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); - if (I > 0) { - SB_AppendStr (&ParamList, ", "); - } - if (SymIsRegVar (Param)) { - SB_AppendStr (&ParamList, "register "); - } - if (!HasAnonName (Param)) { - SB_AppendStr (&Buf, Param->Name); - } - SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - SB_Clear (&Buf); - /* Next argument */ - Param = Param->NextSym; - } - if ((D->Flags & FD_VARIADIC) == 0) { - if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { - SB_AppendStr (&ParamList, "void"); - } - } else { - if (D->ParamCount > 0) { - SB_AppendStr (&ParamList, ", ..."); - } else { - SB_AppendStr (&ParamList, "..."); - } - } - SB_Terminate (&ParamList); - - /* Get the function qualifiers */ - if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { - /* Append a space between the qualifiers and the name */ - SB_AppendChar (&Buf, ' '); - } - SB_Terminate (&Buf); - - /* Get the signature string without the return type */ - SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); - SB_Done (&Buf); - SB_Done (&ParamList); - - /* Complete with the return type */ - GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); - SB_Append (&West, &East); - SB_Terminate (&West); - - /* Output */ - fprintf (F, "%s", SB_GetConstBuf (&West)); - SB_Done (&East); - SB_Done (&West); -} - - - -void PrintRawType (FILE* F, const Type* T) -/* Print a type string in raw hex format (for debugging) */ -{ - while (T->C != T_END) { - fprintf (F, "%04lX ", T->C); - ++T; - } - fprintf (F, "\n"); -} - - - -int TypeHasAttr (const Type* T) -/* Return true if the given type has attribute data */ -{ - return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); -} - - - const Type* GetUnderlyingType (const Type* Type) /* Get the underlying type of an enum or other integer class type */ { @@ -906,7 +787,7 @@ unsigned TypeOf (const Type* T) case T_STRUCT: case T_UNION: - NewType = TypeOfBySize (T); + NewType = TypeOfBySize (SizeOf (T)); if (NewType != CF_NONE) { return NewType; } @@ -968,6 +849,48 @@ Type* IndirectModifiable (Type* T) +Type* NewPointerTo (const Type* T) +/* Return a type string that is "pointer to T". The type string is allocated +** on the heap and may be freed after use. +*/ +{ + /* Get the size of the type string including the terminator */ + unsigned Size = TypeLen (T) + 1; + + /* Allocate the new type string */ + Type* P = TypeAlloc (Size + 1); + + /* Create the return type... */ + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); + memcpy (P+1, T, Size * sizeof (Type)); + + /* ...and return it */ + return P; +} + + + +const Type* AddressOf (const Type* T) +/* Return a type string that is "address of T". The type string is allocated +** on the heap and may be freed after use. +*/ +{ + /* Get the size of the type string including the terminator */ + unsigned Size = TypeLen (T) + 1; + + /* Allocate the new type string */ + Type* P = TypeAlloc (Size + 1); + + /* Create the return type... */ + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST; + memcpy (P+1, T, Size * sizeof (Type)); + + /* ...and return it */ + return P; +} + + + Type* ArrayToPtr (const Type* T) /* Convert an array to a pointer to it's first element */ { @@ -977,6 +900,165 @@ Type* ArrayToPtr (const Type* T) +const Type* PtrConversion (const Type* T) +/* If the type is a function, convert it to pointer to function. If the +** expression is an array, convert it to pointer to first element. Otherwise +** return T. +*/ +{ + if (IsTypeFunc (T)) { + return AddressOf (T); + } else if (IsTypeArray (T)) { + return AddressOf (GetElementType (T)); + } else { + return T; + } +} + + + +const Type* IntPromotion (const Type* T) +/* Apply the integer promotions to T and return the result. The returned type +** string may be T if there is no need to change it. +*/ +{ + /* We must have an int to apply int promotions */ + PRECONDITION (IsClassInt (T)); + + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 + ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or + ** an object that has enumeration type, may be used in an expression wherever an int or + ** unsigned int may be used. If an int can represent all values of the original type, + ** the value is converted to an int; otherwise it is converted to an unsigned int. + ** These are called the integral promotions. + */ + + if (IsTypeChar (T)) { + /* An integer can represent all values from either signed or unsigned char, so convert + ** chars to int. + */ + return type_int; + } else if (IsTypeShort (T)) { + /* An integer cannot represent all values from unsigned short, so convert unsigned short + ** to unsigned int. + */ + return IsSignUnsigned (T) ? type_uint : type_int; + } else if (!IsIncompleteESUType (T)) { + /* The type is a complete type not smaller than int, so leave it alone. */ + return T; + } else { + /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. + ** Assume int to avoid further errors. + */ + return type_int; + } +} + + + +const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) +/* Perform the usual arithmetic conversions for binary operators. */ +{ + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 + ** Many binary operators that expect operands of arithmetic type cause conversions and yield + ** result types in a similar way. The purpose is to yield a common type, which is also the type + ** of the result. This pattern is called the usual arithmetic conversions. + */ + + /* There are additional rules for floating point types that we don't bother with, since + ** floating point types are not (yet) supported. + ** The integral promotions are performed on both operands. + */ + lhst = IntPromotion (lhst); + rhst = IntPromotion (rhst); + + /* If either operand has type unsigned long int, the other operand is converted to + ** unsigned long int. + */ + if ((IsTypeLong (lhst) && IsSignUnsigned (lhst)) || + (IsTypeLong (rhst) && IsSignUnsigned (rhst))) { + return type_ulong; + } + + /* Otherwise, if one operand has type long int and the other has type unsigned int, + ** if a long int can represent all values of an unsigned int, the operand of type unsigned int + ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, + ** both operands are converted to unsigned long int. + */ + if ((IsTypeLong (lhst) && IsTypeInt (rhst) && IsSignUnsigned (rhst)) || + (IsTypeLong (rhst) && IsTypeInt (lhst) && IsSignUnsigned (lhst))) { + /* long can represent all unsigneds, so we are in the first sub-case. */ + return type_long; + } + + /* Otherwise, if either operand has type long int, the other operand is converted to long int. + */ + if (IsTypeLong (lhst) || IsTypeLong (rhst)) { + return type_long; + } + + /* Otherwise, if either operand has type unsigned int, the other operand is converted to + ** unsigned int. + */ + if ((IsTypeInt (lhst) && IsSignUnsigned (lhst)) || + (IsTypeInt (rhst) && IsSignUnsigned (rhst))) { + return type_uint; + } + + /* Otherwise, both operands have type int. */ + CHECK (IsTypeInt (lhst)); + CHECK (IsSignSigned (lhst)); + CHECK (IsTypeInt (rhst)); + CHECK (IsSignSigned (rhst)); + return type_int; +} + + + +const Type* SignedType (const Type* T) +/* Get signed counterpart of the integral type */ +{ + switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { + case T_TYPE_CHAR: + return type_schar; + + case T_TYPE_INT: + case T_TYPE_SHORT: + return type_int; + + case T_TYPE_LONG: + return type_long; + + default: + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); + return T; + } +} + + + +const Type* UnsignedType (const Type* T) +/* Get unsigned counterpart of the integral type */ +{ + switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { + case T_TYPE_CHAR: + return type_uchar; + + case T_TYPE_INT: + case T_TYPE_SHORT: + return type_uint; + + case T_TYPE_LONG: + return type_ulong; + + default: + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); + return T; + } +} + + + int IsClassObject (const Type* T) /* Return true if this is a fully described object type */ { @@ -1266,62 +1348,6 @@ void SetESUSymEntry (Type* T, struct SymEntry* S) -const Type* IntPromotion (const Type* T) -/* Apply the integer promotions to T and return the result. The returned type -** string may be T if there is no need to change it. -*/ -{ - /* We must have an int to apply int promotions */ - PRECONDITION (IsClassInt (T)); - - /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 - ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or an - ** object that has enumeration type, may be used in an expression wherever an int or - ** unsigned int may be used. If an int can represent all values of the original type, the value - ** is converted to an int; otherwise it is converted to an unsigned int. - ** These are called the integral promotions. - */ - - if (IsTypeChar (T)) { - /* An integer can represent all values from either signed or unsigned char, so convert - ** chars to int. - */ - return type_int; - } else if (IsTypeShort (T)) { - /* An integer cannot represent all values from unsigned short, so convert unsigned short - ** to unsigned int. - */ - return IsSignUnsigned (T) ? type_uint : type_int; - } else if (!IsIncompleteESUType (T)) { - /* The type is a complete type not smaller than int, so leave it alone. */ - return T; - } else { - /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. - ** Assume int to avoid further errors. - */ - return type_int; - } -} - - - -const Type* PtrConversion (const Type* T) -/* If the type is a function, convert it to pointer to function. If the -** expression is an array, convert it to pointer to first element. Otherwise -** return T. -*/ -{ - if (IsTypeFunc (T)) { - return NewPointerTo (T); - } else if (IsTypeArray (T)) { - return ArrayToPtr (T); - } else { - return T; - } -} - - - TypeCode AddrSizeQualifier (unsigned AddrSize) /* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ { @@ -1339,3 +1365,101 @@ TypeCode AddrSizeQualifier (unsigned AddrSize) } } + + + +int TypeHasAttr (const Type* T) +/* Return true if the given type has attribute data */ +{ + return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); +} + + + +void PrintType (FILE* F, const Type* T) +/* Print fulle name of the type */ +{ + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T))); + SB_Done (&Buf); +} + + + +void PrintFuncSig (FILE* F, const char* Name, const Type* T) +/* Print a function signature */ +{ + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + StrBuf East = AUTO_STRBUF_INITIALIZER; + StrBuf West = AUTO_STRBUF_INITIALIZER; + + /* Get the function descriptor used in definition */ + const FuncDesc* D = GetFuncDefinitionDesc (T); + + /* Get the parameter list string. Start from the first parameter */ + SymEntry* Param = D->SymTab->SymHead; + unsigned I; + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (&ParamList, ", "); + } + if (SymIsRegVar (Param)) { + SB_AppendStr (&ParamList, "register "); + } + if (!HasAnonName (Param)) { + SB_AppendStr (&Buf, Param->Name); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_Clear (&Buf); + /* Next argument */ + Param = Param->NextSym; + } + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { + SB_AppendStr (&ParamList, "void"); + } + } else { + if (D->ParamCount > 0) { + SB_AppendStr (&ParamList, ", ..."); + } else { + SB_AppendStr (&ParamList, "..."); + } + } + SB_Terminate (&ParamList); + + /* Get the function qualifiers */ + if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { + /* Append a space between the qualifiers and the name */ + SB_AppendChar (&Buf, ' '); + } + SB_Terminate (&Buf); + + /* Get the signature string without the return type */ + SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); + SB_Done (&Buf); + SB_Done (&ParamList); + + /* Complete with the return type */ + GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); + SB_Append (&West, &East); + SB_Terminate (&West); + + /* Output */ + fprintf (F, "%s", SB_GetConstBuf (&West)); + SB_Done (&East); + SB_Done (&West); +} + + + +void PrintRawType (FILE* F, const Type* T) +/* Print a type string in raw hex format (for debugging) */ +{ + while (T->C != T_END) { + fprintf (F, "%04lX ", T->C); + ++T; + } + fprintf (F, "\n"); +} diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index af1c6b8e4..4729284bd 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -288,33 +288,6 @@ unsigned long GetIntegerTypeMax (const Type* Type); ** The type must have a known size. */ -Type* NewPointerTo (const Type* T); -/* Return a type string that is "pointer to T". The type string is allocated -** on the heap and may be freed after use. -*/ - -void PrintType (FILE* F, const Type* T); -/* Print fulle name of the type */ - -void PrintFuncSig (FILE* F, const char* Name, const Type* T); -/* Print a function signature */ - -void PrintRawType (FILE* F, const Type* T); -/* Print a type string in raw hex format (for debugging) */ - -int TypeHasAttr (const Type* T); -/* Return true if the given type has attribute data */ - -#if defined(HAVE_INLINE) -INLINE void CopyTypeAttr (const Type* Src, Type* Dest) -/* Copy attribute data from Src to Dest */ -{ - Dest->A = Src->A; -} -#else -# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A) -#endif - #if defined(HAVE_INLINE) INLINE TypeCode UnqualifiedType (TypeCode T) /* Return the unqalified type code */ @@ -366,9 +339,39 @@ Type* IndirectModifiable (Type* T); ** given type points to. */ +Type* NewPointerTo (const Type* T); +/* Return a type string that is "pointer to T". The type string is allocated +** on the heap and may be freed after use. +*/ + +const Type* AddressOf (const Type* T); +/* Return a type string that is "address of T". The type string is allocated +** on the heap and may be freed after use. +*/ + Type* ArrayToPtr (const Type* T); /* Convert an array to a pointer to it's first element */ +const Type* PtrConversion (const Type* T); +/* If the type is a function, convert it to pointer to function. If the +** expression is an array, convert it to pointer to first element. Otherwise +** return T. +*/ + +const Type* IntPromotion (const Type* T); +/* Apply the integer promotions to T and return the result. The returned type +** string may be T if there is no need to change it. +*/ + +const Type* ArithmeticConvert (const Type* lhst, const Type* rhst); +/* Perform the usual arithmetic conversions for binary operators. */ + +const Type* SignedType (const Type* T); +/* Get signed counterpart of the integral type */ + +const Type* UnsignedType (const Type* T); +/* Get unsigned counterpart of the integral type */ + #if defined(HAVE_INLINE) INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ @@ -892,17 +895,6 @@ struct SymEntry* GetESUSymEntry (const Type* T) attribute ((const)); void SetESUSymEntry (Type* T, struct SymEntry* S); /* Set the SymEntry pointer for an enum/struct/union type */ -const Type* IntPromotion (const Type* T); -/* Apply the integer promotions to T and return the result. The returned type -** string may be T if there is no need to change it. -*/ - -const Type* PtrConversion (const Type* T); -/* If the type is a function, convert it to pointer to function. If the -** expression is an array, convert it to pointer to first element. Otherwise -** return T. -*/ - TypeCode AddrSizeQualifier (unsigned AddrSize); /* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ @@ -926,6 +918,28 @@ INLINE TypeCode DataAddrSizeQualifier (void) # define DataAddrSizeQualifier() (AddrSizeQualifier (DataAddrSize)) #endif +int TypeHasAttr (const Type* T); +/* Return true if the given type has attribute data */ + +#if defined(HAVE_INLINE) +INLINE void CopyTypeAttr (const Type* Src, Type* Dest) +/* Copy attribute data from Src to Dest */ +{ + Dest->A = Src->A; +} +#else +# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A) +#endif + +void PrintType (FILE* F, const Type* T); +/* Print fulle name of the type */ + +void PrintFuncSig (FILE* F, const char* Name, const Type* T); +/* Print a function signature */ + +void PrintRawType (FILE* F, const Type* T); +/* Print a type string in raw hex format (for debugging) */ + /* End of datatype.h */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index b68b3abbb..729b26942 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -153,65 +153,6 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr) -static const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) -/* Perform the usual arithmetic conversions for binary operators. */ -{ - /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 - ** Many binary operators that expect operands of arithmetic type cause conversions and yield - ** result types in a similar way. The purpose is to yield a common type, which is also the type - ** of the result. This pattern is called the usual arithmetic conversions. - */ - - /* There are additional rules for floating point types that we don't bother with, since - ** floating point types are not (yet) supported. - ** The integral promotions are performed on both operands. - */ - lhst = IntPromotion (lhst); - rhst = IntPromotion (rhst); - - /* If either operand has type unsigned long int, the other operand is converted to - ** unsigned long int. - */ - if ((IsTypeLong (lhst) && IsSignUnsigned (lhst)) || - (IsTypeLong (rhst) && IsSignUnsigned (rhst))) { - return type_ulong; - } - - /* Otherwise, if one operand has type long int and the other has type unsigned int, - ** if a long int can represent all values of an unsigned int, the operand of type unsigned int - ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, - ** both operands are converted to unsigned long int. - */ - if ((IsTypeLong (lhst) && IsTypeInt (rhst) && IsSignUnsigned (rhst)) || - (IsTypeLong (rhst) && IsTypeInt (lhst) && IsSignUnsigned (lhst))) { - /* long can represent all unsigneds, so we are in the first sub-case. */ - return type_long; - } - - /* Otherwise, if either operand has type long int, the other operand is converted to long int. - */ - if (IsTypeLong (lhst) || IsTypeLong (rhst)) { - return type_long; - } - - /* Otherwise, if either operand has type unsigned int, the other operand is converted to - ** unsigned int. - */ - if ((IsTypeInt (lhst) && IsSignUnsigned (lhst)) || - (IsTypeInt (rhst) && IsSignUnsigned (rhst))) { - return type_uint; - } - - /* Otherwise, both operands have type int. */ - CHECK (IsTypeInt (lhst)); - CHECK (IsSignSigned (lhst)); - CHECK (IsTypeInt (rhst)); - CHECK (IsSignSigned (rhst)); - return type_int; -} - - - static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) /* Adjust the two values for a binary operation. lhs is expected on stack or ** to be constant, rhs is expected to be in the primary register or constant. @@ -263,7 +204,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) -static void LimitExprValue (ExprDesc* Expr) +void LimitExprValue (ExprDesc* Expr) /* Limit the constant value of the expression to the range of its type */ { switch (GetUnderlyingTypeCode (Expr->Type)) { diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 02f9f7a5f..4909815ee 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -44,6 +44,9 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); ** generated code. */ +void LimitExprValue (ExprDesc* Expr); +/* Limit the constant value of the expression to the range of its type */ + void PushAddr (const ExprDesc* Expr); /* If the expression contains an address that was somehow evaluated, ** push this address on the stack. This is a helper function for all From 004c60de396398a14bd75b17ef0f260c9ae19499 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 15 Apr 2021 22:46:25 +0800 Subject: [PATCH 42/74] Optional flags for the codegen to skip restoring the expression results into the primary registers. --- src/cc65/codegen.c | 71 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index f8a1dcdf6..cf10314b9 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1166,7 +1166,9 @@ void g_putind (unsigned Flags, unsigned Offs) /* Overflow - we need to add the low byte also */ AddCodeLine ("ldy #$00"); AddCodeLine ("clc"); - AddCodeLine ("pha"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pha"); + } AddCodeLine ("lda #$%02X", Offs & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); @@ -1174,7 +1176,9 @@ void g_putind (unsigned Flags, unsigned Offs) AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("pla"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pla"); + } /* Complete address is on stack, new offset is zero */ Offs = 0; @@ -1184,12 +1188,15 @@ void g_putind (unsigned Flags, unsigned Offs) /* We can just add the high byte */ AddCodeLine ("ldy #$01"); AddCodeLine ("clc"); - AddCodeLine ("pha"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pha"); + } AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("pla"); - + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pla"); + } /* Offset is now just the low byte */ Offs &= 0x00FF; } @@ -1696,7 +1703,9 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, if (flags & CF_CONST) { if (val == 1) { AddCodeLine ("inc %s", lbuf); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); + } } else { AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("clc"); @@ -1726,8 +1735,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bne %s", LocalLabelName (L)); AddCodeLine ("inc %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("lda %s", lbuf); /* Hmmm... */ - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); /* Hmmm... */ + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("clc"); @@ -1738,13 +1749,17 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bcc %s", LocalLabelName (L)); AddCodeLine ("inc %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } } else { @@ -1754,8 +1769,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("txa"); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } break; @@ -1837,9 +1854,11 @@ void g_addeqlocal (unsigned flags, int Offs, unsigned long val) AddCodeLine ("lda #$%02X", (int) ((val >> 8) & 0xFF)); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("tax"); - AddCodeLine ("dey"); - AddCodeLine ("lda (sp),y"); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("dey"); + AddCodeLine ("lda (sp),y"); + } } else { g_getimmed (flags, val, 0); AddCodeLine ("jsr addeqysp"); @@ -1919,7 +1938,9 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, if (flags & CF_CONST) { if (val == 1) { AddCodeLine ("dec %s", lbuf); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); + } } else { AddCodeLine ("lda %s", lbuf); AddCodeLine ("sec"); @@ -1953,13 +1974,17 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bcs %s", LocalLabelName (L)); AddCodeLine ("dec %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda %s+1", lbuf); AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } else { AddCodeLine ("eor #$FF"); @@ -1970,8 +1995,10 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("eor #$FF"); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } break; From 1d7bf7355cb26d2da330d0173cecb5aa3213e1da Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 16 May 2021 19:08:43 +0800 Subject: [PATCH 43/74] Better function naming in declare.c. Scalar initialization routines need only 'const Type*' as parameters. --- src/cc65/declare.c | 80 +++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index a18c837b9..2aa620a29 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2243,7 +2243,7 @@ static void DefineData (ExprDesc* Expr) -static void OutputBitFieldData (StructInitData* SI) +static void DefineBitFieldData (StructInitData* SI) /* Output bit field data */ { /* Ignore if we have no data */ @@ -2266,7 +2266,18 @@ static void OutputBitFieldData (StructInitData* SI) -static ExprDesc ParseScalarInitInternal (Type* T) +static void DefineStrData (Literal* Lit, unsigned Count) +{ + /* Translate into target charset */ + TranslateLiteral (Lit); + + /* Output the data */ + g_defbytes (GetLiteralStr (Lit), Count); +} + + + +static ExprDesc ParseScalarInitInternal (const Type* T) /* Parse initializaton for scalar data types. This function will not output the ** data but return it in ED. */ @@ -2293,7 +2304,7 @@ static ExprDesc ParseScalarInitInternal (Type* T) -static unsigned ParseScalarInit (Type* T) +static unsigned ParseScalarInit (const Type* T) /* Parse initializaton for scalar data types. Return the number of data bytes. */ { /* Parse initialization */ @@ -2311,7 +2322,7 @@ static unsigned ParseScalarInit (Type* T) -static unsigned ParsePointerInit (Type* T) +static unsigned ParsePointerInit (const Type* T) /* Parse initializaton for pointer data types. Return the number of data bytes. */ { /* Optional opening brace */ @@ -2364,9 +2375,6 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) NextToken (); } - /* Translate into target charset */ - TranslateLiteral (CurTok.SVal); - /* If the array is one too small for the string literal, omit the ** trailing zero. */ @@ -2379,7 +2387,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Output the data */ - g_defbytes (GetLiteralStr (CurTok.SVal), Count); + DefineStrData (CurTok.SVal, Count); /* Skip the string */ NextToken (); @@ -2453,7 +2461,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initialization of a struct or union. Return the number of data bytes. */ { - SymEntry* Entry; + SymEntry* Sym; SymTable* Tab; StructInitData SI; int HasCurly = 0; @@ -2468,15 +2476,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the struct entry from the type */ - Entry = GetESUSymEntry (T); + Sym = GetESUSymEntry (T); /* Get the size of the struct from the symbol table entry */ - SI.Size = Entry->V.S.Size; + SI.Size = Sym->V.S.Size; /* Check if this struct definition has a field table. If it doesn't, it ** is an incomplete definition. */ - Tab = Entry->V.S.SymTab; + Tab = Sym->V.S.SymTab; if (Tab == 0) { Error ("Cannot initialize variables with incomplete type"); /* Try error recovery */ @@ -2486,7 +2494,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the list of symbols */ - Entry = Tab->SymHead; + Sym = Tab->SymHead; /* Initialize fields */ SI.Offs = 0; @@ -2495,7 +2503,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) while (CurTok.Tok != TOK_RCURLY) { /* Check for excess elements */ - if (Entry == 0) { + if (Sym == 0) { /* Is there just one trailing comma before a closing curly? */ if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) { /* Skip comma and exit scope */ @@ -2511,7 +2519,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Check for special members that don't consume the initializer */ - if ((Entry->Flags & SC_ALIAS) == SC_ALIAS) { + if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) { /* Just skip */ goto NextMember; } @@ -2519,17 +2527,17 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* This may be an anonymous bit-field, in which case it doesn't ** have an initializer. */ - if (SymIsBitField (Entry) && (IsAnonName (Entry->Name))) { + if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) { /* Account for the data and output it if we have at least a full ** word. We may have more if there was storage unit overlap, for ** example two consecutive 10 bit fields. These will be packed ** into 3 bytes. */ - SI.ValBits += Entry->V.B.BitWidth; + SI.ValBits += Sym->V.B.BitWidth; /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); } /* Avoid consuming the comma if any */ goto NextMember; @@ -2541,7 +2549,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) SkipComma = 0; } - if (SymIsBitField (Entry)) { + if (SymIsBitField (Sym)) { /* Parse initialization of one field. Bit-fields need a special ** handling. @@ -2552,14 +2560,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Entry->V.B.BitWidth) - 1U; + unsigned Mask = (1U << Sym->V.B.BitWidth) - 1U; /* Safety ... */ - CHECK (Entry->V.B.Offs * CHAR_BITS + Entry->V.B.BitOffs == - SI.Offs * CHAR_BITS + SI.ValBits); + CHECK (Sym->V.B.Offs * CHAR_BITS + Sym->V.B.BitOffs == + SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ED = ParseScalarInitInternal (Entry->Type); + ED = ParseScalarInitInternal (Sym->Type); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); @@ -2569,31 +2577,31 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** any useful bits. */ Val = (unsigned) ED.IVal & Mask; - if (IsSignUnsigned (Entry->Type)) { + if (IsSignUnsigned (Sym->Type)) { if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %ld to %u", - GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), - Entry->V.B.BitWidth, ED.IVal, Val); + GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), + Sym->V.B.BitWidth, ED.IVal, Val); } } else { /* Sign extend back to full width of host long. */ - unsigned ShiftBits = sizeof (long) * CHAR_BIT - Entry->V.B.BitWidth; + unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->V.B.BitWidth; long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); if (ED.IVal != RestoredVal) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " "changes value from %ld to %ld", - GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), - Entry->V.B.BitWidth, ED.IVal, RestoredVal); + GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), + Sym->V.B.BitWidth, ED.IVal, RestoredVal); } } /* Add the value to the currently stored bit-field value */ - Shift = (Entry->V.B.Offs - SI.Offs) * CHAR_BITS + Entry->V.B.BitOffs; + Shift = (Sym->V.B.Offs - SI.Offs) * CHAR_BITS + Sym->V.B.BitOffs; SI.BitVal |= (Val << Shift); /* Account for the data and output any full bytes we have. */ - SI.ValBits += Entry->V.B.BitWidth; + SI.ValBits += Sym->V.B.BitWidth; /* Make sure unsigned is big enough to hold the value, 22 bits. ** This is 22 bits because the most we can have is 7 bits left ** over from the previous OutputBitField call, plus 15 bits @@ -2604,7 +2612,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); } } else { @@ -2618,7 +2626,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Flexible array members may only be initialized if they are ** the last field (or part of the last struct field). */ - SI.Offs += ParseInitInternal (Entry->Type, Braces, AllowFlexibleMembers && Entry->NextSym == 0); + SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0); } /* More initializers? */ @@ -2633,10 +2641,10 @@ NextMember: /* Next member. For unions, only the first one can be initialized */ if (IsTypeUnion (T)) { /* Union */ - Entry = 0; + Sym = 0; } else { /* Struct */ - Entry = Entry->NextSym; + Sym = Sym->NextSym; } } @@ -2647,7 +2655,7 @@ NextMember: /* If we have data from a bit-field left, output it now */ CHECK (SI.ValBits < CHAR_BITS); - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); /* If there are struct fields left, reserve additional storage */ if (SI.Offs < SI.Size) { From 5adb29ce3165db1c5f525dc9a0b8160170e4613c Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 22 May 2021 19:15:47 +0800 Subject: [PATCH 44/74] Made "bit-field-ness" a type property instead of a SymbolEntry or ExprDesc property. Fixed integer promotion and result type in certain operations. Fixed bit-fields 'op=' and postfix inc/dec operators. --- src/cc65/assignment.c | 811 +++++++++++++++++++++++++++++++++++------- src/cc65/assignment.h | 28 +- src/cc65/datatype.c | 74 +++- src/cc65/datatype.h | 108 ++++-- src/cc65/declare.c | 20 +- src/cc65/expr.c | 720 +++++++++++-------------------------- src/cc65/expr.h | 8 + src/cc65/exprdesc.c | 44 +-- src/cc65/exprdesc.h | 40 +-- src/cc65/loadexpr.c | 66 ++-- src/cc65/stdfunc.c | 10 +- src/cc65/symentry.h | 7 - src/cc65/symtab.c | 12 +- src/cc65/typecmp.c | 15 + src/cc65/typeconv.c | 12 +- 15 files changed, 1171 insertions(+), 804 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 7ebd2c4e1..be6a8116f 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -42,19 +42,35 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "stackptr.h" #include "stdnames.h" #include "typecmp.h" #include "typeconv.h" +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Map a generator function and its attributes to a token */ +typedef struct GenDesc { + token_t Tok; /* Token to map to */ + unsigned Flags; /* Flags for generator function */ + void (*Func) (unsigned, unsigned long); /* Generator func */ +} GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) +static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Copy the struct/union represented by RExpr to the one represented by LExpr */ { /* If the size is that of a basic type (char, int, long), we will copy @@ -127,14 +143,519 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) ** to a boolean in C, but there is no harm to be future-proof. */ ED_MarkAsUntested (LExpr); - - return 1; } -void Assignment (ExprDesc* Expr) -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) +/* Process inc/dec for bit-field */ +{ + int AddrSP; + unsigned Flags; /* Internal codegen flags */ + unsigned Mask; + unsigned ChunkFlags; + const Type* ChunkType; + const Type* ResType; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than + ** that of its declaration type in cc65. So this is OK. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + if (KeepResult == OA_NEED_OLD) { + /* Save the original expression value */ + g_save (Flags | CF_FORCECHAR); + } + + /* Handle for add and sub */ + if (Val > 0) { + g_inc (Flags | CF_CONST, Val); + } else if (Val < 0) { + g_dec (Flags | CF_CONST, -Val); + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + if (KeepResult == OA_NEED_NEW) { + /* Save the result value */ + g_save (Flags | CF_FORCECHAR); + } + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Cache the expression result type */ + ResType = IntPromotion (Expr->Type); + + if (KeepResult != OA_NEED_NONE) { + /* Restore the expression result value */ + g_restore (Flags | CF_FORCECHAR); + + /* Promote if needed */ + if (KeepResult != OA_NEED_OLD) { + /* Do unsigned promotion first */ + g_typecast (TypeOf (ResType) | CF_UNSIGNED, Flags); + + /* Then do sign-extension */ + if (IsSignSigned (Expr->Type) && + Expr->Type->A.B.Width < CHAR_BITS * SizeOf (ResType)) { + /* The way is: + ** x = bits & bit_mask + ** m = 1 << (bit_width - 1) + ** r = (x ^ m) - m + ** Since we have already masked bits with bit_mask, we may skip the + ** first step. + */ + g_xor (Flags | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + g_dec ((Flags & ~CF_FORCECHAR) | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + } + } else { + /* Do promotion with sign-extension */ + g_typecast (TypeOf (ResType), Flags); + } + } + + /* Get the expression result type */ + Expr->Type = ResType; +} + + + +static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for bit-field lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + int AddrSP; + unsigned Mask; + unsigned Flags; + unsigned ChunkFlags; + const Type* ChunkType; + const Type* ResType; + + /* Cache the expression result type */ + ResType = IntPromotion (Expr->Type); + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than + ** that of its declaration type in cc65. So this is OK. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + if (Gen != 0) { + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + /* Backup them on stack */ + GetCodePos (&PushPos); + g_push (Flags & ~CF_FORCECHAR, 0); + + } + + /* Read the expression on the right side of the '=' or 'op=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + if (Gen == 0) { + + /* Get the value and apply the mask */ + unsigned Val = (unsigned)(Expr2.IVal & Mask); + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* If the value is equal to the mask now, all bits are one, and we + ** can skip the mask operation. + */ + if (Val != Mask) { + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + } + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags | CF_CONST, Val << Expr->Type->A.B.Offs); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Load the expression result value */ + if (IsSignSigned (Expr->Type)) { + unsigned SignExtensionMask = 1 << (Expr->Type->A.B.Width - 1); + Val = (Val^ SignExtensionMask) - SignExtensionMask; + } + ED_MakeConstAbs (Expr, Val, ResType); + LimitExprValue (Expr); + LoadExpr (CF_NONE, Expr); + + /* Done */ + goto Done; + + } else { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } else if (Gen->Func == g_sub) { + g_dec (Flags | CF_CONST, Expr2.IVal); + } else { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Error ("Division by zero"); + } else if (Gen->Func == g_mod) { + Error ("Modulo operation with zero"); + } + } + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags) | CF_CONST, Expr2.IVal); + } else { + Gen->Func ((Flags & ~CF_FORCECHAR) | CF_CONST, Expr2.IVal); + } + } + + } + + } else { + + /* Do 'op' if provided */ + if (Gen != 0) { + + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags), 0); + } else { + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + } + + } else { + + /* Do type conversion if necessary */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load rhs into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } + + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + /* Save the expression result value */ + g_save (Flags); + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Restore the expression result value */ + g_restore (Flags); + + /* Do unsigned promotion first */ + g_typecast (TypeOf (ResType) | CF_UNSIGNED, Flags); + + /* Then do sign-extension */ + if (IsSignSigned (Expr->Type) && + Expr->Type->A.B.Width < CHAR_BITS * SizeOf (ResType)) { + /* The way is: + ** x = bits & bit_mask + ** m = 1 << (bit_width - 1) + ** r = (x ^ m) - m + ** Since we have already masked bits with bit_mask, we may skip the + ** first step. + */ + g_xor (Flags | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + g_dec ((Flags & ~CF_FORCECHAR) | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + } + +Done: + + /* Get the expression result type */ + Expr->Type = ResType; + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for arithmetic lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + + unsigned Flags; + int MustScale; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type); + + /* Determine the type of the lhs */ + MustScale = Gen != 0 && (Gen->Func == g_add || Gen->Func == g_sub) && + IsTypePtr (Expr->Type); + + /* Get the address on stack for the store */ + PushAddr (Expr); + + if (Gen == 0) { + + /* Read the expression on the right side of the '=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* Do type conversion if necessary. Beware: Do not use char type + ** here! + */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } else { + + /* Load the original value if necessary */ + LoadExpr (CF_NONE, Expr); + + /* Push lhs on stack */ + GetCodePos (&PushPos); + g_push (Flags, 0); + + /* Read the expression on the right side of the '=' or 'op=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + if (MustScale) { + /* lhs is a pointer, scale rhs */ + Expr2.IVal *= CheckedSizeOf (Expr->Type+1); + } + + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } else if (Gen->Func == g_sub) { + g_dec (Flags | CF_CONST, Expr2.IVal); + } else { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Error ("Division by zero"); + } else if (Gen->Func == g_mod) { + Error ("Modulo operation with zero"); + } + } + Gen->Func (Flags | CF_CONST, Expr2.IVal); + } + + } else { + + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + } + + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Adjust the types of the operands if needed */ + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + + } + } + + /* Generate a store instruction */ + Store (Expr, 0); + + /* Get the expression result type */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ { const Type* ltype = Expr->Type; @@ -142,28 +663,32 @@ void Assignment (ExprDesc* Expr) ED_Init (&Expr2); Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - /* We must have an lvalue for an assignment */ - if (ED_IsRVal (Expr)) { - if (IsTypeArray (Expr->Type)) { - Error ("Array type '%s' is not assignable", GetFullTypeName (Expr->Type)); - } else if (IsTypeFunc (Expr->Type)) { - Error ("Function type '%s' is not assignable", GetFullTypeName (Expr->Type)); - } else { - Error ("Assignment to rvalue"); + /* Only "=" accept struct/union */ + if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (ltype)) { + Error ("Invalid left operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } else { + /* Check for assignment to incomplete type */ + if (IsIncompleteESUType (ltype)) { + Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); + } else if (ED_IsRVal (Expr)) { + /* Assignment can only be used with lvalues */ + if (IsTypeArray (ltype)) { + Error ("Array type '%s' is not assignable", GetFullTypeName (ltype)); + } else if (IsTypeFunc (ltype)) { + Error ("Function type '%s' is not assignable", GetFullTypeName (ltype)); + } else { + Error ("Assignment to rvalue"); + } + } else if (IsQualConst (ltype)) { + /* Check for assignment to const */ + Error ("Assignment to const"); } } - /* Check for assignment to const */ - if (IsQualConst (ltype)) { - Error ("Assignment to const"); - } - - /* Check for assignment to incomplete type */ - if (IsIncompleteESUType (ltype)) { - Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); - } - - /* Skip the '=' token */ + /* Skip the '=' or 'op=' token */ NextToken (); /* cc65 does not have full support for handling structs or unions. Since @@ -174,114 +699,136 @@ void Assignment (ExprDesc* Expr) if (IsClassStruct (ltype)) { /* Copy the struct or union by value */ CopyStruct (Expr, &Expr2); - - } else if (ED_IsBitField (Expr)) { - - CodeMark AndPos; - CodeMark PushPos; - - unsigned Mask; - unsigned Flags; - - /* If the bit-field fits within one byte, do the following operations - ** with bytes. - */ - if (Expr->BitOffs / CHAR_BITS == (Expr->BitOffs + Expr->BitWidth - 1) / CHAR_BITS) { - Expr->Type = type_uchar; - } - - /* Determine code generator flags */ - Flags = TypeOf (Expr->Type); - - /* Assignment to a bit field. Get the address on stack for the store. */ - PushAddr (Expr); - - /* Load the value from the location */ - Expr->Flags &= ~E_BITFIELD; - LoadExpr (CF_NONE, Expr); - - /* Mask unwanted bits */ - Mask = (0x0001U << Expr->BitWidth) - 1U; - GetCodePos (&AndPos); - g_and (Flags | CF_CONST, ~(Mask << Expr->BitOffs)); - - /* Push it on stack */ - GetCodePos (&PushPos); - g_push (Flags, 0); - - /* Read the expression on the right side of the '=' */ - MarkedExprWithCheck (hie1, &Expr2); - - /* Do type conversion if necessary. Beware: Do not use char type - ** here! - */ - TypeConversion (&Expr2, ltype); - - /* Special treatment if the value is constant. */ - /* Beware: Expr2 may contain side effects, so there must not be - ** code generated for Expr2. - */ - if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - - /* Get the value and apply the mask */ - unsigned Val = (unsigned) (Expr2.IVal & Mask); - - /* Since we will do the OR with a constant, we can remove the push */ - RemoveCode (&PushPos); - - /* If the value is equal to the mask now, all bits are one, and we - ** can remove the mask operation from above. - */ - if (Val == Mask) { - RemoveCode (&AndPos); - } - - /* Generate the or operation */ - g_or (Flags | CF_CONST, Val << Expr->BitOffs); - - } else { - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Apply the mask */ - g_and (Flags | CF_CONST, Mask); - - /* Shift it into the right position */ - g_asl (Flags | CF_CONST, Expr->BitOffs); - - /* Or both values */ - g_or (Flags, 0); - } - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Restore the expression type */ - Expr->Type = ltype; - - /* Value is in primary as an rvalue */ - ED_FinalizeRValLoad (Expr); - + } else if (IsTypeBitField (ltype)) { + /* Special care is needed for bit-field 'op=' */ + OpAssignBitField (Gen, Expr, Op); } else { - - /* Get the address on stack if needed */ - PushAddr (Expr); - - /* Read the expression on the right side of the '=' */ - hie1 (&Expr2); - - /* Do type conversion if necessary */ - TypeConversion (&Expr2, ltype); - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Value is in primary as an rvalue */ - ED_FinalizeRValLoad (Expr); - + /* Normal straight 'op=' */ + OpAssignArithmetic (Gen, Expr, Op); } } + + + +void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) +/* Parse a "+=" or "-=" operation */ +{ + ExprDesc Expr2; + unsigned lflags; + unsigned rflags; + int MustScale; + + /* We currently only handle non-bit-fields in some addressing modes here */ + if (IsTypeBitField (Expr->Type) || ED_IsLocPrimaryOrExpr (Expr)) { + /* Use generic routine instead */ + OpAssign (Gen, Expr, Op); + return; + } + + /* There must be an integer or pointer on the left side */ + if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { + Error ("Invalid left operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } else { + /* We must have an lvalue */ + if (ED_IsRVal (Expr)) { + Error ("Invalid lvalue in assignment"); + } else if (IsQualConst (Expr->Type)) { + /* The left side must not be const qualified */ + Error ("Assignment to const"); + } + } + + /* Skip the operator */ + NextToken (); + + /* Check if we have a pointer expression and must scale rhs */ + MustScale = IsTypePtr (Expr->Type); + + /* Initialize the code generator flags */ + lflags = 0; + rflags = 0; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Evaluate the rhs. We expect an integer here, since float is not + ** supported + */ + hie1 (&Expr2); + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } + + /* Setup the code generator flags */ + lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; + rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; + + if (ED_IsConstAbs (&Expr2)) { + /* The resulting value is a constant */ + rflags |= CF_CONST; + lflags |= CF_CONST; + + /* Scale it */ + if (MustScale) { + Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); + } + } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Convert the type of the rhs to that of the lhs */ + g_typecast (lflags, rflags & ~CF_FORCECHAR); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); + } + } + + /* Output apropriate code depending on the location */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Absolute numeric addressed variable, global variable, local + ** static variable, register variable, pooled literal or code + ** label location. + */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } else { + g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } + break; + + case E_LOC_STACK: + /* Value on the stack */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); + } else { + g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); + } + break; + + default: + Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); + } + + /* Get the expression result type */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + + /* Expression is an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); +} diff --git a/src/cc65/assignment.h b/src/cc65/assignment.h index b2cc1548b..6098118b4 100644 --- a/src/cc65/assignment.h +++ b/src/cc65/assignment.h @@ -43,14 +43,38 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Whether to save/restore the original lhs or result value */ +enum { + OA_NEED_NONE, + OA_NEED_OLD, + OA_NEED_NEW, +}; + +/* Forward */ +struct GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -void Assignment (ExprDesc* lval); -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult); +/* Process inc/dec for bit-field */ + +void OpAssign (const struct GenDesc* Gen, ExprDesc* lval, const char* Op); +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ + +void OpAddSubAssign (const struct GenDesc* Gen, ExprDesc *Expr, const char* Op); +/* Parse a "+=" or "-=" operation */ diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6465c27a6..e5d3f8d96 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -207,7 +207,11 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } } - SB_AppendStr (&Buf, GetSymTypeName (T)); + if (!IsTypeBitField (T)) { + SB_AppendStr (&Buf, GetSymTypeName (T)); + } else { + SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); + } if (!SB_IsEmpty (West)) { SB_AppendChar (&Buf, ' '); @@ -231,6 +235,7 @@ const char* GetBasicTypeName (const Type* T) { switch (GetRawType (T)) { case T_TYPE_ENUM: return "enum"; + case T_TYPE_BITFIELD: return "bit-field"; case T_TYPE_FLOAT: return "float"; case T_TYPE_DOUBLE: return "double"; case T_TYPE_VOID: return "void"; @@ -581,6 +586,18 @@ const Type* GetUnderlyingType (const Type* Type) if (Type->A.S->V.E.Type != 0) { return Type->A.S->V.E.Type; } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break; + case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break; + case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break; + default: Type = IsSignSigned (Type) ? type_int : type_uint; break; + } } return Type; @@ -594,13 +611,14 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) */ { TypeCode Underlying = UnqualifiedType (Type->C); - TypeCode TCode; if (IsISOChar (Type)) { return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; } else if (IsTypeEnum (Type)) { + TypeCode TCode; + /* This should not happen, but just in case */ if (Type->A.S == 0) { Internal ("Enum tag type error in GetUnderlyingTypeCode"); @@ -623,6 +641,21 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; default: Underlying |= T_TYPE_INT; break; } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + case SIZEOF_CHAR: Underlying = T_CHAR; break; + case SIZEOF_INT: Underlying = T_INT; break; + case SIZEOF_LONG: Underlying = T_LONG; break; + case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break; + default: Underlying = T_INT; break; + } + Underlying &= ~T_MASK_SIGN; + Underlying |= Type->C & T_MASK_SIGN; } return Underlying; @@ -933,7 +966,11 @@ const Type* IntPromotion (const Type* T) ** These are called the integral promotions. */ - if (IsTypeChar (T)) { + if (IsTypeBitField (T)) { + /* The standard rule is OK for now as we don't support bit-fields with widths > 16. + */ + return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; + } else if (IsTypeChar (T)) { /* An integer can represent all values from either signed or unsigned char, so convert ** chars to int. */ @@ -1059,6 +1096,37 @@ const Type* UnsignedType (const Type* T) +Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth) +/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type +** string is allocated on the heap and may be freed after use. +*/ +{ + Type* P; + + /* The type specifier must be integeral */ + CHECK (IsClassInt (T)); + + /* Allocate the new type string */ + P = TypeAlloc (3); + + /* Create the return type... */ + P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD; + P[0].C |= (T[0].C & T_QUAL_ADDRSIZE); + P[0].A.B.Offs = BitOffs; + P[0].A.B.Width = BitWidth; + + /* Get the declaration type */ + memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1])); + + /* Get done... */ + P[2].C = T_END; + + /* ...and return it */ + return P; +} + + + int IsClassObject (const Type* T) /* Return true if this is a fully described object type */ { diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 4729284bd..e36d7c82e 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -78,54 +78,55 @@ enum { T_TYPE_INT = 0x000003, T_TYPE_LONG = 0x000004, T_TYPE_LONGLONG = 0x000005, - T_TYPE_ENUM = 0x000006, - T_TYPE_FLOAT = 0x000007, - T_TYPE_DOUBLE = 0x000008, - T_TYPE_VOID = 0x000009, - T_TYPE_STRUCT = 0x00000A, - T_TYPE_UNION = 0x00000B, - T_TYPE_ARRAY = 0x00000C, - T_TYPE_PTR = 0x00000D, - T_TYPE_FUNC = 0x00000E, - T_MASK_TYPE = 0x00000F, + T_TYPE_ENUM = 0x000008, + T_TYPE_BITFIELD = 0x000009, + T_TYPE_FLOAT = 0x00000A, + T_TYPE_DOUBLE = 0x00000B, + T_TYPE_VOID = 0x000010, + T_TYPE_STRUCT = 0x000011, + T_TYPE_UNION = 0x000012, + T_TYPE_ARRAY = 0x000018, + T_TYPE_PTR = 0x000019, + T_TYPE_FUNC = 0x00001A, + T_MASK_TYPE = 0x00001F, /* Type classes */ T_CLASS_NONE = 0x000000, - T_CLASS_INT = 0x000010, - T_CLASS_FLOAT = 0x000020, - T_CLASS_PTR = 0x000030, - T_CLASS_STRUCT = 0x000040, - T_CLASS_FUNC = 0x000050, - T_MASK_CLASS = 0x000070, + T_CLASS_INT = 0x000020, + T_CLASS_FLOAT = 0x000040, + T_CLASS_PTR = 0x000060, + T_CLASS_STRUCT = 0x000080, + T_CLASS_FUNC = 0x0000A0, + T_MASK_CLASS = 0x0000E0, /* Type signedness */ T_SIGN_NONE = 0x000000, - T_SIGN_UNSIGNED = 0x000080, - T_SIGN_SIGNED = 0x000100, - T_MASK_SIGN = 0x000180, + T_SIGN_UNSIGNED = 0x000100, + T_SIGN_SIGNED = 0x000200, + T_MASK_SIGN = 0x000300, /* Type size modifiers */ T_SIZE_NONE = 0x000000, - T_SIZE_CHAR = 0x000200, - T_SIZE_SHORT = 0x000400, - T_SIZE_INT = 0x000600, - T_SIZE_LONG = 0x000800, - T_SIZE_LONGLONG = 0x000A00, - T_MASK_SIZE = 0x000E00, + T_SIZE_CHAR = 0x001000, + T_SIZE_SHORT = 0x002000, + T_SIZE_INT = 0x003000, + T_SIZE_LONG = 0x004000, + T_SIZE_LONGLONG = 0x005000, + T_MASK_SIZE = 0x00F000, /* Type qualifiers */ T_QUAL_NONE = 0x000000, - T_QUAL_CONST = 0x001000, - T_QUAL_VOLATILE = 0x002000, - T_QUAL_RESTRICT = 0x004000, + T_QUAL_CONST = 0x010000, + T_QUAL_VOLATILE = 0x020000, + T_QUAL_RESTRICT = 0x040000, T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT, - T_QUAL_NEAR = 0x008000, - T_QUAL_FAR = 0x010000, + T_QUAL_NEAR = 0x080000, + T_QUAL_FAR = 0x100000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, - T_QUAL_FASTCALL = 0x020000, - T_QUAL_CDECL = 0x040000, + T_QUAL_FASTCALL = 0x200000, + T_QUAL_CDECL = 0x400000, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, - T_MASK_QUAL = 0x07F000, + T_MASK_QUAL = 0x7F0000, /* Types */ T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, @@ -140,6 +141,8 @@ enum { T_LONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, T_ULONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, + T_SBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_UBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, T_FLOAT = T_TYPE_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_DOUBLE = T_TYPE_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_VOID = T_TYPE_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, @@ -171,6 +174,10 @@ struct Type { struct SymEntry* S; /* Enum/struct/union tag symbol entry pointer */ long L; /* Numeric attribute value */ unsigned long U; /* Dito, unsigned */ + struct { + unsigned Offs; /* Bit offset into storage unit */ + unsigned Width; /* Width in bits */ + } B; /* Data for bit fields */ } A; /* Type attribute if necessary */ }; @@ -372,6 +379,11 @@ const Type* SignedType (const Type* T); const Type* UnsignedType (const Type* T); /* Get unsigned counterpart of the integral type */ +Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth); +/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type +** string is allocated on the heap and may be freed after use. +*/ + #if defined(HAVE_INLINE) INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ @@ -514,6 +526,36 @@ INLINE int IsTypeEnum (const Type* T) # define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) #endif +#if defined(HAVE_INLINE) +INLINE int IsTypeSignedBitField (const Type* T) +/* Return true if this is a signed bit-field */ +{ + return (UnqualifiedType (T->C) == T_SBITFIELD); +} +#else +# define IsTypeSignedBitField(T) (UnqualifiedType ((T)->C) == T_SBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeUnsignedBitField (const Type* T) +/* Return true if this is an unsigned bit-field */ +{ + return (UnqualifiedType (T->C) == T_UBITFIELD); +} +#else +# define IsTypeUnsignedBitField(T) (UnqualifiedType ((T)->C) == T_UBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeBitField (const Type* T) +/* Return true if this is a bit-field (either signed or unsigned) */ +{ + return IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T); +} +#else +# define IsTypeBitField(T) (IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T)) +#endif + #if defined(HAVE_INLINE) INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 2aa620a29..c1346e872 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2533,7 +2533,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** example two consecutive 10 bit fields. These will be packed ** into 3 bytes. */ - SI.ValBits += Sym->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { @@ -2560,14 +2560,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Sym->V.B.BitWidth) - 1U; + unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U; /* Safety ... */ - CHECK (Sym->V.B.Offs * CHAR_BITS + Sym->V.B.BitOffs == - SI.Offs * CHAR_BITS + SI.ValBits); + CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs == + SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ED = ParseScalarInitInternal (Sym->Type); + ED = ParseScalarInitInternal (IntPromotion (Sym->Type)); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); @@ -2582,26 +2582,26 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %ld to %u", GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), - Sym->V.B.BitWidth, ED.IVal, Val); + Sym->Type->A.B.Width, ED.IVal, Val); } } else { /* Sign extend back to full width of host long. */ - unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->V.B.BitWidth; + unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width; long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); if (ED.IVal != RestoredVal) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " "changes value from %ld to %ld", GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), - Sym->V.B.BitWidth, ED.IVal, RestoredVal); + Sym->Type->A.B.Width, ED.IVal, RestoredVal); } } /* Add the value to the currently stored bit-field value */ - Shift = (Sym->V.B.Offs - SI.Offs) * CHAR_BITS + Sym->V.B.BitOffs; + Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs; SI.BitVal |= (Val << Shift); /* Account for the data and output any full bytes we have. */ - SI.ValBits += Sym->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* Make sure unsigned is big enough to hold the value, 22 bits. ** This is 22 bits because the most we can have is 7 bits left ** over from the previous OutputBitField call, plus 15 bits diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 729b26942..c45005d65 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -47,13 +47,8 @@ -/* Generator attributes */ -#define GEN_NOPUSH 0x01 /* Don't push lhs */ -#define GEN_COMM 0x02 /* Operator is commutative */ -#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ - /* Map a generator function and its attributes to a token */ -typedef struct { +typedef struct GenDesc { token_t Tok; /* Token to map to */ unsigned Flags; /* Flags for generator function */ void (*Func) (unsigned, unsigned long); /* Generator func */ @@ -91,7 +86,7 @@ static void PostDec (ExprDesc* Expr); -static unsigned GlobalModeFlags (const ExprDesc* Expr) +unsigned GlobalModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { @@ -390,117 +385,175 @@ static void DeferDec (const ExprDesc* Expr) -static void DeferredInc (ExprDesc* Expr) -/* Do the deferred post-inc */ +static void DoInc (ExprDesc* Expr, unsigned KeepResult) +/* Do increment */ { unsigned Flags; - unsigned long Val; - - /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; + long Val; /* Get the increment value in bytes */ Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, Val, KeepResult); + return; + } - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } - case E_LOC_GLOBAL: - /* Global variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + if (KeepResult == OA_NEED_OLD) { - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + Flags |= CF_FORCECHAR; - case E_LOC_REGISTER: - /* Register variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + /* Push the address if needed */ + PushAddr (Expr); - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; + /* Do the increment */ + g_inc (Flags | CF_CONST, Val); - case E_LOC_EXPR: - /* An expression in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_addeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_addeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_inc (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_addeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoInc(): 0x%04X", ED_GetLoc (Expr)); + } - default: - Internal ("Invalid location in DeferredInc(): 0x%04X", ED_GetLoc (Expr)); } } -static void DeferredDec (ExprDesc* Expr) -/* Do the deferred post-dec */ +static void DoDec (ExprDesc* Expr, unsigned KeepResult) +/* Do decrement */ { unsigned Flags; - unsigned long Val; + long Val; - /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; - - /* Get the increment value in bytes */ + /* Get the decrement value in bytes */ Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, -Val, KeepResult); + return; + } - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } - case E_LOC_GLOBAL: - /* Global variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + if (KeepResult == OA_NEED_OLD) { - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + Flags |= CF_FORCECHAR; - case E_LOC_REGISTER: - /* Register variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + /* Push the address if needed */ + PushAddr (Expr); - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; + /* Do the decrement */ + g_dec (Flags | CF_CONST, Val); - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_subeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_subeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_dec (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_subeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoDec(): 0x%04X", ED_GetLoc (Expr)); + } - default: - Internal ("Invalid location in DeferredDec(): 0x%04X", ED_GetLoc (Expr)); } } @@ -564,11 +617,11 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) switch (Op->OpType) { case DOT_INC: - DeferredInc (&Op->Expr); + DoInc (&Op->Expr, OA_NEED_NONE); break; case DOT_DEC: - DeferredDec (&Op->Expr); + DoDec (&Op->Expr, OA_NEED_NONE); break; } xfree (&Op->Expr); @@ -595,6 +648,7 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) } + static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Parse the argument list of the called function and pass the arguments to it. ** Depending on several criteria, this may be done by just pushing into each @@ -724,14 +778,17 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) if (IsClassStruct (Expr.Type)) { /* Use the replacement type */ Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + + /* Load the value into the primary if it is not already there */ + LoadExpr (Flags, &Expr); } else { + /* Load the value into the primary if it is not already there */ + LoadExpr (CF_NONE, &Expr); + /* Use the type of the argument for the push */ Flags |= TypeOf (Expr.Type); } - /* Load the value into the primary if it is not already there */ - LoadExpr (Flags, &Expr); - /* If this is a fastcall function, don't push the last argument */ if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { unsigned ArgSize = sizeofarg (Flags); @@ -1023,7 +1080,7 @@ static void Primary (ExprDesc* E) /* Floating point constant */ if (CurTok.Tok == TOK_FCONST) { - E->FVal = CurTok.FVal; + E->V.FVal = CurTok.FVal; E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; E->Type = CurTok.Type; NextToken (); @@ -1198,9 +1255,9 @@ static void Primary (ExprDesc* E) case TOK_WCSCONST: /* String literal */ if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - E->LVal = UseLiteral (CurTok.SVal); + E->V.LVal = UseLiteral (CurTok.SVal); } else { - E->LVal = CurTok.SVal; + E->V.LVal = CurTok.SVal; } E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF; @@ -1400,14 +1457,14 @@ static void StructRef (ExprDesc* Expr) */ BitOffs = Field.V.Offs * CHAR_BITS; if (SymIsBitField (&Field)) { - BitOffs += Field.V.B.BitOffs; + BitOffs += Field.Type->A.B.Offs; g_asr (Flags, BitOffs); /* Mask the value. This is unnecessary if the shift executed above ** moved only zeroes into the value. */ - if (BitOffs + Field.V.B.BitWidth != FieldSize * CHAR_BITS) { + if (BitOffs + Field.Type->A.B.Width != FieldSize * CHAR_BITS) { g_and (CF_INT | CF_UNSIGNED | CF_CONST, - (0x0001U << Field.V.B.BitWidth) - 1U); + (0x0001U << Field.Type->A.B.Width) - 1U); } } else { g_asr (Flags, BitOffs); @@ -1434,12 +1491,7 @@ static void StructRef (ExprDesc* Expr) ED_AddrExpr (Expr); } - /* Make the expression a bit field if necessary */ - if (SymIsBitField (&Field)) { - ED_MakeBitField (Expr, Field.V.B.BitOffs, Field.V.B.BitWidth); - } } - } @@ -1582,9 +1634,6 @@ void Store (ExprDesc* Expr, const Type* StoreType) static void PreInc (ExprDesc* Expr) /* Handle the preincrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1600,49 +1649,8 @@ static void PreInc (ExprDesc* Expr) Error ("Increment of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute numeric addressed variable */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Global variabl, static variable, register variable, pooled - ** literal or code label location. - */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression referenced in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreInc(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the increment */ + DoInc (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ ED_FinalizeRValLoad (Expr); @@ -1653,9 +1661,6 @@ static void PreInc (ExprDesc* Expr) static void PreDec (ExprDesc* Expr) /* Handle the predecrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1671,49 +1676,8 @@ static void PreDec (ExprDesc* Expr) Error ("Decrement of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute numeric addressed variable */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Global variabl, static variable, register variable, pooled - ** literal or code label location. - */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreDec(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the decrement */ + DoDec (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ ED_FinalizeRValLoad (Expr); @@ -1724,7 +1688,7 @@ static void PreDec (ExprDesc* Expr) static void PostInc (ExprDesc* Expr) /* Handle the postincrement operator */ { - unsigned Flags, Loc; + unsigned Flags; NextToken (); @@ -1748,34 +1712,17 @@ static void PostInc (ExprDesc* Expr) */ /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0)); } else { - Loc = ED_GetLoc (Expr); - if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); - - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); - } else { - g_inc (Flags | CF_CONST | CF_FORCECHAR, 1); - } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); + /* Do the increment */ + DoInc (Expr, OA_NEED_OLD); } else { @@ -1787,6 +1734,11 @@ static void PostInc (ExprDesc* Expr) } } + /* Adjust the type of the expression */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + /* The result is always an expression, no reference */ ED_FinalizeRValLoad (Expr); } @@ -1796,11 +1748,11 @@ static void PostInc (ExprDesc* Expr) static void PostDec (ExprDesc* Expr) /* Handle the postdecrement operator */ { - unsigned Flags, Loc; + unsigned Flags; NextToken (); - /* The expression to increment must be an lvalue */ + /* The expression to decrement must be an lvalue */ if (!ED_IsLVal (Expr)) { Error ("Invalid lvalue"); return; @@ -1815,34 +1767,17 @@ static void PostDec (ExprDesc* Expr) Flags = TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0)); } else { - Loc = ED_GetLoc (Expr); - if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); - - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); - } else { - g_dec (Flags | CF_CONST | CF_FORCECHAR, 1); - } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); + /* Do the decrement */ + DoDec (Expr, OA_NEED_OLD); } else { @@ -1854,6 +1789,11 @@ static void PostDec (ExprDesc* Expr) } } + /* Adjust the type of the expression */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + /* The result is always an expression, no reference */ ED_FinalizeRValLoad (Expr); } @@ -1863,8 +1803,6 @@ static void PostDec (ExprDesc* Expr) static void UnaryOp (ExprDesc* Expr) /* Handle unary -/+ and ~ */ { - unsigned Flags; - /* Remember the operator token and skip it */ token_t Tok = CurTok.Tok; NextToken (); @@ -1888,15 +1826,24 @@ static void UnaryOp (ExprDesc* Expr) default: Internal ("Unexpected token: %d", Tok); } + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + /* Limit the calculated value to the range of its type */ LimitExprValue (Expr); } else { + unsigned Flags; + /* Value is not constant */ LoadExpr (CF_NONE, Expr); - /* Adjust the type of the value */ - Flags = g_typeadjust (TypeOf (Expr->Type), TypeOf (type_int) | CF_CONST); + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + TypeConversion (Expr, Expr->Type); + + /* Get code generation flags */ + Flags = TypeOf (Expr->Type); /* Handle the operation */ switch (Tok) { @@ -1909,9 +1856,6 @@ static void UnaryOp (ExprDesc* Expr) /* The result is an rvalue in the primary */ ED_FinalizeRValLoad (Expr); } - - /* Adjust the type of the expression */ - Expr->Type = IntPromotion (Expr->Type); } @@ -2003,13 +1947,13 @@ void hie10 (ExprDesc* Expr) if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { if (ED_IsRVal (Expr)) { Error ("Illegal address"); - break; + /* Continue anyway, just to avoid further warnings */ } - if (ED_IsBitField (Expr)) { + if (IsTypeBitField (Expr->Type)) { Error ("Cannot take address of bit-field"); - /* Do it anyway, just to avoid further warnings */ - ED_DisBitField (Expr); + /* Continue anyway, just to avoid further warnings */ + Expr->Type = GetUnderlyingType (Expr->Type); } /* The & operator yields an rvalue address */ ED_AddrExpr (Expr); @@ -2034,7 +1978,7 @@ void hie10 (ExprDesc* Expr) ED_Init (&Uneval); ED_MarkForUneval (&Uneval); hie10 (&Uneval); - if (ED_IsBitField (&Uneval)) { + if (IsTypeBitField (Uneval.Type)) { Error ("Cannot apply 'sizeof' to bit-field"); Size = 0; } else { @@ -2536,6 +2480,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Determine the signedness of the operands */ int LeftSigned = IsSignSigned (Expr->Type); int RightSigned = IsSignSigned (Expr2.Type); + int CmpSigned = IsClassInt (Expr->Type) && IsClassInt (Expr2.Type) && + IsSignSigned (ArithmeticConvert (Expr->Type, Expr2.Type)); /* If the right hand side is constant, and the generator function ** expects the lhs in the primary, remove the push of the primary @@ -2630,6 +2576,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ */ flags |= (CF_CHAR | CF_FORCECHAR); if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } @@ -2644,10 +2591,15 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_FORCECHAR; } if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } } else { unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + if (CmpSigned) { + ltype &= ~CF_UNSIGNED; + rtype &= ~CF_UNSIGNED; + } flags |= g_typeadjust (ltype, rtype); } @@ -2655,7 +2607,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ ** constant, we may be able to change the compares to something more ** effective. */ - if ((!LeftSigned || !RightSigned) && rconst) { + if (!CmpSigned && rconst) { switch (Tok) { @@ -4121,248 +4073,6 @@ static void hieQuest (ExprDesc* Expr) -static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) -/* Process "op=" operators. */ -{ - unsigned flags; - CodeMark Mark; - int MustScale; - - /* op= can only be used with lvalues */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Skip the operator token */ - NextToken (); - - /* Determine the type of the lhs */ - flags = TypeOf (Expr->Type); - MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && IsTypePtr (Expr->Type); - - /* Get the lhs address on stack (if needed) */ - PushAddr (Expr); - - /* Fetch the lhs into the primary register if needed */ - LoadExpr (CF_NONE, Expr); - - /* Bring the lhs on stack */ - GetCodePos (&Mark); - g_push (flags, 0); - - ExprDesc Expr2; - ED_Init (&Expr2); - Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - - /* Evaluate the rhs */ - MarkedExprWithCheck (hie1, &Expr2); - - /* The rhs must be an integer (or a float, but we don't support that yet */ - if (!IsClassInt (Expr2.Type)) { - Error ("Invalid right operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Check for a constant expression */ - if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - /* The resulting value is a constant. If the generator has the NOPUSH - ** flag set, don't push the lhs. - */ - if (Gen->Flags & GEN_NOPUSH) { - RemoveCode (&Mark); - } - if (MustScale) { - /* lhs is a pointer, scale rhs */ - Expr2.IVal *= CheckedSizeOf (Expr->Type+1); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Special handling for add and sub - some sort of a hack, but short code */ - if (Gen->Func == g_add) { - g_inc (flags | CF_CONST, Expr2.IVal); - } else if (Gen->Func == g_sub) { - g_dec (flags | CF_CONST, Expr2.IVal); - } else { - if (Expr2.IVal == 0) { - /* Check for div by zero/mod by zero */ - if (Gen->Func == g_div) { - Error ("Division by zero"); - } else if (Gen->Func == g_mod) { - Error ("Modulo operation with zero"); - } - } - Gen->Func (flags | CF_CONST, Expr2.IVal); - } - } else { - - /* rhs is not constant. Load into the primary */ - LoadExpr (CF_NONE, &Expr2); - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Adjust the types of the operands if needed */ - Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0); - } - Store (Expr, 0); - ED_FinalizeRValLoad (Expr); -} - - - -static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) -/* Process the += and -= operators */ -{ - ExprDesc Expr2; - unsigned lflags; - unsigned rflags; - int MustScale; - - ED_Init (&Expr2); - Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - - /* We're currently only able to handle some addressing modes */ - if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) { - /* Use generic routine */ - opeq (Gen, Expr, Op); - return; - } - - /* We must have an lvalue */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Skip the operator */ - NextToken (); - - /* Check if we have a pointer expression and must scale rhs */ - MustScale = IsTypePtr (Expr->Type); - - /* Initialize the code generator flags */ - lflags = 0; - rflags = 0; - - /* Evaluate the rhs. We expect an integer here, since float is not - ** supported - */ - hie1 (&Expr2); - if (!IsClassInt (Expr2.Type)) { - Error ("Invalid right operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Setup the code generator flags */ - lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; - rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; - - if (ED_IsConstAbs (&Expr2)) { - /* The resulting value is a constant */ - rflags |= CF_CONST; - lflags |= CF_CONST; - - /* Scale it */ - if (MustScale) { - Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); - } - } else { - /* Not constant, load into the primary */ - LoadExpr (CF_NONE, &Expr2); - - /* Convert the type of the rhs to that of the lhs */ - g_typecast (lflags, rflags & ~CF_FORCECHAR); - - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); - } - } - - /* Output apropriate code depending on the location */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Absolute numeric addressed variable, global variable, local - ** static variable, register variable, pooled literal or code - ** label location. - */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_STACK: - /* Value on the stack */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); - } else { - g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); - } - break; - - default: - Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); - } - - /* Expression is an rvalue in the primary now */ - ED_FinalizeRValLoad (Expr); -} - - - void hie1 (ExprDesc* Expr) /* Parse first level of expression hierarchy. */ { @@ -4370,47 +4080,47 @@ void hie1 (ExprDesc* Expr) switch (CurTok.Tok) { case TOK_ASSIGN: - Assignment (Expr); + OpAssign (0, Expr, "="); break; case TOK_PLUS_ASSIGN: - addsubeq (&GenPASGN, Expr, "+="); + OpAddSubAssign (&GenPASGN, Expr, "+="); break; case TOK_MINUS_ASSIGN: - addsubeq (&GenSASGN, Expr, "-="); + OpAddSubAssign (&GenSASGN, Expr, "-="); break; case TOK_MUL_ASSIGN: - opeq (&GenMASGN, Expr, "*="); + OpAssign (&GenMASGN, Expr, "*="); break; case TOK_DIV_ASSIGN: - opeq (&GenDASGN, Expr, "/="); + OpAssign (&GenDASGN, Expr, "/="); break; case TOK_MOD_ASSIGN: - opeq (&GenMOASGN, Expr, "%="); + OpAssign (&GenMOASGN, Expr, "%="); break; case TOK_SHL_ASSIGN: - opeq (&GenSLASGN, Expr, "<<="); + OpAssign (&GenSLASGN, Expr, "<<="); break; case TOK_SHR_ASSIGN: - opeq (&GenSRASGN, Expr, ">>="); + OpAssign (&GenSRASGN, Expr, ">>="); break; case TOK_AND_ASSIGN: - opeq (&GenAASGN, Expr, "&="); + OpAssign (&GenAASGN, Expr, "&="); break; case TOK_XOR_ASSIGN: - opeq (&GenXOASGN, Expr, "^="); + OpAssign (&GenXOASGN, Expr, "^="); break; case TOK_OR_ASSIGN: - opeq (&GenOASGN, Expr, "|="); + OpAssign (&GenOASGN, Expr, "|="); break; default: diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 4909815ee..841edcb62 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -28,6 +28,11 @@ #define SQP_KEEP_EAX 0x02U #define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ +/* Generator attributes */ +#define GEN_NOPUSH 0x01 /* Don't push lhs */ +#define GEN_COMM 0x02 /* Operator is commutative */ +#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ + /*****************************************************************************/ @@ -36,6 +41,9 @@ +unsigned GlobalModeFlags (const ExprDesc* Expr); +/* Return the addressing mode flags for the given expression */ + void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 1d4fd6872..3d7b7c384 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -56,30 +56,17 @@ ExprDesc* ED_Init (ExprDesc* Expr) /* Initialize an ExprDesc */ { - Expr->Sym = 0; Expr->Type = 0; Expr->Flags = E_NEED_EAX; Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = 0; - Expr->FVal = FP_D_Make (0.0); - Expr->LVal = 0; - Expr->BitOffs = 0; - Expr->BitWidth = 0; + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth) -/* Make this expression a bit field expression */ -{ - Expr->Flags |= E_BITFIELD; - Expr->BitOffs = BitOffs; - Expr->BitWidth = BitWidth; -} - - - #if !defined(HAVE_INLINE) int ED_IsLocQuasiConst (const ExprDesc* Expr) /* Return true if the expression is a constant location of some sort or on the @@ -231,12 +218,12 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs) ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) /* Replace Expr with an absolute const with the given value and type */ { - Expr->Sym = 0; Expr->Type = Type; Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -245,12 +232,12 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) /* Replace Expr with a constant integer expression with the given value */ { - Expr->Sym = 0; Expr->Type = type_int; Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -264,7 +251,7 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -273,13 +260,13 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) /* Finalize the result of LoadExpr to be an rvalue in the primary register */ { - Expr->Sym = 0; - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_ADDRESS_OF); + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF); Expr->Flags &= ~E_CC_SET; Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); + Expr->Sym = 0; Expr->Name = 0; Expr->IVal = 0; /* No offset */ - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -464,8 +451,8 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr) int ED_IsNullPtr (const ExprDesc* Expr) /* Return true if the given expression is a NULL pointer constant */ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == - (E_LOC_NONE|E_RTYPE_RVAL) && + return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == + (E_LOC_NONE|E_RTYPE_RVAL) && Expr->IVal == 0 && IsClassInt (Expr->Type); } @@ -503,7 +490,7 @@ void PrintExprDesc (FILE* F, ExprDesc* E) "Raw type: (unknown)\n"); } fprintf (F, "IVal: 0x%08lX\n", E->IVal); - fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->FVal)); + fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->V.FVal)); Flags = E->Flags; Sep = '('; @@ -558,11 +545,6 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_LOC_CODE; Sep = ','; } - if (Flags & E_BITFIELD) { - fprintf (F, "%cE_BITFIELD", Sep); - Flags &= ~E_BITFIELD; - Sep = ','; - } if (Flags & E_NEED_TEST) { fprintf (F, "%cE_NEED_TEST", Sep); Flags &= ~E_NEED_TEST; diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index a46685b59..a1487a0bd 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -114,7 +114,6 @@ enum { E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK, /* Expression type modifiers */ - E_BITFIELD = 0x0200, /* Expression is a bit-field */ E_ADDRESS_OF = 0x0400, /* Expression is the address of the lvalue */ /* lvalue/rvalue in C language's sense */ @@ -198,17 +197,15 @@ struct Literal; /* Describe the result of an expression */ typedef struct ExprDesc ExprDesc; struct ExprDesc { - struct SymEntry* Sym; /* Symbol table entry if known */ - const Type* Type; /* Type array of expression */ - unsigned Flags; + const Type* Type; /* C type of the expression */ + unsigned Flags; /* Properties of the expression */ uintptr_t Name; /* Name pointer or label number */ + struct SymEntry* Sym; /* Symbol table entry if any */ long IVal; /* Integer value if expression constant */ - Double FVal; /* Floating point value */ - struct Literal* LVal; /* Literal value */ - - /* Bit field stuff */ - unsigned BitOffs; /* Bit offset for bit fields */ - unsigned BitWidth; /* Bit width for bit fields */ + union { + Double FVal; /* Floating point value */ + struct Literal* LVal; /* Literal value */ + } V; /* Start and end of generated code */ CodeMark Start; @@ -331,29 +328,6 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr); */ #endif -#if defined(HAVE_INLINE) -INLINE int ED_IsBitField (const ExprDesc* Expr) -/* Return true if the expression is a bit field */ -{ - return (Expr->Flags & E_BITFIELD) != 0; -} -#else -# define ED_IsBitField(Expr) (((Expr)->Flags & E_BITFIELD) != 0) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_DisBitField (ExprDesc* Expr) -/* Make the expression no longer a bit field */ -{ - Expr->Flags &= ~E_BITFIELD; -} -#else -# define ED_DisBitField(Expr) ((Expr)->Flags &= ~E_BITFIELD) -#endif - -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth); -/* Make this expression a bit field expression */ - #if defined(HAVE_INLINE) INLINE void ED_RequireTest (ExprDesc* Expr) /* Mark the expression for a test. */ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 95617f596..a742087b7 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -124,38 +124,40 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) */ int AdjustBitField = 0; unsigned BitFieldFullWidthFlags = 0; - if (ED_IsBitField (Expr)) { - unsigned EndBit = Expr->BitOffs + Expr->BitWidth; - AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); + if ((Flags & CF_TYPEMASK) == 0) { + if (IsTypeBitField (Expr->Type)) { + unsigned EndBit = Expr->Type->A.B.Offs + Expr->Type->A.B.Width; + AdjustBitField = Expr->Type->A.B.Offs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); - /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are - ** supported. - */ - Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; - if (IsSignUnsigned (Expr->Type)) { - Flags |= CF_UNSIGNED; - } - - /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ - BitFieldFullWidthFlags = Flags; - - /* If we're adjusting, then only load a char (not an int) and do only char ops; - ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the - ** type is not CF_CHAR. - */ - if (AdjustBitField) { - /* If adjusting, then we're sign extending manually, so do everything unsigned - ** to make shifts faster. + /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are + ** supported. */ - Flags |= CF_UNSIGNED | CF_FORCECHAR; - BitFieldFullWidthFlags |= CF_UNSIGNED; + Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; + if (IsSignUnsigned (Expr->Type)) { + Flags |= CF_UNSIGNED; + } + + /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ + BitFieldFullWidthFlags = Flags; + + /* If we're adjusting, then only load a char (not an int) and do only char ops; + ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the + ** type is not CF_CHAR. + */ + if (AdjustBitField) { + /* If adjusting, then we're sign extending manually, so do everything unsigned + ** to make shifts faster. + */ + Flags |= CF_UNSIGNED | CF_FORCECHAR; + BitFieldFullWidthFlags |= CF_UNSIGNED; + } + } else { + /* If Expr is an incomplete ESY type, bail out */ + if (IsIncompleteESUType (Expr->Type)) { + return; + } + Flags |= TypeOf (Expr->Type); } - } else if ((Flags & CF_TYPEMASK) == 0) { - /* If Expr is an incomplete ESY type, bail out */ - if (IsIncompleteESUType (Expr->Type)) { - return; - } - Flags |= TypeOf (Expr->Type); } if (ED_YetToTest (Expr)) { @@ -254,13 +256,13 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) /* We always need to do something with the low byte, so there is no opportunity ** for optimization by skipping it. */ - CHECK (Expr->BitOffs < CHAR_BITS); + CHECK (Expr->Type->A.B.Offs < CHAR_BITS); if (ED_YetToTest (Expr)) { - g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); + g_testbitfield (Flags, Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } else { g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), - Expr->BitOffs, Expr->BitWidth); + Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } } diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index bdc7be006..37566a455 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -832,8 +832,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsLocLiteral (&Arg2.Expr) && IS_Get (&WritableStrings) == 0 && - GetLiteralSize (Arg2.Expr.LVal) == 1 && - GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') { + GetLiteralSize (Arg2.Expr.V.LVal) == 1 && + GetLiteralStr (Arg2.Expr.V.LVal)[0] == '\0') { /* Drop the generated code so we have the first argument in the ** primary @@ -841,7 +841,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) RemoveCode (&Arg1.Push); /* We don't need the literal any longer */ - ReleaseLiteral (Arg2.Expr.LVal); + ReleaseLiteral (Arg2.Expr.V.LVal); /* We do now have Arg1 in the primary. Load the first character from ** this string and cast to int. This is the function result. @@ -1232,10 +1232,10 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { /* Constant string literal */ - ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t); + ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t); /* We don't need the literal any longer */ - ReleaseLiteral (Arg.LVal); + ReleaseLiteral (Arg.V.LVal); /* Bail out, no need for further improvements */ goto ExitPoint; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 56d884bb6..bb87c7472 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -183,13 +183,6 @@ struct SymEntry { const Type* Type; /* Underlying type */ } E; - /* Data for bit fields */ - struct { - unsigned Offs; /* Byte offset into struct */ - unsigned BitOffs; /* Bit offset into storage unit */ - unsigned BitWidth; /* Width in bits */ - } B; - /* Data for functions */ struct { struct Segments* Seg; /* Segments for this function */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 4073a38bc..5d7bd1436 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -881,10 +881,8 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, Entry = NewSymEntry (Name, SC_BITFIELD); /* Set the symbol attributes. Bit-fields are always integral types. */ - Entry->Type = TypeDup (T); - Entry->V.B.Offs = Offs; - Entry->V.B.BitOffs = BitOffs; - Entry->V.B.BitWidth = BitWidth; + Entry->Type = NewBitFieldType (T, BitOffs, BitWidth); + Entry->V.Offs = Offs; if (!SignednessSpecified) { /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, @@ -896,8 +894,10 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, */ CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || IsTypeChar (Entry->Type)); - Entry->Type->C &= ~T_MASK_SIGN; - Entry->Type->C |= T_SIGN_UNSIGNED; + Entry->Type[0].C &= ~T_MASK_SIGN; + Entry->Type[0].C |= T_SIGN_UNSIGNED; + Entry->Type[1].C &= ~T_MASK_SIGN; + Entry->Type[1].C |= T_SIGN_UNSIGNED; } /* Add the entry to the symbol table */ diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 8c9da3445..6052f4a84 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -278,6 +278,21 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SetResult (Result, TC_STRICT_COMPATIBLE); } + /* Bit-fields are considered compatible if they have the same + ** signedness, bit-offset and bit-width. + */ + if (IsTypeBitField (lhs) || IsTypeBitField (rhs)) { + if (!IsTypeBitField (lhs) || + !IsTypeBitField (rhs) || + lhs->A.B.Offs != rhs->A.B.Offs || + lhs->A.B.Width != rhs->A.B.Width) { + SetResult (Result, TC_INCOMPATIBLE); + } + if (LeftType != RightType) { + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } + /* If the underlying types are not identical, the types are incompatible */ if (LeftType != RightType) { SetResult (Result, TC_INCOMPATIBLE); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 16f173cc4..a7528a2f8 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -83,11 +83,16 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Get the sizes of the types. Since we've excluded void types, checking ** for known sizes makes sense here. */ - if (ED_IsBitField (Expr)) { - OldBits = Expr->BitWidth; + if (IsTypeBitField (OldType)) { + OldBits = OldType->A.B.Width; } else { OldBits = CheckedSizeOf (OldType) * CHAR_BITS; } + + /* If the new type is a bit-field, we use its underlying type instead */ + if (IsTypeBitField (NewType)) { + NewType = GetUnderlyingType (NewType); + } NewBits = CheckedSizeOf (NewType) * CHAR_BITS; /* lvalue? */ @@ -167,9 +172,6 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ExitPoint: /* The expression has always the new type */ ReplaceType (Expr, NewType); - - /* Bit-fields are converted to integers */ - ED_DisBitField (Expr); } From d69e81cd66e7bb8ba307ea654a7c643fc8d3d4a6 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 27 May 2021 15:44:52 +0800 Subject: [PATCH 45/74] Moved and improved test cases for Issue #1462. Fixed an old test case for unsigned enum bit-fields that are supposed to be int-promoted. --- test/{todo => val}/bug1462-2.c | 0 test/{todo => val}/bug1462-3.c | 18 +++---- test/val/bug1462-4.c | 85 ++++++++++++++++++++++++++++++++++ test/{todo => val}/bug1462.c | 0 test/val/enum-bitfield.c | 37 +++++++++++---- 5 files changed, 122 insertions(+), 18 deletions(-) rename test/{todo => val}/bug1462-2.c (100%) rename test/{todo => val}/bug1462-3.c (82%) create mode 100644 test/val/bug1462-4.c rename test/{todo => val}/bug1462.c (100%) diff --git a/test/todo/bug1462-2.c b/test/val/bug1462-2.c similarity index 100% rename from test/todo/bug1462-2.c rename to test/val/bug1462-2.c diff --git a/test/todo/bug1462-3.c b/test/val/bug1462-3.c similarity index 82% rename from test/todo/bug1462-3.c rename to test/val/bug1462-3.c index b75d568b9..12559b94f 100644 --- a/test/todo/bug1462-3.c +++ b/test/val/bug1462-3.c @@ -1,6 +1,6 @@ /* issue #1462 - Bit-fields are still broken */ -/* More testson "op= expression result value" that a naive fix might fail with */ +/* More tests on "op= expression result value" that a naive fix might fail with */ #include @@ -27,26 +27,26 @@ void test1(void) T1 a = { 3, 3, 3, 3 }; int i; - i = a.a += a.b + a.c; - if (i != 1) { + i = a.a -= a.b + a.c; + if (i != -3 || a.a != -3) { ++failures1; } printf("i = %d, a.a = %d\n", i, a.a); - i = a.b *= -1; - if (i != 5 || a.b != 5) { + a.b = i = a.b / -1; + if (i != -3 || a.b != 5) { ++failures1; } printf("i = %d, a.b = %d\n", i, a.b); - i = a.c * -1; - if (i != -3) { + i = a.c = 0; + if (i != 0 || a.c != 0) { ++failures1; } printf("i = %d, a.c = %d\n", i, a.c); - i = a.d ^= -1; - if (i != 4 || a.d != 4) { + i = a.d /= -1; + if (i != 5 || a.d != 5) { ++failures1; } printf("i = %d, a.d = %d\n", i, a.d); diff --git a/test/val/bug1462-4.c b/test/val/bug1462-4.c new file mode 100644 index 000000000..f811ddbd6 --- /dev/null +++ b/test/val/bug1462-4.c @@ -0,0 +1,85 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* More tests on "op= expression result value" that a naive fix might fail with */ + +#include +#include + +#define SMALL_WIDTH 4 +#define LARGE_WIDTH (CHAR_BIT * sizeof (unsigned int)) + +typedef struct { + unsigned int a : SMALL_WIDTH; + unsigned int b : SMALL_WIDTH; + unsigned int c : SMALL_WIDTH; + unsigned int d : SMALL_WIDTH; +} T1; + +typedef struct { + unsigned int a : LARGE_WIDTH; + unsigned int b : LARGE_WIDTH; + unsigned int c : LARGE_WIDTH; + unsigned int d : LARGE_WIDTH; +} T2; + + +int failures1 = 0; +int failures2 = 0; + +void test1(void) +{ + T1 a = { 0, 0, 0, 0 }; + + printf("\nunsigned int : %d\n", SMALL_WIDTH); + if (!(~a.a < 0)) { + ++failures1; + } + printf("~a.a < 0 : %d\n", ~a.a < 0); + if (!(0 > ~a.b)) { + ++failures1; + } + printf("0 > ~a.b : %d\n",0 > ~a.b); + if (!(a.c > -1)) { + ++failures1; + } + printf("a.c > -1 : %d\n", a.c > -1); + if (!(-1 < a.d)) { + ++failures1; + } + printf("-1 < a.d : %d\n", -1 < a.d); + + printf("Failures: %d\n", failures1); +} + +void test2(void) +{ + T1 b = { 0, 0, 0, 0 }; + + printf("\nunsigned int : %d\n", LARGE_WIDTH); + if (!(~b.a < 0)) { + ++failures2; + } + printf("~b.a < 0 : %d\n", ~b.a < 0); + if (!(0 > ~b.b)) { + ++failures2; + } + printf("0 > ~b.b : %d\n", 0 > ~b.b); + if (!(b.c > -1)) { + ++failures2; + } + printf("b.c > -1 : %d\n", b.c > -1); + if (!(-1 < b.d)) { + ++failures2; + } + printf("-1 < b.d : %d\n", -1 < b.d); + + printf("Failures: %d\n", failures2); +} + +int main(void) +{ + test1(); + test2(); + return failures1 + failures2; +} + diff --git a/test/todo/bug1462.c b/test/val/bug1462.c similarity index 100% rename from test/todo/bug1462.c rename to test/val/bug1462.c diff --git a/test/val/enum-bitfield.c b/test/val/enum-bitfield.c index a942091c2..5669978c9 100644 --- a/test/val/enum-bitfield.c +++ b/test/val/enum-bitfield.c @@ -23,6 +23,7 @@ */ #include +#include static unsigned char failures = 0; @@ -35,7 +36,7 @@ enum e10u { static struct enum_bitfield_uint { enum e10u x : 1; enum e10u y : 8; - enum e10u z : 16; + enum e10u z : CHAR_BIT * sizeof (enum e10u); } e10ubf = {0, E10U_200, E10U_1000}; static void test_enum_bitfield_uint(void) @@ -68,11 +69,11 @@ static void test_enum_bitfield_uint(void) failures++; } - /* Check signedness, should be unsigned. */ + /* Check signedness, should be signed. */ { long v = e10ubf.x - 2; - if (v < 0) { - printf ("Got negative v = %ld, expected large positive.\n", v); + if (v >= 0) { + printf ("Got non-negative v (= e10ubf.x - 2) = %ld, expected negative.\n", v); failures++; } } @@ -85,6 +86,15 @@ static void test_enum_bitfield_uint(void) printf ("Got e10ubf.z = %u, expected 1023.\n", e10ubf.z); failures++; } + + /* Check signedness, should be unsigned. */ + { + long v = e10ubf.z - 1024; + if (v < 0) { + printf ("Got negative v (= e10ubf.z - 1024) = %ld, expected positive.\n", v); + failures++; + } + } } /* Enum with underlying type signed int. */ @@ -97,7 +107,7 @@ enum e11i { static struct enum_bitfield_int { enum e11i x : 2; enum e11i y : 8; - enum e11i z : 16; + enum e11i z : CHAR_BIT * sizeof (enum e11i); } e11ibf = {E11I_M1, E11I_100, E11I_1000}; static void test_enum_bitfield_int(void) @@ -133,8 +143,8 @@ static void test_enum_bitfield_int(void) /* Check signedness, should be signed. */ { long v = e11ibf.x - 2; - if (v > 0) { - printf ("Got positive v = %ld, expected negative.\n", v); + if (v >= 0) { + printf ("Got non-negative v (= e11ibf.x - 2) = %ld, expected negative.\n", v); failures++; } } @@ -147,6 +157,15 @@ static void test_enum_bitfield_int(void) printf ("Got e11ibf.z = %d, expected 1023.\n", e11ibf.z); failures++; } + + /* Check signedness, should be signed. */ + { + long v = e11ibf.z - 1024; + if (v >= 0) { + printf ("Got non-negative v (= e11ibf.z - 1024) = %ld, expected negative.\n", v); + failures++; + } + } } /* Enum with underlying type unsigned char. */ @@ -157,7 +176,7 @@ enum e7uc { static struct enum_bitfield_uchar { enum e7uc x : 1; enum e7uc y : 4; - enum e7uc z : 8; + enum e7uc z : CHAR_BIT; } e7ucbf = {0, 10, E7UC_100}; static void test_enum_bitfield_uchar(void) @@ -212,7 +231,7 @@ enum e8sc { static struct enum_bitfield_char { enum e8sc x : 1; enum e8sc y : 4; - enum e8sc z : 8; + enum e8sc z : CHAR_BIT; } e8scbf = {0, 5, E8SC_100}; static void test_enum_bitfield_char(void) From 9a523abbfbb2c0939807e1e85fd1d1ef555b044d Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Thu, 10 Jun 2021 15:48:21 +0200 Subject: [PATCH 46/74] limits.h: provide PATH_MAX - stdio.h: define FILENAME_MAX to PATH_MAX - stdio.h, stdio.inc: increase FILENAME_MAX/PATH_MAX for Atari (For DOSes with subdirectory support.) --- asminc/stdio.inc | 2 +- include/limits.h | 12 ++++++++++++ include/stdio.h | 14 ++------------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/asminc/stdio.inc b/asminc/stdio.inc index c727e8d0b..feb061d70 100644 --- a/asminc/stdio.inc +++ b/asminc/stdio.inc @@ -44,7 +44,7 @@ EOF = -1 .if .defined(__APPLE2__) FILENAME_MAX = 64+1 .elseif .defined(__ATARI__) -FILENAME_MAX = 12+1 +FILENAME_MAX = 63+1 .elseif .defined(__LUNIX__) FILENAME_MAX = 80+1 .elseif .defined(__TELESTRAT__) diff --git a/include/limits.h b/include/limits.h index 23474c78c..8dbc83ff2 100644 --- a/include/limits.h +++ b/include/limits.h @@ -63,6 +63,18 @@ #define ULONG_MAX 4294967295UL +/* These defines that are platform dependent */ +#if defined(__APPLE2__) +# define PATH_MAX (64+1) +#elif defined(__ATARI__) +# define PATH_MAX (63+1) +#elif defined(__LUNIX__) +# define PATH_MAX (80+1) +#elif defined(__TELESTRAT__) +# define PATH_MAX (50+1) +#else +# define PATH_MAX (16+1) +#endif /* End of limits.h */ diff --git a/include/stdio.h b/include/stdio.h index 73dc05bdb..916affe71 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -40,6 +40,7 @@ #include #include +#include @@ -64,18 +65,7 @@ extern FILE* stderr; #define SEEK_SET 2 #define TMP_MAX 256 -/* Standard defines that are platform dependent */ -#if defined(__APPLE2__) -# define FILENAME_MAX (64+1) -#elif defined(__ATARI__) -# define FILENAME_MAX (12+1) -#elif defined(__LUNIX__) -# define FILENAME_MAX (80+1) -#elif defined(__TELESTRAT__) -# define FILENAME_MAX (50+1) -#else -# define FILENAME_MAX (16+1) -#endif +#define FILENAME_MAX PATH_MAX #define L_tmpnam FILENAME_MAX From ae9101961e149d75d46440390a772b1de0ba0311 Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Thu, 10 Jun 2021 16:06:37 +0200 Subject: [PATCH 47/74] stdio.inc,stdio.h: increase CBM PATH_MAX/FILENAME_MAX value to 256+1 --- asminc/stdio.inc | 2 ++ include/limits.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/asminc/stdio.inc b/asminc/stdio.inc index feb061d70..6eb458b62 100644 --- a/asminc/stdio.inc +++ b/asminc/stdio.inc @@ -45,6 +45,8 @@ EOF = -1 FILENAME_MAX = 64+1 .elseif .defined(__ATARI__) FILENAME_MAX = 63+1 +.elseif .defined(__CBM__) +FILENAME_MAX = 256+1 .elseif .defined(__LUNIX__) FILENAME_MAX = 80+1 .elseif .defined(__TELESTRAT__) diff --git a/include/limits.h b/include/limits.h index 8dbc83ff2..532f5104a 100644 --- a/include/limits.h +++ b/include/limits.h @@ -68,6 +68,8 @@ # define PATH_MAX (64+1) #elif defined(__ATARI__) # define PATH_MAX (63+1) +#elif defined(__CBM__) +# define PATH_MAX (256+1) #elif defined(__LUNIX__) # define PATH_MAX (80+1) #elif defined(__TELESTRAT__) From c90c3c9133cc561c015562390c869a982c603cad Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Thu, 10 Jun 2021 16:17:43 +0200 Subject: [PATCH 48/74] stdio.inc,stdio.h: set CBM PATH_MAX/FILENAME_MAX value to 255 Some parts of the runtime library cannot handle larger paths. --- asminc/stdio.inc | 2 +- include/limits.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/asminc/stdio.inc b/asminc/stdio.inc index 6eb458b62..426389de3 100644 --- a/asminc/stdio.inc +++ b/asminc/stdio.inc @@ -46,7 +46,7 @@ FILENAME_MAX = 64+1 .elseif .defined(__ATARI__) FILENAME_MAX = 63+1 .elseif .defined(__CBM__) -FILENAME_MAX = 256+1 +FILENAME_MAX = 255 .elseif .defined(__LUNIX__) FILENAME_MAX = 80+1 .elseif .defined(__TELESTRAT__) diff --git a/include/limits.h b/include/limits.h index 532f5104a..531c6bef2 100644 --- a/include/limits.h +++ b/include/limits.h @@ -69,7 +69,7 @@ #elif defined(__ATARI__) # define PATH_MAX (63+1) #elif defined(__CBM__) -# define PATH_MAX (256+1) +# define PATH_MAX (255) /* should be 256+1, see libsrc/common/_cmd.s why it's not */ #elif defined(__LUNIX__) # define PATH_MAX (80+1) #elif defined(__TELESTRAT__) From 7f1f0249f3d79a091f16b8b8462a25ae5c7f701c Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Thu, 10 Jun 2021 17:15:29 +0200 Subject: [PATCH 49/74] enumdevdir.c: allocate path name buffers from the heap. --- samples/enumdevdir.c | 46 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/samples/enumdevdir.c b/samples/enumdevdir.c index ce2dc99ec..97d69d8a6 100644 --- a/samples/enumdevdir.c +++ b/samples/enumdevdir.c @@ -16,31 +16,47 @@ #include -void printdir (char *newdir) +int printdir (char *newdir) { - char olddir[FILENAME_MAX]; - char curdir[FILENAME_MAX]; + char *olddir; + char *curdir; DIR *dir; struct dirent *ent; char *subdirs = NULL; unsigned dirnum = 0; unsigned num; - getcwd (olddir, sizeof (olddir)); + olddir = malloc (FILENAME_MAX); + if (olddir != NULL) { + + perror ("cannot allocate memory"); + return 1; + } + + getcwd (olddir, FILENAME_MAX); if (chdir (newdir)) { /* If chdir() fails we just print the ** directory name - as done for files. */ printf (" Dir %s\n", newdir); - return; + free (olddir); + return 0; + } + + curdir = malloc (FILENAME_MAX); + if (curdir != NULL) { + + perror ("cannot allocate memory"); + return 1; } /* We call getcwd() in order to print the ** absolute pathname for a subdirectory. */ - getcwd (curdir, sizeof (curdir)); + getcwd (curdir, FILENAME_MAX); printf (" Dir %s:\n", curdir); + free (curdir); /* Calling opendir() always with "." avoids ** fiddling around with pathname separators. @@ -65,18 +81,28 @@ void printdir (char *newdir) closedir (dir); for (num = 0; num < dirnum; ++num) { - printdir (subdirs + FILENAME_MAX * num); + if (printdir (subdirs + FILENAME_MAX * num)) + break; } free (subdirs); chdir (olddir); + free (olddir); + return 0; } void main (void) { unsigned char device; - char devicedir[FILENAME_MAX]; + char *devicedir; + + devicedir = malloc (FILENAME_MAX); + if (devicedir != NULL) { + + perror ("cannot allocate memory"); + return; + } /* Calling getfirstdevice()/getnextdevice() does _not_ turn on the motor ** of a drive-type device and does _not_ check for a disk in the drive. @@ -88,7 +114,7 @@ void main (void) /* Calling getdevicedir() _does_ check for a (formatted) disk in a ** floppy-disk-type device and returns NULL if that check fails. */ - if (getdevicedir (device, devicedir, sizeof (devicedir))) { + if (getdevicedir (device, devicedir, FILENAME_MAX)) { printdir (devicedir); } else { printf (" N/A\n"); @@ -100,4 +126,6 @@ void main (void) if (doesclrscrafterexit ()) { getchar (); } + + free (devicedir); } From f3db74395d830d4f5b02573e37ee58ffea1f0c87 Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Thu, 10 Jun 2021 18:11:45 +0200 Subject: [PATCH 50/74] fix last change and use stdbool.h --- samples/enumdevdir.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/samples/enumdevdir.c b/samples/enumdevdir.c index 97d69d8a6..bf1ff3bbb 100644 --- a/samples/enumdevdir.c +++ b/samples/enumdevdir.c @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include -int printdir (char *newdir) +/* returns true for error, false for OK */ +bool printdir (char *newdir) { char *olddir; char *curdir; @@ -27,10 +29,9 @@ int printdir (char *newdir) unsigned num; olddir = malloc (FILENAME_MAX); - if (olddir != NULL) { - + if (olddir == NULL) { perror ("cannot allocate memory"); - return 1; + return true; } getcwd (olddir, FILENAME_MAX); @@ -41,14 +42,13 @@ int printdir (char *newdir) */ printf (" Dir %s\n", newdir); free (olddir); - return 0; + return false; } curdir = malloc (FILENAME_MAX); - if (curdir != NULL) { - + if (curdir == NULL) { perror ("cannot allocate memory"); - return 1; + return true; } /* We call getcwd() in order to print the @@ -88,7 +88,7 @@ int printdir (char *newdir) chdir (olddir); free (olddir); - return 0; + return false; } @@ -98,8 +98,7 @@ void main (void) char *devicedir; devicedir = malloc (FILENAME_MAX); - if (devicedir != NULL) { - + if (devicedir == NULL) { perror ("cannot allocate memory"); return; } From 25a35d6b59b718803d99bb058e6c5ee719e65573 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 9 Jun 2021 17:48:09 +0800 Subject: [PATCH 51/74] Fixed result type of certain operations, which was broken with the bit-field fix. --- src/cc65/assignment.c | 100 ++++-------------------------------------- src/cc65/expr.c | 22 +++------- 2 files changed, 14 insertions(+), 108 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index be6a8116f..05a6d9a96 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -155,7 +155,6 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) unsigned Mask; unsigned ChunkFlags; const Type* ChunkType; - const Type* ResType; /* If the bit-field fits within one byte, do the following operations ** with bytes. @@ -190,11 +189,6 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) /* Fetch the lhs into the primary register if needed */ LoadExpr (CF_NONE, Expr); - if (KeepResult == OA_NEED_OLD) { - /* Save the original expression value */ - g_save (Flags | CF_FORCECHAR); - } - /* Handle for add and sub */ if (Val > 0) { g_inc (Flags | CF_CONST, Val); @@ -205,11 +199,6 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) /* Apply the mask */ g_and (Flags | CF_CONST, Mask); - if (KeepResult == OA_NEED_NEW) { - /* Save the result value */ - g_save (Flags | CF_FORCECHAR); - } - /* Do integral promotion without sign-extension if needed */ g_typecast (ChunkFlags | CF_UNSIGNED, Flags); @@ -229,6 +218,11 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) /* Load the whole data chunk containing the bits to be changed */ LoadExpr (ChunkFlags, Expr); + if (KeepResult == OA_NEED_OLD) { + /* Save the original expression value */ + g_save (ChunkFlags | CF_FORCECHAR); + } + /* Get the bits that are not to be affected */ g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); @@ -238,39 +232,10 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) /* Store the whole data chunk containing the changed bits back */ Store (Expr, ChunkType); - /* Cache the expression result type */ - ResType = IntPromotion (Expr->Type); - - if (KeepResult != OA_NEED_NONE) { - /* Restore the expression result value */ - g_restore (Flags | CF_FORCECHAR); - - /* Promote if needed */ - if (KeepResult != OA_NEED_OLD) { - /* Do unsigned promotion first */ - g_typecast (TypeOf (ResType) | CF_UNSIGNED, Flags); - - /* Then do sign-extension */ - if (IsSignSigned (Expr->Type) && - Expr->Type->A.B.Width < CHAR_BITS * SizeOf (ResType)) { - /* The way is: - ** x = bits & bit_mask - ** m = 1 << (bit_width - 1) - ** r = (x ^ m) - m - ** Since we have already masked bits with bit_mask, we may skip the - ** first step. - */ - g_xor (Flags | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); - g_dec ((Flags & ~CF_FORCECHAR) | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); - } - } else { - /* Do promotion with sign-extension */ - g_typecast (TypeOf (ResType), Flags); - } + if (KeepResult == OA_NEED_OLD) { + /* Restore the original expression value */ + g_restore (ChunkFlags | CF_FORCECHAR); } - - /* Get the expression result type */ - Expr->Type = ResType; } @@ -285,10 +250,6 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op unsigned Flags; unsigned ChunkFlags; const Type* ChunkType; - const Type* ResType; - - /* Cache the expression result type */ - ResType = IntPromotion (Expr->Type); ED_Init (&Expr2); Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; @@ -373,15 +334,6 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op /* Store the whole data chunk containing the changed bits back */ Store (Expr, ChunkType); - /* Load the expression result value */ - if (IsSignSigned (Expr->Type)) { - unsigned SignExtensionMask = 1 << (Expr->Type->A.B.Width - 1); - Val = (Val^ SignExtensionMask) - SignExtensionMask; - } - ED_MakeConstAbs (Expr, Val, ResType); - LimitExprValue (Expr); - LoadExpr (CF_NONE, Expr); - /* Done */ goto Done; @@ -459,9 +411,6 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op /* Apply the mask */ g_and (Flags | CF_CONST, Mask); - /* Save the expression result value */ - g_save (Flags); - /* Do integral promotion without sign-extension if needed */ g_typecast (ChunkFlags | CF_UNSIGNED, Flags); @@ -490,31 +439,8 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op /* Store the whole data chunk containing the changed bits back */ Store (Expr, ChunkType); - /* Restore the expression result value */ - g_restore (Flags); - - /* Do unsigned promotion first */ - g_typecast (TypeOf (ResType) | CF_UNSIGNED, Flags); - - /* Then do sign-extension */ - if (IsSignSigned (Expr->Type) && - Expr->Type->A.B.Width < CHAR_BITS * SizeOf (ResType)) { - /* The way is: - ** x = bits & bit_mask - ** m = 1 << (bit_width - 1) - ** r = (x ^ m) - m - ** Since we have already masked bits with bit_mask, we may skip the - ** first step. - */ - g_xor (Flags | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); - g_dec ((Flags & ~CF_FORCECHAR) | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); - } - Done: - /* Get the expression result type */ - Expr->Type = ResType; - /* Value is in primary as an rvalue */ ED_FinalizeRValLoad (Expr); } @@ -643,11 +569,6 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* /* Generate a store instruction */ Store (Expr, 0); - /* Get the expression result type */ - if (IsClassInt (Expr->Type)) { - Expr->Type = IntPromotion (Expr->Type); - } - /* Value is in primary as an rvalue */ ED_FinalizeRValLoad (Expr); } @@ -824,11 +745,6 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); } - /* Get the expression result type */ - if (IsClassInt (Expr->Type)) { - Expr->Type = IntPromotion (Expr->Type); - } - /* Expression is an rvalue in the primary now */ ED_FinalizeRValLoad (Expr); } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index c45005d65..3b9307a37 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1726,17 +1726,12 @@ static void PostInc (ExprDesc* Expr) } else { - /* Fetch the value and use it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - /* Defer the increment until after the value of this expression is used */ DeferInc (Expr); - } - } - /* Adjust the type of the expression */ - if (IsClassInt (Expr->Type)) { - Expr->Type = IntPromotion (Expr->Type); + /* Just return */ + return; + } } /* The result is always an expression, no reference */ @@ -1781,17 +1776,12 @@ static void PostDec (ExprDesc* Expr) } else { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - /* Defer the decrement until after the value of this expression is used */ DeferDec (Expr); - } - } - /* Adjust the type of the expression */ - if (IsClassInt (Expr->Type)) { - Expr->Type = IntPromotion (Expr->Type); + /* Just return */ + return; + } } /* The result is always an expression, no reference */ From 31128d48099f869331b3b7308150b6c599921202 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 9 Jun 2021 18:43:25 +0800 Subject: [PATCH 52/74] Added test cases for result types of certain operations. --- test/val/opsize.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/val/opsize.c diff --git a/test/val/opsize.c b/test/val/opsize.c new file mode 100644 index 000000000..20c7f0511 --- /dev/null +++ b/test/val/opsize.c @@ -0,0 +1,33 @@ + +/* Test for result types of certain unary operations */ + +#include + +signed char x; +struct S { + unsigned char a : 3; + unsigned int b : 3; +} s; + +int main(void) +{ + _Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type"); + _Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type"); + _Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type"); + _Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type"); + _Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type"); + + _Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type"); + _Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type"); + _Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type"); + + _Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type"); + _Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type"); + _Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type"); + + _Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type"); + _Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type"); + _Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type"); + + return 0; +} From f636d4e634fd2553e4b40f0c39b6e845bb49b902 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 14 May 2021 19:12:59 -0400 Subject: [PATCH 53/74] Fixed the Creativision library's bios_playsound(). It was disabling interrupts permanently. --- libsrc/creativision/psg.s | 76 ++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/libsrc/creativision/psg.s b/libsrc/creativision/psg.s index 18f4ffe2e..c84f92f43 100644 --- a/libsrc/creativision/psg.s +++ b/libsrc/creativision/psg.s @@ -1,63 +1,67 @@ ; void __fastcall__ psg_outb (unsigned char b); -; void __fastcall__ psg_delay (unsigned char c); -; void __fastcall__ bios_playsound (const void *b, unsigned char c); +; void __fastcall__ psg_delay (unsigned char b); +; void __fastcall__ bios_playsound (void *a, unsigned char b); ; void psg_silence (void); - .export _psg_outb, _psg_silence, _psg_delay - .export _bios_playsound - .import popa + + .export _psg_outb, _psg_silence, _psg_delay + .export _bios_playsound + + .import popax + .include "creativision.inc" -_psg_outb: - ;* Let BIOS output the value - jmp $FE77 +songptr := $00 ; Points to current tune data +volptr := $04 ; Points to current volume table + +_psg_outb: + ;* Let BIOS output the value. + jmp $FE77 + _psg_silence: - - jmp $FE54 + jmp $FE54 _psg_delay: - tay -l1: lda #200 -l2: sbc #1 - bne l2 +l1: lda #200 +l2: sbc #1 + bne l2 - lda #200 -l3: sbc #1 - bne l3 + lda #200 +l3: sbc #1 + bne l3 dey - bne l1 - + bne l1 rts ;* Creativision Sound Player +;* Based on BIOS song player. ;* -;* Based on BIOS sound player. -;* Pass a pointer to a set of note triples, terminated with a tempo byte -;* and the len (max 255) +;* Pass a pointer to a set of note triples, terminated with a tempo byte; +;* and pass the length of the triples and tempo (max 255). +;* +;* Note: tune data must be stored backwards. _bios_playsound: - - pha ; Save Length Byte + php + pha ; Save tune length sei - lda #$D5 ; BIOS volume table low - sta $4 - lda #$FC ; BIOS volume table high - sta $5 + lda #<$FCD5 ; A BIOS volume table + ldx #>$FCD5 + sta volptr + stx volptr+1 - jsr popa ; Get Sound table pointer low - sta $0 - jsr popa ; Get Sound table pointer high - sta $1 + jsr popax ; Get tune array pointer + sta songptr + stx songptr+1 pla - tay ; Put length in Y - dey - php - jmp $FBED ; Let BIOS do it's thing + tay + dey ; Point to tempo byte + jmp $FBED ; Let BIOS do its thing From af3d4581d306f00c4a7f0aba62f9d79962a3ff79 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 30 May 2021 13:30:08 -0400 Subject: [PATCH 54/74] Moved Creativision's playsound() into a separate file. It won't waste space in a cartridge if it isn't used. --- asminc/creativision.inc | 47 ++++++++++++++++++--------------- libsrc/creativision/playsound.s | 40 ++++++++++++++++++++++++++++ libsrc/creativision/psg.s | 43 +++--------------------------- 3 files changed, 69 insertions(+), 61 deletions(-) create mode 100644 libsrc/creativision/playsound.s diff --git a/asminc/creativision.inc b/asminc/creativision.inc index 49d55a342..a0259ecce 100644 --- a/asminc/creativision.inc +++ b/asminc/creativision.inc @@ -5,21 +5,21 @@ ;** Screen SCREEN_ROWS = 24 SCREEN_COLS = 32 -SCREEN_PTR = $3A -CURSOR_X = $3C -CURSOR_Y = $3D +SCREEN_PTR := $3A +CURSOR_X := $3C +CURSOR_Y := $3D ;** VDP -VDP_DATA_R = $2000 -VDP_STATUS_R = $2001 -VDP_DATA_W = $3000 -VDP_CONTROL_W = $3001 +VDP_DATA_R := $2000 +VDP_STATUS_R := $2001 +VDP_DATA_W := $3000 +VDP_CONTROL_W := $3001 ;** PIA -PIA0_DATA = $1000 -PIA0_STATUS = $1001 -PIA1_DATA = $1002 -PIA1_STATUS = $1003 +PIA0_DATA := $1000 +PIA0_STATUS := $1001 +PIA1_DATA := $1002 +PIA1_STATUS := $1003 ;** General CH_VLINE = 33 @@ -30,11 +30,11 @@ CH_LLCORNER = 37 CH_LRCORNER = 38 ;** I/O (Zero-page variables) -ZP_KEYBOARD = $10 -ZP_JOY0_DIR = $11 -ZP_JOY1_DIR = $13 -ZP_JOY0_BUTTONS = $16 -ZP_JOY1_BUTTONS = $17 +ZP_KEYBOARD := $10 +ZP_JOY0_DIR := $11 +ZP_JOY1_DIR := $13 +ZP_JOY0_BUTTONS := $16 +ZP_JOY1_BUTTONS := $17 ;** Joystick direction values (ZP_JOY0_DIR/ZP_JOY1_DIR) JOY_N = $49 @@ -54,8 +54,13 @@ JOY_WNW = $4C JOY_NW = $4B JOY_NNW = $4A -;** BIOS -BIOS_IRQ1_ADDR = $FF3F -BIOS_IRQ2_ADDR = $FF52 -BIOS_NMI_RESET_ADDR = $F808 -BIOS_WRITE_VDP_REG = $FE1F +;** BIOS routines +BIOS_NMI_RESET_ADDR := $F808 +BIOS_PLAY_TUNE1 := $FBD6 +BIOS_PLAY_SONG := $FBED +BIOS_PLAY_TUNE2 := $FCE6 +BIOS_WRITE_VDP_REG := $FE1F +BIOS_QUIET_PSG := $FE54 +BIOS_POKE_PSG := $FE77 +BIOS_IRQ1_ADDR := $FF3F +BIOS_IRQ2_ADDR := $FF52 diff --git a/libsrc/creativision/playsound.s b/libsrc/creativision/playsound.s new file mode 100644 index 000000000..55c7a3fed --- /dev/null +++ b/libsrc/creativision/playsound.s @@ -0,0 +1,40 @@ +; void __fastcall__ bios_playsound (void *a, unsigned char b); + + + .export _bios_playsound + + .import popax + + .include "creativision.inc" + + +songptr := $00 ; Points to current tune data +volptr := $04 ; Points to current volume table + + +;* Creativision Sound Player +;* Based on BIOS song player. +;* +;* Pass a pointer to a set of note triples, terminated with a tempo byte; +;* and pass the length of the triples and tempo (max 255). +;* +;* Note: tune data must be stored backwards. + +_bios_playsound: + php + pha ; Save tune length + sei + + lda #<$FCD5 ; BIOS decreasing-volume table + ldx #>$FCD5 + sta volptr + stx volptr+1 + + jsr popax ; Get tune array pointer + sta songptr + stx songptr+1 + + pla + tay + dey ; Point to tempo byte + jmp BIOS_PLAY_SONG ; Let BIOS do its thing diff --git a/libsrc/creativision/psg.s b/libsrc/creativision/psg.s index c84f92f43..ec878af31 100644 --- a/libsrc/creativision/psg.s +++ b/libsrc/creativision/psg.s @@ -1,27 +1,18 @@ ; void __fastcall__ psg_outb (unsigned char b); ; void __fastcall__ psg_delay (unsigned char b); -; void __fastcall__ bios_playsound (void *a, unsigned char b); ; void psg_silence (void); .export _psg_outb, _psg_silence, _psg_delay - .export _bios_playsound - - .import popax .include "creativision.inc" -songptr := $00 ; Points to current tune data -volptr := $04 ; Points to current volume table - -_psg_outb: - ;* Let BIOS output the value. - jmp $FE77 +;* Let BIOS output the value. +_psg_outb := BIOS_POKE_PSG -_psg_silence: - jmp $FE54 +_psg_silence := BIOS_QUIET_PSG _psg_delay: @@ -37,31 +28,3 @@ l3: sbc #1 dey bne l1 rts - - -;* Creativision Sound Player -;* Based on BIOS song player. -;* -;* Pass a pointer to a set of note triples, terminated with a tempo byte; -;* and pass the length of the triples and tempo (max 255). -;* -;* Note: tune data must be stored backwards. - -_bios_playsound: - php - pha ; Save tune length - sei - - lda #<$FCD5 ; A BIOS volume table - ldx #>$FCD5 - sta volptr - stx volptr+1 - - jsr popax ; Get tune array pointer - sta songptr - stx songptr+1 - - pla - tay - dey ; Point to tempo byte - jmp $FBED ; Let BIOS do its thing From 14d05c61b67ab1a856f6b1202a2706e08e1eac6e Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 4 Jun 2021 07:30:26 -0400 Subject: [PATCH 55/74] Made Creativision's joystick driver more efficient. --- libsrc/creativision/joy/creativision-stdjoy.s | 100 ++++++++---------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/libsrc/creativision/joy/creativision-stdjoy.s b/libsrc/creativision/joy/creativision-stdjoy.s index 5cf46c39f..73b0c249f 100644 --- a/libsrc/creativision/joy/creativision-stdjoy.s +++ b/libsrc/creativision/joy/creativision-stdjoy.s @@ -1,11 +1,11 @@ ; ; Standard joystick driver for the Creativision. ; -; Christian Groessler, 2017-03-08 +; 2017-03-08, Christian Groessler +; 2021-06-01, Greg King ; .include "zeropage.inc" - .include "joy-kernel.inc" .include "joy-error.inc" .include "creativision.inc" @@ -13,10 +13,12 @@ .macpack module +buttons := tmp2 + ; ------------------------------------------------------------------------ ; Header. Includes jump table - module_header _creativisionstd_joy + module_header _creativisionstd_joy ; Driver signature @@ -39,16 +41,14 @@ JOY_COUNT = 2 ; Number of joysticks we support -; Symbolic names for joystick masks (similar names like the defines in joystick.h, but not related to them) +; Symbolic names for joystick masks (similar names to the macros in joystick.h, +; with the same values as the masks in creativision.h) JOY_UP = $10 JOY_DOWN = $04 JOY_LEFT = $20 JOY_RIGHT = $08 -; ------------------------------------------------------------------------ -; Code - .code ; ------------------------------------------------------------------------ @@ -59,7 +59,7 @@ JOY_RIGHT = $08 ; INSTALL: lda #JOY_ERR_OK - ldx #0 + ldx #>$0000 ; rts ; Fall through ; ------------------------------------------------------------------------ @@ -82,14 +82,14 @@ COUNT: lda # JOY_UP, JOY_RIGHT, JOY_DOWN, JOY_LEFT ; NE, SE, SW, NW -> (JOY_UP | JOY_RIGHT), (JOY_DOWN | JOY_RIGHT), (JOY_DOWN | JOY_LEFT), (JOY_UP | JOY_LEFT) ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW: -; toggle between straight and diagonal direction for every call, e.g. +; toggle between the straight and diagonal directions for each call, e.g., ; NNE: ; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP | JOY_RIGHT -; etc... +; etc. - txa ; move direction status into A - beq done ; center position (no bits are set), nothing to do + txa ; Move direction status into A + beq done ; Center position (no bits are set), nothing to do - and #$0F ; get rid of the "$40" bit - bit bit0 ; is it a "three letter" direction (NNE, ENE, etc.)? - beq special ; yes (bit #0 is zero) + and #$0F ; Get rid of the "$40" bit + lsr a ; Is it "three-letter" direction (NNE, ENE, etc.)? + tax ; Create index into table + bcc special ; Yes (bit #0 was zero) - lsr a ; create index into table - tax lda dirtable,x -done: ora retval ; include "button" bits - ldx #0 +done: ora buttons ; Include button bits + ldx #>$0000 rts ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW -special: lsr a - tax - - lda toggler ; toggle the toggler +special: lda toggle ; Toggle the flag eor #$01 - sta toggler - bne spec_1 ; toggler is 1, use spectable_1 entry + sta toggle + bne spec_1 ; Flag is 1, use spectable_1 entry - lda spectable_0,x ; toggler is 0, use spectable_0 entry - bne done ; jump always + lda spectable_0,x + bne done ; Jump always spec_1: lda spectable_1,x - bne done ; jump always + bne done ; Jump always ; ------------------------------------------------------------------------ ; .rodata - ; a mapping table of "port values" to "cc65 values" - ; port value had been shifted one bit to the right (range 0..7) + ; A mapping table of "port values" to "cc65 values" + ; Port value had been shifted one bit to the right (range 0..7) dirtable: .byte JOY_DOWN ; S .byte JOY_DOWN | JOY_RIGHT ; SE .byte JOY_RIGHT ; E @@ -205,12 +201,12 @@ dirtable: .byte JOY_DOWN ; S .byte JOY_LEFT ; W .byte JOY_DOWN | JOY_LEFT ; SW - ; two "special" mapping tables for three-letter directions (NNE, etc.) + ; Two "special" mapping tables for three-letter directions (NNE, etc.) spectable_0: .byte JOY_DOWN ; SSW .byte JOY_DOWN ; SSE .byte JOY_RIGHT ; ESE .byte JOY_RIGHT ; ENE - .byte JOY_RIGHT ; NNE + .byte JOY_UP ; NNE .byte JOY_UP ; NNW .byte JOY_LEFT ; WNW .byte JOY_LEFT ; WSW @@ -224,14 +220,10 @@ spectable_1: .byte JOY_DOWN | JOY_LEFT ; SSW .byte JOY_UP | JOY_LEFT ; WNW .byte JOY_DOWN | JOY_LEFT ; WSW -; ------------------------------------------------------------------------ -; -bit0: .byte $01 - ; ------------------------------------------------------------------------ ; .bss -toggler: .res 1 -retval: .res 1 + +toggle: .res 1 .end From fcda94f25895ed792c11bc254052abed7f964617 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 13 Jun 2021 20:36:05 -0400 Subject: [PATCH 56/74] Made a slight improvement in the ld65 expression evaluator. --- src/ld65/expr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ld65/expr.c b/src/ld65/expr.c index 7a2f37d4a..ff210e315 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -321,20 +321,18 @@ long GetExprVal (ExprNode* Expr) return GetExprVal (Expr->Left) * GetExprVal (Expr->Right); case EXPR_DIV: - Left = GetExprVal (Expr->Left); Right = GetExprVal (Expr->Right); if (Right == 0) { Error ("Division by zero"); } - return Left / Right; + return GetExprVal (Expr->Left) / Right; case EXPR_MOD: - Left = GetExprVal (Expr->Left); Right = GetExprVal (Expr->Right); if (Right == 0) { Error ("Modulo operation with zero"); } - return Left % Right; + return GetExprVal (Expr->Left) % Right; case EXPR_OR: return GetExprVal (Expr->Left) | GetExprVal (Expr->Right); From 62da869e49bee55b3f0d7dab2dc7bdf9721b6f07 Mon Sep 17 00:00:00 2001 From: Spiro Trikaliotis Date: Mon, 21 Jun 2021 21:34:19 +0200 Subject: [PATCH 57/74] doc: psg_silence: Remove empty notes The notes section of psg_silence (Creativision funcref) contained an empty Notes section, consisting of an empty only. Newer sgmltools fail on this, as they insist on having an element, or they fail compilation: [ 225s] Processing file ../doc/funcref.sgml [ 225s] onsgmls:/tmp/linuxdoc-tools.NfxbjODQbW/sgmltmp.funcref.01.precmdout:5884:9:E:end tag for "ITEMIZE" which is not finished Fixed this by removing the (empty) Notes section altogether. --- doc/funcref.sgml | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 28faa068a..e6cb42a0f 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -5880,8 +5880,6 @@ void main (void) - , From 52e43879298c2fb30c7bc6fd95fae3b3f1458793 Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 12 Aug 2021 13:21:24 -0400 Subject: [PATCH 58/74] Added a program that tests the Commodore-specific directory functions. --- targettest/cbm/Makefile | 7 ++- targettest/cbm/cbmdir-test.c | 119 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 targettest/cbm/cbmdir-test.c diff --git a/targettest/cbm/Makefile b/targettest/cbm/Makefile index fb7af1a9a..298f80d62 100644 --- a/targettest/cbm/Makefile +++ b/targettest/cbm/Makefile @@ -30,10 +30,13 @@ else LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) endif -all: petscii.prg +all: petscii.prg cbmdir-test.prg petscii.prg: petscii.c $(CL) -t $(SYS) -O -o petscii.prg petscii.c +cbmdir-test.prg: cbmdir-test.c + $(CL) -t $(SYS) -Oris -o $@ $< + clean: - @$(DEL) petscii.prg 2>$(NULLDEV) + @$(DEL) petscii.prg cbmdir-test.prg 2>$(NULLDEV) diff --git a/targettest/cbm/cbmdir-test.c b/targettest/cbm/cbmdir-test.c new file mode 100644 index 000000000..0db9856b7 --- /dev/null +++ b/targettest/cbm/cbmdir-test.c @@ -0,0 +1,119 @@ +/* +** Tests the CBM-specific directory functions. +** +** 2021-08-12, Greg King +*/ + +#include +#include +#include +#include +#include + + +/* device number */ +#define UNIT 8 + +/* directory patterm */ +static const char name[] = "$"; + + +static const char* const type[] = { + "DEL", + "CBM", + "DIR", + "LNK", + "???", + "Directory header", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SEQ", + "PRG", + "USR", + "REL", + "VRP" +}; + +static const char* const access[] = { + "unknown", + "read-only", + "write-only", + "read/write" +}; + +static const char* const error[] = { + "", + "couldn't read it", + "", + "couldn't find start of file-name", + "couldn't find end of file-name", + "couldn't read file-type", + "premature end of file" +}; + + +int main(void) +{ + unsigned char go = 0; + unsigned char rc; + struct cbm_dirent E; + + /* Explain use, and wait for a key. */ + printf ("use the following keys:\n" + " g -> go ahead without stopping\n" + " q -> quit directory lister\n" + "tap any key to start ...\n\n"); + cgetc (); + + /* Open the directory. */ + if (cbm_opendir (1, UNIT, name) != 0) { + printf("error opening %s:\n %s\n", name, _stroserror (_oserror)); + return 1; + } + + /* Output the directory. */ + printf("contents of \"%s\":\n", name); + while ((rc = cbm_readdir (1, &E)) != 2) { + if (rc != 0) { + goto oops; + } + + printf (" name[]: \"%s\"\n", E.name); + printf (" size :%6u\n", E.size); + printf (" type : %s\n", type[E.type]); + printf (" access: %s\n", access[E.access > 3 ? 0 : E.access]); + printf ("----\n"); + + if (!go) { + switch (cgetc ()) { + case 'q': + goto done; + + case 'g': + go = 1; + } + } + } + + printf (" size :%6u free.\n", E.size); + +done: + /* Close the directory. */ + cbm_closedir (1); + return 0; + +oops: + if (rc <= 6) { + printf ("\ndirectory error:\n %s.\n", error[rc]); + } + cbm_closedir (1); + return 1; +} From 28b1687aafc4c124cbb6664d20d4b0759125d350 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 26 Sep 2021 12:09:50 +0200 Subject: [PATCH 59/74] Be explicit about hardware flow control (RTS/CTS) being the only supported option. --- doc/apple2.sgml | 4 ++-- doc/apple2enh.sgml | 4 ++-- doc/atari.sgml | 6 +++--- doc/atmos.sgml | 9 +++++---- doc/c128.sgml | 6 +++--- doc/c64.sgml | 6 +++--- doc/cbm510.sgml | 8 ++++---- doc/cbm610.sgml | 8 ++++---- doc/plus4.sgml | 8 ++++---- 9 files changed, 30 insertions(+), 29 deletions(-) diff --git a/doc/apple2.sgml b/doc/apple2.sgml index f957e1247..bd01b68dc 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not + requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 4aafbc256..56fc05e31 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not + requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/atari.sgml b/doc/atari.sgml index f2ced13e1..903895d17 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -675,9 +675,9 @@ The default callbacks definition (RS232 device drivers

-Currently there is one RS232 driver. It uses the R: device (therefore -an R: driver needs to be installed) and was tested with the 850 -interface module. +Currently there is one RS232 driver. It supports up to 9600 baud, requires hardware flow control +(RTS/CTS) and uses the R: device (therefore an R: driver needs to be installed). It was tested +with the 850 interface module. diff --git a/doc/atmos.sgml b/doc/atmos.sgml index 3fd61abcf..cef7770e4 100644 --- a/doc/atmos.sgml +++ b/doc/atmos.sgml @@ -176,10 +176,11 @@ No mouse drivers are currently available for the Atmos. Driver for the Telestrat integrated serial controller and the Atmos with a - serial add-on. - Note that, because of the peculiarities of the 6551 chip, together with the - use of the NMI, transmits are not interrupt driven; and, the transceiver - blocks if the receiver asserts flow control because of a full buffer. + serial add-on. Supports up to 19200 baud, requires hardware flow control + (RTS/CTS) and does interrupt driven receives. Note that, because of the + peculiarities of the 6551 chip, together with the use of the NMI, transmits + are not interrupt driven; and, the transceiver blocks if the receiver + asserts flow control because of a full buffer.

diff --git a/doc/c128.sgml b/doc/c128.sgml index 8c62b6ad1..60306814c 100644 --- a/doc/c128.sgml +++ b/doc/c128.sgml @@ -324,9 +324,9 @@ The default drivers, - Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow - control (RTS/CTS), and interrupt-driven receives. Note that, because of the - peculiarities of the 6551 chip, together with the use of the NMI, transmits + Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware + flow control (RTS/CTS) and does interrupt driven receives. Note that, because of + the peculiarities of the 6551 chip, together with the use of the NMI, transmits are not interrupt driven; and, the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/c64.sgml b/doc/c64.sgml index 7e00f3b93..de37ed4b7 100644 --- a/doc/c64.sgml +++ b/doc/c64.sgml @@ -410,9 +410,9 @@ The default drivers, - Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow - control (RTS/CTS), and interrupt-driven receives. Note that, because of the - peculiarities of the 6551 chip, together with the use of the NMI, transmits + Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware + flow control (RTS/CTS) and does interrupt driven receives. Note that, because of + the peculiarities of the 6551 chip, together with the use of the NMI, transmits are not interrupt driven; and, the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/cbm510.sgml b/doc/cbm510.sgml index c208f3ead..86bed7607 100644 --- a/doc/cbm510.sgml +++ b/doc/cbm510.sgml @@ -231,10 +231,10 @@ The default drivers, Driver for the 6551 ACIA chip built into the Commodore 510. Supports up to - 19200 BPS, hardware flow control (RTS/CTS), and interrupt-driven receives. - Note that, because of the peculiarities of the 6551 chip, transmits are not - interrupt driven; and, the transceiver blocks if the receiver asserts flow - control because of a full buffer. + 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that, because of the peculiarities of the 6551 chip, transmits + are not interrupt driven; and, the transceiver blocks if the receiver asserts + flow control because of a full buffer.

diff --git a/doc/cbm610.sgml b/doc/cbm610.sgml index 37a445dd1..d86950abc 100644 --- a/doc/cbm610.sgml +++ b/doc/cbm610.sgml @@ -212,10 +212,10 @@ No mouse drivers are currently available for the Commodore 610. Driver for the 6551 ACIA chip built into the Commodore 610. Supports up to - 19200 BPS, hardware flow control (RTS/CTS), and interrupt-driven receives. - Note that, because of the peculiarities of the 6551 chip, transmits are not - interrupt driven; and, the transceiver blocks if the receiver asserts flow - control because of a full buffer. + 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that, because of the peculiarities of the 6551 chip, transmits + are not interrupt driven; and, the transceiver blocks if the receiver asserts + flow control because of a full buffer.

diff --git a/doc/plus4.sgml b/doc/plus4.sgml index 645de5161..79a2597d0 100644 --- a/doc/plus4.sgml +++ b/doc/plus4.sgml @@ -195,10 +195,10 @@ No mouse drivers are currently available for the Plus/4. Driver for the 6551 ACIA chip built into the Plus/4. Supports up to 19200 - baud, hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not - interrupt driven, and the transceiver blocks if the receiver asserts flow - control because of a full buffer. + baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that because of the peculiarities of the 6551 chip transmits + are not interrupt driven, and the transceiver blocks if the receiver asserts + flow control because of a full buffer. You need an adapter to use the builtin port, since the output levels available at the user port don't follow the RS232 standard. From eeaa111835d5c82cc821e120a4eba2d548bd2c32 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 28 Sep 2021 17:30:10 +0800 Subject: [PATCH 60/74] Fixed crash in Opt_a_toscmpbool caused by wrong order of condition checks. --- src/cc65/coptstop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 23636e533..08f6c820e 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1113,9 +1113,9 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) D->IP = D->OpIndex + 1; - if (!D->RhsMultiChg && - (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0 && - (D->Rhs.A.Flags & LI_DIRECT) != 0) { + if (!D->RhsMultiChg && + (D->Rhs.A.Flags & LI_DIRECT) != 0 && + (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { /* cmp */ AddOpLow (D, OP65_CMP, &D->Rhs); From 6ba8a385a0b81af5388f47d18ce45c3753cde6ba Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 28 Sep 2021 15:59:54 +0200 Subject: [PATCH 61/74] add test related tu issue #1562 --- test/val/bug1562.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/val/bug1562.c diff --git a/test/val/bug1562.c b/test/val/bug1562.c new file mode 100644 index 000000000..7e6c1751e --- /dev/null +++ b/test/val/bug1562.c @@ -0,0 +1,30 @@ + +/* bug 1562: cc65 generates incorrect code for logical expression with -O */ + +#include +#include + +int failures = 0; + +char input[256]; + +#define DEBUGTRUE(x) printf("%s=%d\n", #x, (x)); failures += (x) ? 0 : 1 + +#define DEBUGFALSE(x) printf("%s=%d\n", #x, (x)); failures += (x) ? 1 : 0 + +int main(void) { + char* r; + strcpy(input, "\"XYZ\""); + r = input+4; + DEBUGFALSE(*r != '"'); // = false + DEBUGTRUE(*r == '"'); // = true + DEBUGFALSE(*(r+1) == '"'); // = false + // Next answer should be false because + // (false || true && false) is false, but it is true with -O. + DEBUGFALSE(*r != '"' || *r == '"' && *(r+1) == '"'); + // Adding parens fixes it even with -O. + DEBUGFALSE(*r != '"' || (*r == '"' && *(r+1) == '"')); + + printf("failures: %d\n", failures); + return failures; +} From 86f19652022ed7c6cc5152c219ee9e8976f78d9e Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 28 Sep 2021 18:55:23 +0200 Subject: [PATCH 62/74] added test related to issue #1552 fixed in pr #1571 --- test/val/bug1552.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/val/bug1552.c diff --git a/test/val/bug1552.c b/test/val/bug1552.c new file mode 100644 index 000000000..42f39eec6 --- /dev/null +++ b/test/val/bug1552.c @@ -0,0 +1,42 @@ + +/* + bug #1552 - crash in fuzix xec.c + + cc65 -t none -O bug1552.c +*/ + +#include + +typedef struct trenod *TREPTR; +typedef struct whnod *WHPTR; + +struct trenod { + int tretyp; +}; + +struct whnod { + int whtyp; + TREPTR whtre; +}; + +int execute(TREPTR argt, int execflg, int *pf1, int *pf2) +{ + register TREPTR t; + int type; + switch (type) + { + case 6: + { + while ((execute(((WHPTR) t)->whtre, 0, NULL, NULL) == 0) == (type == 5)) { + + } + break; + } + } + return 0; +} + +int main(void) +{ + return execute((TREPTR)42, 2, (int *)3, (int *)4); +} From cf1c0b67747be04a6a0b9aabff4c1aae2f761214 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 28 Sep 2021 22:18:49 +0200 Subject: [PATCH 63/74] move hints on how to run binaries from the target specific pages to the intro page, where they should be. --- doc/gamate.sgml | 11 ++---- doc/intro.sgml | 94 ++++++++++++++++++++++++++++++++++++++++--------- doc/pce.sgml | 7 +--- 3 files changed, 80 insertions(+), 32 deletions(-) diff --git a/doc/gamate.sgml b/doc/gamate.sgml index 8e18ab76d..b61053ce8 100644 --- a/doc/gamate.sgml +++ b/doc/gamate.sgml @@ -3,7 +3,7 @@

Gamate System specific information for cc65 <author> -<url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen"> +<url url="mailto:groepaz@gmx.net" name="Groepaz"> <abstract> An overview over the Gamate runtime system as it is implemented for the @@ -117,14 +117,7 @@ following functions (and a few others): <sect>Other hints<p> <itemize> -<item>The Gamate is emulated by MESS (<url url="http://www.mess.org/">), -run like this: <tt>mess gamate -debug -window -skip_gameinfo -cart test.bin</tt> -</itemize> - -some resources on the Gamate: - -<itemize> -<item><url url="http://en.wikipedia.org/wiki/Gamate"> +<item>some resources on the Gamate: <url url="http://en.wikipedia.org/wiki/Gamate"> </itemize> <sect>License<p> diff --git a/doc/intro.sgml b/doc/intro.sgml index b2b141d10..0617b0ab3 100644 --- a/doc/intro.sgml +++ b/doc/intro.sgml @@ -6,6 +6,7 @@ <url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline> <url url="mailto:cbmnut@hushmail.com" name="CbmNut">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King">,<newline> +<url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline> <url url="mailto:stephan.muehlstrasser@web.de" name="Stephan Mühlstrasser"> <abstract> @@ -458,12 +459,8 @@ Substitute the name of a Commodore computer for that <tt/<sys>/: Start the desired version of the emulator (CBM610 programs run on the CBM II [<tt/xcbm2/] emulator). -In the Windows versions of VICE, choose <bf>File>Autoboot disk/tape -image...</bf>, choose your executable, and click <bf/OK/. - -In the Unix versions, hold down the mouse's first button. Move the pointer to -<bf>Smart-attach disk/tape...</bf>, and release the button. Choose your -executable, and click <bf/Autostart/. +Choose <bf>File>Autostart disk/tape image...</bf>, choose your executable, +and click <bf/OK/. The file has a 14-byte header which corresponds to a PRG-format BASIC program, consisting of a single line, similar to this: @@ -499,6 +496,29 @@ The output will appear on a separate line, and you will be returned to a BASIC prompt. +<sect1>Gamate<p> + +Before you can run the cartridge image produced by the linker, the binary has to +be patched using the <bf/gamate-fixcart/ tool that is included in the cc65 +package in the util/gamata directory. + +<tscreen><verb> +gamate-fixcart <image.bin> +</verb></tscreen> + +<sect2>MESS<p> +Available at <url +url="https://www.mamedev.org">: + +MESS (Multiple Emulator Super System) is a multi system emulator that emulates +various cc65 targets. It once started as a MAME fork, but was marged into MAME +again at some point. + +<tscreen><verb> +mess gamate -debug -window -skip_gameinfo -cart <image.bin> +</verb></tscreen> + + <sect1>GEOS<p> Available at <it/Click Here Software's/ <url url="http://cbmfiles.com/geos/index.html" name="GEOS download section">: @@ -535,17 +555,8 @@ feature on. </quote> <quote> -VICE even has different ways that depend on which operating system is running -the emulator. -<itemize> -<item>In Windows, you must click on <bf/Options/ (in an always visible menu). - Then, you must click on <bf/True drive emulation/. -<item>In Unix, you must <em/hold down/ the second button on your mouse. Move - the pointer down to <bf/Drive settings/. Then, move the pointer over to - <bf/Enable true drive emulation/. (If there is a check-mark in front of - those words, that feature already is turned on -- then, move the pointer - off of that menu.) Release the mouse button. -</itemize> +In VICE, got to <bf/Settings/ -> <bf/Settings/, then <bf/Peripherial devices/ -> +<bf/Drive/. Then, you must enable the <bf/True drive emulation/ checkbox. </quote> Find the <bf/CONVERT/ program on the boot disk [tap the 6-key; then, you @@ -572,6 +583,29 @@ directory notePad. Look at the eight file-positions on each page until you see The output is shown in a GEOS dialog box; click <bf/OK/ when you have finished reading it. +Alternatively you can use the <bf/c1541/ program that comes with VICE to write the +file to a disk image directly in GEOS format, so it can be used in GEOS directly +without having to use the <bf/CONVERT/ program. + +<tscreen><verb> +c1541 -attach geos.d64 -geoswrite hello1 +</verb></tscreen> + + +<sect1>Nintendo Entertainment System<p> + +<sect2>Mednafen (NES)<p> +Available at <url +url="https://mednafen.github.io/releases/">: + +Mednafen is a multi system emulator that emulates a couple of the supported +targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and +PC Engine/TurboGrafx 16. + +<tscreen><verb> +mednafen -force_module nes <image.bin> +</verb></tscreen> + <sect1>Ohio Scientific Challenger 1P<p> The <tt/osic1p/ runtime library returns to the boot prompt when the main() @@ -694,6 +728,32 @@ Press <RETURN>. After hitting the RETURN key, you should see the boot prompt again. +<sect1>PC Engine/TurboGrafx 16<p> + +For the cartridge image produced by the linker to work in emulators and on real +hardware, its content must be rearranged so the first 8k block becomes the last +8k block in the image. + +For example, for a 32k image this can be done using <bf/dd/ as follows: + +<tscreen><verb> +dd if=infile.bin bs=8K skip=3 > outfile.pce +dd if=infile.bin bs=8K count=3 >> outfile.pce +</verb></tscreen> + +<sect2>Mednafen<p> +Available at <url +url="https://mednafen.github.io/releases/">: + +Mednafen is a multi system emulator that emulates a couple of the supported +targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and +PC Engine/TurboGrafx 16. + +<tscreen><verb> +mednafen -force_module pce <image.pce> +</verb></tscreen> + + <sect1>Contributions wanted<p> We need your help! Recommended emulators and instructions for other targets diff --git a/doc/pce.sgml b/doc/pce.sgml index 42a1ca9d3..47eeabcfd 100644 --- a/doc/pce.sgml +++ b/doc/pce.sgml @@ -2,7 +2,7 @@ <article> <title>PC-Engine (TurboGrafx 16) System-specific information for cc65 -<author><url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen">,<newline> +<author><url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King"> <abstract> @@ -206,11 +206,6 @@ following functions (and a few others): <sect>Other hints<p> -<itemize> -<item><url url="https://mednafen.github.io/" name= "Mednafen"> is a good -emulator to use for the PC-Engine. -</itemize> - Some useful resources on PCE coding: <itemize> From 94445cd16ffdcd245e1c0d1a1f23a2375fac2ca9 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Tue, 28 Sep 2021 22:37:34 +0200 Subject: [PATCH 64/74] remove conio.pce from the default target and print a message instead --- targettest/pce/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile index 1ecc0566f..f91f0eed8 100644 --- a/targettest/pce/Makefile +++ b/targettest/pce/Makefile @@ -42,14 +42,15 @@ else COUNT := 1 endif -all: conio.pce +all: conio.bin + +%.bin: %.c + $(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ + @echo "use 'make conio.pce' to produce a .pce file using dd" %.pce: %.bin dd if=$< bs=8K skip=${COUNT} > $@ dd if=$< bs=8K count=${COUNT} >> $@ -%.bin: %.c - $(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ - clean: @$(DEL) conio.o conio.??? 2>$(NULLDEV) From 2338e70709294a93d69d1f8bdc50d51509566b24 Mon Sep 17 00:00:00 2001 From: cc65 Owner <41281059+cc65-github@users.noreply.github.com> Date: Wed, 29 Sep 2021 10:37:44 +0200 Subject: [PATCH 65/74] travis-ci.org -> travis-ci.com --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c84b7430..36a4b56cd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [Wiki](https://github.com/cc65/wiki/wiki) -[![Build Status](https://api.travis-ci.org/cc65/cc65.svg?branch=master)](https://travis-ci.org/cc65/cc65/builds) +[![Build Status](https://app.travis-ci.com/cc65/cc65.svg?branch=master)](https://app.travis-ci.com/cc65/cc65) cc65 is a complete cross development package for 65(C)02 systems, including a powerful macro assembler, a C compiler, linker, librarian and several From 674a5439096b6bb16a3622e45b8a5f0c5218c998 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Wed, 29 Sep 2021 12:33:51 +0200 Subject: [PATCH 66/74] Parallelize build Travis CI defaults to 2 core environments. --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0b25e5d16..c9068193a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,14 +9,14 @@ jobs: - sudo apt-get update - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass script: - - make bin USER_CFLAGS=-Werror - - make lib QUIET=1 - - make test QUIET=1 - - make samples + - make -j2 bin USER_CFLAGS=-Werror + - make -j2 lib QUIET=1 + - make -j2 test QUIET=1 + - make -j2 samples - make -C src clean - - make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + - make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- - make -C samples clean - - make doc zip + - make -j2 doc zip after_success: - make -f Makefile.travis From c48e821c4bfe9d4bd178a742261808c79bd65e1a Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Wed, 29 Sep 2021 14:48:09 +0200 Subject: [PATCH 67/74] Don --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c9068193a..4b0919a10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ jobs: script: - make -j2 bin USER_CFLAGS=-Werror - make -j2 lib QUIET=1 - - make -j2 test QUIET=1 + - make test QUIET=1 - make -j2 samples - make -C src clean - make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- From f6636635fae4ed40bb687ed13eff5f8e91e56883 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Tue, 5 Oct 2021 19:17:16 +0200 Subject: [PATCH 68/74] targettest/atari/multi-xex.cfg: fix comments --- targettest/atari/multi-xex.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/targettest/atari/multi-xex.cfg b/targettest/atari/multi-xex.cfg index 7558aa895..f13a9eabe 100644 --- a/targettest/atari/multi-xex.cfg +++ b/targettest/atari/multi-xex.cfg @@ -5,13 +5,13 @@ MEMORY { ZP: file = "", define = yes, start = $0082, size = $007E; # First memory segment in file, show message LOADER: file = %O, start = $680, size = 128; - # First memory segment in file, load over COLOR registers: + # Second memory segment in file, load over COLOR registers: COLOR: file = %O, start = $2C4, size = 5; - # Second memory segment, load at page 6: + # Third memory segment, load at page 6: PAGE6: file = %O, start = $600, size = 128; - # Third memory segment in file, load over SDLST register: + # Fourth memory segment in file, load over SDLST register: SDLST: file = %O, start = $230, size = 2; - # Main segment, load at "STARTADDRESS" + # Fifth/Main segment, load at "STARTADDRESS" MAIN: file = %O, start = %S, size = $BC20 - %S; } FILES { From c3d7a900844673cc4a85f0c132a2faa00886f711 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Tue, 5 Oct 2021 19:19:54 +0200 Subject: [PATCH 69/74] targettest/atari/ostype.c: remove warnings --- targettest/atari/ostype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targettest/atari/ostype.c b/targettest/atari/ostype.c index 552735ac8..5561f64fd 100644 --- a/targettest/atari/ostype.c +++ b/targettest/atari/ostype.c @@ -9,9 +9,9 @@ int main(void) { + char *rev; unsigned int t, v; unsigned char palntsc; - unsigned char *rev; unsigned char minor; unsigned char c; From 4f87c7cc645c72903f99c78abcf52124b1e14623 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 23 Oct 2021 01:18:17 +0200 Subject: [PATCH 70/74] move samples that only work for a specific target into subdirs named the same as the target --- samples/Makefile | 23 +-- samples/atari2600/Makefile | 59 +++++++ .../{atari2600hello.c => atari2600/hello.c} | 0 samples/cbm/Makefile | 164 ++++++++++++++++++ samples/{ => cbm}/fire.c | 0 samples/{ => cbm}/nachtm.c | 0 samples/{ => cbm}/plasma.c | 0 samples/readme.txt | 89 ++++++---- samples/supervision/Makefile | 59 +++++++ .../hello.c} | 0 10 files changed, 343 insertions(+), 51 deletions(-) create mode 100644 samples/atari2600/Makefile rename samples/{atari2600hello.c => atari2600/hello.c} (100%) create mode 100644 samples/cbm/Makefile rename samples/{ => cbm}/fire.c (100%) rename samples/{ => cbm}/nachtm.c (100%) rename samples/{ => cbm}/plasma.c (100%) create mode 100644 samples/supervision/Makefile rename samples/{supervisionhello.c => supervision/hello.c} (100%) diff --git a/samples/Makefile b/samples/Makefile index 7e5c1934d..db7bb0d84 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -154,7 +154,7 @@ endif # Lists of subdirectories # disasm depends on cpp -DIRLIST = tutorial geos +DIRLIST = tutorial geos atari2600 supervision cbm # -------------------------------------------------------------------------- # Lists of executables @@ -205,28 +205,22 @@ EXELIST_bbc = \ EXELIST_c64 = \ ascii \ enumdevdir \ - fire \ gunzip65 \ hello \ mandelbrot \ mousedemo \ multdemo \ - nachtm \ ovrldemo \ - plasma \ sieve \ tgidemo EXELIST_c128 = \ ascii \ enumdevdir \ - fire \ gunzip65 \ hello \ mandelbrot \ mousedemo \ - nachtm \ - plasma \ sieve \ tgidemo @@ -237,19 +231,15 @@ EXELIST_c16 = \ EXELIST_cbm510 = \ ascii \ - fire \ gunzip65 \ hello \ mousedemo \ - nachtm \ - plasma \ sieve EXELIST_cbm610 = \ ascii \ gunzip65 \ hello \ - nachtm \ sieve EXELIST_creativision = \ @@ -302,7 +292,6 @@ EXELIST_plus4 = \ enumdevdir \ gunzip65 \ hello \ - plasma \ sieve EXELIST_sim6502 = \ @@ -311,7 +300,7 @@ EXELIST_sim6502 = \ EXELIST_sim65c02 = $(EXELIST_sim6502) EXELIST_supervision = \ - supervisionhello + notavailable EXELIST_telestrat = \ ascii \ @@ -398,7 +387,7 @@ $(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$( endef # D64_WRITE_SEQ_recipe samples.d64: samples - @$(C1541) -format samples,AA d64 $@ >$(NULLDEV) + @$(C1541) -format "samples,00" d64 $@ >$(NULLDEV) $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) @@ -460,11 +449,17 @@ install: $(INSTALL) -d $(DESTDIR)$(samplesdir) $(INSTALL) -d $(DESTDIR)$(samplesdir)/geos $(INSTALL) -d $(DESTDIR)$(samplesdir)/tutorial + $(INSTALL) -d $(DESTDIR)$(samplesdir)/atari2600 + $(INSTALL) -d $(DESTDIR)$(samplesdir)/cbm + $(INSTALL) -d $(DESTDIR)$(samplesdir)/supervision $(INSTALL) -m0644 *.* $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 geos/*.* $(DESTDIR)$(samplesdir)/geos $(INSTALL) -m0644 tutorial/*.* $(DESTDIR)$(samplesdir)/tutorial + $(INSTALL) -m0644 atari2600/*.* $(DESTDIR)$(samplesdir)/atari2600 + $(INSTALL) -m0644 cbm/*.* $(DESTDIR)$(samplesdir)/cbm + $(INSTALL) -m0644 supervision/*.* $(DESTDIR)$(samplesdir)/supervision # -------------------------------------------------------------------------- # Packaging rules diff --git a/samples/atari2600/Makefile b/samples/atari2600/Makefile new file mode 100644 index 000000000..a02ec9e80 --- /dev/null +++ b/samples/atari2600/Makefile @@ -0,0 +1,59 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= atari2600 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +EXELIST_atari2600 = \ + hello + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: atari 2600 samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +hello: hello.c + $(CL) -t $(SYS) -O -o hello -m hello.map hello.c + +clean: + @$(DEL) $(EXELIST_atari2600) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) diff --git a/samples/atari2600hello.c b/samples/atari2600/hello.c similarity index 100% rename from samples/atari2600hello.c rename to samples/atari2600/hello.c diff --git a/samples/cbm/Makefile b/samples/cbm/Makefile new file mode 100644 index 000000000..989710932 --- /dev/null +++ b/samples/cbm/Makefile @@ -0,0 +1,164 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +ifneq ($(filter disk samples.%,$(MAKECMDGOALS)),) + ifdef CC65_HOME + TARGET_PATH = $(CC65_HOME)/target + else + TARGET_PATH := $(if $(wildcard ../target),../target,$(shell $(CL) --print-target-path)) + endif + + # If TARGET_PATH contains spaces then it is presumed to contain escaped spaces. GNU make + # has very limited support for paths containing spaces. $(wildcard) is the only function + # that is aware of escaped spaces. However, $(wildcard) never returns paths with escaped + # spaces !!! So if it e.g. finds 4 files in a path with 2 spaces then one ends up with a + # return value consisting of 12 plain words :-(( + # + # Fortunately we can work around that behaviour here because we know that the files we + # are looking for have known extensions. So we can $(filter) the in our example above 12 + # words for file extensions so we come up with 4 path fragments. Then we remove those + # path fragments with $(notdir) from the file names. + # + # So far so good. But here we want to process files from different paths in a single + # recipe further down below and therefore want to prepend the paths to the files with + # $(addprefix). However, $(foreach) isn't aware of escaped spaces (only $(wildcard) is). + # Therefore, we need to replace the spaces with some other character temporarily in order + # to have $(foreach) generate one invocation per file. We use the character '?' for that + # purpose here, just because it is known to not be part of file names. + # + # Inside the recipe generated per file we then replace the '?' again with a space. As we + # want to be compatible with cmd.exe for execution we're not using an escaped space but + # rather double-quote the whole path. + # + # Note: The "strange" $(wildcard) further down below just serves the purpose to unescape + # spaces for cmd.exe. This could have as well been done with another $(subst). + + SUBST_TARGET_PATH := $(subst \$(SPACE),?,$(TARGET_PATH)) + + EMD := $(wildcard $(TARGET_PATH)/$(SYS)/drv/emd/*) + MOU := $(wildcard $(TARGET_PATH)/$(SYS)/drv/mou/*) + TGI := $(wildcard $(TARGET_PATH)/$(SYS)/drv/tgi/*) + + EMD := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/emd/,$(notdir $(filter %.emd,$(EMD)))) + MOU := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/mou/,$(notdir $(filter %.mou,$(MOU)))) + TGI := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/tgi/,$(notdir $(filter %.tgi,$(TGI)))) + + # This one comes with the VICE emulator. + # See http://vice-emu.sourceforge.net/ + C1541 ?= c1541 +endif + +DISK_c64 = samples.d64 + +EXELIST_c64 = \ + fire \ + plasma \ + nachtm + +EXELIST_c128 = \ + fire \ + plasma \ + nachtm + +EXELIST_cbm510 = \ + fire \ + plasma \ + nachtm + +EXELIST_cbm610 = \ + nachtm + +EXELIST_plus4 = \ + plasma + +EXELIST_c16 = \ + notavailable + +EXELIST_pet = \ + notavailable + +EXELIST_vic20 = \ + notavailable + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +disk: $(DISK_$(SYS)) + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: cbm samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +fire: fire.c + $(CL) -t $(SYS) -O -o fire -m fire.map fire.c +plasma: plasma.c + $(CL) -t $(SYS) -O -o plasma -m plasma.map plasma.c +nachtm: nachtm.c + $(CL) -t $(SYS) -O -o nachtm -m nachtm.map nachtm.c + +# -------------------------------------------------------------------------- +# Rule to make a CBM disk with all samples. Needs the c1541 program that comes +# with the VICE emulator. + +define D64_WRITE_PRG_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),p >$(NULLDEV) + +endef # D64_WRITE_PRG_recipe + +define D64_WRITE_SEQ_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV) + +endef # D64_WRITE_SEQ_recipe + +samples.d64: samples + @$(C1541) -format "samples,00" d64 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) + +clean: + @$(DEL) $(EXELIST_$(SYS)) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) + @$(DEL) $(DISK_$(SYS)) 2>$(NULLDEV) diff --git a/samples/fire.c b/samples/cbm/fire.c similarity index 100% rename from samples/fire.c rename to samples/cbm/fire.c diff --git a/samples/nachtm.c b/samples/cbm/nachtm.c similarity index 100% rename from samples/nachtm.c rename to samples/cbm/nachtm.c diff --git a/samples/plasma.c b/samples/cbm/plasma.c similarity index 100% rename from samples/plasma.c rename to samples/cbm/plasma.c diff --git a/samples/readme.txt b/samples/readme.txt index 3c9247c39..56b275764 100644 --- a/samples/readme.txt +++ b/samples/readme.txt @@ -10,11 +10,13 @@ Please note: similar systems. If you're using Windows, then consider installing Cygwin or MSys2. - * The makefile specifies the C64 as the default target system because all - but three of the programs run on that platform. When compiling for another - system, you will have to change the line that specifies the target system - at the top of the makefile, specify the system with SYS=<target> on the - make command line, or set a SYS environment variable. + * The makefile specifies the C64 as the default target system because most + of the programs run on that platform. When compiling for another system, + you will have to change the line that specifies the target system at the + top of the makefile, specify the system with SYS=<target> on the make + command line, or set a SYS environment variable. For example: + + make SYS=apple2 * Use "make disk" to build a disk image with all sample programs. @@ -31,11 +33,6 @@ Description: Shows the ASCII (or ATASCII, PETSCII) codes of typed <greg.king5@verizon.com>. Platforms: All platforms with conio or stdio (compile time configurable). ------------------------------------------------------------------------------ -Name: atari2600hello -Description: A "Hello world" type program. -Platforms: Runs on only the Atari 2600 Video Console System. - ----------------------------------------------------------------------------- Name: diodemo Description: A disc copy program written and contributed by Oliver @@ -52,12 +49,6 @@ Platforms: All systems with device enumeration and directory access (currently the Commodore machines, the Commander X16, and the Apple ][). ------------------------------------------------------------------------------ -Name: fire -Description: Another graphics demo written by groepaz/hitmen. -Platforms: The program currently is running on only the C64, but should - be portable to the C128 and CBM510 (and maybe more machines). - ----------------------------------------------------------------------------- Name: gunzip65 Description: A gunzip utility for 6502-based machines, written by Piotr @@ -76,8 +67,8 @@ Platforms: Runs on all platforms that support conio, which means: ----------------------------------------------------------------------------- Name: mandelbrot Description: A mandelbrot demo using integer arithmetic. The demo was - written by groepaz/hitmen, and converted to cc65 using TGI - graphics by Stephan Haubenthal. + written by groepaz, and converted to cc65 using TGI graphics + by Stephan Haubenthal. Platforms: Runs on all platforms that have TGI support: Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, NES, and Lynx. @@ -97,13 +88,6 @@ Platforms: All systems with an overlay linker config., disk directory access, and EMD support (currently the C64, the C128, the Atari, and the Apple ][). ------------------------------------------------------------------------------ -Name: nachtm -Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart. -Platforms: All systems that have the Commodore SID (Sound Interface - Device): - C64, C128, CBM510, CBM610. - ----------------------------------------------------------------------------- Name: overlaydemo Description: Shows how to load overlay files from disk. Written and @@ -111,13 +95,6 @@ Description: Shows how to load overlay files from disk. Written and Platforms: All systems with an overlay linker config. (currently the C64, the C128, the Atari, and the Apple ][). ------------------------------------------------------------------------------ -Name: plasma -Description: A fancy graphics demo written by groepaz/hitmen. -Platforms: The program needs a VIC-II or a TED, so it runs on the following - systems: - C64, C128, CBM510, Plus/4. - ----------------------------------------------------------------------------- Name: sieve Description: Implements the "Sieve of Eratosthenes" as a way to find all @@ -128,11 +105,6 @@ Platforms: All systems with conio and clock support: Commander X16, Apple ][ (without timing due to missing clock support). ------------------------------------------------------------------------------ -Name: supervisionhello -Description: A "Hello world" type program. -Platforms: Runs on only the Watara Supervision game console. - ----------------------------------------------------------------------------- Name: tgidemo Description: Shows some of the graphics capabilities of the "Tiny Graphics @@ -140,3 +112,46 @@ Description: Shows some of the graphics capabilities of the "Tiny Graphics Platforms: Runs on all platforms that have TGI support: Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, NES, and Lynx. + +============================================================================= + +Platform specific samples follow: + +atari 2600: +----------- + +Name: hello +Description: A "Hello world" type program. +Platforms: Runs on only the Atari 2600 Video Console System. +----------------------------------------------------------------------------- + +cbm: +---- + +Name: fire +Description: Another graphics demo written by groepaz. +Platforms: C64, C128, CBM510 + +----------------------------------------------------------------------------- +Name: nachtm +Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart. +Platforms: All systems that have the Commodore SID (Sound Interface + Device): + C64, C128, CBM510, CBM610. + +----------------------------------------------------------------------------- +Name: plasma +Description: A fancy graphics demo written by groepaz. +Platforms: The program needs a VIC-II or a TED, so it runs on the following + systems: + C64, C128, CBM510, Plus/4. +----------------------------------------------------------------------------- + + +supervision: +------------ + +Name: hello +Description: A "Hello world" type program. +Platforms: Runs on only the Watara Supervision game console. +---------------------------------------------------------------------------- diff --git a/samples/supervision/Makefile b/samples/supervision/Makefile new file mode 100644 index 000000000..5829b3f01 --- /dev/null +++ b/samples/supervision/Makefile @@ -0,0 +1,59 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= supervision + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +EXELIST_supervision = \ + hello + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: supervision samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +hello: hello.c + $(CL) -t $(SYS) -O -o hello -m hello.map hello.c + +clean: + @$(DEL) $(EXELIST_supervision) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) diff --git a/samples/supervisionhello.c b/samples/supervision/hello.c similarity index 100% rename from samples/supervisionhello.c rename to samples/supervision/hello.c From f796c260910cf6047623c3c550fa1153f4e839ae Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Sun, 21 Nov 2021 22:56:02 +0100 Subject: [PATCH 71/74] Added hint on clock(). clock() isn't available on the Apple II - and never will be. --- doc/library.sgml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/library.sgml b/doc/library.sgml index 303809c59..bcd5db86e 100644 --- a/doc/library.sgml +++ b/doc/library.sgml @@ -59,6 +59,9 @@ Functions that are <em/not/ available: Functions not available on all supported systems: <itemize> + <item><tt>clock</tt>: Support depends on the capabilities of the target + machine. + <p> <item><tt>fopen/fread/fwrite/fclose/fputs/fgets/fscanf</tt>: The functions are built on open/read/write/close. Those latter functions are not available on all systems. From 6637e288318f10cf789faedb3ef46f2323691fa3 Mon Sep 17 00:00:00 2001 From: Gabriele Gorla <44734244+gorlik@users.noreply.github.com> Date: Wed, 8 Sep 2021 15:00:20 -0700 Subject: [PATCH 72/74] saves 2 bytes in the standard c64 joystick driver remove redundant code and add jmp to the common sequence two more bytes could be saved at the expense of longer sequence with interrupts disabled by moving sei/cli --- libsrc/c64/joy/c64-stdjoy.s | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libsrc/c64/joy/c64-stdjoy.s b/libsrc/c64/joy/c64-stdjoy.s index c983d81bb..d11093fba 100644 --- a/libsrc/c64/joy/c64-stdjoy.s +++ b/libsrc/c64/joy/c64-stdjoy.s @@ -93,9 +93,7 @@ joy1: lda #$7F sta CIA1_PRA lda CIA1_PRB cli - and #$1F - eor #$1F - rts + jmp end ; Read joystick 2 @@ -107,8 +105,6 @@ joy2: ldx #0 lda CIA1_PRA sty CIA1_DDRA cli - and #$1F +end: and #$1F eor #$1F rts - - From 1918f0ac9b81e119256b6c71b6bab79e3d55d47a Mon Sep 17 00:00:00 2001 From: gorlik <44734244+gorlik@users.noreply.github.com> Date: Mon, 13 Sep 2021 12:27:37 -0700 Subject: [PATCH 73/74] adding missing VIC register definition to c64.inc --- asminc/c64.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asminc/c64.inc b/asminc/c64.inc index 2cfc50db4..d131c7860 100644 --- a/asminc/c64.inc +++ b/asminc/c64.inc @@ -77,6 +77,8 @@ VIC_SPR_EXP_Y := $D017 VIC_SPR_EXP_X := $D01D VIC_SPR_MCOLOR := $D01C VIC_SPR_BG_PRIO := $D01B +VIC_SPR_COLL := $D01E +VIC_SPR_BG_COLL := $D01F VIC_SPR_MCOLOR0 := $D025 VIC_SPR_MCOLOR1 := $D026 From a7e6f9840ca12798629efaf7e992b12d31a929e4 Mon Sep 17 00:00:00 2001 From: Gerhard Gruber <gerhard.gruber@gdata.de> Date: Wed, 22 Sep 2021 11:38:49 +0200 Subject: [PATCH 74/74] VIC-20 and C128 cfg added for ASM programming --- cfg/c128-asm.cfg | 20 ++++++++++++++++++++ cfg/vic20-asm.cfg | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 cfg/c128-asm.cfg create mode 100644 cfg/vic20-asm.cfg diff --git a/cfg/c128-asm.cfg b/cfg/c128-asm.cfg new file mode 100644 index 000000000..0da296c71 --- /dev/null +++ b/cfg/c128-asm.cfg @@ -0,0 +1,20 @@ +FEATURES { + STARTADDRESS: default = $1c01; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $00FE, define = yes; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $D000 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = rw; + RODATA: load = MAIN, type = ro, optional = yes; + DATA: load = MAIN, type = rw, optional = yes; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/cfg/vic20-asm.cfg b/cfg/vic20-asm.cfg new file mode 100644 index 000000000..7ab70888c --- /dev/null +++ b/cfg/vic20-asm.cfg @@ -0,0 +1,19 @@ +FEATURES { + STARTADDRESS: default = $1001; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A, define = yes; + LOADADDR: file = %O, start = $1001, size = $0002; + MAIN: file = %O, start = %S, size = $0DF3 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +}