From 6617a017faa43382bd2bebb40a3e8f810d35f6b6 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 5 Feb 2021 17:29:49 +0800 Subject: [PATCH 001/520] Added Opt_a_tossub for 8-bit subtraction. --- src/cc65/coptstop.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index ae40a55e9..13470dc18 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1292,6 +1292,58 @@ static unsigned Opt_a_tosne (StackOpData* D) +static unsigned Opt_a_tossub (StackOpData* D) +/* Optimize the tossubax sequence. */ +{ + CodeEntry* X; + + + /* Inline the sbc */ + D->IP = D->OpIndex+1; + + /* Must be true because of OP_RHS_LOAD */ + CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0); + + /* sec */ + X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Add code for low operand */ + AddOpLow (D, OP65_SBC, &D->Rhs); + + /* Do sign-extension as high-byte operation only when its result is used */ + if ((GetRegInfo (D->Code, D->IP, REG_X) & REG_X) != 0) { + CodeLabel* L; + CodeEntry* N; + + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (0), 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Add sign extension - N is unused now */ + N = CS_GetEntry (D->Code, D->IP); + CHECK (N != 0); + L = CS_GenLabel (D->Code, N); + + X = NewCodeEntry (OP65_BCS, AM65_BRA, L->Name, L, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + + /* Rhs load entries must be removed */ + D->Rhs.X.Flags |= LI_REMOVE; + D->Rhs.A.Flags |= LI_REMOVE; + + /* Remove the push and the call to the tossubax function */ + RemoveRemainders (D); + + /* We changed the sequence */ + return 1; +} + + + static unsigned Opt_a_tosuge (StackOpData* D) /* Optimize the tosgeax and tosugeax sequences */ { @@ -1363,6 +1415,7 @@ static const OptFuncDesc FuncRegATable[] = { { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, + { "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, From 8b6d78a075dafde8583c4992d37995d9b4c9db97 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 1 Mar 2021 22:11:22 +0800 Subject: [PATCH 002/520] Added Opt_a_tosbitwise for 8-bit bitwise operations. --- src/cc65/coptstop.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 13470dc18..7e024ae88 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1105,6 +1105,62 @@ static unsigned Opt_tosxorax (StackOpData* D) +static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC) +/* Optimize the tosandax/tosorax/tosxorax sequence. */ +{ + CodeEntry* X; + + + /* Inline the bitwise operation */ + D->IP = D->OpIndex+1; + + /* Backup lhs if necessary */ + if ((D->Rhs.A.Flags & LI_DIRECT) == 0) { + if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) { + /* Just reload lhs */ + X = NewCodeEntry (OPC, D->Lhs.A.LoadEntry->AM, D->Lhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else { + /* Backup lhs */ + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); + /* Add code for low operand */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + } else { + /* Add code for low operand */ + X = NewCodeEntry (OPC, D->Rhs.A.LoadEntry->AM, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Rhs load entries may be removed */ + D->Rhs.A.Flags |= LI_REMOVE; + } + + /* Do high-byte operation only when its result is used */ + if ((GetRegInfo (D->Code, D->IP, REG_X) & REG_X) != 0) { + /* Replace the high-byte load with 0 for EOR, or just leave it alone */ + if (OPC == OP65_EOR) { + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (0), 0, D->Rhs.X.ChgEntry->LI); + InsertEntry (D, X, D->IP++); + D->Rhs.X.Flags |= LI_REMOVE; + } else { + D->Rhs.X.Flags |= LI_DONT_REMOVE; + } + } else { + /* Rhs load entries may be removed */ + D->Rhs.X.Flags |= LI_REMOVE; + } + + /* Remove the push and the call to the tossubax function */ + RemoveRemainders (D); + + /* We changed the sequence */ + return 1; +} + + + static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) /* Optimize the TOS compare sequence with a bool transformer */ { @@ -1177,6 +1233,14 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) +static unsigned Opt_a_tosand (StackOpData* D) +/* Optimize the tosandax sequence. */ +{ + return Opt_a_tosbitwise (D, OP65_AND); +} + + + static unsigned Opt_a_toseq (StackOpData* D) /* Optimize the toseqax sequence */ { @@ -1292,6 +1356,14 @@ static unsigned Opt_a_tosne (StackOpData* D) +static unsigned Opt_a_tosor (StackOpData* D) +/* Optimize the tosorax sequence. */ +{ + return Opt_a_tosbitwise (D, OP65_ORA); +} + + + static unsigned Opt_a_tossub (StackOpData* D) /* Optimize the tossubax sequence. */ { @@ -1376,6 +1448,14 @@ static unsigned Opt_a_tosult (StackOpData* D) +static unsigned Opt_a_tosxor (StackOpData* D) +/* Optimize the tosxorax sequence. */ +{ + return Opt_a_tosbitwise (D, OP65_EOR); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -1408,6 +1488,7 @@ static const OptFuncDesc FuncTable[] = { }; static const OptFuncDesc FuncRegATable[] = { + { "tosandax", Opt_a_tosand, REG_NONE, OP_NONE }, { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, @@ -1415,11 +1496,13 @@ static const OptFuncDesc FuncRegATable[] = { { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, + { "tosorax", Opt_a_tosor, REG_NONE, OP_NONE }, { "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, + { "tosxorax", Opt_a_tosxor, REG_NONE, OP_NONE }, }; #define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) From 2324bd62f6e55084d6d763f6c3a8a9b7bfd5c876 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 8 Jun 2021 14:16:14 +0800 Subject: [PATCH 003/520] If __A__,__AX__ or __EAX__ is used, post-inc/dec within the same statement will not modify it. Moved testcase from src/test/todo to src/test/val plus minor improvement on portability. --- src/cc65.vcxproj | 2 + src/cc65/expr.c | 60 +++++++++++++++--------- src/cc65/expr.h | 5 -- src/cc65/goto.c | 1 + src/cc65/initdata.c | 1 + src/cc65/locals.c | 1 + src/cc65/seqpoint.c | 67 ++++++++++++++++++++++++++ src/cc65/seqpoint.h | 70 ++++++++++++++++++++++++++++ src/cc65/stdfunc.c | 1 + src/cc65/stmt.c | 27 ++++++++--- src/cc65/testexpr.c | 1 + test/{todo => val}/inline-asm-1489.c | 22 ++++++++- 12 files changed, 222 insertions(+), 36 deletions(-) create mode 100644 src/cc65/seqpoint.c create mode 100644 src/cc65/seqpoint.h rename test/{todo => val}/inline-asm-1489.c (83%) diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 7e2e8ea8c..556c616b0 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -110,6 +110,7 @@ + @@ -190,6 +191,7 @@ + diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 37af494b5..61a24329f 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -31,6 +31,7 @@ #include "macrotab.h" #include "preproc.h" #include "scanner.h" +#include "seqpoint.h" #include "shiftexpr.h" #include "stackptr.h" #include "standard.h" @@ -677,6 +678,7 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) int I; unsigned Size = 0; int Count = GetDeferredOpCount (); + unsigned StmtFlags = GetSQPFlags (); /* Nothing to be done */ if (Count <= 0) { @@ -684,26 +686,38 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) } /* Backup some regs/processor flags around the inc/dec */ - if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + if ((StmtFlags & SQP_KEEP_TEST) != 0 || + ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr))) { /* Sufficient to add a pair of PHP/PLP for all cases */ AddCodeLine ("php"); } - /* Backup the content of EAX around the inc/dec */ - if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { - /* Get the size */ - Size = CheckedSizeOf (Expr->Type); + if ((Flags & SQP_MASK_EAX) != 0 && ED_NeedsPrimary (Expr)) { + Size = SizeOf (Expr->Type); + } - if (Size < 2) { - AddCodeLine ("pha"); - } else if (Size < 3) { - AddCodeLine ("sta regsave"); - AddCodeLine ("stx regsave+1"); - } else { - AddCodeLine ("jsr saveeax"); + /* Get the size of the backup */ + if ((StmtFlags & SQP_MASK_EAX) != 0) { + switch (StmtFlags & SQP_MASK_EAX) { + case SQP_KEEP_A: if (Size < 1) Size = 1; break; + case SQP_KEEP_AX: if (Size < 2) Size = 2; break; + case SQP_KEEP_EAX: if (Size < 4) Size = 4; break; + default: ; } } + /* Backup the content of EAX around the inc/dec */ + if (Size == 1) { + AddCodeLine ("pha"); + } else if (Size == 2) { + AddCodeLine ("sta regsave"); + AddCodeLine ("stx regsave+1"); + } else if (Size == 3 || Size == 4) { + AddCodeLine("jsr saveeax"); + } else if (Size > 4) { + Error ("Unsupported deferred operand size: %u", Size); + } + for (I = 0; I < Count; ++I) { DeferredOp* Op = CollAtUnchecked (&DeferredOps, I); switch (Op->OpType) { @@ -721,19 +735,18 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) CollDeleteAll (&DeferredOps); /* Restore the content of EAX around the inc/dec */ - if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { - if (Size < 2) { - AddCodeLine ("pla"); - } else if (Size < 3) { - AddCodeLine ("lda regsave"); - AddCodeLine ("ldx regsave+1"); - } else { - AddCodeLine ("jsr resteax"); - } + if (Size == 1) { + AddCodeLine ("pla"); + } else if (Size == 2) { + AddCodeLine ("lda regsave"); + AddCodeLine ("ldx regsave+1"); + } else if (Size == 3 || Size == 4) { + AddCodeLine ("jsr resteax"); } /* Restore the regs/processor flags around the inc/dec */ - if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + if ((StmtFlags & SQP_KEEP_TEST) != 0 || + ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr))) { /* Sufficient to pop the processor flags */ AddCodeLine ("plp"); } @@ -1362,6 +1375,7 @@ static void Primary (ExprDesc* E) case TOK_A: /* Register pseudo variable */ + SetSQPFlags (SQP_KEEP_A); E->Type = type_uchar; E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL; NextToken (); @@ -1369,6 +1383,7 @@ static void Primary (ExprDesc* E) case TOK_AX: /* Register pseudo variable */ + SetSQPFlags (SQP_KEEP_AX); E->Type = type_uint; E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL; NextToken (); @@ -1376,6 +1391,7 @@ static void Primary (ExprDesc* E) case TOK_EAX: /* Register pseudo variable */ + SetSQPFlags (SQP_KEEP_EAX); E->Type = type_ulong; E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL; NextToken (); diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 5644fb82d..8bf7c00b5 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -23,11 +23,6 @@ -#define SQP_KEEP_NONE 0x00 -#define SQP_KEEP_TEST 0x01U -#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 */ diff --git a/src/cc65/goto.c b/src/cc65/goto.c index 7d3ff1a6a..44ae0595e 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -43,6 +43,7 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "seqpoint.h" #include "standard.h" #include "symtab.h" #include "goto.h" diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index f63e9e95c..f576c0255 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -58,6 +58,7 @@ #include "litpool.h" #include "pragma.h" #include "scanner.h" +#include "seqpoint.h" #include "shift.h" #include "standard.h" #include "symtab.h" diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 68ac00e62..7bc5697ff 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -49,6 +49,7 @@ #include "initdata.h" #include "loadexpr.h" #include "locals.h" +#include "seqpoint.h" #include "stackptr.h" #include "standard.h" #include "staticassert.h" diff --git a/src/cc65/seqpoint.c b/src/cc65/seqpoint.c new file mode 100644 index 000000000..a6d992113 --- /dev/null +++ b/src/cc65/seqpoint.c @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* seqpoint.h */ +/* */ +/* Stuff involved in sequence points */ +/* */ +/* */ +/* */ +/* Copyright 2022 The cc65 Authors */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "seqpoint.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Remeber if __A__, __AX__ and __EAX__ are being used */ +unsigned PendingSqpFlags = SQP_KEEP_NONE; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void SetSQPFlags (unsigned Flags) +/* Set the SQP_KEEP_* flags for the deferred operations in the statement */ +{ + PendingSqpFlags = Flags; +} + + + +unsigned GetSQPFlags (void) +/* Get the SQP_KEEP_* flags for the deferred operations in the statement */ +{ + return PendingSqpFlags; +} diff --git a/src/cc65/seqpoint.h b/src/cc65/seqpoint.h new file mode 100644 index 000000000..07ff0d315 --- /dev/null +++ b/src/cc65/seqpoint.h @@ -0,0 +1,70 @@ +/*****************************************************************************/ +/* */ +/* seqpoint.h */ +/* */ +/* Stuff involved in sequence points */ +/* */ +/* */ +/* */ +/* Copyright 2022 The cc65 Authors */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SEQPOINT_H +#define SEQPOINT_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +#define SQP_KEEP_NONE 0x00U +#define SQP_KEEP_A 0x01U +#define SQP_KEEP_AX 0x03U +#define SQP_KEEP_EAX 0x07U +#define SQP_MASK_EAX 0x07U +#define SQP_KEEP_TEST 0x10U +#define SQP_KEEP_EXPR 0x17U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void SetSQPFlags (unsigned Flags); +/* Set the SQP_KEEP_* flags for the deferred operations in the statement */ + +unsigned GetSQPFlags (void); +/* Get the SQP_KEEP_* flags for the deferred operations in the statement */ + + + +/* End of seqpoint.h */ + +#endif diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index d22c73dcf..e968aaf1f 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -50,6 +50,7 @@ #include "litpool.h" #include "loadexpr.h" #include "scanner.h" +#include "seqpoint.h" #include "stackptr.h" #include "stdfunc.h" #include "stdnames.h" diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 7355e88a8..613129e1b 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -56,6 +56,7 @@ #include "loop.h" #include "pragma.h" #include "scanner.h" +#include "seqpoint.h" #include "stackptr.h" #include "stmt.h" #include "swstmt.h" @@ -674,6 +675,8 @@ int AnyStatement (int* PendingToken) ** NULL, the function will skip the token. */ { + int GotBreak = 0; + /* Assume no pending token */ if (PendingToken) { *PendingToken = 0; @@ -689,7 +692,8 @@ int AnyStatement (int* PendingToken) switch (CurTok.Tok) { case TOK_IF: - return IfStatement (); + GotBreak = IfStatement (); + break; case TOK_SWITCH: SwitchStatement (); @@ -710,22 +714,26 @@ int AnyStatement (int* PendingToken) case TOK_GOTO: GotoStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_RETURN: ReturnStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_BREAK: BreakStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_CONTINUE: ContinueStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_PRAGMA: DoPragma (); @@ -737,12 +745,17 @@ int AnyStatement (int* PendingToken) break; case TOK_LCURLY: - return CompoundStatement (PendingToken); + GotBreak = CompoundStatement (PendingToken); + break; default: /* Simple statement */ Statement (PendingToken); break; } - return 0; + + /* Reset SQP flags */ + SetSQPFlags (SQP_KEEP_NONE); + + return GotBreak; } diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index bad8b95f1..14160f13f 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -39,6 +39,7 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "seqpoint.h" #include "testexpr.h" diff --git a/test/todo/inline-asm-1489.c b/test/val/inline-asm-1489.c similarity index 83% rename from test/todo/inline-asm-1489.c rename to test/val/inline-asm-1489.c index 57699ab0a..a531c7405 100644 --- a/test/todo/inline-asm-1489.c +++ b/test/val/inline-asm-1489.c @@ -1,9 +1,11 @@ +#include #include #include #include #include +#ifdef __CC65__ #define testasm1(C) (__AX__ = (C), \ asm("and #$3f"),\ __AX__) @@ -11,6 +13,22 @@ #define testasm2(C) (__A__ = (C), \ asm("and #$3f"),\ __A__) +#else +/* Non-cc65 compiler. Just make the code compile and work. */ +uint16_t testasm1(uint16_t C) +{ + uint16_t AX = C; + AX &= 0x3f; + return AX; +} + +uint8_t testasm2(uint8_t C) +{ + uint8_t A = C; + A &= 0x3f; + return A; +} +#endif uint8_t src[32] = { 0x10, 0x41, 0x62, 0x83, 0xb4, 0xf5, 0xe6, 0xc7, 0, 0 }; uint8_t src2[32] = { 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0, 0 }; @@ -46,7 +64,7 @@ void dotest1b(uint8_t *s, uint8_t *d) void dotest2a (void) { char *p = &src2[0]; - uint16_t scaddr=&dest[0]; //output to line 11 on the screen + uintptr_t scaddr=&dest[0]; //output to line 11 on the screen printf("dotest2a\n"); while (*p != 0) { @@ -58,7 +76,7 @@ void dotest2a (void) void dotest2b (void) { char *p = &src2[0]; - uint16_t scaddr=&dest[0]; //output to line 11 on the screen + uintptr_t scaddr=&dest[0]; //output to line 11 on the screen printf("dotest2b\n"); while (*p != 0) { From 799aec23a64c9f11fa2591f5e8dc7a93ee298430 Mon Sep 17 00:00:00 2001 From: Dave Plummer Date: Sat, 4 Jun 2022 13:00:48 -0700 Subject: [PATCH 004/520] Add KIM-1 Support --- asminc/kim1.inc | 28 +++++++++++++++++ cfg/kim1.cfg | 41 ++++++++++++++++++++++++ include/kim1.h | 67 ++++++++++++++++++++++++++++++++++++++++ libsrc/Makefile | 1 + libsrc/kim1/crt0.s | 47 ++++++++++++++++++++++++++++ libsrc/kim1/ctype.s | 5 +++ libsrc/kim1/read.s | 51 ++++++++++++++++++++++++++++++ libsrc/kim1/tapeio.s | 39 +++++++++++++++++++++++ libsrc/kim1/write.s | 49 +++++++++++++++++++++++++++++ samples/Makefile | 6 +++- samples/kim1/Makefile | 56 +++++++++++++++++++++++++++++++++ samples/kim1/kimHello | Bin 0 -> 2789 bytes samples/kim1/kimHello.c | 24 ++++++++++++++ src/ca65/main.c | 4 +++ src/cc65/main.c | 4 +++ src/common/target.c | 2 ++ src/common/target.h | 1 + targettest/Makefile | 1 + 18 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 asminc/kim1.inc create mode 100644 cfg/kim1.cfg create mode 100644 include/kim1.h create mode 100644 libsrc/kim1/crt0.s create mode 100644 libsrc/kim1/ctype.s create mode 100644 libsrc/kim1/read.s create mode 100644 libsrc/kim1/tapeio.s create mode 100644 libsrc/kim1/write.s create mode 100644 samples/kim1/Makefile create mode 100644 samples/kim1/kimHello create mode 100644 samples/kim1/kimHello.c diff --git a/asminc/kim1.inc b/asminc/kim1.inc new file mode 100644 index 000000000..f0d1555a7 --- /dev/null +++ b/asminc/kim1.inc @@ -0,0 +1,28 @@ +; --------------------------------------------------------------------------- +; +; KIM-1 definitions +; +; --------------------------------------------------------------------------- + + +RAMSTART := $0200 ; Entry point + +; --------------------------------------------------------------------------- +; Monitor Functions +; --------------------------------------------------------------------------- + + +OUTCHR := $1EA0 ; Output character +INTCHR := $1E5A ; Input character without case conversion +DUMPT := $1800 ; Dump memory to tape +LOADT := $1873 ; Load memory from tape +SAL := $17F5 ; Tape load address low +SAH := $17F6 ; Tape load address high +EAL := $17F7 ; Tape address end low +EAH := $17F8 ; Tape address end high +ID := $17F9 ; Tape Identification number + +; --------------------------------------------------------------------------- +; System Memory +; --------------------------------------------------------------------------- + diff --git a/cfg/kim1.cfg b/cfg/kim1.cfg new file mode 100644 index 000000000..69636065e --- /dev/null +++ b/cfg/kim1.cfg @@ -0,0 +1,41 @@ +# kim1.cfg (4k) +# +# for unexpanded Kim-1 +# +# ld65 --config kim1.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $0200; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $1000 - %S - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/include/kim1.h b/include/kim1.h new file mode 100644 index 000000000..52e690c9d --- /dev/null +++ b/include/kim1.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* kim1.h */ +/* */ +/* Kim-1 system-specific definitions */ +/* */ +/* */ +/* */ +/* (C) 2022 Dave Plummer */ +/* Email: davepl@davepl.com */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + +#ifndef _KIM1_H +#define _KIM1_H + +/* Check for errors */ +#if !defined(__KIM1__) +# error This module may only be used when compiling for the Kim-1! +#endif + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Hardware */ +/*****************************************************************************/ + + +// Todo (davepl) +// +// #include <_6530.h> +// #define RIOT3 (*(struct __6530*)0x1700) // U25 +// #define RIOT2 (*(struct __6530*)0x1740) // U28 + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + +/* Read from tape */ +int __fastcall__ loadt (unsigned char); + +/* Write to tape */ +int __fastcall__ dumpt (unsigned char, const void*, const void*); + +/* End of sym1.h */ +#endif diff --git a/libsrc/Makefile b/libsrc/Makefile index 2018de801..627897d9b 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -27,6 +27,7 @@ TARGETS = apple2 \ $(CBMS) \ $(GEOS) \ gamate \ + kim1 \ lynx \ nes \ none \ diff --git a/libsrc/kim1/crt0.s b/libsrc/kim1/crt0.s new file mode 100644 index 000000000..7e7dd485b --- /dev/null +++ b/libsrc/kim1/crt0.s @@ -0,0 +1,47 @@ +; +; Startup code for cc65 (kim-1 version) +; + + .export _init, _exit + .export __STARTUP__ : absolute = 1 ; Mark as startup + + .import _main + .import initlib, donelib, copydata, zerobss + .import __RAM_START__, __RAM_SIZE__ ; Linker generated + .import __STACKSIZE__ ; Linker generated + + .include "zeropage.inc" + .include "kim1.inc" + + +; Place the startup code in a special segment + +.segment "STARTUP" + + +; A little light housekeeping + +_init: cld ; Clear decimal mode + +; Set cc65 argument stack pointer + + lda #<(__RAM_START__ + __RAM_SIZE__) + sta sp + lda #>(__RAM_START__ + __RAM_SIZE__) + sta sp+1 + +; Initialize memory storage + + jsr zerobss ; Clear BSS segment + jsr copydata ; Initialize DATA segment + jsr initlib ; Run constructors + +; Call main() + + jsr _main + +; Back from main (this is also the _exit entry). There may be a more elegant way to9 +; return to the monitor on the Kim-1, but I don't know it! + +_exit: brk + diff --git a/libsrc/kim1/ctype.s b/libsrc/kim1/ctype.s new file mode 100644 index 000000000..1301965eb --- /dev/null +++ b/libsrc/kim1/ctype.s @@ -0,0 +1,5 @@ +; Character specification table. +; +; uses the "common" definition + + .include "ctype_common.inc" diff --git a/libsrc/kim1/read.s b/libsrc/kim1/read.s new file mode 100644 index 000000000..5566a9f27 --- /dev/null +++ b/libsrc/kim1/read.s @@ -0,0 +1,51 @@ +; +; int __fastcall__ read (int fd, void* buf, unsigned count); +; + +.include "kim1.inc" + +.import popax, popptr1 +.importzp ptr1, ptr2, ptr3 + +.export _read + +.proc _read + + sta ptr3 + stx ptr3+1 ; Count in ptr3 + inx + stx ptr2+1 ; Increment and store in ptr2 + tax + inx + stx ptr2 + jsr popptr1 ; Buffer address in ptr1 + jsr popax + +begin: dec ptr2 + bne getch + dec ptr2+1 + beq done ; If buffer full, return + +getch: jsr INTCHR ; Get character using Monitor ROM call + ;jsr OUTCHR ; Echo it + and #$7F ; Clear top bit + cmp #$07 ; Check for '\a' + bne chkcr ; ...if BEL character + ;jsr BEEP ; Make beep sound TODO +chkcr: cmp #$0D ; Check for '\r' + bne putch ; ...if CR character + lda #$0A ; Replace with '\n' + jsr OUTCHR ; and echo it + +putch: ldy #$00 ; Put char into return buffer + sta (ptr1),y + inc ptr1 ; Increment pointer + bne begin + inc ptr1+1 + bne begin + +done: lda ptr3 + ldx ptr3+1 + rts ; Return count + +.endproc diff --git a/libsrc/kim1/tapeio.s b/libsrc/kim1/tapeio.s new file mode 100644 index 000000000..4a16d6b9c --- /dev/null +++ b/libsrc/kim1/tapeio.s @@ -0,0 +1,39 @@ +; +; int __fastcall__ loadt (unsigned char id); +; int __fastcall__ dumpt (unsigned char id, void* start_addr, void* end_addr); +; + +.include "kim1.inc" + +.import popa, popax, return0, return1 + +.export _loadt, _dumpt + +.segment "CODE" + +.proc _loadt: near + + sta ID ; Tape record ID to P1L + jsr LOADT ; Read data from tape + bcs error + jmp return0 ; Return 0 if sucessful +error: jmp return1 ; or 1 if not + +.endproc + +.proc _dumpt: near + + sta EAL ; End address + stx EAH + jsr popax + sta SAL ; Start address + stx SAH + jsr popa + sta ID ; Tape Record ID + ldx #$00 + jsr DUMPT ; Write data to tape + bcs error + jmp return0 ; Return 0 if sucessful +error: jmp return1 ; or 1 if not + +.endproc diff --git a/libsrc/kim1/write.s b/libsrc/kim1/write.s new file mode 100644 index 000000000..216f5031c --- /dev/null +++ b/libsrc/kim1/write.s @@ -0,0 +1,49 @@ +; +; int __fastcall__ write (int fd, const void* buf, int count); +; + +.include "kim1.inc" + +.import popax, popptr1 +.importzp ptr1, ptr2, ptr3 + +.export _write + +.proc _write + + sta ptr3 + stx ptr3+1 ; Count in ptr3 + inx + stx ptr2+1 ; Increment and store in ptr2 + tax + inx + stx ptr2 + jsr popptr1 ; Buffer address in ptr1 + jsr popax + +begin: dec ptr2 + bne outch + dec ptr2+1 + beq done + +outch: ldy #0 + lda (ptr1),y + jsr OUTCHR ; Send character using Monitor call + cmp #$07 ; Check for '\a' + bne chklf ; ...if BEL character +;jsr BEEP ; Make beep sound +chklf: cmp #$0A ; Check for 'n' + bne next ; ...if LF character + lda #$0D ; Add a carriage return + jsr OUTCHR + +next: inc ptr1 + bne begin + inc ptr1+1 + jmp begin + +done: lda ptr3 + ldx ptr3+1 + rts ; Return count + +.endproc diff --git a/samples/Makefile b/samples/Makefile index 4007e3522..cec4bed89 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -154,7 +154,7 @@ endif # Lists of subdirectories # disasm depends on cpp -DIRLIST = tutorial geos atari2600 atari5200 apple2 gamate lynx supervision sym1 cbm +DIRLIST = tutorial geos atari2600 atari5200 apple2 gamate lynx supervision sym1 kim1 cbm # -------------------------------------------------------------------------- # Lists of executables @@ -329,6 +329,9 @@ EXELIST_supervision = \ EXELIST_sym1 = \ notavailable +EXELIST_kim1 = \ + notavailable + EXELIST_telestrat = \ ascii \ checkversion \ @@ -395,6 +398,7 @@ TARGETS := \ creativision \ cx16 \ gamate \ + kim1 \ lunix \ lynx \ nes \ diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile new file mode 100644 index 000000000..89600ad5a --- /dev/null +++ b/samples/kim1/Makefile @@ -0,0 +1,56 @@ + +# Run 'make SYS='; or, set a SYS env. +# var. to build for another target system. +SYS ?= kim1 + +# 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 +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) +endif + +EXELIST_kim1 = \ + kimHello.bin + +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: kim1 tests not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +kimHello.bin: kimHello.c + $(CL) -t kim1 -O -o kimHello.bin kimHello.c + +clean: + @$(DEL) kimHello.bin 2>$(NULLDEV) diff --git a/samples/kim1/kimHello b/samples/kim1/kimHello new file mode 100644 index 0000000000000000000000000000000000000000..5842567557dc9f321409d269970f08832b736d86 GIT binary patch literal 2789 zcmbVOTWl2989pPwHj|K(i1^Ur@f{@6caBR^#e^evV4CFt9Pc8bVHbVBy0T$R2rJ0ktL?4Nib zo8J=YZ<8)^P!)bIYN9})f5+@!{>KkCxqxB*K%{?2S`KParRkDPJQrCHdD} z0@!q1Hct~T-8U=R>2Yzg!)n|iY!z*Y=9&Z-6I^_pQ(G)ZbVziBX1ZQ%rU7*b56R8W zzS-W9rvhdCYxE5<>*1Q5z-yyl4%@htlkG^!b8X~$(5S~j+uJQr9m%C)In)>Rzec2U&e!Iy&9tvVtJlaZjs>yGJKl==tlO% zXy-%Q-io#@kV>@SXK-OY>rI55LlFu+v5UA_}tI20RRB<@m$XfCNhAl?vz z*O?A!QC=cGD&HgK$#}2_4)wDQE^2_eqDvcKzUV$#xl8D8#8IhNJ$09aiQy$4dPkw( zl=z_otb=D4E^yChsu-eC#o(0SAX^8=PTeG7SGHipjkp+>v<0Hw1`NNKE{b^vuV5Tv znu{uT$PBp0Kg`wzgN$(7hz7RuOMb#sIwHcIJEJ=fgQ2gno@@ial?cm!4FLHT1GeOY z!%SYuxB&&ugE=eeD@s|{Dk-h8Y)Hr00-4jb#%*%)dP*BlRCH}PrS&!D$#q?;H*OI= z3-PV*Y?p7563}_lm?KQ0-4gw(1Y*cVz%Lsb_;HnHO=UC3a>z!Ia6;a4+Lw-ui%@Ig zkOUrtWm_oNnsLM9_pKo!Oem-DMU)*zQWrOa$|I>LVuZn7Le%? z!UWJnm~h)AlO{pNp$>ErstQ_C2IlG~GW5Lci}Xqo&O<{IsGxu;$pD32=9iL}+H7^u zX5Ac5j#V=pCz8c#<{VdCbOL2z2H8-l$nMUYA0*3*P;ACapa#1FSvZb6* z&s&+D?S3H(nb42wp2(RkdQ!xtyanM{j&+s}CmhL>cq0*kl&UCStRl-ccnebss!a+S zJt%Vx8nZ>ej%(7W@t(q-^TfCu$86Q5BTRGDiQ8&bDH==WUvbG;?Qd{URcR?;zsmbC z4U@d`%(pQB_~2~nBi_H)ifg6yXN{5Aw9&#vHg|I3iVDpgfT0N4Cl1P2l~9^uOCG6d z7E8FOWUmX%~9jU$gh?>da zx+muh+bHcd_U(naBAfN}TFNrF$OU;6*YpA&(j9C*FWWjl@4yFytL@Z|2d7hZsI>$1-l@(MIr=3XX6;Y7of>)5}T24^F|l z#REB0Q0p@VWjUUc3X4su;S==xk5T8bo?iq>h60VneFaoFhFh3@+LGBvk?&8ZvrTj~ zod>kj9bD`H(c&56f^khH#8d2EOPyi2k6HY?_zEwPFiratJt9;w?x)ao*&IO5cuGx$ zbhY&;LUdG#9%~=jl4I0T;p03v)~}W7XLV1SPK{t&DoyM47A_Up69>}YE)&`trCnTRNqAu{5TWMvyKOpFlOxSER-TGIo*tPY4?Xl|Zg7xp|L(5s-+PK48p!P) z%xzQ@q-1jg0|)7=2j3jn^90wGUHRPyemKbG-lw10wD}v)Zs~jO`ES3lZTpT5&pf?( z%jUjK&u;ERNDzsv7+YU**j^a5Vv)0G$Y^e JbXnmD{{hxi-7)|G literal 0 HcmV?d00001 diff --git a/samples/kim1/kimHello.c b/samples/kim1/kimHello.c new file mode 100644 index 000000000..dcbfb4e67 --- /dev/null +++ b/samples/kim1/kimHello.c @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------- +// Hello World for Kim-1 +// +// Dave Plummer based on Sym-1 sample by Wayne Parham +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include +#include + +int main (void) +{ + char str[100]; + char c = 0x00; + + printf ("\nHello World!\n\n"); + printf ("Type a line and press ENTER, please.\n\n"); + + gets( str ); + + printf ("\n\nThanks: %s\n\n", str); + return 0; +} diff --git a/src/ca65/main.c b/src/ca65/main.c index b1ef3a3db..4146aaf11 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -342,6 +342,10 @@ static void SetSys (const char* Sys) NewSymbol ("__SYM1__", 1); break; + case TGT_KIM1: + NewSymbol ("__KIM1__", 1); + break; + default: AbEnd ("Invalid target name: '%s'", Sys); diff --git a/src/cc65/main.c b/src/cc65/main.c index c08616efa..f800ac43e 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -299,6 +299,10 @@ static void SetSys (const char* Sys) DefineNumericMacro ("__SYM1__", 1); break; + case TGT_KIM1: + DefineNumericMacro ("__KIM1__", 1); + break; + default: AbEnd ("Unknown target system '%s'", Sys); } diff --git a/src/common/target.c b/src/common/target.c index 4a851034a..ad62990bd 100644 --- a/src/common/target.c +++ b/src/common/target.c @@ -163,6 +163,7 @@ static const TargetEntry TargetMap[] = { { "geos", TGT_GEOS_CBM }, { "geos-apple", TGT_GEOS_APPLE }, { "geos-cbm", TGT_GEOS_CBM }, + { "kim1", TGT_KIM1 }, { "lunix", TGT_LUNIX }, { "lynx", TGT_LYNX }, { "module", TGT_MODULE }, @@ -219,6 +220,7 @@ static const TargetProperties PropertyTable[TGT_COUNT] = { { "c65", CPU_4510, BINFMT_BINARY, CTPET }, { "cx16", CPU_65C02, BINFMT_BINARY, CTPET }, { "sym1", CPU_6502, BINFMT_BINARY, CTNone }, + { "kim1", CPU_6502, BINFMT_BINARY, CTNone }, }; /* Target system */ diff --git a/src/common/target.h b/src/common/target.h index 7087048e2..7439fa621 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -87,6 +87,7 @@ typedef enum { TGT_C65, TGT_CX16, TGT_SYM1, + TGT_KIM1, // Added at end so as not to shift existing entries TGT_COUNT /* Number of target systems */ } target_t; diff --git a/targettest/Makefile b/targettest/Makefile index 0450bfd4e..23463ccd9 100644 --- a/targettest/Makefile +++ b/targettest/Makefile @@ -742,6 +742,7 @@ TARGETS := \ creativision \ cx16 \ gamate \ + kim1 \ lunix \ lynx \ nes \ From 8f9777d9e0d34b4be32ff09750980138d61e9e01 Mon Sep 17 00:00:00 2001 From: Dave Plummer Date: Mon, 6 Jun 2022 15:14:01 -0700 Subject: [PATCH 005/520] Fix ushot overflow, capitalization --- cfg/kim1-60k.cfg | 41 +++++++++++++ include/kim1.h | 4 +- libsrc/kim1/crt0.s | 2 +- samples/kim1/Makefile | 8 ++- samples/kim1/kimSieve.c | 125 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 cfg/kim1-60k.cfg create mode 100644 samples/kim1/kimSieve.c diff --git a/cfg/kim1-60k.cfg b/cfg/kim1-60k.cfg new file mode 100644 index 000000000..a6704d9dd --- /dev/null +++ b/cfg/kim1-60k.cfg @@ -0,0 +1,41 @@ +# kim1.cfg (4k) +# +# for unexpanded Kim-1 +# +# ld65 --config kim1.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $2000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $E000 - %S - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/include/kim1.h b/include/kim1.h index 52e690c9d..bb616f7a0 100644 --- a/include/kim1.h +++ b/include/kim1.h @@ -2,7 +2,7 @@ /* */ /* kim1.h */ /* */ -/* Kim-1 system-specific definitions */ +/* KIM-1 system-specific definitions */ /* */ /* */ /* */ @@ -33,7 +33,7 @@ /* Check for errors */ #if !defined(__KIM1__) -# error This module may only be used when compiling for the Kim-1! +# error This module may only be used when compiling for the KIM-1! #endif diff --git a/libsrc/kim1/crt0.s b/libsrc/kim1/crt0.s index 7e7dd485b..aefdc3545 100644 --- a/libsrc/kim1/crt0.s +++ b/libsrc/kim1/crt0.s @@ -1,5 +1,5 @@ ; -; Startup code for cc65 (kim-1 version) +; Startup code for cc65 (KIM-1 version) ; .export _init, _exit diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile index 89600ad5a..74c415fdc 100644 --- a/samples/kim1/Makefile +++ b/samples/kim1/Makefile @@ -32,7 +32,8 @@ else endif EXELIST_kim1 = \ - kimHello.bin + kimHello.bin \ + kimSieve.bin ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) @@ -49,8 +50,13 @@ else @echo > $(NULLDEV) endif +kimSieve.bin: kimSieve.c + $(CL) -t kim1 -C kim1-60k.cfg -O -o kimSieve.bin kimSieve.c + kimHello.bin: kimHello.c $(CL) -t kim1 -O -o kimHello.bin kimHello.c clean: + @$(DEL) kimSieve.bin 2>$(NULLDEV) @$(DEL) kimHello.bin 2>$(NULLDEV) + diff --git a/samples/kim1/kimSieve.c b/samples/kim1/kimSieve.c new file mode 100644 index 000000000..29cd7c7e9 --- /dev/null +++ b/samples/kim1/kimSieve.c @@ -0,0 +1,125 @@ +#include +#include + +typedef unsigned char byte; +typedef unsigned short int ushort; +typedef unsigned long int ulong; + +#define LIMIT 100000L + +// BITARRAY +// +// My bit-access macros pre-divide by two on the presumption that you'll never +// try try access both odd and even bits! + +#define GETBIT(array, bit) (array[bit >> 4] & (1 << ((bit >> 1) & 7))) +#define SETBIT(array, bit) (array[bit >> 4] |= (1 << ((bit >> 1) & 7))) +#define CLRBIT(array, bit) (array[bit >> 4] &= ~(1 << ((bit >> 1) & 7))) + +// RepeatChar +// +// Outputs a given character N times + +void RepeatChar(char c, size_t count) +{ + while (count--) + putc(c, stdout); +} + +// sqrti +// +// Binary search integer square root + +ushort sqrti(ulong num) +{ + long i; + ulong ret = 0; + + for(i = 15; i >= 0; i--) + { + ulong temp = ret | (1L << (ulong)i); + if(temp * temp <= num) + { + ret = temp; + } + } + return ret; +} + +// main() +// +// CC65 main function receives no parameters + +int main(void) +{ + // CC65 cannot mix code and data so we have to declare all variables here in the function prolog + + ulong iNumber; + ushort currentFactor; + ulong numBytesAllocated, rootOfLimit; + byte *array; + ulong countOfPrimes; + + rootOfLimit = sqrti(LIMIT); + puts("\r\n\r\n"); + RepeatChar('*', 70); + puts("\r\n** Prime Number Sieve - Dave Plummer 2022 **"); + RepeatChar('*', 70); + + printf("\r\n\r\nCalculating primes to %ld using a sqrt of %ld...\r\n", LIMIT, rootOfLimit); + + // Calculate how much memory should be allocated + + numBytesAllocated = (LIMIT + 15) / 16; + array = malloc(numBytesAllocated); + if (!array) + { + printf("Unable to allocate %ld bytes for %ld bits\r\n", numBytesAllocated, LIMIT); + return 0; + } + else + { + printf("Allocated %ld bytes for %ld slots\r\n", numBytesAllocated, LIMIT); + + // Preset all the bits to true + + for (iNumber = 0; iNumber < numBytesAllocated; iNumber++) + array[iNumber] = 0xFF; + } + + // Search for next unmarked factor + + currentFactor = 3; + while (currentFactor <= rootOfLimit) + { + ulong num, n; + + for (num = currentFactor; num <= LIMIT; num += 2) + { + if (GETBIT(array, num)) + { + currentFactor = num; + break; + } + } + + for (n = (ulong) currentFactor * currentFactor; n <= LIMIT; n += currentFactor * 2) + CLRBIT(array, n); + + currentFactor += 2; + } + + // Display results + // + // printf("The following primes were found at or below %ld:\r\n2, ", LIMIT); + + countOfPrimes = 1; + for (iNumber = 3; iNumber <= LIMIT; iNumber += 2) + if (GETBIT(array, iNumber)) + countOfPrimes++; + + printf("[END: Count = %ld]\r\n", countOfPrimes); + + free(array); + return 1; +} From 46541237e0d238ad635a53b09f39764c3b4fd173 Mon Sep 17 00:00:00 2001 From: David W Plummer Date: Mon, 6 Jun 2022 15:17:19 -0700 Subject: [PATCH 006/520] Add to contributor list for KIM-1 target --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e917a13e0..f011af83c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ External contributors: * [karrika](https://github.com/karrika): Atari 7800 target * [Stephan Mühlstrasser](https://github.com/smuehlst): osic1p target * [Wayne Parham](https://github.com/WayneParham): Sym-1 target +* [Dave Plummeer](https://github.com/davepl): KIM-1 target *(The above list is incomplete, if you feel left out - please speak up or add yourself in a PR)* From db44d59f7c99e602ff9619893bcaa6c0f319c216 Mon Sep 17 00:00:00 2001 From: David W Plummer Date: Mon, 6 Jun 2022 15:17:39 -0700 Subject: [PATCH 007/520] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f011af83c..287520320 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ External contributors: * [karrika](https://github.com/karrika): Atari 7800 target * [Stephan Mühlstrasser](https://github.com/smuehlst): osic1p target * [Wayne Parham](https://github.com/WayneParham): Sym-1 target -* [Dave Plummeer](https://github.com/davepl): KIM-1 target +* [Dave Plummer](https://github.com/davepl): KIM-1 target *(The above list is incomplete, if you feel left out - please speak up or add yourself in a PR)* From cb8cb876ec741c96b506f03539165b12b5d342e7 Mon Sep 17 00:00:00 2001 From: Rutger van Bergen Date: Mon, 19 Sep 2022 19:56:57 +0200 Subject: [PATCH 008/520] Add documentation, make capitalization uniform --- asminc/kim1.inc | 13 ++-- cfg/kim1-60k.cfg | 6 +- cfg/kim1.cfg | 2 +- doc/index.sgml | 3 + doc/kim1.sgml | 148 ++++++++++++++++++++++++++++++++++++++++ libsrc/kim1/crt0.s | 4 +- samples/kim1/kimHello.c | 2 +- src/common/target.h | 2 +- 8 files changed, 165 insertions(+), 15 deletions(-) create mode 100644 doc/kim1.sgml diff --git a/asminc/kim1.inc b/asminc/kim1.inc index f0d1555a7..b1046b01c 100644 --- a/asminc/kim1.inc +++ b/asminc/kim1.inc @@ -7,22 +7,21 @@ RAMSTART := $0200 ; Entry point + ; --------------------------------------------------------------------------- ; Monitor Functions ; --------------------------------------------------------------------------- - - OUTCHR := $1EA0 ; Output character INTCHR := $1E5A ; Input character without case conversion DUMPT := $1800 ; Dump memory to tape LOADT := $1873 ; Load memory from tape + + +; --------------------------------------------------------------------------- +; System Memory +; --------------------------------------------------------------------------- SAL := $17F5 ; Tape load address low SAH := $17F6 ; Tape load address high EAL := $17F7 ; Tape address end low EAH := $17F8 ; Tape address end high ID := $17F9 ; Tape Identification number - -; --------------------------------------------------------------------------- -; System Memory -; --------------------------------------------------------------------------- - diff --git a/cfg/kim1-60k.cfg b/cfg/kim1-60k.cfg index a6704d9dd..087715560 100644 --- a/cfg/kim1-60k.cfg +++ b/cfg/kim1-60k.cfg @@ -1,8 +1,8 @@ -# kim1.cfg (4k) +# kim1-60k.cfg (4k) # -# for unexpanded Kim-1 +# for expanded KIM-1 # -# ld65 --config kim1.cfg -o .bin .o +# ld65 --config kim1-60k.cfg -o .bin .o FEATURES { STARTADDRESS: default = $2000; diff --git a/cfg/kim1.cfg b/cfg/kim1.cfg index 69636065e..f48fed80e 100644 --- a/cfg/kim1.cfg +++ b/cfg/kim1.cfg @@ -1,6 +1,6 @@ # kim1.cfg (4k) # -# for unexpanded Kim-1 +# for unexpanded KIM-1 # # ld65 --config kim1.cfg -o .bin .o diff --git a/doc/index.sgml b/doc/index.sgml index bb3ad5357..bfce63486 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -154,6 +154,9 @@ Topics specific to the Bit Corporation Gamate Console. + + Topics specific to the MOS Technology KIM-1. + Topics specific to the Atari Lynx Game Console. diff --git a/doc/kim1.sgml b/doc/kim1.sgml new file mode 100644 index 000000000..0c8b54311 --- /dev/null +++ b/doc/kim1.sgml @@ -0,0 +1,148 @@ + + +
+MOS Technology KIM-1 specific information for cc65 +<author><url url="mailto:dave@davepl.com" name="Dave Plummer"> + +<abstract> +An overview over the KIM-1 runtime system as it is implemented for the cc65 C compiler. +</abstract> + +<!-- Table of contents --> +<toc> + +<!-- Begin the document --> + +<sect>Overview<p> + +This file contains an overview of the KIM-1 runtime system as it comes with the cc65 C compiler. +It describes the memory layout, KIM-1 specific header files, available drivers, and any pitfalls +specific to the platform. + +Please note that KIM-1 specific functions are just mentioned here, they are described in detail +in the separate <url url="funcref.html" name="function reference">. Even functions marked as +"platform dependent" may be available on more than one platform. Please see the +function reference for more information. + +<sect>Binary format<p> + +The output format generated by the linker for the KIM-1 target is a raw binary BIN file, which +is essentially a memory image. You can convert this to a papertape format file using +Convert8bithexformat or KIMPaper, which are open-source conversion utility programs. +A papertape format files can be transferred to the KIM-1 using the RS-232 terminal port (TTY), +just as if the machine-code was entered by hand. Enter 'L' in the TTY and start the paper tape file +transfer. + +<p> + +Included with this distribution is a 4k configuration file and a 60k config file. The KIM-1 +on-board memory is limited to 4 kbytes but system memory can be increased to 60 kbytes of +contiguous RAM with aftermarket add-on boards. So choose the config file that matches your +system configuration before compiling and linking user programs. + +<sect>Memory layout<p> + +The ROMs and I/O areas are defined in the configuration files, as are most of the entry points +for useful subroutines in the KIM-1 monitor ROM. cc65 generated programs compiled and linked +using 4k config run in the memory range of $200 - $0FFF. The 60k config expands +this range to $DFFF. The starting memory location and entry point for running the program is +$200, so when the program is transferred to the KIM-1, it is executed by typing '200 G'. + +Special locations: + +<descrip> + <tag/Text screen/ + Conio support is not currently available for the KIM-1. But stdio console functions are available. + + <tag/Stack/ + The C runtime stack is located at $0FFF on 4kb KIM-1s, or at $DFFF for 60kb systems. + The stack always grows downwards. + + <tag/Heap/ + The C heap is located at the end of the program and grows towards the C runtime stack. + +</descrip><p> + +<sect>Platform specific header files<p> + +Programs containing KIM-1 code may use the <tt/kim.h/ header file. See the header file for more information. + +<sect>Loadable drivers<p> + +<sect1>Graphics drivers<p> + +No graphics drivers are currently available for the KIM-1. + +<sect1>Joystick drivers<p> + +No joystick driver is currently available for the KIM-1. + +<sect1>Mouse drivers<p> + +No mouse drivers are currently available for the KIM-1. + +<sect1>RS232 device drivers<p> + +No communication port drivers are currently available for the KIM-1. It has only the "master console" +e.g. stdin and stdout. + +<sect>Limitations<p> + +<sect1>Disk I/O<p> + +The existing library for the KIM-1 doesn't implement C file I/O. + +To be more specific, this limitation means that you cannot use any of the following functions (and a few others): + +<itemize> +<item>fopen +<item>fclose +<item>fread +<item>fwrite +<item>... +</itemize> + +<sect>Other hints<p> + +<sect1>kim1.h<p> +This header exposes KIM-1 specific I/O functions that are useful for reading and writing its ports and front panel. +See the <tt/kim1.h/ include file for a list of the functions available. + +<sect1>Limited memory applications<p> + +As stated earlier, there are config files for 4kb and 60kb systems. If you have 60kb RAM, then you will probably +want to use the kim1-60k configuration, but if not - if you are using the kim1-4k configuration - then you may +want to use functions like getchar, putchar, gets and puts rather than functions like scanf and printf. +Printf, for example, requires about 1KB because it needs to know how to process all the format specifiers. + +<sect1>Sample programs<p> + +These sample programs can be found in the samples/kim1 directory: + +<itemize> +<item>kimHello prints "Hello World!" and then inputs characters, which are echoed on the screen. + This program will run on both 4kb and 60kb systems.</item> +<item>kimSieve finds the prime numbers up to 100,000 using the Sieve of Eratosthenes algorithm, and prints how many + prime numbers were found. This program requires a 60kb system to run.</item> +</itemize> + +<sect>License<p> + +This software is provided 'as-is', without any expressed or implied warranty. In no event will the authors be held +liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter +it and redistribute it freely, subject to the following restrictions: + +<enum> +<item> The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +<item> Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +<item> This notice may not be removed or altered from any source + distribution. +</enum> + +</article> diff --git a/libsrc/kim1/crt0.s b/libsrc/kim1/crt0.s index aefdc3545..f1fee86c1 100644 --- a/libsrc/kim1/crt0.s +++ b/libsrc/kim1/crt0.s @@ -40,8 +40,8 @@ _init: cld ; Clear decimal mode jsr _main -; Back from main (this is also the _exit entry). There may be a more elegant way to9 -; return to the monitor on the Kim-1, but I don't know it! +; Back from main (this is also the _exit entry). There may be a more elegant way to +; return to the monitor on the KIM-1, but I don't know it! _exit: brk diff --git a/samples/kim1/kimHello.c b/samples/kim1/kimHello.c index dcbfb4e67..0dca1345f 100644 --- a/samples/kim1/kimHello.c +++ b/samples/kim1/kimHello.c @@ -1,5 +1,5 @@ // -------------------------------------------------------------------------- -// Hello World for Kim-1 +// Hello World for KIM-1 // // Dave Plummer based on Sym-1 sample by Wayne Parham // diff --git a/src/common/target.h b/src/common/target.h index 7439fa621..0cec74b6e 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -87,7 +87,7 @@ typedef enum { TGT_C65, TGT_CX16, TGT_SYM1, - TGT_KIM1, // Added at end so as not to shift existing entries + TGT_KIM1, TGT_COUNT /* Number of target systems */ } target_t; From ab6840712b88e8c0249563d350c0b63d901a4a54 Mon Sep 17 00:00:00 2001 From: David W Plummer <davepl@davepl.com> Date: Mon, 19 Sep 2022 14:17:41 -0700 Subject: [PATCH 009/520] Update kim1.sgml --- doc/kim1.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/kim1.sgml b/doc/kim1.sgml index 0c8b54311..6db9fc908 100644 --- a/doc/kim1.sgml +++ b/doc/kim1.sgml @@ -2,10 +2,10 @@ <article> <title>MOS Technology KIM-1 specific information for cc65 -<author><url url="mailto:dave@davepl.com" name="Dave Plummer"> +<author><url url="mailto:davepl@davepl.com" name="Dave Plummer"> <abstract> -An overview over the KIM-1 runtime system as it is implemented for the cc65 C compiler. +An overview of the KIM-1 runtime system as it is implemented for the cc65 C compiler. </abstract> <!-- Table of contents --> From 3b431d9fa3ca4110c23623887338685117b2bec6 Mon Sep 17 00:00:00 2001 From: Rutger van Bergen <rbergen@xs4all.nl> Date: Mon, 19 Sep 2022 23:57:26 +0200 Subject: [PATCH 010/520] Remove trailing whitespace --- asminc/kim1.inc | 2 +- doc/kim1.sgml | 12 ++++++------ samples/kim1/kimHello.c | 4 ++-- samples/kim1/kimSieve.c | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/asminc/kim1.inc b/asminc/kim1.inc index b1046b01c..68f059490 100644 --- a/asminc/kim1.inc +++ b/asminc/kim1.inc @@ -1,6 +1,6 @@ ; --------------------------------------------------------------------------- ; -; KIM-1 definitions +; KIM-1 definitions ; ; --------------------------------------------------------------------------- diff --git a/doc/kim1.sgml b/doc/kim1.sgml index 6db9fc908..c3494b9b9 100644 --- a/doc/kim1.sgml +++ b/doc/kim1.sgml @@ -27,10 +27,10 @@ function reference for more information. <sect>Binary format<p> The output format generated by the linker for the KIM-1 target is a raw binary BIN file, which -is essentially a memory image. You can convert this to a papertape format file using -Convert8bithexformat or KIMPaper, which are open-source conversion utility programs. -A papertape format files can be transferred to the KIM-1 using the RS-232 terminal port (TTY), -just as if the machine-code was entered by hand. Enter 'L' in the TTY and start the paper tape file +is essentially a memory image. You can convert this to a papertape format file using +Convert8bithexformat or KIMPaper, which are open-source conversion utility programs. +A papertape format files can be transferred to the KIM-1 using the RS-232 terminal port (TTY), +just as if the machine-code was entered by hand. Enter 'L' in the TTY and start the paper tape file transfer. <p> @@ -45,8 +45,8 @@ system configuration before compiling and linking user programs. The ROMs and I/O areas are defined in the configuration files, as are most of the entry points for useful subroutines in the KIM-1 monitor ROM. cc65 generated programs compiled and linked using 4k config run in the memory range of $200 - $0FFF. The 60k config expands -this range to $DFFF. The starting memory location and entry point for running the program is -$200, so when the program is transferred to the KIM-1, it is executed by typing '200 G'. +this range to $DFFF. The starting memory location and entry point for running the program is +$200, so when the program is transferred to the KIM-1, it is executed by typing '200 G'. Special locations: diff --git a/samples/kim1/kimHello.c b/samples/kim1/kimHello.c index 0dca1345f..9e5ca8ab6 100644 --- a/samples/kim1/kimHello.c +++ b/samples/kim1/kimHello.c @@ -9,14 +9,14 @@ #include <stdio.h> #include <kim1.h> -int main (void) +int main (void) { char str[100]; char c = 0x00; printf ("\nHello World!\n\n"); printf ("Type a line and press ENTER, please.\n\n"); - + gets( str ); printf ("\n\nThanks: %s\n\n", str); diff --git a/samples/kim1/kimSieve.c b/samples/kim1/kimSieve.c index 29cd7c7e9..d13f776ea 100644 --- a/samples/kim1/kimSieve.c +++ b/samples/kim1/kimSieve.c @@ -8,11 +8,11 @@ typedef unsigned long int ulong; #define LIMIT 100000L // BITARRAY -// +// // My bit-access macros pre-divide by two on the presumption that you'll never // try try access both odd and even bits! -#define GETBIT(array, bit) (array[bit >> 4] & (1 << ((bit >> 1) & 7))) +#define GETBIT(array, bit) (array[bit >> 4] & (1 << ((bit >> 1) & 7))) #define SETBIT(array, bit) (array[bit >> 4] |= (1 << ((bit >> 1) & 7))) #define CLRBIT(array, bit) (array[bit >> 4] &= ~(1 << ((bit >> 1) & 7))) @@ -65,7 +65,7 @@ int main(void) RepeatChar('*', 70); puts("\r\n** Prime Number Sieve - Dave Plummer 2022 **"); RepeatChar('*', 70); - + printf("\r\n\r\nCalculating primes to %ld using a sqrt of %ld...\r\n", LIMIT, rootOfLimit); // Calculate how much memory should be allocated @@ -80,7 +80,7 @@ int main(void) else { printf("Allocated %ld bytes for %ld slots\r\n", numBytesAllocated, LIMIT); - + // Preset all the bits to true for (iNumber = 0; iNumber < numBytesAllocated; iNumber++) @@ -102,7 +102,7 @@ int main(void) break; } } - + for (n = (ulong) currentFactor * currentFactor; n <= LIMIT; n += currentFactor * 2) CLRBIT(array, n); From d1e84a6dae2b867bbf20622171868d0665d4d886 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Tue, 11 Oct 2022 14:23:21 +0800 Subject: [PATCH 011/520] Avoided unnecessary BSS name duplication for tentative global variables. --- src/cc65/compile.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 9d7fbe20a..713e70d2a 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -285,11 +285,14 @@ static void Parse (void) */ const char* bssName = GetSegName (SEG_BSS); - if (Sym->V.BssName && strcmp (Sym->V.BssName, bssName) != 0) { - Error ("Global variable '%s' already was defined in the '%s' segment.", - Sym->Name, Sym->V.BssName); + if (Sym->V.BssName != 0) { + if (strcmp (Sym->V.BssName, bssName) != 0) { + Error ("Global variable '%s' already was defined in the '%s' segment", + Sym->Name, Sym->V.BssName); + } + } else { + Sym->V.BssName = xstrdup (bssName); } - Sym->V.BssName = xstrdup (bssName); /* This is to make the automatical zeropage setting of the symbol ** work right. From a4a1230c62d8920950b3c2ab0f751a47cd1f0840 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 12 Oct 2022 13:10:17 +0800 Subject: [PATCH 012/520] Renamed some C type facility and fixed a few comments. Added some new C type code facility. Removed some unused type predicates. --- src/cc65/assignment.c | 2 +- src/cc65/codeseg.c | 2 +- src/cc65/datatype.c | 231 +++++++++++++++--------------- src/cc65/datatype.h | 324 ++++++++++++++++++++---------------------- src/cc65/declare.c | 16 +-- src/cc65/expr.c | 12 +- src/cc65/function.c | 4 +- src/cc65/initdata.c | 8 +- src/cc65/stdfunc.c | 12 +- src/cc65/swstmt.c | 2 +- src/cc65/symtab.c | 4 +- src/cc65/typecmp.c | 63 ++++---- src/cc65/typeconv.c | 2 +- 13 files changed, 330 insertions(+), 352 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 54ab28d4e..30e2f0fca 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -607,7 +607,7 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Only "=" accept struct/union */ - if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (ltype)) { + if (IsClassStruct (ltype) ? Gen != 0 : !IsScalarType (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. diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index e621147ab..9f1bf4cc5 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -521,7 +521,7 @@ CodeSeg* NewCodeSeg (const char* SegName, SymEntry* Func) /* If we have a function given, get the return type of the function. ** Assume ANY return type besides void will use the A and X registers. */ - if (S->Func && !IsTypeVoid ((RetType = GetFuncReturn (Func->Type)))) { + if (S->Func && !IsTypeVoid ((RetType = GetFuncReturnType (Func->Type)))) { if (SizeOf (RetType) == SizeOf (type_long)) { S->ExitRegs = REG_EAX; } else { diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 023aefaf7..6907ee099 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -150,7 +150,7 @@ void TypeFree (Type* T) int SignExtendChar (int C) -/* Do correct sign extension of a character */ +/* Do correct sign extension of a character to an int */ { if (IS_Get (&SignedChars) && (C & 0x80) != 0) { return C | ~0xFF; @@ -214,7 +214,7 @@ unsigned BitSizeOf (const Type* T) unsigned SizeOf (const Type* T) /* Compute size (in bytes) of object represented by type array */ { - switch (GetUnderlyingTypeCode (T)) { + switch (GetUnqualTypeCode (T)) { case T_VOID: /* A void variable is a cc65 extension. @@ -347,7 +347,7 @@ unsigned CheckedPSizeOf (const Type* T) -static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth) +static unsigned GetMinimalTypeSizeByBitWidth (unsigned BitWidth) /* Return the size of the smallest integer type that may have BitWidth bits */ { /* Since all integer types supported in cc65 for bit-fields have sizes that @@ -366,14 +366,14 @@ static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth) -TypeCode GetUnderlyingTypeCode (const Type* Type) -/* Get the type code of the unqualified underlying type of TCode. -** Return UnqualTypeCode (Type) if Type is not scalar. +TypeCode GetUnqualTypeCode (const Type* Type) +/* Get the type code of the unqualified underlying type of Type. +** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ { - TypeCode Underlying = UnqualifiedType (Type->C); + TypeCode Underlying = GetUnqualRawTypeCode (Type); - if (IsISOChar (Type)) { + if (IsDeclTypeChar (Type)) { return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; @@ -382,7 +382,7 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) /* This should not happen, but just in case */ if (Type->A.S == 0) { - Internal ("Enum tag type error in GetUnderlyingTypeCode"); + Internal ("Enum tag type error in GetUnqualTypeCode"); } /* Inspect the underlying type of the enum */ @@ -390,24 +390,24 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) /* Incomplete enum type is used */ return Underlying; } - TCode = UnqualifiedType (Type->A.S->V.E.Type->C); + TCode = GetUnqualRawTypeCode (Type->A.S->V.E.Type); /* Replace the type code with integer */ - Underlying = (TCode & ~T_MASK_TYPE); + Underlying = (TCode & ~T_MASK_RANK); switch (TCode & T_MASK_SIZE) { - case T_SIZE_INT: Underlying |= T_TYPE_INT; break; - case T_SIZE_LONG: Underlying |= T_TYPE_LONG; break; - case T_SIZE_SHORT: Underlying |= T_TYPE_SHORT; break; - case T_SIZE_CHAR: Underlying |= T_TYPE_CHAR; break; - case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; - default: Underlying |= T_TYPE_INT; break; + case T_SIZE_INT: Underlying |= T_RANK_INT; break; + case T_SIZE_LONG: Underlying |= T_RANK_LONG; break; + case T_SIZE_SHORT: Underlying |= T_RANK_SHORT; break; + case T_SIZE_CHAR: Underlying |= T_RANK_CHAR; break; + case T_SIZE_LONGLONG: Underlying |= T_RANK_LONGLONG; break; + default: Underlying |= T_RANK_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. */ - switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) { + switch (GetMinimalTypeSizeByBitWidth (Type->A.B.Width)) { case SIZEOF_CHAR: Underlying = T_CHAR; break; case SIZEOF_INT: Underlying = T_INT; break; case SIZEOF_LONG: Underlying = T_LONG; break; @@ -494,7 +494,7 @@ Type* NewPointerTo (const Type* T) -Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth) +Type* NewBitFieldOf (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. */ @@ -560,20 +560,6 @@ const Type* Indirect (const Type* T) -Type* IndirectModifiable (Type* T) -/* Do one indirection for the given type, that is, return the type where the -** given type points to. -*/ -{ - /* We are expecting a pointer expression */ - CHECK (IsClassPtr (T)); - - /* Skip the pointer or array token itself */ - return T + 1; -} - - - Type* ArrayToPtr (const Type* T) /* Convert an array to a pointer to it's first element */ { @@ -648,12 +634,12 @@ const Type* IntPromotion (const Type* T) return IsSignUnsigned (T) ? type_ulong : type_long; } return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; - } else if (IsTypeChar (T)) { + } else if (IsRankChar (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)) { + } else if (IsRankShort (T)) { /* An integer cannot represent all values from unsigned short, so convert unsigned short ** to unsigned int. */ @@ -690,8 +676,8 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* 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))) { + if ((IsRankLong (lhst) && IsSignUnsigned (lhst)) || + (IsRankLong (rhst) && IsSignUnsigned (rhst))) { return type_ulong; } @@ -700,74 +686,74 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) ** 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))) { + if ((IsRankLong (lhst) && IsRankInt (rhst) && IsSignUnsigned (rhst)) || + (IsRankLong (rhst) && IsRankInt (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)) { + if (IsRankLong (lhst) || IsRankLong (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))) { + if ((IsRankInt (lhst) && IsSignUnsigned (lhst)) || + (IsRankInt (rhst) && IsSignUnsigned (rhst))) { return type_uint; } /* Otherwise, both operands have type int. */ - CHECK (IsTypeInt (lhst)); + CHECK (IsRankInt (lhst)); CHECK (IsSignSigned (lhst)); - CHECK (IsTypeInt (rhst)); + CHECK (IsRankInt (rhst)); CHECK (IsSignSigned (rhst)); return type_int; } -const Type* SignedType (const Type* T) +const Type* GetSignedType (const Type* T) /* Get signed counterpart of the integral type */ { - switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { - case T_TYPE_CHAR: + switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + case T_RANK_CHAR: return type_schar; - case T_TYPE_INT: - case T_TYPE_SHORT: + case T_RANK_INT: + case T_RANK_SHORT: return type_int; - case T_TYPE_LONG: + case T_RANK_LONG: return type_long; default: - Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); + Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); return T; } } -const Type* UnsignedType (const Type* T) +const Type* GetUnsignedType (const Type* T) /* Get unsigned counterpart of the integral type */ { - switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { - case T_TYPE_CHAR: + switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + case T_RANK_CHAR: return type_uchar; - case T_TYPE_INT: - case T_TYPE_SHORT: + case T_RANK_INT: + case T_RANK_SHORT: return type_uint; - case T_TYPE_LONG: + case T_RANK_LONG: return type_ulong; default: - Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); + Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); return T; } } @@ -777,12 +763,12 @@ const Type* UnsignedType (const Type* T) const Type* GetUnderlyingType (const Type* Type) /* Get the underlying type of an enum or other integer class type */ { - if (IsISOChar (Type)) { + if (IsDeclTypeChar (Type)) { return IS_Get (&SignedChars) ? type_schar : type_uchar; } else if (IsTypeEnum (Type)) { /* This should not happen, but just in case */ if (Type->A.S == 0) { - Internal ("Enum tag type error in GetUnderlyingTypeCode"); + Internal ("Enum tag type error in GetUnderlyingType"); } /* If incomplete enum type is used, just return its raw type */ @@ -794,7 +780,7 @@ const Type* GetUnderlyingType (const Type* Type) ** bit-field, instead of the type used in the declaration, the truly ** underlying of the bit-field. */ - switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) { + switch (GetMinimalTypeSizeByBitWidth (Type->A.B.Width)) { 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; @@ -808,7 +794,7 @@ const Type* GetUnderlyingType (const Type* Type) const Type* GetStructReplacementType (const Type* SType) -/* Get a replacement type for passing a struct/union in the primary register */ +/* Get a replacement type for passing a struct/union by value in the primary */ { const Type* NewType; /* If the size is less than or equal to that of a long, we will copy the @@ -837,7 +823,7 @@ const Type* GetBitFieldChunkType (const Type* Type) return GetUnderlyingType (Type); } - ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width); + ChunkSize = GetMinimalTypeSizeByBitWidth (Type->A.B.Offs + Type->A.B.Width); if (ChunkSize < SizeOf (Type + 1)) { /* The end of the bit-field is offset by some bits so that it requires ** more bytes to be accessed as a whole than its underlying type does. @@ -875,50 +861,50 @@ int IsTypeFragBitField (const Type* T) -int IsClassObject (const Type* T) +int IsObjectType (const Type* T) /* Return true if this is a fully described object type */ { - return !IsTypeFunc (T) && !IsClassIncomplete (T); + return !IsTypeFunc (T) && !IsIncompleteType (T); } -int IsClassIncomplete (const Type* T) +int IsIncompleteType (const Type* T) /* Return true if this is an object type lacking size info */ { if (IsTypeArray (T)) { - return GetElementCount (T) == UNSPECIFIED || IsClassIncomplete (T + 1); + return GetElementCount (T) == UNSPECIFIED || IsIncompleteType (T + 1); } return IsTypeVoid (T) || IsIncompleteESUType (T); } -int IsClassArithmetic (const Type* T) -/* Return true if this is an integer or real floating type */ +int IsArithmeticType (const Type* T) +/* Return true if this is an integer or floating type */ { return IsClassInt (T) || IsClassFloat (T); } -int IsClassBasic (const Type* T) -/* Return true if this is a char, integer or floating type */ +int IsBasicType (const Type* T) +/* Return true if this is a character, integer or floating type */ { - return IsClassChar (T) || IsClassInt (T) || IsClassFloat (T); + return IsDeclRankChar (T) || IsClassInt (T) || IsClassFloat (T); } -int IsClassScalar (const Type* T) +int IsScalarType (const Type* T) /* Return true if this is an arithmetic or pointer type */ { - return IsClassArithmetic (T) || IsTypePtr (T); + return IsArithmeticType (T) || IsTypePtr (T); } -int IsClassDerived (const Type* T) +int IsDerivedType (const Type* T) /* Return true if this is an array, struct, union, function or pointer type */ { return IsTypeArray (T) || IsClassStruct (T) || IsClassFunc (T) || IsTypePtr (T); @@ -926,7 +912,7 @@ int IsClassDerived (const Type* T) -int IsClassAggregate (const Type* T) +int IsAggregateType (const Type* T) /* Return true if this is an array or struct type */ { return IsTypeArray (T) || IsTypeStruct (T); @@ -937,7 +923,7 @@ int IsClassAggregate (const Type* T) int IsRelationType (const Type* T) /* Return true if this is an arithmetic, array or pointer type */ { - return IsClassArithmetic (T) || IsClassPtr (T); + return IsArithmeticType (T) || IsClassPtr (T); } @@ -945,7 +931,7 @@ int IsRelationType (const Type* T) int IsCastType (const Type* T) /* Return true if this type can be used for casting */ { - return IsClassScalar (T) || IsTypeVoid (T); + return IsScalarType (T) || IsTypeVoid (T); } @@ -987,7 +973,7 @@ int HasUnknownSize (const Type* T) -int TypeHasAttr (const Type* T) +int TypeHasAttrData (const Type* T) /* Return true if the given type has attribute data */ { return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); @@ -1044,7 +1030,7 @@ int IsFastcallFunc (const Type* T) ** Check fails if the type is not a function or a pointer to function. */ { - if (UnqualifiedType (T->C) == T_PTR) { + if (GetUnqualRawTypeCode (T) == T_PTR) { /* Pointer to function */ ++T; } @@ -1056,7 +1042,7 @@ int IsFastcallFunc (const Type* T) FuncDesc* GetFuncDesc (const Type* T) /* Get the FuncDesc pointer from a function or pointer-to-function type */ { - if (UnqualifiedType (T->C) == T_PTR) { + if (GetUnqualRawTypeCode (T) == T_PTR) { /* Pointer to function */ ++T; } @@ -1073,7 +1059,7 @@ FuncDesc* GetFuncDesc (const Type* T) void SetFuncDesc (Type* T, FuncDesc* F) /* Set the FuncDesc pointer in a function or pointer-to-function type */ { - if (UnqualifiedType (T->C) == T_PTR) { + if (GetUnqualRawTypeCode (T) == T_PTR) { /* Pointer to function */ ++T; } @@ -1087,10 +1073,10 @@ void SetFuncDesc (Type* T, FuncDesc* F) -const Type* GetFuncReturn (const Type* T) +const Type* GetFuncReturnType (const Type* T) /* Return a pointer to the return type of a function or pointer-to-function type */ { - if (UnqualifiedType (T->C) == T_PTR) { + if (GetUnqualRawTypeCode (T) == T_PTR) { /* Pointer to function */ ++T; } @@ -1104,10 +1090,10 @@ const Type* GetFuncReturn (const Type* T) -Type* GetFuncReturnModifiable (Type* T) +Type* GetFuncReturnTypeModifiable (Type* T) /* Return a non-const pointer to the return type of a function or pointer-to-function type */ { - if (UnqualifiedType (T->C) == T_PTR) { + if (GetUnqualRawTypeCode (T) == T_PTR) { /* Pointer to function */ ++T; } @@ -1164,7 +1150,16 @@ void SetElementCount (Type* T, long Count) const Type* GetElementType (const Type* T) -/* Return the element type of the given array type. */ +/* Return the element type of the given array type */ +{ + CHECK (IsTypeArray (T)); + return T + 1; +} + + + +Type* GetElementTypeModifiable (Type* T) +/* Return the element type of the given array type */ { CHECK (IsTypeArray (T)); return T + 1; @@ -1228,48 +1223,48 @@ const char* GetBasicTypeName (const Type* T) ** Return "type" for unknown basic types. */ { - 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"; - case T_TYPE_STRUCT: return "struct"; - case T_TYPE_UNION: return "union"; - case T_TYPE_ARRAY: return "array"; - case T_TYPE_PTR: return "pointer"; - case T_TYPE_FUNC: return "function"; - case T_TYPE_NONE: /* FALLTHROUGH */ + switch (GetRawTypeRank (T)) { + case T_RANK_ENUM: return "enum"; + case T_RANK_BITFIELD: return "bit-field"; + case T_RANK_FLOAT: return "float"; + case T_RANK_DOUBLE: return "double"; + case T_RANK_VOID: return "void"; + case T_RANK_STRUCT: return "struct"; + case T_RANK_UNION: return "union"; + case T_RANK_ARRAY: return "array"; + case T_RANK_PTR: return "pointer"; + case T_RANK_FUNC: return "function"; + case T_RANK_NONE: /* FALLTHROUGH */ default: break; } if (IsClassInt (T)) { if (IsRawSignSigned (T)) { - switch (GetRawType (T)) { - case T_TYPE_CHAR: return "signed char"; - case T_TYPE_SHORT: return "short"; - case T_TYPE_INT: return "int"; - case T_TYPE_LONG: return "long"; - case T_TYPE_LONGLONG: return "long long"; + switch (GetRawTypeRank (T)) { + case T_RANK_CHAR: return "signed char"; + case T_RANK_SHORT: return "short"; + case T_RANK_INT: return "int"; + case T_RANK_LONG: return "long"; + case T_RANK_LONGLONG: return "long long"; default: return "signed integer"; } } else if (IsRawSignUnsigned (T)) { - switch (GetRawType (T)) { - case T_TYPE_CHAR: return "unsigned char"; - case T_TYPE_SHORT: return "unsigned short"; - case T_TYPE_INT: return "unsigned int"; - case T_TYPE_LONG: return "unsigned long"; - case T_TYPE_LONGLONG: return "unsigned long long"; + switch (GetRawTypeRank (T)) { + case T_RANK_CHAR: return "unsigned char"; + case T_RANK_SHORT: return "unsigned short"; + case T_RANK_INT: return "unsigned int"; + case T_RANK_LONG: return "unsigned long"; + case T_RANK_LONGLONG: return "unsigned long long"; default: return "unsigned integer"; } } else { - switch (GetRawType (T)) { - case T_TYPE_CHAR: return "char"; - case T_TYPE_SHORT: return "short"; - case T_TYPE_INT: return "int"; - case T_TYPE_LONG: return "long"; - case T_TYPE_LONGLONG: return "long long"; + switch (GetRawTypeRank (T)) { + case T_RANK_CHAR: return "char"; + case T_RANK_SHORT: return "short"; + case T_RANK_INT: return "int"; + case T_RANK_LONG: return "long"; + case T_RANK_LONGLONG: return "long long"; default: return "integer"; } @@ -1600,7 +1595,7 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) SB_Done (&ParamList); /* Complete with the return type */ - GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); + GetFullTypeNameWestEast (&West, &East, GetFuncReturnType (T)); SB_Append (&West, &East); SB_Terminate (&West); diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 4a20422fb..eebd3abd8 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -71,24 +71,24 @@ struct SymEntry; enum { T_END = 0x000000, - /* Basic types */ - T_TYPE_NONE = 0x000000, - T_TYPE_CHAR = 0x000001, - T_TYPE_SHORT = 0x000002, - T_TYPE_INT = 0x000003, - T_TYPE_LONG = 0x000004, - T_TYPE_LONGLONG = 0x000005, - 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, + /* Basic type ranks */ + T_RANK_NONE = 0x000000, + T_RANK_CHAR = 0x000001, + T_RANK_SHORT = 0x000002, + T_RANK_INT = 0x000003, + T_RANK_LONG = 0x000004, + T_RANK_LONGLONG = 0x000005, + T_RANK_ENUM = 0x000008, + T_RANK_BITFIELD = 0x000009, + T_RANK_FLOAT = 0x00000A, + T_RANK_DOUBLE = 0x00000B, + T_RANK_VOID = 0x000010, + T_RANK_STRUCT = 0x000011, + T_RANK_UNION = 0x000012, + T_RANK_ARRAY = 0x000018, + T_RANK_PTR = 0x000019, + T_RANK_FUNC = 0x00001A, + T_MASK_RANK = 0x00001F, /* Type classes */ T_CLASS_NONE = 0x000000, @@ -129,28 +129,28 @@ enum { T_MASK_QUAL = 0x7F0000, /* Types */ - T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, - T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, - T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, - T_SHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, - T_USHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_SHORT, - T_INT = T_TYPE_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_INT, - T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_INT, - T_LONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONG, - T_ULONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONG, - 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, - T_STRUCT = T_TYPE_STRUCT | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, - T_UNION = T_TYPE_UNION | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, - T_ARRAY = T_TYPE_ARRAY | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, - T_PTR = T_TYPE_PTR | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, - T_FUNC = T_TYPE_FUNC | T_CLASS_FUNC | T_SIGN_NONE | T_SIZE_NONE, + T_CHAR = T_RANK_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, + T_SCHAR = T_RANK_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, + T_UCHAR = T_RANK_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, + T_SHORT = T_RANK_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, + T_USHORT = T_RANK_SHORT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_SHORT, + T_INT = T_RANK_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_INT, + T_UINT = T_RANK_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_INT, + T_LONG = T_RANK_LONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONG, + T_ULONG = T_RANK_LONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONG, + T_LONGLONG = T_RANK_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, + T_ULONGLONG = T_RANK_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, + T_ENUM = T_RANK_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, + T_SBITFIELD = T_RANK_BITFIELD | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_UBITFIELD = T_RANK_BITFIELD | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + T_FLOAT = T_RANK_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, + T_DOUBLE = T_RANK_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, + T_VOID = T_RANK_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, + T_STRUCT = T_RANK_STRUCT | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, + T_UNION = T_RANK_UNION | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, + T_ARRAY = T_RANK_ARRAY | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, + T_PTR = T_RANK_PTR | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, + T_FUNC = T_RANK_FUNC | T_CLASS_FUNC | T_SIGN_NONE | T_SIZE_NONE, /* More types for convenience */ T_C_CHAR = T_CHAR | T_QUAL_CONST, @@ -316,7 +316,9 @@ unsigned CheckedPSizeOf (const Type* T); #if defined(HAVE_INLINE) INLINE TypeCode GetQualifier (const Type* T) -/* Get the qualifier from the given type string */ +/* Get the qualifier from the given type. This doesn't have a "raw" version +** since an underlying type can never be qualified. +*/ { return (T->C & T_MASK_QUAL); } @@ -324,66 +326,78 @@ INLINE TypeCode GetQualifier (const Type* T) # define GetQualifier(T) ((T)->C & T_MASK_QUAL) #endif -TypeCode GetUnderlyingTypeCode (const Type* Type); -/* Get the type code of the unqualified underlying type of TCode. -** Return TCode if it is not scalar. +TypeCode GetUnqualTypeCode (const Type* Type); +/* Get the type code of the unqualified underlying type of Type. +** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ #if defined(HAVE_INLINE) -INLINE TypeCode UnqualifiedType (TypeCode T) -/* Return the unqualified type code */ +INLINE TypeCode GetUnqualRawTypeCode (const Type* T) +/* Return the unqualified raw type code */ { - return (T & ~T_MASK_QUAL); + return (T->C & ~T_MASK_QUAL); } #else -# define UnqualifiedType(T) ((T) & ~T_MASK_QUAL) +# define GetUnqualRawTypeCode(T) ((T)->C & ~T_MASK_QUAL) #endif #if defined(HAVE_INLINE) -INLINE TypeCode GetClass (const Type* T) -/* Get the class of a type string */ +INLINE TypeCode GetTypeClass (const Type* T) +/* Get the class of a type. This doesn't have a "raw" version since an +** underlying type can never be in a different class. +*/ { return (T->C & T_MASK_CLASS); } #else -# define GetClass(T) ((T)->C & T_MASK_CLASS) +# define GetTypeClass(T) ((T)->C & T_MASK_CLASS) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetTypeRank (const Type* T) +/* Get the type rank of a type */ +{ + return (GetUnqualTypeCode (T) & T_MASK_RANK); +} +#else +# define GetTypeRank(T) (GetUnqualTypeCode (T) & T_MASK_RANK) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetSignedness (const Type* T) /* Get the signedness of a type */ { - return (GetUnderlyingTypeCode (T) & T_MASK_SIGN); + return (GetUnqualTypeCode (T) & T_MASK_SIGN); } #else -# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) +# define GetSignedness(T) (GetUnqualTypeCode (T) & T_MASK_SIGN) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetSizeModifier (const Type* T) /* Get the size modifier of a type */ { - return (GetUnderlyingTypeCode (T) & T_MASK_SIZE); + return (GetUnqualTypeCode (T) & T_MASK_SIZE); } #else -# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE) +# define GetSizeModifier(T) (GetUnqualTypeCode (T) & T_MASK_SIZE) #endif #if defined(HAVE_INLINE) -INLINE TypeCode GetRawType (const Type* T) -/* Get the raw type */ +INLINE TypeCode GetRawTypeRank (const Type* T) +/* Get the raw type rank of a type */ { - return (T->C & T_MASK_TYPE); + return (T->C & T_MASK_RANK); } #else -# define GetRawType(T) ((T)->C & T_MASK_TYPE) +# define GetRawTypeRank(T) ((T)->C & T_MASK_RANK) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetRawSignedness (const Type* T) /* Get the raw signedness of a type */ { - return ((T)->C & T_MASK_SIGN); + return (T->C & T_MASK_SIGN); } #else # define GetRawSignedness(T) ((T)->C & T_MASK_SIGN) @@ -391,7 +405,7 @@ INLINE TypeCode GetRawSignedness (const Type* T) #if defined(HAVE_INLINE) INLINE TypeCode GetRawSizeModifier (const Type* T) -/* Get the size modifier of a raw type */ +/* Get the raw size modifier of a type */ { return (T->C & T_MASK_SIZE); } @@ -418,7 +432,7 @@ Type* NewPointerTo (const Type* T); ** on the heap and may be freed after use. */ -Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth); +Type* NewBitFieldOf (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. */ @@ -433,11 +447,6 @@ const Type* Indirect (const Type* T); ** given type points to. */ -Type* IndirectModifiable (Type* T); -/* Do one indirection for the given type, that is, return the type where the -** given type points to. -*/ - Type* ArrayToPtr (const Type* T); /* Convert an array to a pointer to it's first element */ @@ -461,17 +470,17 @@ const Type* IntPromotion (const Type* T); const Type* ArithmeticConvert (const Type* lhst, const Type* rhst); /* Perform the usual arithmetic conversions for binary operators. */ -const Type* SignedType (const Type* T); +const Type* GetSignedType (const Type* T); /* Get signed counterpart of the integral type */ -const Type* UnsignedType (const Type* T); +const Type* GetUnsignedType (const Type* T); /* Get unsigned counterpart of the integral type */ const Type* GetUnderlyingType (const Type* Type); /* Get the underlying type of an enum or other integer class type */ const Type* GetStructReplacementType (const Type* SType); -/* Get a replacement type for passing a struct/union in the primary register */ +/* Get a replacement type for passing a struct/union by value in the primary */ const Type* GetBitFieldChunkType (const Type* Type); /* Get the type needed to operate on the byte chunk containing the bit-field */ @@ -485,155 +494,127 @@ const Type* GetBitFieldChunkType (const Type* Type); #if defined(HAVE_INLINE) -INLINE int IsTypeChar (const Type* T) -/* Return true if this is a char type */ +INLINE int IsRankChar (const Type* T) +/* Return true if this is a character type */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR); + return (GetTypeRank (T) == T_RANK_CHAR); } #else -# define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR) +# define IsRankChar(T) (GetTypeRank (T) == T_RANK_CHAR) #endif #if defined(HAVE_INLINE) -INLINE int IsTypeShort (const Type* T) +INLINE int IsRankShort (const Type* T) /* Return true if this is a short type (signed or unsigned) */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT); + return (GetTypeRank (T) == T_RANK_SHORT); } #else -# define IsTypeShort(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT) +# define IsRankShort(T) (GetTypeRank (T) == T_RANK_SHORT) #endif #if defined(HAVE_INLINE) -INLINE int IsTypeInt (const Type* T) +INLINE int IsRankInt (const Type* T) /* Return true if this is an int type (signed or unsigned) */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT); + return (GetTypeRank (T) == T_RANK_INT); } #else -# define IsTypeInt(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT) +# define IsRankInt(T) (GetTypeRank (T) == T_RANK_INT) #endif #if defined(HAVE_INLINE) -INLINE int IsTypeLong (const Type* T) +INLINE int IsRankLong (const Type* T) /* Return true if this is a long int type (signed or unsigned) */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG); + return (GetTypeRank (T) == T_RANK_LONG); } #else -# define IsTypeLong(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG) +# define IsRankLong(T) (GetTypeRank (T) == T_RANK_LONG) #endif #if defined(HAVE_INLINE) -INLINE int IsISOChar (const Type* T) -/* Return true if this is a narrow character type (without signed/unsigned) */ -{ - return (UnqualifiedType (T->C) == T_CHAR); -} -#else -# define IsISOChar(T) (UnqualifiedType ((T)->C) == T_CHAR) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsClassChar (const Type* T) -/* Return true if this is a narrow character type (including signed/unsigned). -** For now this is the same as IsRawTypeChar(T). +INLINE int IsDeclTypeChar (const Type* T) +/* Return true if this is declared as a char type (without signed/unsigned). +** This function is to exclude enums whose underlying type is char. */ { - return (GetRawType (T) == T_TYPE_CHAR); + return (GetUnqualRawTypeCode (T) == T_CHAR); } #else -# define IsClassChar(T) (GetRawType (T) == T_TYPE_CHAR) +# define IsDeclTypeChar(T) (GetUnqualRawTypeCode (T) == T_CHAR) #endif #if defined(HAVE_INLINE) -INLINE int IsRawTypeChar (const Type* T) -/* Return true if this is a char raw type (including signed/unsigned) */ +INLINE int IsDeclRankChar (const Type* T) +/* Return true if this is declared as a character type (including signed/unsigned). +** This function is to exclude enums whose underlying types are character types. +*/ { - return (GetRawType (T) == T_TYPE_CHAR); + return (GetRawTypeRank (T) == T_RANK_CHAR); } #else -# define IsRawTypeChar(T) (GetRawType (T) == T_TYPE_CHAR) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsRawTypeInt (const Type* T) -/* Return true if this is an int raw type (signed or unsigned) */ -{ - return (GetRawType (T) == T_TYPE_INT); -} -#else -# define IsRawTypeInt(T) (GetRawType (T) == T_TYPE_INT) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsRawTypeLong (const Type* T) -/* Return true if this is a long raw type (signed or unsigned) */ -{ - return (GetRawType (T) == T_TYPE_LONG); -} -#else -# define IsRawTypeLong(T) (GetRawType (T) == T_TYPE_LONG) +# define IsDeclRankChar(T) (GetRawTypeRank (T) == T_RANK_CHAR) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFloat (const Type* T) /* Return true if this is a float type */ { - return (GetRawType (T) == T_TYPE_FLOAT); + return (GetRawTypeRank (T) == T_RANK_FLOAT); } #else -# define IsTypeFloat(T) (GetRawType (T) == T_TYPE_FLOAT) +# define IsTypeFloat(T) (GetRawTypeRank (T) == T_RANK_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeDouble (const Type* T) /* Return true if this is a double type */ { - return (GetRawType (T) == T_TYPE_DOUBLE); + return (GetRawTypeRank (T) == T_RANK_DOUBLE); } #else -# define IsTypeDouble(T) (GetRawType (T) == T_TYPE_DOUBLE) +# define IsTypeDouble(T) (GetRawTypeRank (T) == T_RANK_DOUBLE) #endif #if defined(HAVE_INLINE) INLINE int IsTypePtr (const Type* T) /* Return true if this is a pointer type */ { - return (GetRawType (T) == T_TYPE_PTR); + return (GetRawTypeRank (T) == T_RANK_PTR); } #else -# define IsTypePtr(T) (GetRawType (T) == T_TYPE_PTR) +# define IsTypePtr(T) (GetRawTypeRank (T) == T_RANK_PTR) #endif #if defined(HAVE_INLINE) INLINE int IsTypeEnum (const Type* T) /* Return true if this is an enum type */ { - return (GetRawType (T) == T_TYPE_ENUM); + return (GetRawTypeRank (T) == T_RANK_ENUM); } #else -# define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) +# define IsTypeEnum(T) (GetRawTypeRank (T) == T_RANK_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); + return (GetUnqualRawTypeCode (T) == T_SBITFIELD); } #else -# define IsTypeSignedBitField(T) (UnqualifiedType ((T)->C) == T_SBITFIELD) +# define IsTypeSignedBitField(T) (GetUnqualRawTypeCode (T) == 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); + return (GetUnqualRawTypeCode (T) == T_UBITFIELD); } #else -# define IsTypeUnsignedBitField(T) (UnqualifiedType ((T)->C) == T_UBITFIELD) +# define IsTypeUnsignedBitField(T) (GetUnqualRawTypeCode (T) == T_UBITFIELD) #endif #if defined(HAVE_INLINE) @@ -653,55 +634,55 @@ int IsTypeFragBitField (const Type* T); INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ { - return (GetRawType (T) == T_TYPE_STRUCT); + return (GetRawTypeRank (T) == T_RANK_STRUCT); } #else -# define IsTypeStruct(T) (GetRawType (T) == T_TYPE_STRUCT) +# define IsTypeStruct(T) (GetRawTypeRank (T) == T_RANK_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeUnion (const Type* T) /* Return true if this is a union type */ { - return (GetRawType (T) == T_TYPE_UNION); + return (GetRawTypeRank (T) == T_RANK_UNION); } #else -# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION) +# define IsTypeUnion(T) (GetRawTypeRank (T) == T_RANK_UNION) #endif #if defined(HAVE_INLINE) INLINE int IsTypeArray (const Type* T) /* Return true if this is an array type */ { - return (GetRawType (T) == T_TYPE_ARRAY); + return (GetRawTypeRank (T) == T_RANK_ARRAY); } #else -# define IsTypeArray(T) (GetRawType (T) == T_TYPE_ARRAY) +# define IsTypeArray(T) (GetRawTypeRank (T) == T_RANK_ARRAY) #endif #if defined(HAVE_INLINE) INLINE int IsTypeVoid (const Type* T) /* Return true if this is a void type */ { - return (GetRawType (T) == T_TYPE_VOID); + return (GetRawTypeRank (T) == T_RANK_VOID); } #else -# define IsTypeVoid(T) (GetRawType (T) == T_TYPE_VOID) +# define IsTypeVoid(T) (GetRawTypeRank (T) == T_RANK_VOID) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFunc (const Type* T) -/* Return true if this is a function class */ +/* Return true if this is a function type */ { - return (GetRawType (T) == T_TYPE_FUNC); + return (GetRawTypeRank (T) == T_RANK_FUNC); } #else -# define IsTypeFunc(T) (GetRawType (T) == T_TYPE_FUNC) +# define IsTypeFunc(T) (GetRawTypeRank (T) == T_RANK_FUNC) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFuncPtr (const Type* T) -/* Return true if this is a function pointer */ +/* Return true if this is a function pointer type */ { return (IsTypePtr (T) && IsTypeFunc (T+1)); } @@ -713,71 +694,71 @@ INLINE int IsTypeFuncPtr (const Type* T) INLINE int IsClassInt (const Type* T) /* Return true if this is an integer type */ { - return (GetClass (T) == T_CLASS_INT); + return (GetTypeClass (T) == T_CLASS_INT); } #else -# define IsClassInt(T) (GetClass (T) == T_CLASS_INT) +# define IsClassInt(T) (GetTypeClass (T) == T_CLASS_INT) #endif #if defined(HAVE_INLINE) INLINE int IsClassFloat (const Type* T) -/* Return true if this is a float type */ +/* Return true if this is a floating type */ { - return (GetClass (T) == T_CLASS_FLOAT); + return (GetTypeClass (T) == T_CLASS_FLOAT); } #else -# define IsClassFloat(T) (GetClass (T) == T_CLASS_FLOAT) +# define IsClassFloat(T) (GetTypeClass (T) == T_CLASS_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsClassPtr (const Type* T) -/* Return true if this is a pointer type */ +/* Return true if this is a pointer or array type */ { - return (GetClass (T) == T_CLASS_PTR); + return (GetTypeClass (T) == T_CLASS_PTR); } #else -# define IsClassPtr(T) (GetClass (T) == T_CLASS_PTR) +# define IsClassPtr(T) (GetTypeClass (T) == T_CLASS_PTR) #endif #if defined(HAVE_INLINE) INLINE int IsClassStruct (const Type* T) /* Return true if this is a struct or union type */ { - return (GetClass (T) == T_CLASS_STRUCT); + return (GetTypeClass (T) == T_CLASS_STRUCT); } #else -# define IsClassStruct(T) (GetClass (T) == T_CLASS_STRUCT) +# define IsClassStruct(T) (GetTypeClass (T) == T_CLASS_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsClassFunc (const Type* T) /* Return true if this is a function type */ { - return (GetClass (T) == T_CLASS_FUNC); + return (GetTypeClass (T) == T_CLASS_FUNC); } #else -# define IsClassFunc(T) (GetClass (T) == T_CLASS_FUNC) +# define IsClassFunc(T) (GetTypeClass (T) == T_CLASS_FUNC) #endif -int IsClassObject (const Type* T); +int IsObjectType (const Type* T); /* Return true if this is a fully described object type */ -int IsClassIncomplete (const Type* T); +int IsIncompleteType (const Type* T); /* Return true if this is an object type lacking size info */ -int IsClassArithmetic (const Type* T); -/* Return true if this is an integer or real floating type */ +int IsArithmeticType (const Type* T); +/* Return true if this is an integer or floating type */ -int IsClassBasic (const Type* T); +int IsBasicType (const Type* T); /* Return true if this is a char, integer or floating type */ -int IsClassScalar (const Type* T); +int IsScalarType (const Type* T); /* Return true if this is an arithmetic or pointer type */ -int IsClassDerived (const Type* T); +int IsDerivedType (const Type* T); /* Return true if this is an array, struct, union, function or pointer type */ -int IsClassAggregate (const Type* T); +int IsAggregateType (const Type* T); /* Return true if this is an array or struct type */ int IsRelationType (const Type* T); @@ -798,7 +779,7 @@ int IsEmptiableObjectType (const Type* T); int HasUnknownSize (const Type* T); /* Return true if this is an incomplete ESU type or an array of unknown size */ -int TypeHasAttr (const Type* T); +int TypeHasAttrData (const Type* T); /* Return true if the given type has attribute data */ #if defined(HAVE_INLINE) @@ -978,10 +959,10 @@ FuncDesc* GetFuncDesc (const Type* T) attribute ((const)); void SetFuncDesc (Type* T, FuncDesc* F); /* Set the FuncDesc pointer in a function or pointer-to-function type */ -const Type* GetFuncReturn (const Type* T) attribute ((const)); +const Type* GetFuncReturnType (const Type* T) attribute ((const)); /* Return a pointer to the return type of a function or pointer-to-function type */ -Type* GetFuncReturnModifiable (Type* T) attribute ((const)); +Type* GetFuncReturnTypeModifiable (Type* T) attribute ((const)); /* Return a non-const pointer to the return type of a function or pointer-to-function type */ const FuncDesc* GetFuncDefinitionDesc (const Type* T) attribute ((const)); @@ -1006,7 +987,10 @@ void SetElementCount (Type* T, long Count); */ const Type* GetElementType (const Type* T); -/* Return the element type of the given array type. */ +/* Return the element type of the given array type */ + +Type* GetElementTypeModifiable (Type* T); +/* Return the element type of the given array type */ const Type* GetBaseElementType (const Type* T); /* Return the base element type of a given type. If T is not an array, this diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 459ffa103..59eb555c4 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -406,7 +406,7 @@ static void FixQualifiers (Type* DataType) if (IsTypeArray (T)) { /* Extract any type qualifiers */ Q |= GetQualifier (T); - T->C = UnqualifiedType (T->C); + T->C = GetUnqualRawTypeCode (T); } else { /* Add extracted type qualifiers here */ T->C |= Q; @@ -646,7 +646,7 @@ static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) /* Enumerate by adding one to the previous value */ EnumVal = (long)(((unsigned long)EnumVal + 1UL) & 0xFFFFFFFFUL); - if (UnqualifiedType (MemberType->C) == T_ULONG && EnumVal == 0) { + if (GetUnqualRawTypeCode (MemberType) == T_ULONG && EnumVal == 0) { /* Error since the new value cannot be represented in the ** largest unsigned integer type supported by cc65 for enum. */ @@ -688,7 +688,7 @@ static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) if (PrevErrorCount == ErrorCount && IsIncremented && (!IsSigned || EnumVal >= 0) && - NewType->C != UnqualifiedType (MemberType->C)) { + NewType->C != GetUnqualRawTypeCode (MemberType)) { /* The possible overflow here can only be when EnumVal > 0 */ Warning ("Enumerator '%s' (value = %lu) implies type '%s'", Ident, @@ -959,7 +959,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) } /* Check for incomplete types including 'void' */ - if (IsClassIncomplete (Decl.Type)) { + if (IsIncompleteType (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); @@ -1159,7 +1159,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } /* Check for incomplete types including 'void' */ - if (IsClassIncomplete (Decl.Type)) { + if (IsIncompleteType (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); @@ -2036,7 +2036,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) if (IsTypeFunc (D->Type) || IsTypeFuncPtr (D->Type)) { /* A function. Check the return type */ - Type* RetType = GetFuncReturnModifiable (D->Type); + Type* RetType = GetFuncReturnTypeModifiable (D->Type); /* Functions may not return functions or arrays */ if (IsTypeFunc (RetType)) { @@ -2048,13 +2048,13 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* The return type must not be qualified */ if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { - if (GetRawType (RetType) == T_TYPE_VOID) { + if (GetRawTypeRank (RetType) == T_RANK_VOID) { /* A qualified void type is always an error */ Error ("function definition has qualified void return type"); } else { /* For others, qualifiers are ignored */ Warning ("type qualifiers ignored on function return type"); - RetType[0].C = UnqualifiedType (RetType[0].C); + RetType[0].C = GetUnqualRawTypeCode (RetType); } } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 78a4c516a..691010b0a 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -128,7 +128,7 @@ unsigned TypeOf (const Type* T) { unsigned NewType; - switch (GetUnderlyingTypeCode (T)) { + switch (GetUnqualTypeCode (T)) { case T_SCHAR: return CF_CHAR; @@ -187,7 +187,7 @@ unsigned TypeOf (const Type* T) unsigned FuncTypeOf (const Type* T) /* Get the code generator flag for calling the function */ { - if (GetUnderlyingTypeCode (T) == T_FUNC) { + if (GetUnqualTypeCode (T) == T_FUNC) { return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; } else { Error ("Illegal function type %04lX", T->C); @@ -290,7 +290,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) void LimitExprValue (ExprDesc* Expr, int WarnOverflow) /* Limit the constant value of the expression to the range of its type */ { - switch (GetUnderlyingTypeCode (Expr->Type)) { + switch (GetUnqualTypeCode (Expr->Type)) { case T_INT: case T_SHORT: if (WarnOverflow && ((Expr->IVal < -0x8000) || (Expr->IVal > 0x7FFF))) { @@ -1146,7 +1146,7 @@ static void FunctionCall (ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - ReturnType = GetFuncReturn (Expr->Type); + ReturnType = GetFuncReturnType (Expr->Type); /* Handle struct/union specially */ if (IsClassStruct (ReturnType)) { @@ -2568,7 +2568,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } /* Determine the type of the operation. */ - if (IsTypeChar (Expr->Type) && rconst && (!LeftSigned || RightSigned)) { + if (IsRankChar (Expr->Type) && rconst && (!LeftSigned || RightSigned)) { /* Left side is unsigned char, right side is constant. ** Determine the minimum and maximum values @@ -2651,7 +2651,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_UNSIGNED; } - } else if (IsTypeChar (Expr->Type) && IsTypeChar (Expr2.Type) && + } else if (IsRankChar (Expr->Type) && IsRankChar (Expr2.Type) && GetSignedness (Expr->Type) == GetSignedness (Expr2.Type)) { /* Both are chars with the same signedness. We can encode the diff --git a/src/cc65/function.c b/src/cc65/function.c index 737b068a3..39f04843f 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -81,7 +81,7 @@ static Function* NewFunction (struct SymEntry* Sym, FuncDesc* D) /* Initialize the fields */ F->FuncEntry = Sym; - F->ReturnType = GetFuncReturn (Sym->Type); + F->ReturnType = GetFuncReturnType (Sym->Type); F->Desc = D; F->Reserved = 0; F->RetLab = 0; @@ -540,7 +540,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Determine if this is a main function in a C99 environment that ** returns an int. */ - if (IsRawTypeInt (F_GetReturnType (CurrentFunc)) && + if (GetUnqualRawTypeCode (ReturnType) == T_INT && IS_Get (&Standard) == STD_C99) { C99MainFunc = 1; } diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 2b151e59e..619fe4897 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -330,12 +330,12 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) int HasCurly = 0; /* Get the array data */ - Type* ElementType = IndirectModifiable (T); + Type* ElementType = GetElementTypeModifiable (T); unsigned ElementSize = SizeOf (ElementType); long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ - if (IsClassChar (ElementType) && + if (IsDeclRankChar (ElementType) && (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || (CurTok.Tok == TOK_LCURLY && (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { @@ -669,7 +669,7 @@ static unsigned ParseVoidInit (Type* T) Size = 0; do { ExprDesc Expr = NoCodeConstExpr (hie1); - switch (GetUnderlyingTypeCode (&Expr.Type[0])) { + switch (GetUnqualTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -737,7 +737,7 @@ static unsigned ParseVoidInit (Type* T) static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { - switch (GetUnderlyingTypeCode (T)) { + switch (GetUnqualTypeCode (T)) { case T_SCHAR: case T_UCHAR: diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index fc19ce7f0..d22c73dcf 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -531,7 +531,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); /* Bail out, no need for further processing */ goto ExitPoint; @@ -540,7 +540,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); ExitPoint: /* We expect the closing brace */ @@ -757,7 +757,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); /* Bail out, no need for further processing */ goto ExitPoint; @@ -766,7 +766,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); ExitPoint: /* We expect the closing brace */ @@ -968,7 +968,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); /* We expect the closing brace */ ConsumeRParen (); @@ -1165,7 +1165,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); ExitPoint: /* We expect the closing brace */ diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 00555ffc3..0466ddf4a 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -133,7 +133,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = GetUnderlyingTypeCode (&SwitchExpr.Type[0]); + SwitchData.ExprType = GetUnqualTypeCode (&SwitchExpr.Type[0]); SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 961f36046..72a2ac007 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1007,7 +1007,7 @@ 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 = NewBitFieldType (T, BitOffs, BitWidth); + Entry->Type = NewBitFieldOf (T, BitOffs, BitWidth); Entry->V.Offs = Offs; if (!SignednessSpecified) { @@ -1019,7 +1019,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, ** `char -> unsigned char` adjustment that is performed with other integral types. */ CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || - IsTypeChar (Entry->Type)); + IsRankChar (Entry->Type)); Entry->Type[0].C &= ~T_MASK_SIGN; Entry->Type[0].C |= T_SIGN_UNSIGNED; Entry->Type[1].C &= ~T_MASK_SIGN; diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 1a108159f..c3239652f 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -244,7 +244,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SymEntry* Sym2; FuncDesc* F1; FuncDesc* F2; - TypeCode LeftType, RightType; + TypeCode LeftRank, RightRank; long LeftCount, RightCount; @@ -262,9 +262,23 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) return; } - /* Get the left and right types */ - LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE); - RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE); + /* Get the ranks of the left and right hands */ + LeftRank = (GetUnqualTypeCode (lhs) & T_MASK_RANK); + RightRank = (GetUnqualTypeCode (rhs) & T_MASK_RANK); + + /* If one side is a pointer and the other side is an array, both are + ** compatible. + */ + if (Result->Indirections == 0) { + if (LeftRank == T_RANK_PTR && RightRank == T_RANK_ARRAY) { + RightRank = T_RANK_PTR; + SetResult (Result, TC_PTR_DECAY); + } + if (LeftRank == T_RANK_ARRAY && RightRank == T_RANK_PTR) { + LeftRank = T_RANK_PTR; + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } /* Bit-fields are considered compatible if they have the same ** signedness, bit-offset and bit-width. @@ -275,29 +289,14 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) lhs->A.B.Offs != rhs->A.B.Offs || lhs->A.B.Width != rhs->A.B.Width) { SetResult (Result, TC_INCOMPATIBLE); - return; } - if (LeftType != RightType) { + if (LeftRank != RightRank) { SetResult (Result, TC_STRICT_COMPATIBLE); } } - /* If one side is a pointer and the other side is an array, both are - ** compatible. - */ - if (Result->Indirections == 0) { - if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) { - RightType = T_TYPE_PTR; - SetResult (Result, TC_PTR_DECAY); - } - if (LeftType == T_TYPE_ARRAY && RightType == T_TYPE_PTR) { - LeftType = T_TYPE_PTR; - SetResult (Result, TC_STRICT_COMPATIBLE); - } - } - - /* If the underlying types are not identical, the types are incompatible */ - if (LeftType != RightType) { + /* If the ranks are different, the types are incompatible */ + if (LeftRank != RightRank) { SetResult (Result, TC_INCOMPATIBLE); return; } @@ -337,8 +336,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } /* 'char' is neither 'signed char' nor 'unsigned char' */ - if ((IsISOChar (lhs) && !IsISOChar (rhs)) || - (!IsISOChar (lhs) && IsISOChar (rhs))) { + if ((IsDeclTypeChar (lhs) && !IsDeclTypeChar (rhs)) || + (!IsDeclTypeChar (lhs) && IsDeclTypeChar (rhs))) { SetResult (Result, TC_SIGN_DIFF); } @@ -350,14 +349,14 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } /* Check for special type elements */ - switch (LeftType) { - case T_TYPE_PTR: + switch (LeftRank) { + case T_RANK_PTR: ++Result->Indirections; if (Result->Indirections == 1) { - if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + if ((GetUnqualTypeCode (lhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_LEFT; } - if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + if ((GetUnqualTypeCode (rhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_RIGHT; } } else { @@ -365,7 +364,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } break; - case T_TYPE_FUNC: + case T_RANK_FUNC: /* Compare the function descriptors */ F1 = GetFuncDesc (lhs); F2 = GetFuncDesc (rhs); @@ -399,7 +398,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Keep on and compare the return type */ break; - case T_TYPE_ARRAY: + case T_RANK_ARRAY: /* Check member count */ LeftCount = GetElementCount (lhs); RightCount = GetElementCount (rhs); @@ -420,8 +419,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } break; - case T_TYPE_STRUCT: - case T_TYPE_UNION: + case T_RANK_STRUCT: + case T_RANK_UNION: /* Compare the tag types */ Sym1 = GetESUTagSym (lhs); Sym2 = GetESUTagSym (rhs); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index c72b2c5eb..18a9f4dfa 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -430,7 +430,7 @@ void TypeComposition (Type* lhs, const Type* rhs) } /* Check for sanity */ - CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); + CHECK (GetUnqualTypeCode (lhs) == GetUnqualTypeCode (rhs)); /* Check for special type elements */ if (IsTypeFunc (lhs)) { From 5fd91699049d2bafbbdbbe1b1cb5a46451a486cc Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 12 Oct 2022 13:10:17 +0800 Subject: [PATCH 013/520] Renamed type facility functions for deciding code generation type flags. --- src/cc65/assignment.c | 28 ++++++++-------- src/cc65/expr.c | 74 +++++++++++++++++++++---------------------- src/cc65/expr.h | 8 ++--- src/cc65/function.c | 6 ++-- src/cc65/initdata.c | 4 +-- src/cc65/loadexpr.c | 8 ++--- src/cc65/locals.c | 6 ++-- src/cc65/shiftexpr.c | 4 +-- src/cc65/stdfunc.c | 2 +- src/cc65/stmt.c | 2 +- src/cc65/typeconv.c | 4 +-- 11 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 54ab28d4e..ba12cc7e3 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -90,7 +90,7 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Check if the value of the rhs is not in the primary yet */ if (!ED_IsLocPrimary (RExpr)) { /* Just load the value into the primary as the replacement type. */ - LoadExpr (TypeOf (stype) | CF_FORCECHAR, RExpr); + LoadExpr (CG_TypeOf (stype) | CF_FORCECHAR, RExpr); } /* Store it into the location referred in the primary */ @@ -145,8 +145,8 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) ChunkType = GetBitFieldChunkType (Expr->Type); /* Determine code generator flags */ - Flags = TypeOf (Expr->Type) | CF_FORCECHAR; - ChunkFlags = TypeOf (ChunkType); + Flags = CG_TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = CG_TypeOf (ChunkType); if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { ChunkFlags |= CF_FORCECHAR; } @@ -232,8 +232,8 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op ChunkType = GetBitFieldChunkType (Expr->Type); /* Determine code generator flags */ - Flags = TypeOf (Expr->Type) | CF_FORCECHAR; - ChunkFlags = TypeOf (ChunkType); + Flags = CG_TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = CG_TypeOf (ChunkType); if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { ChunkFlags |= CF_FORCECHAR; } @@ -358,7 +358,7 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op 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); + AdjustedFlags = g_typeadjust (AdjustedFlags, CG_TypeOf (Expr2.Type) | CF_CONST); } Gen->Func (g_typeadjust (Flags, AdjustedFlags) | CF_CONST, Expr2.IVal); } else { @@ -381,11 +381,11 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op 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); + AdjustedFlags = g_typeadjust (AdjustedFlags, CG_TypeOf (Expr2.Type) | CF_CONST); } Gen->Func (g_typeadjust (Flags, AdjustedFlags), 0); } else { - Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + Gen->Func (g_typeadjust (Flags, CG_TypeOf (Expr2.Type)), 0); } } else { @@ -452,7 +452,7 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Determine code generator flags */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* Determine the type of the lhs */ MustScale = Gen != 0 && (Gen->Func == g_add || Gen->Func == g_sub) && @@ -572,7 +572,7 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* if (MustScale) { /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + g_scale (CG_TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); } /* If the lhs is character sized, the operation may be later done @@ -583,7 +583,7 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* } /* Adjust the types of the operands if needed */ - Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + Gen->Func (g_typeadjust (Flags, CG_TypeOf (Expr2.Type)), 0); } } @@ -715,8 +715,8 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) } /* Setup the code generator flags */ - lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; - rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; + lflags |= CG_TypeOf (Expr->Type) | CG_AddrModeFlags (Expr) | CF_FORCECHAR; + rflags |= CG_TypeOf (Expr2.Type) | CF_FORCECHAR; if (ED_IsConstAbs (&Expr2)) { /* The resulting value is a constant */ @@ -736,7 +736,7 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) if (MustScale) { /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); + g_scale (CG_TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); } } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 78a4c516a..37611c490 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -80,7 +80,7 @@ static void PostDec (ExprDesc* Expr); -unsigned GlobalModeFlags (const ExprDesc* Expr) +unsigned CG_AddrModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { @@ -95,7 +95,7 @@ unsigned GlobalModeFlags (const ExprDesc* Expr) case E_LOC_LITERAL: return CF_LITERAL; case E_LOC_CODE: return CF_CODE; default: - Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); + Internal ("CG_AddrModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); /* NOTREACHED */ return 0; } @@ -103,7 +103,7 @@ unsigned GlobalModeFlags (const ExprDesc* Expr) -static unsigned TypeOfBySize (unsigned Size) +static unsigned CG_TypeOfBySize (unsigned Size) /* Get the code generator replacement type of the object by its size */ { unsigned NewType; @@ -123,7 +123,7 @@ static unsigned TypeOfBySize (unsigned Size) -unsigned TypeOf (const Type* T) +unsigned CG_TypeOf (const Type* T) /* Get the code generator base type of the object */ { unsigned NewType; @@ -163,7 +163,7 @@ unsigned TypeOf (const Type* T) case T_STRUCT: case T_UNION: - NewType = TypeOfBySize (SizeOf (T)); + NewType = CG_TypeOfBySize (SizeOf (T)); if (NewType != CF_NONE) { return NewType; } @@ -184,8 +184,8 @@ unsigned TypeOf (const Type* T) -unsigned FuncTypeOf (const Type* T) -/* Get the code generator flag for calling the function */ +unsigned CG_CallFlags (const Type* T) +/* Get the code generator flags for calling the function */ { if (GetUnderlyingTypeCode (T) == T_FUNC) { return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; @@ -254,7 +254,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) const Type* rhst = rhs->Type; /* Generate type adjustment code if needed */ - ltype = TypeOf (lhst); + ltype = CG_TypeOf (lhst); if (ED_IsConstAbsInt (lhs) && ltype == CF_INT && lhs->IVal >= 0 && lhs->IVal < 256) { /* If the lhs is a int constant that fits in an unsigned char, use unsigned char. ** g_typeadjust will either promote this to int or unsigned int as appropriate @@ -269,7 +269,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) /* Value is in primary register*/ ltype |= CF_PRIMARY; } - rtype = TypeOf (rhst); + rtype = CG_TypeOf (rhst); if (ED_IsConstAbsInt (rhs) && rtype == CF_INT && rhs->IVal >= 0 && rhs->IVal < 256) { rtype = CF_CHAR | CF_UNSIGNED; } @@ -493,7 +493,7 @@ static void DoInc (ExprDesc* Expr, unsigned KeepResult) } /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + Flags = CG_TypeOf (Expr->Type) | CG_AddrModeFlags (Expr) | CF_FORCECHAR | CF_CONST; if (KeepResult != OA_NEED_NEW) { /* No need to get the result */ Flags |= CF_NOKEEP; @@ -580,7 +580,7 @@ static void DoDec (ExprDesc* Expr, unsigned KeepResult) } /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + Flags = CG_TypeOf (Expr->Type) | CG_AddrModeFlags (Expr) | CF_FORCECHAR | CF_CONST; if (KeepResult != OA_NEED_NEW) { /* No need to get the result */ Flags |= CF_NOKEEP; @@ -873,7 +873,7 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Handle struct/union specially */ if (IsClassStruct (Expr.Type)) { /* Use the replacement type */ - Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + Flags |= CG_TypeOf (GetStructReplacementType (Expr.Type)); /* Load the value into the primary if it is not already there */ LoadExpr (Flags, &Expr); @@ -882,7 +882,7 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) LoadExpr (CF_NONE, &Expr); /* Use the type of the argument for the push */ - Flags |= TypeOf (Expr.Type); + Flags |= CG_TypeOf (Expr.Type); } /* If this is a fastcall function, don't push the last argument */ @@ -1069,7 +1069,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Call the function */ - g_callind (FuncTypeOf (Expr->Type+1), ArgSize, PtrOffs); + g_callind (CG_CallFlags (Expr->Type+1), ArgSize, PtrOffs); } else { @@ -1137,9 +1137,9 @@ static void FunctionCall (ExprDesc* Expr) SB_Done (&S); - g_call (FuncTypeOf (Expr->Type), Func->WrappedCall->Name, ArgSize); + g_call (CG_CallFlags (Expr->Type), Func->WrappedCall->Name, ArgSize); } else { - g_call (FuncTypeOf (Expr->Type), (const char*) Expr->Name, ArgSize); + g_call (CG_CallFlags (Expr->Type), (const char*) Expr->Name, ArgSize); } } @@ -1665,7 +1665,7 @@ void Store (ExprDesc* Expr, const Type* StoreType) } /* Prepare the code generator flags */ - Flags = TypeOf (StoreType) | GlobalModeFlags (Expr); + Flags = CG_TypeOf (StoreType) | CG_AddrModeFlags (Expr); /* Do the store depending on the location */ switch (ED_GetLoc (Expr)) { @@ -1793,7 +1793,7 @@ static void PostInc (ExprDesc* Expr) } /* Get the data type */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* We are allowed by the C standard to defer the inc operation until after ** the expression is used, so that we don't need to save and reload @@ -1854,7 +1854,7 @@ static void PostDec (ExprDesc* Expr) } /* Get the data type */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { @@ -1934,7 +1934,7 @@ static void UnaryOp (ExprDesc* Expr) TypeConversion (Expr, Expr->Type); /* Get code generation flags */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* Handle the operation */ switch (Tok) { @@ -1984,7 +1984,7 @@ void hie10 (ExprDesc* Expr) } else { /* Not constant, load into the primary */ LoadExpr (CF_NONE, Expr); - g_bneg (TypeOf (Expr->Type)); + g_bneg (CG_TypeOf (Expr->Type)); ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* bneg will set cc */ } @@ -2141,7 +2141,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Get the lhs on stack */ GetCodePos (&Mark1); - ltype = TypeOf (Expr->Type); + ltype = CG_TypeOf (Expr->Type); lconst = ED_IsConstAbs (Expr); if (lconst) { /* Constant value */ @@ -2255,7 +2255,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ ** operation because this allows for better code. */ unsigned rtype = ltype | CF_CONST; - ltype = TypeOf (Expr2.Type); /* Expr2 is now left */ + ltype = CG_TypeOf (Expr2.Type); /* Expr2 is now left */ type = CF_CONST; if ((Gen->Flags & GEN_NOPUSH) == 0) { g_push (ltype, 0); @@ -2279,7 +2279,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ ** expects the lhs in the primary, remove the push of the primary ** now. */ - unsigned rtype = TypeOf (Expr2.Type); + unsigned rtype = CG_TypeOf (Expr2.Type); type = 0; if (rconst) { /* As above, but for the RHS. */ @@ -2355,7 +2355,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Get the lhs on stack */ GetCodePos (&Mark1); - ltype = TypeOf (Expr->Type); + ltype = CG_TypeOf (Expr->Type); if (ED_IsConstAbs (Expr)) { /* Numeric constant value */ GetCodePos (&Mark2); @@ -2667,7 +2667,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } else { - unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + unsigned rtype = CG_TypeOf (Expr2.Type) | (flags & CF_CONST); flags |= g_typeadjust (ltype, rtype); } @@ -2887,7 +2887,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) g_addaddr_local (flags, Expr->IVal); } else { /* Static address */ - g_addaddr_static (flags | GlobalModeFlags (Expr), Expr->Name, Expr->IVal); + g_addaddr_static (flags | CG_AddrModeFlags (Expr), Expr->Name, Expr->IVal); } } else { /* Lhs is not numeric. Load it. */ @@ -2905,7 +2905,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) g_addaddr_local (flags, Expr2.IVal); } else { /* Static address */ - g_addaddr_static (flags | GlobalModeFlags (&Expr2), Expr2.Name, Expr2.IVal); + g_addaddr_static (flags | CG_AddrModeFlags (&Expr2), Expr2.Name, Expr2.IVal); } } @@ -2932,7 +2932,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) flags |= CF_CONST; } else { /* Constant address label */ - flags |= GlobalModeFlags (Expr); + flags |= CG_AddrModeFlags (Expr); } /* Check for pointer arithmetic */ @@ -2976,7 +2976,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) /* Load lhs */ LoadExpr (CF_NONE, Expr); /* Use new flags */ - flags = CF_CHAR | GlobalModeFlags (&Expr2); + flags = CF_CHAR | CG_AddrModeFlags (&Expr2); /* Add the variable */ if (ED_IsLocStack (&Expr2)) { g_addlocal (flags, Expr2.IVal); @@ -2998,7 +2998,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) ** not a numeric constant, and the scale factor is not one ** (no scaling), we must take the long way over the stack. */ - g_push (TypeOf (Expr2.Type), 0); /* rhs --> stack */ + g_push (CG_TypeOf (Expr2.Type), 0); /* rhs --> stack */ LoadExpr (CF_NONE, Expr); g_scale (CF_PTR, lscale); g_add (CF_PTR, 0); @@ -3014,7 +3014,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) /* Left hand side is not constant. Get the value onto the stack. */ LoadExpr (CF_NONE, Expr); /* --> primary register */ GetCodePos (&Mark); - flags = TypeOf (Expr->Type); /* default codegen type */ + flags = CG_TypeOf (Expr->Type); /* default codegen type */ g_push (flags, 0); /* --> stack */ /* Evaluate the rhs */ @@ -3078,7 +3078,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) g_push (CF_PTR, 0); /* --> stack */ LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */ } else { - g_tosint (TypeOf (lhst)); /* Make sure TOS is int */ + g_tosint (CG_TypeOf (lhst)); /* Make sure TOS is int */ LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */ if (lscale != 1) { g_swap (CF_INT); /* Swap TOS and primary */ @@ -3198,9 +3198,9 @@ static void parsesub (ExprDesc* Expr) /* Remember the output queue position, then bring the value onto the stack */ GetCodePos (&Mark1); - LoadExpr (CF_NONE, Expr); /* --> primary register */ + LoadExpr (CF_NONE, Expr); /* --> primary register */ GetCodePos (&Mark2); - g_push (TypeOf (lhst), 0); /* --> stack */ + g_push (CG_TypeOf (lhst), 0); /* --> stack */ /* Parse the right hand side */ MarkedExprWithCheck (hie9, &Expr2); @@ -3357,7 +3357,7 @@ static void parsesub (ExprDesc* Expr) } /* Load rhs into the primary */ LoadExpr (CF_NONE, &Expr2); - g_scale (TypeOf (rhst), rscale); + g_scale (CG_TypeOf (rhst), rscale); /* Generate code for the sub (the & is a hack here) */ g_sub (flags & ~CF_CONST, 0); } @@ -3394,7 +3394,7 @@ static void parsesub (ExprDesc* Expr) } /* Load rhs into the primary */ LoadExpr (CF_NONE, &Expr2); - g_scale (TypeOf (rhst), rscale); + g_scale (CG_TypeOf (rhst), rscale); /* Generate code for the sub (the & is a hack here) */ g_sub (flags & ~CF_CONST, 0); } diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 5644fb82d..726ea882b 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -48,14 +48,14 @@ typedef struct GenDesc { -unsigned GlobalModeFlags (const ExprDesc* Expr); +unsigned CG_AddrModeFlags (const ExprDesc* Expr); /* Return the addressing mode flags for the given expression */ -unsigned TypeOf (const Type* T); +unsigned CG_TypeOf (const Type* T); /* Get the code generator base type of the object */ -unsigned FuncTypeOf (const Type* T); -/* Get the code generator flag for calling the function */ +unsigned CG_CallFlags (const Type* T); +/* Get the code generator flags for calling the function */ void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 737b068a3..cd38c0081 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -565,15 +565,15 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Generate the push */ /* Handle struct/union specially */ if (IsClassStruct (D->LastParam->Type)) { - Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; + Flags = CG_TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { - Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; + Flags = CG_TypeOf (D->LastParam->Type) | CF_FORCECHAR; } g_push (Flags, 0); } /* Generate function entry code if needed */ - g_enter (FuncTypeOf (Func->Type), F_GetParamSize (CurrentFunc)); + g_enter (CG_CallFlags (Func->Type), F_GetParamSize (CurrentFunc)); /* If stack checking code is requested, emit a call to the helper routine */ if (IS_Get (&CheckStack)) { diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 2b151e59e..0c763f616 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -168,12 +168,12 @@ static void DefineData (ExprDesc* Expr) case E_LOC_NONE: /* Immediate numeric value with no storage */ - g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + g_defdata (CF_IMM | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_ABS: /* Absolute numeric address */ - g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + g_defdata (CF_ABSOLUTE | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index c5ac43f78..d06ca9437 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -133,10 +133,10 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) AdjustBitField = 1; /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ - BitFieldFullWidthFlags = Flags | TypeOf (Expr->Type); + BitFieldFullWidthFlags = Flags | CG_TypeOf (Expr->Type); /* Flags we need operate on the whole chunk containing the bit-field. */ - Flags |= TypeOf (GetBitFieldChunkType (Expr->Type)); + Flags |= CG_TypeOf (GetBitFieldChunkType (Expr->Type)); /* 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 @@ -151,7 +151,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) if (IsIncompleteESUType (Expr->Type)) { return; } - Flags |= TypeOf (Expr->Type); + Flags |= CG_TypeOf (Expr->Type); } } @@ -175,7 +175,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) case E_LOC_NONE: /* Immediate number constant */ - g_getimmed (Flags | CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + g_getimmed (Flags | CF_IMM | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_ABS: diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 68ac00e62..fe88de3a8 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -164,7 +164,7 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg) LoadExpr (CF_NONE, &Expr); /* Store the value into the variable */ - g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0); + g_putstatic (CF_REGVAR | CG_TypeOf (Sym->Type), Reg, 0); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); @@ -274,7 +274,7 @@ static void ParseAutoDecl (Declarator* Decl) } /* Push the value */ - g_push (Flags | TypeOf (Sym->Type), Expr.IVal); + g_push (Flags | CG_TypeOf (Sym->Type), Expr.IVal); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); @@ -353,7 +353,7 @@ static void ParseAutoDecl (Declarator* Decl) LoadExpr (CF_NONE, &Expr); /* Store the value into the variable */ - g_putstatic (CF_STATIC | TypeOf (Sym->Type), DataLabel, 0); + g_putstatic (CF_STATIC | CG_TypeOf (Sym->Type), DataLabel, 0); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index b8fb70434..9fdceb7a2 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -95,14 +95,14 @@ void ShiftExpr (struct ExprDesc* Expr) ResultType = IntPromotion (Expr->Type); /* Prepare the code generator flags */ - GenFlags = TypeOf (ResultType); + GenFlags = CG_TypeOf (ResultType); /* Calculate the number of bits the lhs operand has */ ExprBits = SizeOf (ResultType) * 8; /* Get the lhs on stack */ GetCodePos (&Mark1); - ltype = TypeOf (Expr->Type); + ltype = CG_TypeOf (Expr->Type); lconst = ED_IsConstAbs (Expr); if (lconst) { /* Constant value */ diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index fc19ce7f0..a9b595320 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -184,7 +184,7 @@ static void ParseArg (ArgDesc* Arg, const Type* Type, ExprDesc* Expr) GetCodePos (&Arg->End); /* Use the type of the argument for the push */ - Arg->Flags |= TypeOf (Arg->Expr.Type); + Arg->Flags |= CG_TypeOf (Arg->Expr.Type); /* Propagate from subexpressions */ Expr->Flags |= Arg->Expr.Flags & E_MASK_VIRAL; diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 7355e88a8..f89f573e4 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -345,7 +345,7 @@ static void ReturnStatement (void) if (ReturnType == Expr.Type) { Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); } - LoadExpr (TypeOf (ReturnType), &Expr); + LoadExpr (CG_TypeOf (ReturnType), &Expr); } else { /* Load the value into the primary */ diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index c72b2c5eb..8b23ae0ec 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -111,7 +111,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) LoadExpr (CF_NONE, Expr); /* Emit typecast code */ - g_typecast (TypeOf (NewType), TypeOf (OldType)); + g_typecast (CG_TypeOf (NewType), CG_TypeOf (OldType)); /* Value is now in primary and an rvalue */ ED_FinalizeRValLoad (Expr); @@ -162,7 +162,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) LoadExpr (CF_NONE, Expr); /* Emit typecast code. */ - g_typecast (TypeOf (NewType), TypeOf (OldType)); + g_typecast (CG_TypeOf (NewType), CG_TypeOf (OldType)); /* Value is now an rvalue in the primary */ ED_FinalizeRValLoad (Expr); From 01d2b809a1411da08b1642f8a0ccf7aee9898a72 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 21 Oct 2023 23:41:36 +0800 Subject: [PATCH 014/520] Renamed struct Segments to SegContext as well as some related functions. --- src/cc65/asmlabel.c | 4 ++-- src/cc65/asmlabel.h | 4 ++-- src/cc65/function.c | 4 ++-- src/cc65/segments.c | 48 +++++++++++++++++++++++++-------------------- src/cc65/segments.h | 24 +++++++++++------------ src/cc65/symentry.h | 4 ++-- 6 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c index 7d5db75e6..a07607048 100644 --- a/src/cc65/asmlabel.c +++ b/src/cc65/asmlabel.c @@ -52,7 +52,7 @@ -static struct Segments* CurrentFunctionSegment; +static struct SegContext* CurrentFunctionSegment; @@ -62,7 +62,7 @@ static struct Segments* CurrentFunctionSegment; -void UseLabelPoolFromSegments (struct Segments* Seg) +void UseLabelPoolFromSegments (struct SegContext* Seg) /* Use the info in segments for generating new label numbers */ { CurrentFunctionSegment = Seg; diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h index dbfe2f443..7e8039cc4 100644 --- a/src/cc65/asmlabel.h +++ b/src/cc65/asmlabel.h @@ -44,7 +44,7 @@ -struct Segments; +struct SegContext; @@ -54,7 +54,7 @@ struct Segments; -void UseLabelPoolFromSegments (struct Segments* Seg); +void UseLabelPoolFromSegments (struct SegContext* Seg); /* Use the info in segments for generating new label numbers */ unsigned GetLocalLabel (void); diff --git a/src/cc65/function.c b/src/cc65/function.c index 38a8f45aa..1fee9b342 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -547,7 +547,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) } /* Allocate code and data segments for this function */ - Func->V.F.Seg = PushSegments (Func); + Func->V.F.Seg = PushSegContext (Func); /* Use the info in the segments for generating new local labels */ UseLabelPoolFromSegments (Func->V.F.Seg); @@ -696,7 +696,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) } /* Switch back to the old segments */ - PopSegments (); + PopSegContext (); /* Reset the current function pointer */ FreeFunction (CurrentFunc); diff --git a/src/cc65/segments.c b/src/cc65/segments.c index 8283b9da5..764ae29ea 100644 --- a/src/cc65/segments.c +++ b/src/cc65/segments.c @@ -69,11 +69,11 @@ typedef struct { } SegAddrSize_t; -/* Pointer to the current segment list. Output goes here. */ -Segments* CS = 0; +/* Pointer to the current segment context. Output goes here. */ +SegContext* CS = 0; -/* Pointer to the global segment list */ -Segments* GS = 0; +/* Pointer to the global segment context */ +SegContext* GS = 0; /* Actual names for the segments */ static StrStack SegmentNames[SEG_COUNT]; @@ -86,12 +86,12 @@ static Collection SegmentAddrSizes; ** maximum stack depth is 2, so there is not really a need for a better ** implementation. */ -static Collection SegmentStack = STATIC_COLLECTION_INITIALIZER; +static Collection SegContextStack = STATIC_COLLECTION_INITIALIZER; /*****************************************************************************/ -/* Code */ +/* Segment name and address size */ /*****************************************************************************/ @@ -226,11 +226,17 @@ const char* GetSegName (segment_t Seg) -static Segments* NewSegments (SymEntry* Func) -/* Initialize a Segments structure (set all fields to NULL) */ +/*****************************************************************************/ +/* Segment context */ +/*****************************************************************************/ + + + +static SegContext* NewSegContext (SymEntry* Func) +/* Initialize a SegContext structure (set all fields to NULL) */ { /* Allocate memory */ - Segments* S = xmalloc (sizeof (Segments)); + SegContext* S = xmalloc (sizeof (SegContext)); /* Initialize the fields */ S->Text = NewTextSeg (Func); @@ -248,14 +254,14 @@ static Segments* NewSegments (SymEntry* Func) -Segments* PushSegments (SymEntry* Func) -/* Make the new segment list current but remember the old one */ +SegContext* PushSegContext (SymEntry* Func) +/* Make the new segment context current but remember the old one */ { /* Push the current pointer onto the stack */ - CollAppend (&SegmentStack, CS); + CollAppend (&SegContextStack, CS); - /* Create a new Segments structure */ - CS = NewSegments (Func); + /* Create a new SegContext structure */ + CS = NewSegContext (Func); /* Return the new struct */ return CS; @@ -263,14 +269,14 @@ Segments* PushSegments (SymEntry* Func) -void PopSegments (void) -/* Pop the old segment list (make it current) */ +void PopSegContext (void) +/* Pop the old segment context (make it current) */ { /* Must have something on the stack */ - PRECONDITION (CollCount (&SegmentStack) > 0); + PRECONDITION (CollCount (&SegContextStack) > 0); /* Pop the last segment and set it as current */ - CS = CollPop (&SegmentStack); + CS = CollPop (&SegContextStack); } @@ -278,13 +284,13 @@ void PopSegments (void) void CreateGlobalSegments (void) /* Create the global segments and remember them in GS */ { - GS = PushSegments (0); + GS = PushSegContext (0); } void UseDataSeg (segment_t DSeg) -/* For the current segment list, use the data segment DSeg */ +/* For the current segment context, use the data segment DSeg */ { /* Check the input */ PRECONDITION (CS && DSeg != SEG_CODE); @@ -372,7 +378,7 @@ void RemoveGlobalCode (void) -void OutputSegments (const Segments* S) +void OutputSegments (const SegContext* S) /* Output the given segments to the output file */ { /* Output the function prologue if the segments came from a function */ diff --git a/src/cc65/segments.h b/src/cc65/segments.h index 91d702df6..d7c9e3c11 100644 --- a/src/cc65/segments.h +++ b/src/cc65/segments.h @@ -78,8 +78,8 @@ typedef enum segment_t { } segment_t; /* A list of all segments used when generating code */ -typedef struct Segments Segments; -struct Segments { +typedef struct SegContext SegContext; +struct SegContext { struct TextSeg* Text; /* Text segment */ struct CodeSeg* Code; /* Code segment */ struct DataSeg* Data; /* Data segment */ @@ -90,11 +90,11 @@ struct Segments { unsigned NextDataLabel; /* Number to generate unique data labels */ }; -/* Pointer to the current segment list. Output goes here. */ -extern Segments* CS; +/* Pointer to the current segment context. Output goes here. */ +extern SegContext* CS; -/* Pointer to the global segment list */ -extern Segments* GS; +/* Pointer to the global segment context */ +extern SegContext* GS; @@ -132,17 +132,17 @@ void PopSegName (segment_t Seg); const char* GetSegName (segment_t Seg); /* Get the name of the given segment */ -Segments* PushSegments (struct SymEntry* Func); -/* Make the new segment list current but remember the old one */ +SegContext* PushSegContext (struct SymEntry* Func); +/* Make the new segment context current but remember the old one */ -void PopSegments (void); -/* Pop the old segment list (make it current) */ +void PopSegContext (void); +/* Pop the old segment context (make it current) */ void CreateGlobalSegments (void); /* Create the global segments and remember them in GS */ void UseDataSeg (segment_t DSeg); -/* For the current segment list, use the data segment DSeg */ +/* For the current segment context, use the data segment DSeg */ struct DataSeg* GetDataSeg (void); /* Return the current data segment */ @@ -165,7 +165,7 @@ int HaveGlobalCode (void); void RemoveGlobalCode (void); /* Remove all code from the global code segment. Used for error recovery. */ -void OutputSegments (const Segments* S); +void OutputSegments (const SegContext* S); /* Output the given segments to the output file */ diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 5ebd30a75..76a4272ae 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -56,7 +56,7 @@ -struct Segments; +struct SegContext; struct LiteralPool; struct CodeEntry; @@ -156,7 +156,7 @@ struct SymEntry { /* Data for functions */ struct { - struct Segments* Seg; /* Segments for this function */ + struct SegContext* Seg; /* SegContext for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ } F; From cf3a0c4a3053b0fd82de735a7df0d5299a458937 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 13 Oct 2022 13:45:22 +0800 Subject: [PATCH 015/520] Corrected and entailed comments and cleaned up code with type comparison. --- src/cc65/typecmp.c | 100 ++++++++++++++++++++------------------------- src/cc65/typecmp.h | 5 ++- 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index c3239652f..bb8bca880 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -141,7 +141,7 @@ static void SetResult (typecmp_t* Result, typecmpcode_t Val) static typecmp_t* CmpQuals (const Type* lhst, const Type* rhst, typecmp_t* Result) -/* Copare the types regarding thier qualifiers. Return the Result */ +/* Compare the types regarding their qualifiers. Return via pointer *Result */ { TypeCode LeftQual, RightQual; @@ -249,16 +249,10 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Compare two types. Determine, where they differ */ - while (lhs->C != T_END) { - - /* Check if the end of the type string is reached */ - if (rhs->C == T_END) { - /* End of comparison reached */ - break; - } - + while (lhs->C != T_END && rhs->C != T_END) { /* Compare qualifiers */ if (CmpQuals (lhs, rhs, Result)->C == TC_INCOMPATIBLE) { + /* No need to compare further */ return; } @@ -266,6 +260,22 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) LeftRank = (GetUnqualTypeCode (lhs) & T_MASK_RANK); RightRank = (GetUnqualTypeCode (rhs) & T_MASK_RANK); + /* 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) { + /* Incompatible */ + goto Incompatible; + } + if (LeftRank != RightRank) { + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } + /* If one side is a pointer and the other side is an array, both are ** compatible. */ @@ -280,56 +290,35 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } } - /* 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 (LeftRank != RightRank) { - SetResult (Result, TC_STRICT_COMPATIBLE); - } - } - /* If the ranks are different, the types are incompatible */ if (LeftRank != RightRank) { - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } /* Enums must be handled specially */ if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) { /* Compare the tag types */ - Sym1 = IsTypeEnum (lhs) ? GetESUTagSym (lhs) : 0; - Sym2 = IsTypeEnum (rhs) ? GetESUTagSym (rhs) : 0; + Sym1 = GetESUTagSym (lhs); + Sym2 = GetESUTagSym (rhs); + /* For the two to be identical, they must be declared in the same + ** scope and have the same name. + */ if (Sym1 != Sym2) { if (Sym1 == 0 || Sym2 == 0) { - /* Only one is an enum. So they can't be identical */ SetResult (Result, TC_STRICT_COMPATIBLE); - - } else { - /* For the two to be identical, they must be in the same - ** scope and have the same name. + } else if (Sym1->Owner != Sym2->Owner || + strcmp (Sym1->Name, Sym2->Name) != 0) { + /* If any one of the two is incomplete, we can't guess + ** their underlying types and have to assume that they + ** be incompatible. */ - if (Sym1->Owner != Sym2->Owner || - strcmp (Sym1->Name, Sym2->Name) != 0) { - - /* If any one of the two is incomplete, we can't guess - ** their underlying types and have to assume that they - ** be incompatible. - */ - if (SizeOf (lhs) == 0 || SizeOf (rhs) == 0) { - SetResult (Result, TC_INCOMPATIBLE); - return; - } + if (SizeOf (lhs) == 0 || SizeOf (rhs) == 0) { + goto Incompatible; } + SetResult (Result, TC_STRICT_COMPATIBLE); } } @@ -383,15 +372,13 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check the remaining flags */ if ((F1->Flags & ~FD_IGNORE) != (F2->Flags & ~FD_IGNORE)) { /* Flags differ */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } /* Compare the parameter lists */ if (EqualFuncParams (F1, F2) == 0) { /* Parameter list is not identical */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } } @@ -406,8 +393,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) if (LeftCount != UNSPECIFIED && RightCount != UNSPECIFIED) { /* Member count given but different */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } /* We take into account which side is more specified */ @@ -436,8 +422,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* This shouldn't happen in the current code base, but ** we still handle this case to be future-proof. */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } } @@ -453,9 +438,11 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check if lhs and rhs both reached ends */ if (lhs->C == T_END && rhs->C == T_END) { SetResult (Result, TC_IDENTICAL); - } else { - SetResult (Result, TC_INCOMPATIBLE); + return; } + +Incompatible: + SetResult (Result, TC_INCOMPATIBLE); } @@ -483,7 +470,10 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg) -/* Print error or warning message about type compatibility with proper type names */ +/* Print error or warning message about type compatibility with proper type +** names. The format string shall contain two '%s' specifiers for the names of +** the two types. +*/ { StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER; StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER; diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index 367df5245..fa97ca176 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -97,7 +97,10 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs); /* Compare two types and return the result */ void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); -/* Print error or warning message about type compatibility with proper type names */ +/* Print error or warning message about type compatibility with proper type +** names. The format string shall contain two '%s' specifiers for the names of +** the two types. +*/ From 1dccd1333bebca6d1d64e8b5fbf6b83193ac5509 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 12 Oct 2022 13:10:17 +0800 Subject: [PATCH 016/520] Added more type utility functions. Changed result of GetBasicTypeName() for unknown types from "type" to "<type>". --- src/cc65/datatype.c | 43 ++++++++++++++++++++++++++++++++++++++++--- src/cc65/datatype.h | 25 ++++++++++++++++++++++++- src/cc65/expr.c | 12 ++++++------ src/cc65/symtab.c | 11 ++++------- 4 files changed, 74 insertions(+), 17 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 37fe54023..cb013ca21 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -816,6 +816,14 @@ const Type* GetStructReplacementType (const Type* SType) +const Type* GetBitFieldDeclType (const Type* Type) +/* Get the original integer type used to declare the bit-field */ +{ + return Type + 1; +} + + + const Type* GetBitFieldChunkType (const Type* Type) /* Get the type needed to operate on the byte chunk containing the bit-field */ { @@ -864,6 +872,16 @@ int IsTypeFragBitField (const Type* T) +#if !defined(HAVE_INLINE) +int IsTypeFuncLike (const Type* T) +/* Return true if this is a function or a function pointer */ +{ + return IsTypeFunc (T) || IsTypeFuncPtr (T); +} +#endif + + + int IsObjectType (const Type* T) /* Return true if this is a fully described object type */ { @@ -923,6 +941,14 @@ int IsAggregateType (const Type* T) +int IsDerivedDeclaratorType (const Type* T) +/* Return true if this is an array, function or pointer type */ +{ + return IsTypeArray (T) || IsTypeFunc (T) || IsTypePtr (T); +} + + + int IsRelationType (const Type* T) /* Return true if this is an arithmetic, array or pointer type */ { @@ -957,6 +983,17 @@ int IsIncompleteESUType (const Type* T) +int IsPassByRefType (const Type* T) +/* Return true if this is a large struct/union type that doesn't fit in the +** primary. This returns false for the void value extension type since it is +** not passable at all. +*/ +{ + return IsClassStruct (T) && GetStructReplacementType (T) == T; +} + + + int IsEmptiableObjectType (const Type* T) /* Return true if this is a struct/union/void type that can have zero size */ { @@ -1223,7 +1260,7 @@ void SetESUTagSym (Type* T, struct SymEntry* S) const char* GetBasicTypeName (const Type* T) /* Return a const name string of the basic type. -** Return "type" for unknown basic types. +** Return "<type>" for unknown basic types. */ { switch (GetRawTypeRank (T)) { @@ -1273,7 +1310,7 @@ const char* GetBasicTypeName (const Type* T) } } } - return "type"; + return "<type>"; } @@ -1433,7 +1470,7 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu if (!IsTypeBitField (T)) { SB_AppendStr (&Buf, GetTagSymName (T)); } else { - SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); + SB_AppendStr (&Buf, GetBasicTypeName (GetBitFieldDeclType (T))); } if (!SB_IsEmpty (West)) { diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index eebd3abd8..06bb9168b 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -482,6 +482,9 @@ const Type* GetUnderlyingType (const Type* Type); const Type* GetStructReplacementType (const Type* SType); /* Get a replacement type for passing a struct/union by value in the primary */ +const Type* GetBitFieldDeclType (const Type* Type); +/* Get the original integer type used to declare the bit-field */ + const Type* GetBitFieldChunkType (const Type* Type); /* Get the type needed to operate on the byte chunk containing the bit-field */ @@ -690,6 +693,17 @@ INLINE int IsTypeFuncPtr (const Type* T) # define IsTypeFuncPtr(T) (IsTypePtr (T) && IsTypeFunc (T+1)) #endif +#if defined(HAVE_INLINE) +INLINE int IsTypeFuncLike (const Type* T) +/* Return true if this is a function or a function pointer */ +{ + return IsTypeFunc (T) || IsTypeFuncPtr (T); +} +#else +int IsTypeFuncLike (const Type* T); +/* Return true if this is a function or a function pointer */ +#endif + #if defined(HAVE_INLINE) INLINE int IsClassInt (const Type* T) /* Return true if this is an integer type */ @@ -761,6 +775,9 @@ int IsDerivedType (const Type* T); int IsAggregateType (const Type* T); /* Return true if this is an array or struct type */ +int IsDerivedDeclaratorType (const Type* T); +/* Return true if this is an array, function or pointer type */ + int IsRelationType (const Type* T); /* Return true if this is an arithmetic, array or pointer type */ @@ -773,6 +790,12 @@ int IsESUType (const Type* T); int IsIncompleteESUType (const Type* T); /* Return true if this is an incomplete ESU type */ +int IsPassByRefType (const Type* T); +/* Return true if this is a large struct/union type that doesn't fit in the +** primary. This returns false for the void value extension type since it is +** not passable at all. +*/ + int IsEmptiableObjectType (const Type* T); /* Return true if this is a struct/union/void type that can have zero size */ @@ -1024,7 +1047,7 @@ void SetESUTagSym (Type* T, struct SymEntry* S); const char* GetBasicTypeName (const Type* T); /* Return a const name string of the basic type. -** Return "type" for unknown basic types. +** Return "<type>" for unknown basic types. */ const char* GetFullTypeName (const Type* T); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index aef0679d4..4a8fd6eab 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1619,9 +1619,9 @@ static void hie11 (ExprDesc *Expr) break; case TOK_LPAREN: - /* Function call. */ - if (!IsTypeFunc (Expr->Type) && !IsTypeFuncPtr (Expr->Type)) { - /* Not a function */ + /* Function call */ + if (!IsTypeFuncLike (Expr->Type)) { + /* Not a function or function pointer */ Error ("Illegal function call"); /* Force the type to be a implicitly defined function, one ** returning an int and taking any number of arguments. @@ -2035,7 +2035,7 @@ void hie10 (ExprDesc* Expr) ** of dereference operators is legal, since the result will ** always be converted to "pointer to function". */ - if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) { + if (IsTypeFuncLike (Expr->Type)) { /* Expression not storable */ ED_MarkExprAsRVal (Expr); } else { @@ -3216,7 +3216,7 @@ static void parsesub (ExprDesc* Expr) Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* lhs cannot be function or pointer to function */ - if (IsTypeFunc (Expr->Type) || IsTypeFuncPtr (Expr->Type)) { + if (IsTypeFuncLike (Expr->Type)) { Error ("Invalid left operand for binary operator '-'"); /* Make it pointer to char to avoid further errors */ Expr->Type = type_uchar; @@ -3241,7 +3241,7 @@ static void parsesub (ExprDesc* Expr) MarkedExprWithCheck (hie9, &Expr2); /* rhs cannot be function or pointer to function */ - if (IsTypeFunc (Expr2.Type) || IsTypeFuncPtr (Expr2.Type)) { + if (IsTypeFuncLike (Expr2.Type)) { Error ("Invalid right operand for binary operator '-'"); /* Make it pointer to char to avoid further errors */ Expr2.Type = type_uchar; diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 951ed9e5e..b54cba2db 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1009,7 +1009,6 @@ 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 = NewBitFieldOf (T, BitOffs, BitWidth); Entry->V.Offs = Offs; if (!SignednessSpecified) { @@ -1020,12 +1019,10 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, ** is controlled by `--signed-chars`. In bit-fields, however, we perform the same ** `char -> unsigned char` adjustment that is performed with other integral types. */ - CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || - IsRankChar (Entry->Type)); - 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; + CHECK (IsSignSigned (T) || IsRankChar (T)); + Entry->Type = NewBitFieldOf (GetUnsignedType (T), BitOffs, BitWidth); + } else { + Entry->Type = NewBitFieldOf (T, BitOffs, BitWidth); } /* Add the entry to the symbol table */ From 45da20e770fbdd36f5cf313afc6f7e0102ba6d86 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 12 Oct 2022 13:13:37 +0800 Subject: [PATCH 017/520] Fixed CheckedSizeOf() for unknown-size types (no such use case yet though). --- src/cc65/datatype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 37fe54023..2cfb964cc 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -315,7 +315,7 @@ unsigned CheckedSizeOf (const Type* T) { unsigned Size = SizeOf (T); if (Size == 0) { - if (HasUnknownSize (T + 1)) { + if (HasUnknownSize (T)) { Error ("Size of type '%s' is unknown", GetFullTypeName (T)); } else { Error ("Size of type '%s' is 0", GetFullTypeName (T)); From e57c409894ef877bb2d9e5c8cdb04a8f606dacc6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 12 Oct 2022 13:13:15 +0800 Subject: [PATCH 018/520] Fixed TypeHasAttrData(), GetSignedType() and GetUnsignedType(), none in use yet though. --- src/cc65/datatype.c | 16 +++++++++++++--- src/cc65/datatype.h | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 2cfb964cc..3bcca53a9 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -63,6 +63,8 @@ const Type type_char[] = { TYPE(T_CHAR), TYPE(T_END) }; const Type type_schar[] = { TYPE(T_SCHAR), TYPE(T_END) }; const Type type_uchar[] = { TYPE(T_UCHAR), TYPE(T_END) }; +const Type type_short[] = { TYPE(T_SHORT), TYPE(T_END) }; +const Type type_ushort[] = { TYPE(T_USHORT), TYPE(T_END) }; const Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; const Type type_uint[] = { TYPE(T_UINT), TYPE(T_END) }; const Type type_long[] = { TYPE(T_LONG), TYPE(T_END) }; @@ -727,8 +729,10 @@ const Type* GetSignedType (const Type* T) case T_RANK_CHAR: return type_schar; - case T_RANK_INT: case T_RANK_SHORT: + return type_short; + + case T_RANK_INT: return type_int; case T_RANK_LONG: @@ -749,8 +753,10 @@ const Type* GetUnsignedType (const Type* T) case T_RANK_CHAR: return type_uchar; - case T_RANK_INT: case T_RANK_SHORT: + return type_ushort; + + case T_RANK_INT: return type_uint; case T_RANK_LONG: @@ -979,7 +985,11 @@ int HasUnknownSize (const Type* T) int TypeHasAttrData (const Type* T) /* Return true if the given type has attribute data */ { - return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); + return IsClassStruct (T) || + IsTypeArray (T) || + IsClassFunc (T) || + IsTypeVoid (T) || + IsTypeBitField (T); } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index eebd3abd8..9d7b5d49d 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -215,6 +215,8 @@ struct Type { extern const Type type_char[]; extern const Type type_schar[]; extern const Type type_uchar[]; +extern const Type type_short[]; +extern const Type type_ushort[]; extern const Type type_int[]; extern const Type type_uint[]; extern const Type type_long[]; From 03ceeb4ad14fde60594b66e40265caaae9583fad Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 22 Oct 2022 12:45:51 +0800 Subject: [PATCH 019/520] Removed warning on implicit "return 0" in C99 standard main function in default cc65 mode. --- src/cc65/function.c | 53 +++++++++++++++---------------- test/ref/custom-reference-error.c | 8 ++--- test/ref/custom-reference.c | 1 - test/ref/custom-reference.cref | 4 +-- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/cc65/function.c b/src/cc65/function.c index 06800b133..4b4060f2a 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -518,11 +518,21 @@ void NewFunc (SymEntry* Func, FuncDesc* D) Error ("'main' cannot be declared as __fastcall__"); } - /* If cc65 extensions aren't enabled, don't allow a main function that - ** doesn't return an int. - */ - if (IS_Get (&Standard) != STD_CC65 && ReturnType[0].C != T_INT) { - Error ("'main' must always return an int"); + /* Check return type */ + if (GetUnqualRawTypeCode (ReturnType) == T_INT) { + /* Determine if this is a main function in a C99 environment that + ** returns an int. + */ + if (IS_Get (&Standard) >= STD_C99) { + C99MainFunc = 1; + } + } else { + /* If cc65 extensions aren't enabled, don't allow a main function + ** that doesn't return an int. + */ + if (IS_Get (&Standard) != STD_CC65) { + Error ("'main' must always return an int"); + } } /* Add a forced import of a symbol that is contained in the startup @@ -540,14 +550,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* The start-up code doesn't fast-call main(). */ Func->Type->C |= T_QUAL_CDECL; } - - /* Determine if this is a main function in a C99 environment that - ** returns an int. - */ - if (GetUnqualRawTypeCode (ReturnType) == T_INT && - IS_Get (&Standard) == STD_C99) { - C99MainFunc = 1; - } } /* Allocate code and data segments for this function */ @@ -652,21 +654,16 @@ void NewFunc (SymEntry* Func, FuncDesc* D) AnyStatement (0); } - /* If this is not a void function, and not the main function in a C99 - ** environment returning int, output a warning if we didn't see a return - ** statement. - */ - if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc && IS_Get (&WarnReturnType)) { - Warning ("Control reaches end of non-void function [-Wreturn-type]"); - } - - /* If this is the main function in a C99 environment returning an int, let - ** it always return zero. Note: Actual return statements jump to the return - ** label defined below. - ** The code is removed by the optimizer if unused. - */ - if (C99MainFunc) { - g_getimmed (CF_INT | CF_CONST, 0, 0); + /* Check if this function is missing a return value */ + if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc)) { + /* If this is the main function in a C99 environment returning an int, + ** let it always return zero. Otherwise output a warning. + */ + if (C99MainFunc) { + g_getimmed (CF_INT | CF_CONST, 0, 0); + } else if (IS_Get (&WarnReturnType)) { + Warning ("Control reaches end of non-void function [-Wreturn-type]"); + } } /* Output the function exit code label */ diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c index c86a8b9e9..857145fc0 100644 --- a/test/ref/custom-reference-error.c +++ b/test/ref/custom-reference-error.c @@ -13,9 +13,9 @@ and then "make" again to confirm */ -int main(int argc, char* argv[]) +short main(int argc, char* argv[]) { - printf("%02x", 0x42); - n = 0; /* produce an error */ - /* another error */ + printf("%02x", 0x42); /* produce an error */ + n = 0; /* produce an error */ + /* produce a warning */ } diff --git a/test/ref/custom-reference.c b/test/ref/custom-reference.c index 5d9c356df..b48f8eb78 100644 --- a/test/ref/custom-reference.c +++ b/test/ref/custom-reference.c @@ -20,5 +20,4 @@ int main(int argc, char* argv[]) { printf("%02x", 0x42); /* produce a warning */ - return 0; } diff --git a/test/ref/custom-reference.cref b/test/ref/custom-reference.cref index 4dba6009b..868710849 100644 --- a/test/ref/custom-reference.cref +++ b/test/ref/custom-reference.cref @@ -1,2 +1,2 @@ -custom-reference.c:24: Warning: Parameter 'argc' is never used -custom-reference.c:24: Warning: Parameter 'argv' is never used +custom-reference.c:23: Warning: Parameter 'argc' is never used +custom-reference.c:23: Warning: Parameter 'argv' is never used From 67384a29b7789875662617eb7c7b56ff1f892385 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 11:03:57 +0300 Subject: [PATCH 020/520] Start on 65816 --- doc/da65.sgml | 28 +++++++++++++++--- src/da65/code.c | 11 ++++++++ src/da65/code.h | 3 ++ src/da65/handler.c | 57 +++++++++++++++++++++++++++++++------ src/da65/main.c | 9 ++++-- src/da65/opctable.c | 1 + util/parse-bsnes-log.awk | 61 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 156 insertions(+), 14 deletions(-) create mode 100755 util/parse-bsnes-log.awk diff --git a/doc/da65.sgml b/doc/da65.sgml index 185dbe0e6..aa6ad40bf 100644 --- a/doc/da65.sgml +++ b/doc/da65.sgml @@ -115,14 +115,14 @@ Here is a description of all the command line options: <item>6502dtv <item>65sc02 <item>65c02 + <item>65816 <item>huc6280 <item>4510 </itemize> 6502x is for the NMOS 6502 with unofficial opcodes. 6502dtv is for the emulated CPU of the C64DTV device. huc6280 is the CPU of the PC engine. - 4510 is the CPU of the Commodore C65. Support for the 65816 currently - is not available. + 4510 is the CPU of the Commodore C65. 65816 is the CPU of the SNES. <label id="option--formfeeds"> @@ -263,8 +263,9 @@ can produce output that can not be re-assembled, when one or more of those branches point outside of the disassembled memory. This can happen when text or binary data is processed. -While there is some code for the 65816 in the sources, it is currently -unsupported. +The 65816 support requires annotating ranges with the M and X flag states. +This can be recorded with an emulator that supports Code and Data Logging, +for example. Disassemble one bank at a time. <sect1>Attribute map<p> @@ -521,6 +522,11 @@ following attributes are recognized: </descrip> + <tag><tt>ADDRMODE</tt></tag> + When disassembling 65816 code, this specifies the M and X flag states + for this range. It's a string argument of the form "mx", capital letters + mean the flag is enabled. + </descrip> @@ -693,6 +699,20 @@ directives explained above: +<sect>Helper scripts<p> + +<tt>util/parse-bsnes-log.awk</tt> is a supplied script for 65816 disassembly, +to parse bsnes-plus Code-Data log files and output the RANGE sections +for your info file. For typical usage, you'd check the S-CPU log and trace +log mask boxes in the bsnes-plus debugger, play through the game, then grep +for the bank you're disassembling, and pass that to this script. + +<tscreen><verb> +grep ^83 my-game-log | parse-bsnes-log.awk +</verb></tscreen> + + + <sect>Copyright<p> da65 (and all cc65 binutils) is (C) Copyright 1998-2011, Ullrich von diff --git a/src/da65/code.c b/src/da65/code.c index 3fb6a21d3..a162e6482 100644 --- a/src/da65/code.c +++ b/src/da65/code.c @@ -194,6 +194,17 @@ unsigned long GetCodeDWord (unsigned Addr) +unsigned GetCodeLongAddr (unsigned Addr) +/* Get a word from the given address */ +{ + unsigned Lo = GetCodeByte (Addr); + unsigned Mid = GetCodeByte (Addr+1); + unsigned Hi = GetCodeByte (Addr+2); + return Lo | (Mid << 8) | (Hi << 16); +} + + + unsigned GetRemainingBytes (void) /* Return the number of remaining code bytes */ { diff --git a/src/da65/code.h b/src/da65/code.h index 50e68ebdf..aa3c6a290 100644 --- a/src/da65/code.h +++ b/src/da65/code.h @@ -72,6 +72,9 @@ unsigned GetCodeWord (unsigned Addr); unsigned long GetCodeDWord (unsigned Addr); /* Get a dword from the given address */ +unsigned GetCodeLongAddr (unsigned Addr); +/* Get a 24-bit address from the given address */ + unsigned GetRemainingBytes (void); /* Return the number of remaining code bytes */ diff --git a/src/da65/handler.c b/src/da65/handler.c index 255b8da86..605d6219d 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -121,8 +121,10 @@ static const char* GetAddrArg (unsigned Flags, unsigned Addr) static char Buf [32]; if (Addr < 0x100) { xsprintf (Buf, sizeof (Buf), "$%02X", Addr); - } else { + } else if (Addr < 0x10000) { xsprintf (Buf, sizeof (Buf), "$%04X", Addr); + } else { + xsprintf (Buf, sizeof (Buf), "$%06X", Addr); } return Buf; } @@ -322,14 +324,28 @@ void OH_AbsoluteY (const OpcDesc* D) void OH_AbsoluteLong (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeLongAddr (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "%s%s", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteLongX (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeLongAddr (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "%s%s,x", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } @@ -358,7 +374,17 @@ void OH_Relative (const OpcDesc* D) void OH_RelativeLong (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + signed short Offs = GetCodeWord (PC+1); + + /* Calculate the target address */ + unsigned Addr = (((int) PC+3) + Offs) & 0xFFFF; + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } @@ -541,14 +567,15 @@ void OH_ImmediateAbsoluteX (const OpcDesc* D) void OH_StackRelative (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Output the line */ + OneLine (D, "$%02X,s", GetCodeByte (PC+1)); } void OH_DirectIndirectLongX (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + Error ("Not implemented %s", __FUNCTION__); } @@ -571,14 +598,28 @@ void OH_StackRelativeIndirectY4510 (const OpcDesc* D attribute ((unused))) void OH_DirectIndirectLong (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeByte (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "[%s]", GetAddrArg (D->Flags, Addr)); } void OH_DirectIndirectLongY (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeByte (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "[%s],y", GetAddrArg (D->Flags, Addr)); } diff --git a/src/da65/main.c b/src/da65/main.c index 1fc07f006..772846ad9 100644 --- a/src/da65/main.c +++ b/src/da65/main.c @@ -427,8 +427,13 @@ static void OneOpcode (unsigned RemainingBytes) switch (Style) { case atDefault: - D->Handler (D); - PC += D->Size; + if (CPU == CPU_65816) { + DataByteLine (1); + ++PC; + } else { + D->Handler (D); + PC += D->Size; + } break; case atCode: diff --git a/src/da65/opctable.c b/src/da65/opctable.c index 255a3557f..d9068d253 100644 --- a/src/da65/opctable.c +++ b/src/da65/opctable.c @@ -74,6 +74,7 @@ void SetOpcTable (cpu_t CPU) case CPU_6502DTV: OpcTable = OpcTable_6502DTV; break; case CPU_65SC02: OpcTable = OpcTable_65SC02; break; case CPU_65C02: OpcTable = OpcTable_65C02; break; + case CPU_65816: OpcTable = OpcTable_65816; break; case CPU_HUC6280: OpcTable = OpcTable_HuC6280; break; case CPU_M740: OpcTable = OpcTable_M740; break; case CPU_4510: OpcTable = OpcTable_4510; break; diff --git a/util/parse-bsnes-log.awk b/util/parse-bsnes-log.awk new file mode 100755 index 000000000..d6a014165 --- /dev/null +++ b/util/parse-bsnes-log.awk @@ -0,0 +1,61 @@ +#!/usr/bin/awk -nf +# +# Parse the bsnes-plus cpu log for the 65816 address mode ranges. +# Since the log doesn't show the op size, we have to be careful +# and add +3 to the range end - this may need manual cleanup. +# +# Grep the input for only one bank's content beforehand. +# +# (C) Lauri Kasanen, under the cc65 license +# + +{ + addr = $1 + + IGNORECASE = 1 + if ($9 ~ /^nv/) { + val = $9 + } else if ($10 ~ /^nv/) { + val = $10 + } else { + val = $11 + } + IGNORECASE = 0 + + val = substr(val, 3, 2) + if (val == "1B") # emulation mode + val = "MX" + + addrs["0x" substr(addr, 3)] = val +} + +END { + PROCINFO["sorted_in"] = "@ind_num_asc" + + start = -1 + prevval = "" + + for (addr in addrs) { + cur = addrs[addr] + if (start == -1) { # first range + start = addr + } else { + if (prevval != cur || addr - prevaddr > 4) { # start a new range + #print "diff " addr - prevaddr ", addr " addr ", pa " prevaddr + if (prevaddr + 3 >= addr + 0) + fmt = sprintf("%04x", addr - 1) + else + fmt = sprintf("%04x", prevaddr + 3) + print "RANGE { START $" substr(start, 3) "; END $" fmt "; ADDRMODE \"" prevval "\"; TYPE Code;};" + start = addr + } + } + #print "\t" addr " " addrs[addr] + + prevval = cur + prevaddr = addr + } + + fmt = sprintf("%04x", prevaddr + 3) + print "RANGE { START $" substr(start, 3) "; END $" fmt "; ADDRMODE \"" prevval "\"; TYPE Code;};" +} From ea924ededd0ed3d5ba1bdb133550644c54192d08 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 11:41:20 +0300 Subject: [PATCH 021/520] Teach attrtab, labels and comments about long addresses --- src/da65/attrtab.c | 58 +++++++++++++++++++++++++++++++-- src/da65/attrtab.h | 3 ++ src/da65/comments.c | 47 ++++++++++++++++++++++++--- src/da65/labels.c | 79 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 169 insertions(+), 18 deletions(-) diff --git a/src/da65/attrtab.c b/src/da65/attrtab.c index a9143584a..b70e017a1 100644 --- a/src/da65/attrtab.c +++ b/src/da65/attrtab.c @@ -34,6 +34,7 @@ /* da65 */ +#include "cpu.h" #include "error.h" #include "attrtab.h" @@ -48,6 +49,12 @@ /* Attribute table */ static unsigned short AttrTab[0x10000]; +/* 65816 attribute table */ +#define MAX_LONG_ATTRS 256 +static unsigned short LongAttrVal[MAX_LONG_ATTRS]; +static unsigned LongAttrAddr[MAX_LONG_ATTRS]; +static unsigned LongAttrsUsed; + /*****************************************************************************/ @@ -59,12 +66,19 @@ static unsigned short AttrTab[0x10000]; void AddrCheck (unsigned Addr) /* Check if the given address has a valid range */ { - if (Addr >= 0x10000) { + if (Addr >= 0x10000 && CPU != CPU_65816) { Error ("Address out of range: %08X", Addr); } } +unsigned char IsLongAddr (unsigned Addr) +/* Is it 24-bit? */ +{ + return Addr >= 0x10000 && CPU == CPU_65816; +} + + attr_t GetAttr (unsigned Addr) /* Return the attribute for the given address */ @@ -72,6 +86,17 @@ attr_t GetAttr (unsigned Addr) /* Check the given address */ AddrCheck (Addr); + if (IsLongAddr (Addr)) { + unsigned i; + for (i = 0; i < LongAttrsUsed; i++) { + if (LongAttrAddr[i] == Addr) { + return LongAttrVal[i]; + } + } + + return 0; + } + /* Return the attribute */ return AttrTab[Addr]; } @@ -148,6 +173,33 @@ void MarkAddr (unsigned Addr, attr_t Attr) /* Check the given address */ AddrCheck (Addr); + if (IsLongAddr (Addr)) { + unsigned i; + for (i = 0; i < LongAttrsUsed; i++) { + if (LongAttrAddr[i] == Addr) { + + /* We must not have more than one style bit */ + if (Attr & atStyleMask) { + if (LongAttrVal[i] & atStyleMask) { + Error ("Duplicate style for long address %06X", Addr); + } + } + LongAttrVal[i] |= Attr; + + return; + } + } + + if (LongAttrsUsed >= MAX_LONG_ATTRS) { + Error ("Too many long addresses"); + } + LongAttrVal[LongAttrsUsed] |= Attr; + LongAttrAddr[LongAttrsUsed] = Addr; + LongAttrsUsed++; + + return; + } + /* We must not have more than one style bit */ if (Attr & atStyleMask) { if (AttrTab[Addr] & atStyleMask) { @@ -168,7 +220,7 @@ attr_t GetStyleAttr (unsigned Addr) AddrCheck (Addr); /* Return the attribute */ - return (AttrTab[Addr] & atStyleMask); + return (GetAttr (Addr) & atStyleMask); } @@ -180,5 +232,5 @@ attr_t GetLabelAttr (unsigned Addr) AddrCheck (Addr); /* Return the attribute */ - return (AttrTab[Addr] & atLabelMask); + return (GetAttr (Addr) & atLabelMask); } diff --git a/src/da65/attrtab.h b/src/da65/attrtab.h index 18515ce49..c9af89a4d 100644 --- a/src/da65/attrtab.h +++ b/src/da65/attrtab.h @@ -88,6 +88,9 @@ typedef enum attr_t { void AddrCheck (unsigned Addr); /* Check if the given address has a valid range */ +unsigned char IsLongAddr (unsigned Addr); +/* Check if the given address is 24-bit */ + attr_t GetAttr (unsigned Addr); /* Return the attribute for the given address */ diff --git a/src/da65/comments.c b/src/da65/comments.c index 7c671131f..f136ae3d2 100644 --- a/src/da65/comments.c +++ b/src/da65/comments.c @@ -52,6 +52,11 @@ /* Comment table */ static const char* CommentTab[0x10000]; +#define MAX_LONG_COMMENTS 256 +static const char* LongCommentVal[MAX_LONG_COMMENTS]; +static unsigned LongCommentAddr[MAX_LONG_COMMENTS]; +static unsigned LongCommentsUsed; + /*****************************************************************************/ @@ -60,17 +65,43 @@ static const char* CommentTab[0x10000]; +static unsigned FindLongIndex (unsigned Addr) +{ + unsigned i; + for (i = 0; i < LongCommentsUsed; i++) { + if (LongCommentAddr[i] == Addr) { + return i; + } + } + return -1; +} + + + void SetComment (unsigned Addr, const char* Comment) /* Set a comment for the given address */ { /* Check the given address */ AddrCheck (Addr); - /* If we do already have a comment, warn and ignore the new one */ - if (CommentTab[Addr]) { - Warning ("Duplicate comment for address $%04X", Addr); + if (IsLongAddr (Addr)) { + if (FindLongIndex (Addr)) { + Warning ("Duplicate comment for address $%06X", Addr); + } else { + if (LongCommentsUsed >= MAX_LONG_COMMENTS) { + Error("Too many long-address comments"); + } + LongCommentVal[LongCommentsUsed] = xstrdup (Comment); + LongCommentAddr[LongCommentsUsed] = Addr; + LongCommentsUsed++; + } } else { - CommentTab[Addr] = xstrdup (Comment); + /* If we do already have a comment, warn and ignore the new one */ + if (CommentTab[Addr]) { + Warning ("Duplicate comment for address $%04X", Addr); + } else { + CommentTab[Addr] = xstrdup (Comment); + } } } @@ -82,6 +113,14 @@ const char* GetComment (unsigned Addr) /* Check the given address */ AddrCheck (Addr); + if (IsLongAddr (Addr)) { + const unsigned i = FindLongIndex (Addr); + if (i < LongCommentsUsed) { + return LongCommentVal[i]; + } + return NULL; + } + /* Return the label if any */ return CommentTab[Addr]; } diff --git a/src/da65/labels.c b/src/da65/labels.c index 542205c11..5ca303a33 100644 --- a/src/da65/labels.c +++ b/src/da65/labels.c @@ -60,6 +60,12 @@ /* Symbol table */ static const char* SymTab[0x10000]; +/* 65816 symbol table */ +#define MAX_LONG_LABELS 256 +static const char* LongSymVal[MAX_LONG_LABELS]; +static unsigned LongSymAddr[MAX_LONG_LABELS]; +static unsigned LongLabelsUsed; + /*****************************************************************************/ @@ -74,12 +80,27 @@ static const char* MakeLabelName (unsigned Addr) */ { static char LabelBuf [32]; - xsprintf (LabelBuf, sizeof (LabelBuf), "L%04X", Addr); + xsprintf (LabelBuf, sizeof (LabelBuf), + IsLongAddr (Addr) ? "L%06X" : "L%04X", Addr); return LabelBuf; } +static unsigned FindLongIndex (unsigned Addr) +{ + unsigned i; + for (i = 0; i < LongLabelsUsed; i++) { + if (LongSymAddr[i] == Addr) { + return i; + } + } + + return -1; +} + + + static void AddLabel (unsigned Addr, attr_t Attr, const char* Name) /* Add a label */ { @@ -91,19 +112,41 @@ static void AddLabel (unsigned Addr, attr_t Attr, const char* Name) /* Allow redefinition if identical. Beware: Unnamed labels don't ** have a name (you guessed that, didn't you?). */ - if (ExistingAttr == Attr && - ((Name == 0 && SymTab[Addr] == 0) || - (Name != 0 && SymTab[Addr] != 0 && - strcmp (SymTab[Addr], Name) == 0))) { - return; + if (IsLongAddr (Addr)) { + const unsigned i = FindLongIndex (Addr); + if (ExistingAttr == Attr && + ((Name == 0 && LongSymVal[i] == 0) || + (Name != 0 && LongSymVal[i] != 0 && + strcmp (LongSymVal[i], Name) == 0))) { + return; + } + Error ("Duplicate label for address $%06X (%s): '%s'", Addr, + LongSymVal[i] == 0 ? "<unnamed label>" : LongSymVal[i], + Name == 0 ? "<unnamed label>" : Name); + } else { + if (ExistingAttr == Attr && + ((Name == 0 && SymTab[Addr] == 0) || + (Name != 0 && SymTab[Addr] != 0 && + strcmp (SymTab[Addr], Name) == 0))) { + return; + } + Error ("Duplicate label for address $%04X (%s): '%s'", Addr, + SymTab[Addr] == 0 ? "<unnamed label>" : SymTab[Addr], + Name == 0 ? "<unnamed label>" : Name); } - Error ("Duplicate label for address $%04X (%s): '%s'", Addr, - SymTab[Addr] == 0 ? "<unnamed label>" : SymTab[Addr], - Name == 0 ? "<unnamed label>" : Name); } /* Create a new label (xstrdup will return NULL if input NULL) */ - SymTab[Addr] = xstrdup (Name); + if (IsLongAddr (Addr)) { + if (LongLabelsUsed >= MAX_LONG_LABELS) { + Error ("Too many long labels"); + } + LongSymAddr[LongLabelsUsed] = Addr; + LongSymVal[LongLabelsUsed] = xstrdup (Name); + LongLabelsUsed++; + } else { + SymTab[Addr] = xstrdup (Name); + } /* Remember the attribute */ MarkAddr (Addr, Attr); @@ -254,6 +297,10 @@ const char* GetLabelName (unsigned Addr) */ if (A == atUnnamedLabel) { return ""; + } else if (IsLongAddr (Addr)) { + /* Return the label if any */ + const unsigned i = FindLongIndex (Addr); + return i < LongLabelsUsed ? LongSymVal[i] : NULL; } else { /* Return the label if any */ return SymTab[Addr]; @@ -327,6 +374,10 @@ const char* GetLabel (unsigned Addr, unsigned RefFrom) return FwdLabels[Count-1]; } + } else if (IsLongAddr (Addr)) { + /* Return the label if any */ + const unsigned i = FindLongIndex (Addr); + return i < LongLabelsUsed ? LongSymVal[i] : NULL; } else { /* Return the label if any */ return SymTab[Addr]; @@ -371,7 +422,13 @@ static void DefOutOfRangeLabel (unsigned long Addr) case atIntLabel: case atExtLabel: - DefConst (SymTab[Addr], GetComment (Addr), Addr); + if (IsLongAddr (Addr)) { + const unsigned i = FindLongIndex (Addr); + DefConst (i < LongLabelsUsed ? LongSymVal[i] : NULL, + GetComment (Addr), Addr); + } else { + DefConst (SymTab[Addr], GetComment (Addr), Addr); + } break; case atUnnamedLabel: From 89fc5e30c49439f178279be783ef2bac14e05638 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 12:19:01 +0300 Subject: [PATCH 022/520] Remove unused and conflicting value --- src/da65/attrtab.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/da65/attrtab.h b/src/da65/attrtab.h index c9af89a4d..847f44b8a 100644 --- a/src/da65/attrtab.h +++ b/src/da65/attrtab.h @@ -66,8 +66,6 @@ typedef enum attr_t { atDepLabel = 0x0040, /* Dependent label */ atUnnamedLabel = 0x0080, /* Unnamed label */ - atLabelDefined = 0x0100, /* True if we defined the label */ - atStyleMask = 0x000F, /* Output style */ atLabelMask = 0x00F0, /* Label information */ From e8ee8435e90c191f5e74c4a73e80ad0b708da27d Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 12:36:26 +0300 Subject: [PATCH 023/520] Add addrMode to RANGE --- src/da65/attrtab.h | 15 ++++++++-- src/da65/infofile.c | 68 +++++++++++++++++++++++++++++++++++++-------- src/da65/scanner.h | 1 + 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/da65/attrtab.h b/src/da65/attrtab.h index 847f44b8a..37143c0d1 100644 --- a/src/da65/attrtab.h +++ b/src/da65/attrtab.h @@ -66,13 +66,22 @@ typedef enum attr_t { atDepLabel = 0x0040, /* Dependent label */ atUnnamedLabel = 0x0080, /* Unnamed label */ - atStyleMask = 0x000F, /* Output style */ - atLabelMask = 0x00F0, /* Label information */ - /* Segment */ atSegment = 0x0100, /* Code is in a segment */ atSegmentEnd = 0x0200, /* Segment end */ atSegmentStart = 0x0400, /* Segment start */ + + /* 65816 addressing mode */ + atMem8 = 0x1000, /* M flag enabled, 8-bit */ + atMem16 = 0x2000, /* M flag disabled, 16-bit */ + atIdx8 = 0x4000, /* X flag enabled, 8-bit */ + atIdx16 = 0x8000, /* X flag disabled, 16-bit */ + + atStyleMask = 0x000F, /* Output style */ + atLabelMask = 0x00F0, /* Label information */ + atSegmentMask = 0x0F00, /* Segment information */ + at65816Mask = 0xF000, /* 65816 information */ + } attr_t; diff --git a/src/da65/infofile.c b/src/da65/infofile.c index 48a95c9b0..923cc53c9 100644 --- a/src/da65/infofile.c +++ b/src/da65/infofile.c @@ -35,6 +35,7 @@ #include <stdio.h> #include <string.h> +#include <ctype.h> #include <limits.h> #if defined(_MSC_VER) /* Microsoft compiler */ @@ -521,11 +522,12 @@ static void RangeSection (void) /* Parse a range section */ { static const IdentTok RangeDefs[] = { - { "COMMENT", INFOTOK_COMMENT }, - { "END", INFOTOK_END }, - { "NAME", INFOTOK_NAME }, - { "START", INFOTOK_START }, - { "TYPE", INFOTOK_TYPE }, + { "COMMENT", INFOTOK_COMMENT }, + { "END", INFOTOK_END }, + { "NAME", INFOTOK_NAME }, + { "START", INFOTOK_START }, + { "TYPE", INFOTOK_TYPE }, + { "ADDRMODE", INFOTOK_ADDRMODE }, }; static const IdentTok TypeDefs[] = { @@ -543,12 +545,13 @@ static void RangeSection (void) /* Which values did we get? */ enum { - tNone = 0x00, - tStart = 0x01, - tEnd = 0x02, - tType = 0x04, - tName = 0x08, - tComment= 0x10, + tNone = 0x00, + tStart = 0x01, + tEnd = 0x02, + tType = 0x04, + tName = 0x08, + tComment = 0x10, + tAddrMode = 0x20, tNeeded = (tStart | tEnd | tType) }; unsigned Attributes = tNone; @@ -557,6 +560,7 @@ static void RangeSection (void) unsigned Start = 0; unsigned End = 0; unsigned char Type = 0; + unsigned AddrMode = 0; char* Name = 0; char* Comment = 0; unsigned MemberSize = 0; @@ -647,6 +651,37 @@ static void RangeSection (void) InfoNextTok (); break; + case INFOTOK_ADDRMODE: + AddAttr ("ADDRMODE", &Attributes, tAddrMode); + InfoNextTok (); + InfoAssureStr (); + if (InfoSVal[0] == '\0') { + InfoError ("AddrMode may not be empty"); + } + if (InfoSVal[1] == '\0') { + InfoError ("AddrMode must be two characters long"); + } + if (tolower(InfoSVal[0]) == 'm') { + if (InfoSVal[0] == 'm') { + AddrMode = atMem16; + } else { + AddrMode = atMem8; + } + } else { + InfoError ("AddrMode syntax: mx"); + } + if (tolower(InfoSVal[1]) == 'x') { + if (InfoSVal[1] == 'x') { + AddrMode |= atIdx16; + } else { + AddrMode |= atIdx8; + } + } else { + InfoError ("AddrMode syntax: mx"); + } + InfoNextTok (); + break; + default: Internal ("Unexpected token: %u", InfoTok); } @@ -661,6 +696,15 @@ static void RangeSection (void) InfoError ("Required values missing from this section"); } + if (CPU == CPU_65816) { + if (Type == atCode && !(Attributes & tAddrMode)) { + InfoError ("65816 code sections require addressing mode"); + } + if (Type != atCode && (Attributes & tAddrMode)) { + InfoError ("AddrMode is only valid for code sections"); + } + } + /* Start must be less than end */ if (Start > End) { InfoError ("Start value must not be greater than end value"); @@ -672,7 +716,7 @@ static void RangeSection (void) } /* Set the range */ - MarkRange (Start, End, Type); + MarkRange (Start, End, Type | AddrMode); /* Do we have a label? */ if (Attributes & tName) { diff --git a/src/da65/scanner.h b/src/da65/scanner.h index 63d3273f6..60648a40c 100644 --- a/src/da65/scanner.h +++ b/src/da65/scanner.h @@ -90,6 +90,7 @@ typedef enum token_t { INFOTOK_START, INFOTOK_END, INFOTOK_TYPE, + INFOTOK_ADDRMODE, INFOTOK_CODE, INFOTOK_BYTETAB, From 1294c1c2539396d79a280fb2d51bd4d50ad9e025 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 14:45:10 +0300 Subject: [PATCH 024/520] Implement changing-size opcodes --- src/da65/handler.c | 22 ++++++++++++++++++++++ src/da65/handler.h | 2 ++ src/da65/main.c | 9 +++++++++ src/da65/opc65816.c | 24 ++++++++++++------------ src/da65/opcdesc.h | 3 ++- 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/da65/handler.c b/src/da65/handler.c index 605d6219d..97c79db6d 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -231,6 +231,28 @@ void OH_Immediate (const OpcDesc* D) +void OH_Immediate65816M (const OpcDesc* D) +{ + if (GetAttr (PC) & atMem16) { + OneLine (D, "#$%04X", GetCodeWord (PC+1)); + } else { + OneLine (D, "#$%02X", GetCodeByte (PC+1)); + } +} + + + +void OH_Immediate65816X (const OpcDesc* D) +{ + if (GetAttr (PC) & atIdx16) { + OneLine (D, "#$%04X", GetCodeWord (PC+1)); + } else { + OneLine (D, "#$%02X", GetCodeByte (PC+1)); + } +} + + + void OH_ImmediateWord (const OpcDesc* D) { OneLine (D, "#$%04X", GetCodeWord (PC+1)); diff --git a/src/da65/handler.h b/src/da65/handler.h index eaa66e7fd..2bfaed9c3 100644 --- a/src/da65/handler.h +++ b/src/da65/handler.h @@ -57,6 +57,8 @@ void OH_Illegal (const OpcDesc* D attribute ((unused))); void OH_Accumulator (const OpcDesc*); void OH_Implicit (const OpcDesc*); void OH_Immediate (const OpcDesc*); +void OH_Immediate65816M (const OpcDesc*); +void OH_Immediate65816X (const OpcDesc*); void OH_ImmediateWord (const OpcDesc*); void OH_Direct (const OpcDesc*); void OH_DirectX (const OpcDesc*); diff --git a/src/da65/main.c b/src/da65/main.c index 772846ad9..8d68dc4cc 100644 --- a/src/da65/main.c +++ b/src/da65/main.c @@ -55,6 +55,7 @@ #include "data.h" #include "error.h" #include "global.h" +#include "handler.h" #include "infofile.h" #include "labels.h" #include "opctable.h" @@ -447,6 +448,14 @@ static void OneOpcode (unsigned RemainingBytes) } /* Output the insn */ D->Handler (D); + if (CPU == CPU_65816 && (D->Flags & flSizeChanges)) { + if ((D->Handler == OH_Immediate65816M && + GetAttr (PC) & atMem16) || + (D->Handler == OH_Immediate65816X && + GetAttr (PC) & atIdx16)) { + PC++; + } + } PC += D->Size; break; } diff --git a/src/da65/opc65816.c b/src/da65/opc65816.c index b7775d2e2..877e5536f 100644 --- a/src/da65/opc65816.c +++ b/src/da65/opc65816.c @@ -56,7 +56,7 @@ const OpcDesc OpcTable_65816[256] = { { "asl", 2, flUseLabel, OH_Direct }, /* $06 */ { "ora", 2, flUseLabel, OH_DirectIndirectLong }, /* $07 */ { "php", 1, flNone, OH_Implicit }, /* $08 */ - { "ora", 2, flNone, OH_Immediate }, /* $09 */ + { "ora", 2, flSizeChanges, OH_Immediate65816M }, /* $09 */ { "asl", 1, flNone, OH_Accumulator }, /* $0a */ { "phd", 1, flNone, OH_Implicit }, /* $0b */ { "tsb", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0c */ @@ -88,7 +88,7 @@ const OpcDesc OpcTable_65816[256] = { { "rol", 2, flUseLabel, OH_Direct }, /* $26 */ { "and", 2, flUseLabel, OH_DirectIndirectLong }, /* $27 */ { "plp", 1, flNone, OH_Implicit }, /* $28 */ - { "and", 2, flNone, OH_Immediate }, /* $29 */ + { "and", 2, flSizeChanges, OH_Immediate65816M }, /* $29 */ { "rol", 1, flNone, OH_Accumulator }, /* $2a */ { "pld", 1, flNone, OH_Implicit }, /* $2b */ { "bit", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2c */ @@ -120,7 +120,7 @@ const OpcDesc OpcTable_65816[256] = { { "lsr", 2, flUseLabel, OH_Direct }, /* $46 */ { "eor", 2, flUseLabel, OH_DirectIndirectLong }, /* $47 */ { "pha", 1, flNone, OH_Implicit }, /* $48 */ - { "eor", 2, flNone, OH_Immediate }, /* $49 */ + { "eor", 2, flSizeChanges, OH_Immediate65816M }, /* $49 */ { "lsr", 1, flNone, OH_Accumulator }, /* $4a */ { "phk", 1, flNone, OH_Implicit }, /* $4b */ { "jmp", 3, flLabel, OH_JmpAbsolute }, /* $4c */ @@ -152,7 +152,7 @@ const OpcDesc OpcTable_65816[256] = { { "ror", 2, flUseLabel, OH_Direct }, /* $66 */ { "adc", 2, flUseLabel, OH_DirectIndirectLong }, /* $67 */ { "pla", 1, flNone, OH_Implicit }, /* $68 */ - { "adc", 2, flNone, OH_Immediate }, /* $69 */ + { "adc", 2, flSizeChanges, OH_Immediate65816M }, /* $69 */ { "ror", 1, flNone, OH_Accumulator }, /* $6a */ { "rtl", 1, flNone, OH_Implicit }, /* $6b */ { "jmp", 3, flLabel, OH_JmpAbsoluteIndirect }, /* $6c */ @@ -184,7 +184,7 @@ const OpcDesc OpcTable_65816[256] = { { "stx", 2, flUseLabel, OH_Direct }, /* $86 */ { "sta", 2, flUseLabel, OH_DirectIndirectLong }, /* $87 */ { "dey", 1, flNone, OH_Implicit }, /* $88 */ - { "bit", 2, flNone, OH_Immediate }, /* $89 */ + { "bit", 2, flSizeChanges, OH_Immediate65816M }, /* $89 */ { "txa", 1, flNone, OH_Implicit }, /* $8a */ { "phb", 1, flNone, OH_Implicit }, /* $8b */ { "sty", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8c */ @@ -207,16 +207,16 @@ const OpcDesc OpcTable_65816[256] = { { "sta", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9d */ { "stz", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9e */ { "sta", 4, flUseLabel, OH_AbsoluteLongX }, /* $9f */ - { "ldy", 2, flNone, OH_Immediate }, /* $a0 */ + { "ldy", 2, flSizeChanges, OH_Immediate65816X }, /* $a0 */ { "lda", 2, flUseLabel, OH_DirectXIndirect }, /* $a1 */ - { "ldx", 2, flNone, OH_Immediate }, /* $a2 */ + { "ldx", 2, flSizeChanges, OH_Immediate65816X }, /* $a2 */ { "lda", 2, flNone, OH_StackRelative }, /* $a3 */ { "ldy", 2, flUseLabel, OH_Direct }, /* $a4 */ { "lda", 2, flUseLabel, OH_Direct }, /* $a5 */ { "ldx", 2, flUseLabel, OH_Direct }, /* $a6 */ { "lda", 2, flUseLabel, OH_DirectIndirectLong }, /* $a7 */ { "tay", 1, flNone, OH_Implicit }, /* $a8 */ - { "lda", 2, flNone, OH_Immediate }, /* $a9 */ + { "lda", 2, flSizeChanges, OH_Immediate65816M }, /* $a9 */ { "tax", 1, flNone, OH_Implicit }, /* $aa */ { "plb", 1, flNone, OH_Implicit }, /* $ab */ { "ldy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ac */ @@ -239,7 +239,7 @@ const OpcDesc OpcTable_65816[256] = { { "lda", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bd */ { "ldx", 3, flUseLabel|flAbsOverride, OH_AbsoluteY }, /* $be */ { "lda", 4, flUseLabel, OH_AbsoluteLongX }, /* $bf */ - { "cpy", 2, flNone, OH_Immediate }, /* $c0 */ + { "cpy", 2, flSizeChanges, OH_Immediate65816X }, /* $c0 */ { "cmp", 2, flUseLabel, OH_DirectXIndirect }, /* $c1 */ { "rep", 2, flNone, OH_Immediate }, /* $c2 */ { "cmp", 2, flNone, OH_StackRelative }, /* $c3 */ @@ -248,7 +248,7 @@ const OpcDesc OpcTable_65816[256] = { { "dec", 2, flUseLabel, OH_Direct }, /* $c6 */ { "cmp", 2, flUseLabel, OH_DirectIndirectLong }, /* $c7 */ { "iny", 1, flNone, OH_Implicit }, /* $c8 */ - { "cmp", 2, flNone, OH_Immediate }, /* $c9 */ + { "cmp", 2, flSizeChanges, OH_Immediate65816M }, /* $c9 */ { "dex", 1, flNone, OH_Implicit }, /* $ca */ { "wai", 1, flNone, OH_Implicit }, /* $cb */ { "cpy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cc */ @@ -271,7 +271,7 @@ const OpcDesc OpcTable_65816[256] = { { "cmp", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $dd */ { "dec", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $de */ { "cmp", 4, flUseLabel, OH_AbsoluteLongX }, /* $df */ - { "cpx", 2, flNone, OH_Immediate }, /* $e0 */ + { "cpx", 2, flSizeChanges, OH_Immediate65816X }, /* $e0 */ { "sbc", 2, flUseLabel, OH_DirectXIndirect }, /* $e1 */ { "sep", 2, flNone, OH_Immediate }, /* $e2 */ { "sbc", 2, flNone, OH_StackRelative }, /* $e3 */ @@ -280,7 +280,7 @@ const OpcDesc OpcTable_65816[256] = { { "inc", 2, flUseLabel, OH_Direct }, /* $e6 */ { "sbc", 2, flUseLabel, OH_DirectIndirectLong }, /* $e7 */ { "inx", 1, flNone, OH_Implicit }, /* $e8 */ - { "sbc", 2, flNone, OH_Immediate }, /* $e9 */ + { "sbc", 2, flSizeChanges, OH_Immediate65816M }, /* $e9 */ { "nop", 1, flNone, OH_Implicit }, /* $ea */ { "xba", 1, flNone, OH_Implicit }, /* $eb */ { "cpx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ec */ diff --git a/src/da65/opcdesc.h b/src/da65/opcdesc.h index 399a0962d..6baffe377 100644 --- a/src/da65/opcdesc.h +++ b/src/da65/opcdesc.h @@ -53,7 +53,8 @@ enum { flLabel = flUseLabel|flGenLabel, /* Generate and use a label */ flIllegal = 0x10, /* Illegal instruction */ flAbsOverride = 0x20, /* Need a: override */ - flFarOverride = 0x40 /* Need f: override */ + flFarOverride = 0x40, /* Need f: override */ + flSizeChanges = 0x80 /* 65816: size may change */ }; /* Forward/typedef for struct OpcDesc */ From 4b04f81d3773321a14d998915d40b408c5e7d4e1 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 14:59:51 +0300 Subject: [PATCH 025/520] Fix jsl length --- src/da65/opc65816.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/da65/opc65816.c b/src/da65/opc65816.c index 877e5536f..7457a25c2 100644 --- a/src/da65/opc65816.c +++ b/src/da65/opc65816.c @@ -81,7 +81,7 @@ const OpcDesc OpcTable_65816[256] = { { "ora", 4, flUseLabel, OH_AbsoluteLongX }, /* $1f */ { "jsr", 3, flLabel, OH_JsrAbsolute }, /* $20 */ { "and", 2, flUseLabel, OH_DirectXIndirect }, /* $21 */ - { "jsl", 3, flLabel, OH_AbsoluteLong }, /* $22 */ + { "jsl", 4, flLabel, OH_AbsoluteLong }, /* $22 */ { "and", 2, flNone, OH_StackRelative }, /* $23 */ { "bit", 2, flUseLabel, OH_Direct }, /* $24 */ { "and", 2, flUseLabel, OH_Direct }, /* $25 */ From 65907b1f105b52af2b9b263425056cbea2019587 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 15:16:24 +0300 Subject: [PATCH 026/520] Output MX states --- src/da65/main.c | 20 ++++++++++++++++++++ src/da65/output.c | 20 ++++++++++++++++++++ src/da65/output.h | 6 ++++++ 3 files changed, 46 insertions(+) diff --git a/src/da65/main.c b/src/da65/main.c index 8d68dc4cc..67a01dc3b 100644 --- a/src/da65/main.c +++ b/src/da65/main.c @@ -64,6 +64,8 @@ #include "segment.h" +static unsigned PrevAddrMode; + /*****************************************************************************/ /* Code */ @@ -442,6 +444,22 @@ static void OneOpcode (unsigned RemainingBytes) ** following insn, fall through to byte mode. */ if (D->Size <= RemainingBytes) { + if (CPU == CPU_65816) { + const unsigned AddrMode = GetAttr (PC) & at65816Mask; + if (PrevAddrMode != AddrMode) { + if ((PrevAddrMode & atMem8) != (AddrMode & atMem8) || + (PrevAddrMode & atMem16) != (AddrMode & atMem16)) { + OutputMFlag(!!(AddrMode & atMem8)); + } + if ((PrevAddrMode & atIdx8) != (AddrMode & atIdx8) || + (PrevAddrMode & atIdx16) != (AddrMode & atIdx16)) { + OutputXFlag(!!(AddrMode & atIdx8)); + } + + PrevAddrMode = AddrMode; + } + } + /* Output labels within the next insn */ for (I = 1; I < D->Size; ++I) { ForwardLabel (I); @@ -517,6 +535,8 @@ static void OnePass (void) { unsigned Count; + PrevAddrMode = 0; + /* Disassemble until nothing left */ while ((Count = GetRemainingBytes()) > 0) { OneOpcode (Count); diff --git a/src/da65/output.c b/src/da65/output.c index 5b0b6b79c..8e786e130 100644 --- a/src/da65/output.c +++ b/src/da65/output.c @@ -401,3 +401,23 @@ void OutputSettings (void) LineFeed (); LineFeed (); } + + + +void OutputMFlag (unsigned char enabled) +/* Output the 65816 M-flag state */ +{ + Indent (MCol); + Output (enabled ? ".a8" : ".a16"); + LineFeed (); +} + + + +void OutputXFlag (unsigned char enabled) +/* Output the 65816 X-flag state */ +{ + Indent (MCol); + Output (enabled ? ".i8" : ".i16"); + LineFeed (); +} diff --git a/src/da65/output.h b/src/da65/output.h index 13ea0cc85..bc20aace0 100644 --- a/src/da65/output.h +++ b/src/da65/output.h @@ -108,6 +108,12 @@ void LineComment (unsigned PC, unsigned Count); void OutputSettings (void); /* Output CPU and other settings */ +void OutputMFlag (unsigned char enabled); +/* Output the 65816 M-flag state */ + +void OutputXFlag (unsigned char enabled); +/* Output the 65816 X-flag state */ + /* End of output.h */ From db351b42285e9e4cf9997beeabc84cff00e1cb5a Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 15:36:28 +0300 Subject: [PATCH 027/520] Output remaining long labels --- src/da65/labels.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/da65/labels.c b/src/da65/labels.c index 5ca303a33..4cae0316a 100644 --- a/src/da65/labels.c +++ b/src/da65/labels.c @@ -447,6 +447,7 @@ void DefOutOfRangeLabels (void) /* Output any labels that are out of the loaded code range */ { unsigned long Addr; + unsigned i; SeparatorLine (); @@ -469,5 +470,10 @@ void DefOutOfRangeLabels (void) DefOutOfRangeLabel (Addr++); } + /* 65816 long range */ + for (i = 0; i < LongLabelsUsed; i++) { + DefOutOfRangeLabel (LongSymAddr[i]); + } + SeparatorLine (); } From b90bf258e2e27ca0bf592d9fa5de38adc2781e2d Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Wed, 12 Oct 2022 16:02:37 +0300 Subject: [PATCH 028/520] Implement far override --- src/da65/handler.c | 4 +++- src/da65/opc65816.c | 36 ++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/da65/handler.c b/src/da65/handler.c index 97c79db6d..f2d3ecab3 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -99,7 +99,9 @@ static const char* GetAbsOverride (unsigned Flags, unsigned Addr) ** string, otherwise return the empty string. */ { - if ((Flags & flAbsOverride) != 0 && Addr < 0x100) { + if ((Flags & flFarOverride) != 0 && Addr < 0x10000) { + return "f:"; + } else if ((Flags & flAbsOverride) != 0 && Addr < 0x100) { return "a:"; } else { return ""; diff --git a/src/da65/opc65816.c b/src/da65/opc65816.c index 7457a25c2..64629506c 100644 --- a/src/da65/opc65816.c +++ b/src/da65/opc65816.c @@ -62,7 +62,7 @@ const OpcDesc OpcTable_65816[256] = { { "tsb", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0c */ { "ora", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0d */ { "asl", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0e */ - { "ora", 4, flUseLabel, OH_AbsoluteLong }, /* $0f */ + { "ora", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $0f */ { "bpl", 2, flLabel, OH_Relative }, /* $10 */ { "ora", 2, flUseLabel, OH_DirectIndirectY }, /* $11 */ { "ora", 2, flUseLabel, OH_DirectIndirect }, /* $12 */ @@ -78,10 +78,10 @@ const OpcDesc OpcTable_65816[256] = { { "trb", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $1c */ { "ora", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1d */ { "asl", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1e */ - { "ora", 4, flUseLabel, OH_AbsoluteLongX }, /* $1f */ + { "ora", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $1f */ { "jsr", 3, flLabel, OH_JsrAbsolute }, /* $20 */ { "and", 2, flUseLabel, OH_DirectXIndirect }, /* $21 */ - { "jsl", 4, flLabel, OH_AbsoluteLong }, /* $22 */ + { "jsl", 4, flLabel|flFarOverride, OH_AbsoluteLong }, /* $22 */ { "and", 2, flNone, OH_StackRelative }, /* $23 */ { "bit", 2, flUseLabel, OH_Direct }, /* $24 */ { "and", 2, flUseLabel, OH_Direct }, /* $25 */ @@ -94,7 +94,7 @@ const OpcDesc OpcTable_65816[256] = { { "bit", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2c */ { "and", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2d */ { "rol", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2e */ - { "and", 4, flUseLabel, OH_AbsoluteLong }, /* $2f */ + { "and", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $2f */ { "bmi", 2, flLabel, OH_Relative }, /* $30 */ { "and", 2, flUseLabel, OH_DirectIndirectY }, /* $31 */ { "and", 2, flUseLabel, OH_DirectIndirect }, /* $32 */ @@ -110,7 +110,7 @@ const OpcDesc OpcTable_65816[256] = { { "bit", 3, flUseLabel, OH_AbsoluteX }, /* $3c */ { "and", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3d */ { "rol", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3e */ - { "and", 4, flUseLabel, OH_AbsoluteLongX }, /* $3f */ + { "and", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $3f */ { "rti", 1, flNone, OH_Rts }, /* $40 */ { "eor", 2, flUseLabel, OH_DirectXIndirect }, /* $41 */ { "wdm", 2, flNone, OH_Implicit }, /* $42 */ @@ -126,7 +126,7 @@ const OpcDesc OpcTable_65816[256] = { { "jmp", 3, flLabel, OH_JmpAbsolute }, /* $4c */ { "eor", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $4d */ { "lsr", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $4e */ - { "eor", 4, flUseLabel, OH_AbsoluteLong }, /* $4f */ + { "eor", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $4f */ { "bvc", 2, flLabel, OH_Relative }, /* $50 */ { "eor", 2, flUseLabel, OH_DirectIndirectY }, /* $51 */ { "eor", 2, flUseLabel, OH_DirectIndirect }, /* $52 */ @@ -139,10 +139,10 @@ const OpcDesc OpcTable_65816[256] = { { "eor", 3, flUseLabel, OH_AbsoluteY }, /* $59 */ { "phy", 1, flNone, OH_Implicit }, /* $5a */ { "tcd", 1, flNone, OH_Implicit }, /* $5b */ - { "jml", 4, flLabel, OH_AbsoluteLong }, /* $5c */ + { "jml", 4, flLabel|flFarOverride, OH_AbsoluteLong }, /* $5c */ { "eor", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5d */ { "lsr", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5e */ - { "eor", 4, flUseLabel, OH_AbsoluteLongX }, /* $5f */ + { "eor", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $5f */ { "rts", 1, flNone, OH_Rts }, /* $60 */ { "adc", 2, flUseLabel, OH_DirectXIndirect }, /* $61 */ { "per", 3, flLabel, OH_RelativeLong }, /* $62 */ @@ -158,7 +158,7 @@ const OpcDesc OpcTable_65816[256] = { { "jmp", 3, flLabel, OH_JmpAbsoluteIndirect }, /* $6c */ { "adc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6d */ { "ror", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6e */ - { "adc", 4, flUseLabel, OH_AbsoluteLong }, /* $6f */ + { "adc", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $6f */ { "bvs", 2, flLabel, OH_Relative }, /* $70 */ { "adc", 2, flUseLabel, OH_DirectIndirectY }, /* $71 */ { "adc", 2, flUseLabel, OH_DirectIndirect }, /* $72 */ @@ -174,7 +174,7 @@ const OpcDesc OpcTable_65816[256] = { { "jmp", 3, flLabel, OH_AbsoluteXIndirect }, /* $7c */ { "adc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7d */ { "ror", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7e */ - { "adc", 4, flUseLabel, OH_AbsoluteLongX }, /* $7f */ + { "adc", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $7f */ { "bra", 2, flLabel, OH_Relative }, /* $80 */ { "sta", 2, flUseLabel, OH_DirectXIndirect }, /* $81 */ { "brl", 3, flLabel, OH_RelativeLong }, /* $82 */ @@ -190,7 +190,7 @@ const OpcDesc OpcTable_65816[256] = { { "sty", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8c */ { "sta", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8d */ { "stx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8e */ - { "sta", 4, flUseLabel, OH_AbsoluteLong }, /* $8f */ + { "sta", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $8f */ { "bcc", 2, flLabel, OH_Relative }, /* $90 */ { "sta", 2, flUseLabel, OH_DirectIndirectY }, /* $91 */ { "sta", 2, flUseLabel, OH_DirectIndirect }, /* $92 */ @@ -206,7 +206,7 @@ const OpcDesc OpcTable_65816[256] = { { "stz", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $9c */ { "sta", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9d */ { "stz", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9e */ - { "sta", 4, flUseLabel, OH_AbsoluteLongX }, /* $9f */ + { "sta", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $9f */ { "ldy", 2, flSizeChanges, OH_Immediate65816X }, /* $a0 */ { "lda", 2, flUseLabel, OH_DirectXIndirect }, /* $a1 */ { "ldx", 2, flSizeChanges, OH_Immediate65816X }, /* $a2 */ @@ -222,7 +222,7 @@ const OpcDesc OpcTable_65816[256] = { { "ldy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ac */ { "lda", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ad */ { "ldx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ae */ - { "lda", 4, flUseLabel, OH_AbsoluteLong }, /* $af */ + { "lda", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $af */ { "bcs", 2, flLabel, OH_Relative }, /* $b0 */ { "lda", 2, flUseLabel, OH_DirectIndirectY }, /* $b1 */ { "lda", 2, flUseLabel, OH_DirectIndirect }, /* $b2 */ @@ -238,7 +238,7 @@ const OpcDesc OpcTable_65816[256] = { { "ldy", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bc */ { "lda", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bd */ { "ldx", 3, flUseLabel|flAbsOverride, OH_AbsoluteY }, /* $be */ - { "lda", 4, flUseLabel, OH_AbsoluteLongX }, /* $bf */ + { "lda", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $bf */ { "cpy", 2, flSizeChanges, OH_Immediate65816X }, /* $c0 */ { "cmp", 2, flUseLabel, OH_DirectXIndirect }, /* $c1 */ { "rep", 2, flNone, OH_Immediate }, /* $c2 */ @@ -254,7 +254,7 @@ const OpcDesc OpcTable_65816[256] = { { "cpy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cc */ { "cmp", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cd */ { "dec", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ce */ - { "cmp", 4, flUseLabel, OH_AbsoluteLong }, /* $cf */ + { "cmp", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $cf */ { "bne", 2, flLabel, OH_Relative }, /* $d0 */ { "cmp", 2, flUseLabel, OH_DirectIndirectY }, /* $d1 */ { "cmp", 2, flUseLabel, OH_DirectIndirect }, /* $d2 */ @@ -270,7 +270,7 @@ const OpcDesc OpcTable_65816[256] = { { "jml", 3, flLabel, OH_AbsoluteIndirect }, /* $dc */ { "cmp", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $dd */ { "dec", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $de */ - { "cmp", 4, flUseLabel, OH_AbsoluteLongX }, /* $df */ + { "cmp", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $df */ { "cpx", 2, flSizeChanges, OH_Immediate65816X }, /* $e0 */ { "sbc", 2, flUseLabel, OH_DirectXIndirect }, /* $e1 */ { "sep", 2, flNone, OH_Immediate }, /* $e2 */ @@ -286,7 +286,7 @@ const OpcDesc OpcTable_65816[256] = { { "cpx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ec */ { "sbc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ed */ { "inc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ee */ - { "sbc", 4, flUseLabel, OH_AbsoluteLong }, /* $ef */ + { "sbc", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $ef */ { "beq", 2, flLabel, OH_Relative }, /* $f0 */ { "sbc", 2, flUseLabel, OH_DirectIndirectY }, /* $f1 */ { "sbc", 2, flUseLabel, OH_DirectIndirect }, /* $f2 */ @@ -302,5 +302,5 @@ const OpcDesc OpcTable_65816[256] = { { "jsr", 3, flLabel, OH_AbsoluteXIndirect }, /* $fc */ { "sbc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fd */ { "inc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fe */ - { "sbc", 4, flUseLabel, OH_AbsoluteLongX }, /* $ff */ + { "sbc", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $ff */ }; From 3ea999f0349d0e8233e95e94d858d9547a309ec2 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Mon, 17 Oct 2022 12:50:34 +0300 Subject: [PATCH 029/520] Correct mvn, mvp, pei --- src/da65/handler.c | 13 +++++++++++++ src/da65/handler.h | 1 + src/da65/opc65816.c | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/da65/handler.c b/src/da65/handler.c index f2d3ecab3..79b3192de 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -678,6 +678,19 @@ void OH_BlockMove (const OpcDesc* D) +void OH_BlockMove65816 (const OpcDesc* D) +{ + /* Get source operand */ + unsigned Src = GetCodeByte (PC+2); + /* Get destination operand */ + unsigned Dst = GetCodeByte (PC+1); + + /* Output the line */ + OneLine (D, "#$%02X, #$%02X", Src, Dst); +} + + + void OH_AbsoluteXIndirect (const OpcDesc* D attribute ((unused))) { /* Get the operand */ diff --git a/src/da65/handler.h b/src/da65/handler.h index 2bfaed9c3..ee9b18bbc 100644 --- a/src/da65/handler.h +++ b/src/da65/handler.h @@ -91,6 +91,7 @@ void OH_StackRelativeIndirectY4510 (const OpcDesc*); void OH_DirectIndirectLong (const OpcDesc*); void OH_DirectIndirectLongY (const OpcDesc*); void OH_BlockMove (const OpcDesc*); +void OH_BlockMove65816 (const OpcDesc*); void OH_AbsoluteXIndirect (const OpcDesc*); /* Mitsubishi 740 */ diff --git a/src/da65/opc65816.c b/src/da65/opc65816.c index 64629506c..06272f318 100644 --- a/src/da65/opc65816.c +++ b/src/da65/opc65816.c @@ -115,7 +115,7 @@ const OpcDesc OpcTable_65816[256] = { { "eor", 2, flUseLabel, OH_DirectXIndirect }, /* $41 */ { "wdm", 2, flNone, OH_Implicit }, /* $42 */ { "eor", 2, flNone, OH_StackRelative }, /* $43 */ - { "mvp", 3, flNone, OH_BlockMove }, /* $44 */ + { "mvp", 3, flNone, OH_BlockMove65816 }, /* $44 */ { "eor", 2, flUseLabel, OH_Direct }, /* $45 */ { "lsr", 2, flUseLabel, OH_Direct }, /* $46 */ { "eor", 2, flUseLabel, OH_DirectIndirectLong }, /* $47 */ @@ -131,7 +131,7 @@ const OpcDesc OpcTable_65816[256] = { { "eor", 2, flUseLabel, OH_DirectIndirectY }, /* $51 */ { "eor", 2, flUseLabel, OH_DirectIndirect }, /* $52 */ { "eor", 2, flNone, OH_StackRelativeIndirectY}, /* $53 */ - { "mvn", 3, flNone, OH_BlockMove }, /* $54 */ + { "mvn", 3, flNone, OH_BlockMove65816 }, /* $54 */ { "eor", 2, flUseLabel, OH_DirectX }, /* $55 */ { "lsr", 2, flUseLabel, OH_DirectX }, /* $56 */ { "eor", 2, flUseLabel, OH_DirectIndirectLongY }, /* $57 */ @@ -259,7 +259,7 @@ const OpcDesc OpcTable_65816[256] = { { "cmp", 2, flUseLabel, OH_DirectIndirectY }, /* $d1 */ { "cmp", 2, flUseLabel, OH_DirectIndirect }, /* $d2 */ { "cmp", 2, flNone, OH_StackRelativeIndirectY}, /* $d3 */ - { "pei", 2, flUseLabel, OH_Direct }, /* $d4 */ + { "pei", 2, flUseLabel, OH_DirectIndirect }, /* $d4 */ { "cmp", 2, flUseLabel, OH_DirectX }, /* $d5 */ { "dec", 2, flUseLabel, OH_DirectX }, /* $d6 */ { "cmp", 2, flUseLabel, OH_DirectIndirectLongY }, /* $d7 */ From 9aae1efd033fea2252670fcaafad2b6cc370d516 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Mon, 17 Oct 2022 12:50:59 +0300 Subject: [PATCH 030/520] Add a da65 65186 test --- test/dasm/65816.cfg | 7 ++ test/dasm/65816.info | 7 ++ test/dasm/Makefile | 17 ++- test/dasm/test65816.s | 273 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 test/dasm/65816.cfg create mode 100644 test/dasm/65816.info create mode 100644 test/dasm/test65816.s diff --git a/test/dasm/65816.cfg b/test/dasm/65816.cfg new file mode 100644 index 000000000..620439c7b --- /dev/null +++ b/test/dasm/65816.cfg @@ -0,0 +1,7 @@ +MEMORY { + ROM: start = $8000, size = $8000; +} + +SEGMENTS { + CODE: load = ROM; +} diff --git a/test/dasm/65816.info b/test/dasm/65816.info new file mode 100644 index 000000000..2a3394680 --- /dev/null +++ b/test/dasm/65816.info @@ -0,0 +1,7 @@ +GLOBAL { + startaddr $8000; +}; + +RANGE { START $8000; END $8229; ADDRMODE "MX"; TYPE Code;}; +RANGE { START $822a; END $824b; ADDRMODE "mx"; TYPE Code;}; + diff --git a/test/dasm/Makefile b/test/dasm/Makefile index 542ce7d5e..d9ac7ac21 100644 --- a/test/dasm/Makefile +++ b/test/dasm/Makefile @@ -19,6 +19,8 @@ ifdef QUIET endif CL65 := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) +CA65 := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) DA65 := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) WORKDIR = ../../testwrk/dasm @@ -33,7 +35,7 @@ START = --start-addr 0x8000 .PHONY: all clean SOURCES := $(wildcard *.s) -BINS = $(SOURCES:%disass.s=$(WORKDIR)/%reass.bin) +BINS = $(SOURCES:%disass.s=$(WORKDIR)/%reass.bin) $(WORKDIR)/65816-reass.bin CPUS = $(SOURCES:%-disass.s=%) all: $(BINS) @@ -61,5 +63,18 @@ endef # DISASS_template $(foreach cpu,$(CPUS),$(eval $(call DISASS_template,$(cpu)))) +$(WORKDIR)/test65816.bin: test65816.s | $(WORKDIR) + $(CA65) -o $(WORKDIR)/test65816.o $< + $(LD65) -o $@ -C 65816.cfg $(WORKDIR)/test65816.o + +$(WORKDIR)/65816-reass.s: $(WORKDIR)/test65816.bin + $(DA65) --cpu 65816 -i 65816.info -o $@ $< + +$(WORKDIR)/65816-reass.bin: $(WORKDIR)/65816-reass.s $(ISEQUAL) + $(if $(QUIET),echo dasm/65816-reass.bin) + $(CA65) -o $(WORKDIR)/65816-reass.o $< + $(LD65) -o $@ -C 65816.cfg $(WORKDIR)/65816-reass.o + $(ISEQUAL) --binary $(WORKDIR)/test65816.bin $@ + clean: @$(call RMDIR,$(WORKDIR)) diff --git a/test/dasm/test65816.s b/test/dasm/test65816.s new file mode 100644 index 000000000..3d764fb6e --- /dev/null +++ b/test/dasm/test65816.s @@ -0,0 +1,273 @@ +.setcpu "65816" + +ADC ($10,X) +ADC $32,S +ADC $10 +ADC [$10] +ADC #$54 +ADC $9876 +ADC $FEDBCA +ADC ($10),Y +ADC ($10) +ADC ($32,S),Y +ADC $10,X +ADC [$10],Y +ADC $9876,Y +ADC $9876,X +ADC $FEDCBA,X +SBC ($10,X) +SBC $32,S +SBC $10 +SBC [$10] +SBC #$54 +SBC $9876 +SBC $FEDBCA +SBC ($10),Y +SBC ($10) +SBC ($32,S),Y +SBC $10,X +SBC [$10],Y +SBC $9876,Y +SBC $9876,X +SBC $FEDCBA,X +CMP ($10,X) +CMP $32,S +CMP $10 +CMP [$10] +CMP #$54 +CMP $9876 +CMP $FEDBCA +CMP ($10),Y +CMP ($10) +CMP ($32,S),Y +CMP $10,X +CMP [$10],Y +CMP $9876,Y +CMP $9876,X +CMP $FEDCBA,X +CPX #$54 +CPX $10 +CPX $9876 +CPY #$54 +CPY $10 +CPY $9876 +DEC +DEC $10 +DEC $9876 +DEC $10,X +DEC $9876,X +DEX +DEY +INC +INC $10 +INC $9876 +INC $10,X +INC $9876,X +INX +INY +AND ($10,X) +AND $32,S +AND $10 +AND [$10] +AND #$54 +AND $9876 +AND $FEDBCA +AND ($10),Y +AND ($10) +AND ($32,S),Y +AND $10,X +AND [$10],Y +AND $9876,Y +AND $9876,X +AND $FEDCBA,X +EOR ($10,X) +EOR $32,S +EOR $10 +EOR [$10] +EOR #$54 +EOR $9876 +EOR $FEDBCA +EOR ($10),Y +EOR ($10) +EOR ($32,S),Y +EOR $10,X +EOR [$10],Y +EOR $9876,Y +EOR $9876,X +EOR $FEDCBA,X +ORA ($10,X) +ORA $32,S +ORA $10 +ORA [$10] +ORA #$54 +ORA $9876 +ORA $FEDBCA +ORA ($10),Y +ORA ($10) +ORA ($32,S),Y +ORA $10,X +ORA [$10],Y +ORA $9876,Y +ORA $9876,X +ORA $FEDCBA,X +BIT $10 +BIT $9876 +BIT $10,X +BIT $9876,X +BIT #$54 +TRB $10 +TRB $9876 +TSB $10 +TSB $9876 +ASL $10 +ASL +ASL $9876 +ASL $10,X +ASL $9876,X +LSR $10 +LSR +LSR $9876 +LSR $10,X +LSR $9876,X +ROL $10 +ROL +ROL $9876 +ROL $10,X +ROL $9876,X +ROR $10 +ROR +ROR $9876 +ROR $10,X +ROR $9876,X +LABEL: +BCC LABEL +BCS LABEL +BEQ LABEL +BMI LABEL +BNE LABEL +BPL LABEL +BRA LABEL +BVC LABEL +BVS LABEL +BRL LABEL +JMP $1234 +JMP $FEDCBA +JMP ($1234) +JMP ($1234,X) +JMP [$1234] +JSL $123456 +JSR $1234 +JSR ($1234,X) +RTL +RTS +BRK +RTI +CLC +CLD +CLI +CLV +SEC +SED +SEI +REP #$12 +SEP #$12 +LDA ($10,X) +LDA $32,S +LDA $10 +LDA [$10] +LDA #$54 +LDA $9876 +LDA $FEDBCA +LDA ($10),Y +LDA ($10) +LDA ($32,S),Y +LDA $10,X +LDA [$10],Y +LDA $9876,Y +LDA $9876,X +LDA $FEDCBA,X +LDX #$54 +LDX $10 +LDX $9876 +LDX $10,Y +LDX $9876,Y +LDY #$54 +LDY $10 +LDY $9876 +LDY $10,X +LDY $9876,X +STA ($10,X) +STA $32,S +STA $10 +STA [$10] +STA $9876 +STA $FEDBCA +STA ($10),Y +STA ($10) +STA ($32,S),Y +STA $10,X +STA [$10],Y +STA $9876,Y +STA $9876,X +STA $FEDCBA,X +STX $10 +STX $9876 +STX $10,Y +STY $10 +STY $9876 +STY $10,X +STZ $10 +STZ $10,X +STZ $9876 +STZ $9876,X +MVN #$12,#$34 +MVP #$12,#$34 +NOP +PEA $1234 +PEI ($12) +PER LABEL +PHA +PHX +PHY +PLA +PLX +PLY +PHB +PHD +PHK +PHP +PLB +PLD +PLP +STP +WAI +TAX +TAY +TSX +TXA +TXS +TXY +TYA +TYX +TCD +TCS +TDC +TSC +XBA +XCE + +.a16 +.i16 +longs: +ADC #$5432 +SBC #$5432 +CMP #$5432 +CPX #$5432 +CPY #$5432 +AND #$5432 +EOR #$5432 +ORA #$5432 +BIT #$5432 +LDA #$5432 +LDX #$5432 +LDY #$5432 From b0ef3572ead29871ea23cd1b2e890e6fe552838f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 12 Nov 2022 12:28:22 +0800 Subject: [PATCH 031/520] Improved error messages about missing identifiers. --- src/cc65/declare.c | 4 ++-- src/cc65/expr.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 59eb555c4..543cca421 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1452,7 +1452,7 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci NextToken (); } else { if (CurTok.Tok != TOK_LCURLY) { - Error ("Identifier expected"); + Error ("Identifier expected for enum tag name"); } AnonName (Ident, "enum"); } @@ -1573,7 +1573,7 @@ static void ParseOldStyleParamList (FuncDesc* F) } else { /* Not a parameter name */ - Error ("Identifier expected"); + Error ("Identifier expected for parameter name"); /* Try some smart error recovery */ SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index fa6f21fb2..47a05eca0 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1439,7 +1439,7 @@ static void StructRef (ExprDesc* Expr) /* Skip the token and check for an identifier */ NextToken (); if (CurTok.Tok != TOK_IDENT) { - Error ("Identifier expected"); + Error ("Identifier expected for %s member", GetBasicTypeName (Expr->Type)); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); return; From 894ba49cb59bafe8871042b15287139b145c2c7c Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 12 Nov 2022 12:28:24 +0800 Subject: [PATCH 032/520] Improved error messages about missing type specifiers. --- src/cc65/declare.c | 29 +++++++++++++++++++++++++---- src/cc65/declare.h | 5 +++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 543cca421..3367b04d7 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1504,8 +1504,8 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci /* FALL THROUGH */ default: - if ((TSFlags & TS_MASK_DEFAULT_TYPE) != TS_DEFAULT_TYPE_INT) { - Error ("Type expected"); + if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { + D->Flags |= DS_NO_TYPE; D->Type[0].C = T_INT; D->Type[1].C = T_END; } else { @@ -1553,8 +1553,7 @@ static const Type* ParamTypeCvt (Type* T) static void ParseOldStyleParamList (FuncDesc* F) /* Parse an old-style (K&R) parameter list */ { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_RPAREN, TOK_SEMI }; + unsigned PrevErrorCount = ErrorCount; /* Parse params */ while (CurTok.Tok != TOK_RPAREN) { @@ -1572,6 +1571,9 @@ static void ParseOldStyleParamList (FuncDesc* F) NextToken (); } else { + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_COMMA, TOK_RPAREN, TOK_SEMI }; + /* Not a parameter name */ Error ("Identifier expected for parameter name"); @@ -1608,6 +1610,12 @@ static void ParseOldStyleParamList (FuncDesc* F) Error ("Illegal storage class"); } + /* Type must be specified */ + if ((Spec.Flags & DS_NO_TYPE) != 0) { + Error ("Expected declaration specifiers"); + break; + } + /* Parse a comma separated variable list */ while (1) { @@ -1655,6 +1663,14 @@ static void ParseOldStyleParamList (FuncDesc* F) /* Variable list must be semicolon terminated */ ConsumeSemi (); } + + if (PrevErrorCount != ErrorCount) { + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI }; + + /* Try some smart error recovery */ + SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + } } @@ -1689,6 +1705,11 @@ static void ParseAnsiParamList (FuncDesc* F) Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } + /* Type must be specified */ + if ((Spec.Flags & DS_NO_TYPE) != 0) { + Error ("Type specifier missing"); + } + /* Warn about new local type declaration */ if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { Warning ("'%s' will be invisible out of this function", diff --git a/src/cc65/declare.h b/src/cc65/declare.h index ee9e1fc63..0facccba3 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -71,8 +71,9 @@ enum typespec_t { /* Masks for the Flags field in DeclSpec */ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */ -#define DS_DEF_TYPE 0x0002U /* Default type used */ -#define DS_EXTRA_TYPE 0x0004U /* Extra type declared */ +#define DS_NO_TYPE 0x0002U /* No type explicitly specified */ +#define DS_DEF_TYPE 0x0006U /* Default type used */ +#define DS_EXTRA_TYPE 0x0008U /* Extra type declared */ #define DS_NEW_TYPE_DECL 0x0010U /* New type declared */ #define DS_NEW_TYPE_DEF 0x0020U /* New type defined */ #define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF) From eb595b1f5f1054072cc5632c1c8be8e3a629f3b4 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 12 Nov 2022 12:28:27 +0800 Subject: [PATCH 033/520] Improved error recovery with K&R-style function declarations. --- src/cc65/compile.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 73380f3df..1cb109bbe 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -178,7 +178,7 @@ static void Parse (void) ** or semicolon, it must be followed by a function body. */ if ((Decl.StorageClass & SC_FUNC) != 0) { - if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) { + if (CurTok.Tok == TOK_LCURLY) { /* A definition */ Decl.StorageClass |= SC_DEF; @@ -190,6 +190,10 @@ static void Parse (void) FuncDef->Flags = (FuncDef->Flags & ~FD_EMPTY) | FD_VOID_PARAM; } } else { + if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) { + Error ("Expected ',' or ';' after top level declarator"); + } + /* Just a declaration */ Decl.StorageClass |= SC_DECL; } @@ -325,7 +329,7 @@ static void Parse (void) if (CurTok.Tok == TOK_SEMI) { /* Prototype only */ NextToken (); - } else { + } else if (CurTok.Tok == TOK_LCURLY) { /* Parse the function body */ NewFunc (Sym, FuncDef); From 3af77e73333d34addc3b0e1658d41c265fb74123 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 12 Nov 2022 12:28:29 +0800 Subject: [PATCH 034/520] Improved error recovery in declarations with curly braces. --- src/cc65/declare.c | 11 ++++++++++- test/ref/bug1889-missing-identifier.cref | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 3367b04d7..99161e539 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1891,12 +1891,21 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } else { if (Mode == DM_NEED_IDENT) { /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI }; + static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, TOK_LCURLY, TOK_RCURLY }; Error ("Identifier expected"); /* Try some smart error recovery */ SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + + /* Skip curly braces */ + if (CurTok.Tok == TOK_LCURLY) { + static const token_t CurlyToken[] = { TOK_RCURLY }; + SkipTokens (CurlyToken, sizeof(CurlyToken) / sizeof(CurlyToken[0])); + NextToken (); + } else if (CurTok.Tok == TOK_RCURLY) { + NextToken (); + } } D->Ident[0] = '\0'; } diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index cd3f76849..acaf53f94 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,3 +1,5 @@ bug1889-missing-identifier.c:3: Error: Identifier expected +bug1889-missing-identifier.c:3: Error: ';' expected +bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature bug1889-missing-identifier.c:4: Error: Identifier expected bug1889-missing-identifier.c:4: Warning: Implicit 'int' is an obsolete feature From dae4b2d9b0e9b45e5ff633592aecfc8eead30313 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Tue, 6 Dec 2022 14:47:03 +0100 Subject: [PATCH 035/520] add note on optimizer --- Contributing.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index 6fa5ba7c2..9fe0e0dee 100644 --- a/Contributing.md +++ b/Contributing.md @@ -181,7 +181,11 @@ The only exception to the above are actions that are exclusive to the github act * the printf family of function does not completely implement all printf modifiers and does not behave as expected in some cases - all this should be documented in detail -## Floating point support +## Compiler + +* We need a way that makes it possible to feed arbitrary assembler code into the optimzer, so we can have proper tests for it + +### Floating point support The first step is implementing the datatype "float" as IEEE488 floats. Help welcomed! From 78e6bcf6435809126fac2c702fdb51d16823d044 Mon Sep 17 00:00:00 2001 From: Irgendwer <C.Krueger.B@web.de> Date: Wed, 7 Dec 2022 00:20:20 +0100 Subject: [PATCH 036/520] Update Contributing.md --- Contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index 9fe0e0dee..74e6ead17 100644 --- a/Contributing.md +++ b/Contributing.md @@ -187,7 +187,7 @@ The only exception to the above are actions that are exclusive to the github act ### Floating point support -The first step is implementing the datatype "float" as IEEE488 floats. Help welcomed! +The first step is implementing the datatype "float" as IEEE 754 floats. Help welcomed! * WIP compiler/library changes are here: https://github.com/cc65/cc65/pull/1777 From ddab16007a23c0b489ac63b9d25dac67d5390229 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 11 Dec 2022 12:08:30 +0100 Subject: [PATCH 037/520] run branch fixer again after replacing BRA by JMP. should fix #1936 --- src/cc65/codeopt.c | 4 ++ test/misc/bug1936.c | 93 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 test/misc/bug1936.c diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 440b10751..208ada134 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -850,6 +850,10 @@ static unsigned RunOptGroup7 (CodeSeg* S) C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptBranchDist2, 1); + /* Adjust branch distances again, since the previous step may change code + between branches */ + C += RunOptFunc (S, &DOptBranchDist, 3); + Changes += C; /* If we had changes, we must run dead code elimination again, ** since the changes may have introduced dead code. diff --git a/test/misc/bug1936.c b/test/misc/bug1936.c new file mode 100644 index 000000000..788313bd2 --- /dev/null +++ b/test/misc/bug1936.c @@ -0,0 +1,93 @@ + +/* bug #1936 - Compiler produces broken Assembly (129 operand for bne) */ + +#include <stdint.h> + +static uint8_t item_counter; + + +static uint8_t freeze; +static uint8_t powerUp; + +static uint8_t wall_appeared; +static uint8_t freeze_locked; +static uint8_t zombie_locked; + + +struct ItemStruct +{ + uint8_t _active; + void(*_effect)(void); +} ; +typedef struct ItemStruct Item; + +static Item freezeItem; +static Item powerUpItem; +static Item wallItem; +static Item zombieItem; + + +static Item extraPointsItem[1]; + + +uint8_t find_inactive(Item* itemArray) +{ +} + + +void drop_item(register Item *item, uint8_t max_counter) +{ +} + + +void handle_item_drop(void) +{ + { + if(item_counter==1) + { + if(!powerUpItem._active) + { + drop_item(&powerUpItem,35); + } + } + else if((!freeze_locked)&&(!freeze)) + { + if(!freezeItem._active) + { + drop_item(&freezeItem,45); + } + } + else if(!wall_appeared&&(powerUp>=9)) + { + if(!wallItem._active) + { + drop_item(&wallItem,35); + } + } + else if(!zombie_locked && !zombieItem._active) + { + drop_item(&zombieItem,50); + } + else + { + uint8_t index; + + index = find_inactive(extraPointsItem); + if(index!=1) // REMARK: compilation does not fail with 0 + { + drop_item(&extraPointsItem[index],90); + } + } + } +} + +int main(void) +{ + + while(1) // Game (re-)start + { + } + + return 0; +} + From 2b941e255a45ad30db26f0ce0fef3492ca5f0812 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 11 Dec 2022 12:22:41 +0100 Subject: [PATCH 038/520] move test. oops --- test/{misc => val}/bug1936.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{misc => val}/bug1936.c (100%) diff --git a/test/misc/bug1936.c b/test/val/bug1936.c similarity index 100% rename from test/misc/bug1936.c rename to test/val/bug1936.c From d9ebfa7192b9d161ead58db45300ffd363233495 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 11 Dec 2022 12:29:11 +0100 Subject: [PATCH 039/520] all good things are three --- test/val/bug1936.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/val/bug1936.c b/test/val/bug1936.c index 788313bd2..e97231797 100644 --- a/test/val/bug1936.c +++ b/test/val/bug1936.c @@ -83,11 +83,6 @@ void handle_item_drop(void) int main(void) { - - while(1) // Game (re-)start - { - } - return 0; } From d90c7e9853c43f6d2dbf241127006e5bb4fc3830 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Thu, 22 Dec 2022 18:06:16 +0100 Subject: [PATCH 040/520] Introduced the notion of a standard serial driver. There's no target with more than one serial driver (and I don't see that change anytime soon) so it's a no-brainer to apply the standard driver concept to serial drivers. --- include/apple2.h | 2 +- include/apple2enh.h | 2 +- include/atari.h | 4 ++-- include/atmos.h | 2 +- include/c128.h | 2 +- include/c64.h | 2 +- include/cbm510.h | 2 +- include/cbm610.h | 2 +- include/lynx.h | 2 +- include/plus4.h | 2 +- include/serial.h | 7 +++++++ libsrc/apple2/ser_stat_stddrv.s | 22 ++++++++++++++++++++++ libsrc/apple2/ser_stddrv.s | 18 ++++++++++++++++++ libsrc/atari/ser_stat_stddrv.s | 22 ++++++++++++++++++++++ libsrc/atari/ser_stddrv.s | 18 ++++++++++++++++++ libsrc/atmos/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/atmos/ser_stddrv.s | 13 +++++++++++++ libsrc/c128/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/c128/ser_stddrv.s | 13 +++++++++++++ libsrc/c64/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/c64/ser_stddrv.s | 13 +++++++++++++ libsrc/cbm510/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/cbm510/ser_stddrv.s | 13 +++++++++++++ libsrc/cbm610/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/cbm610/ser_stddrv.s | 13 +++++++++++++ libsrc/lynx/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/plus4/ser_stat_stddrv.s | 14 ++++++++++++++ libsrc/plus4/ser_stddrv.s | 13 +++++++++++++ 28 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 libsrc/apple2/ser_stat_stddrv.s create mode 100644 libsrc/apple2/ser_stddrv.s create mode 100644 libsrc/atari/ser_stat_stddrv.s create mode 100644 libsrc/atari/ser_stddrv.s create mode 100644 libsrc/atmos/ser_stat_stddrv.s create mode 100644 libsrc/atmos/ser_stddrv.s create mode 100644 libsrc/c128/ser_stat_stddrv.s create mode 100644 libsrc/c128/ser_stddrv.s create mode 100644 libsrc/c64/ser_stat_stddrv.s create mode 100644 libsrc/c64/ser_stddrv.s create mode 100644 libsrc/cbm510/ser_stat_stddrv.s create mode 100644 libsrc/cbm510/ser_stddrv.s create mode 100644 libsrc/cbm610/ser_stat_stddrv.s create mode 100644 libsrc/cbm610/ser_stddrv.s create mode 100644 libsrc/lynx/ser_stat_stddrv.s create mode 100644 libsrc/plus4/ser_stat_stddrv.s create mode 100644 libsrc/plus4/ser_stddrv.s diff --git a/include/apple2.h b/include/apple2.h index cb15cab97..9f644bc97 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -171,7 +171,7 @@ extern struct { extern void a2_auxmem_emd[]; extern void a2_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void a2_stdmou_mou[]; /* Referred to by mouse_static_stddrv[] */ -extern void a2_ssc_ser[]; +extern void a2_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ extern void a2_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2_lo_tgi[]; #endif diff --git a/include/apple2enh.h b/include/apple2enh.h index 58e0b397f..bfe5cdb18 100644 --- a/include/apple2enh.h +++ b/include/apple2enh.h @@ -99,7 +99,7 @@ extern void a2e_auxmem_emd[]; extern void a2e_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void a2e_stdmou_mou[]; /* Referred to by mouse_static_stddrv[] */ -extern void a2e_ssc_ser[]; +extern void a2e_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ extern void a2e_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2e_lo_tgi[]; diff --git a/include/atari.h b/include/atari.h index 781ee7a80..deae8fdf5 100644 --- a/include/atari.h +++ b/include/atari.h @@ -261,7 +261,7 @@ extern void atrst_mou[]; /* referred to by mouse_static_stddrv[] extern void atrami_mou[]; extern void atrtrk_mou[]; extern void atrtt_mou[]; -extern void atrrdev_ser[]; +extern void atrrdev_ser[]; /* referred to by ser_static_stddrv[] */ extern void atr3_tgi[]; extern void atr4_tgi[]; extern void atr5_tgi[]; @@ -286,7 +286,7 @@ extern void atrxst_mou[]; /* referred to by mouse_static_stddrv[] extern void atrxami_mou[]; extern void atrxtrk_mou[]; extern void atrxtt_mou[]; -extern void atrxrdev_ser[]; +extern void atrxrdev_ser[]; /* referred to by ser_static_stddrv[] */ extern void atrx3_tgi[]; extern void atrx4_tgi[]; extern void atrx5_tgi[]; diff --git a/include/atmos.h b/include/atmos.h index 227c387aa..38d423c46 100644 --- a/include/atmos.h +++ b/include/atmos.h @@ -133,7 +133,7 @@ /* The addresses of the static drivers */ extern void atmos_pase_joy[]; /* Referred to by joy_static_stddrv[] */ extern void atmos_ijk_joy[]; -extern void atmos_acia_ser[]; +extern void atmos_acia_ser[]; /* Referred to by ser_static_stddrv[] */ extern void atmos_228_200_3_tgi[]; extern void atmos_240_200_2_tgi[]; /* Referred to by tgi_static_stddrv[] */ diff --git a/include/c128.h b/include/c128.h index ee1dce99e..5a34904e0 100644 --- a/include/c128.h +++ b/include/c128.h @@ -140,7 +140,7 @@ extern void c128_1351_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void c128_joy_mou[]; extern void c128_inkwell_mou[]; extern void c128_pot_mou[]; -extern void c128_swlink_ser[]; +extern void c128_swlink_ser[]; /* Referred to by ser_static_stddrv[] */ extern void c128_hi_tgi[]; extern void c128_vdc_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void c128_vdc2_tgi[]; diff --git a/include/c64.h b/include/c64.h index 13d252dcb..ffac801ef 100644 --- a/include/c64.h +++ b/include/c64.h @@ -155,7 +155,7 @@ extern void c64_1351_mou[]; /* Referred to by mouse_static_stddrv[] extern void c64_joy_mou[]; extern void c64_inkwell_mou[]; extern void c64_pot_mou[]; -extern void c64_swlink_ser[]; +extern void c64_swlink_ser[]; /* Referred to by ser_static_stddrv[] */ extern void c64_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ diff --git a/include/cbm510.h b/include/cbm510.h index 20b334ed9..8ebbdf3c1 100644 --- a/include/cbm510.h +++ b/include/cbm510.h @@ -128,7 +128,7 @@ extern void cbm510_inkwl_mou[]; extern void cbm510_joy_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void cbm510_ram_emd[]; extern void cbm510_std_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void cbm510_std_ser[]; +extern void cbm510_std_ser[]; /* Referred to by ser_static_stddrv[] */ diff --git a/include/cbm610.h b/include/cbm610.h index de7aa50f8..64beab9e6 100644 --- a/include/cbm610.h +++ b/include/cbm610.h @@ -105,7 +105,7 @@ /* The addresses of the static drivers */ extern void cbm610_ram_emd[]; -extern void cbm610_std_ser[]; +extern void cbm610_std_ser[]; /* Referred to by ser_static_stddrv[] */ diff --git a/include/lynx.h b/include/lynx.h index 1b4828a72..41dc5acb3 100644 --- a/include/lynx.h +++ b/include/lynx.h @@ -115,7 +115,7 @@ /* The addresses of the static drivers */ extern void lynx_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void lynx_comlynx_ser[]; +extern void lynx_comlynx_ser[]; /* Referred to by ser_static_stddrv[] */ extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */ diff --git a/include/plus4.h b/include/plus4.h index 325ba7d89..7730938e8 100644 --- a/include/plus4.h +++ b/include/plus4.h @@ -56,7 +56,7 @@ /* The addresses of the static drivers */ extern void plus4_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void plus4_stdser_ser[]; +extern void plus4_stdser_ser[]; /* Referred to by ser_static_stddrv[] */ diff --git a/include/serial.h b/include/serial.h index 35d7b8f66..0510cd219 100644 --- a/include/serial.h +++ b/include/serial.h @@ -123,6 +123,13 @@ struct ser_params { unsigned char handshake; /* Type of handshake to use */ }; +/* The name of the standard serial driver for a platform */ +extern const char ser_stddrv[]; + +/* The address of the static standard serial driver for a platform */ +extern const void ser_static_stddrv[]; + + /*****************************************************************************/ /* Code */ diff --git a/libsrc/apple2/ser_stat_stddrv.s b/libsrc/apple2/ser_stat_stddrv.s new file mode 100644 index 000000000..690fe4853 --- /dev/null +++ b/libsrc/apple2/ser_stat_stddrv.s @@ -0,0 +1,22 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .ifdef __APPLE2ENH__ + .import _a2e_ssc_ser + .else + .import _a2_ssc_ser + .endif + +.rodata + + .ifdef __APPLE2ENH__ +_ser_static_stddrv := _a2e_ssc_ser + .else +_ser_static_stddrv := _a2_ssc_ser + .endif diff --git a/libsrc/apple2/ser_stddrv.s b/libsrc/apple2/ser_stddrv.s new file mode 100644 index 000000000..2e8361865 --- /dev/null +++ b/libsrc/apple2/ser_stddrv.s @@ -0,0 +1,18 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: + .ifdef __APPLE2ENH__ + .asciiz "A2E.SSC.SER" + .else + .asciiz "A2.SSC.SER" + .endif diff --git a/libsrc/atari/ser_stat_stddrv.s b/libsrc/atari/ser_stat_stddrv.s new file mode 100644 index 000000000..b054f7f32 --- /dev/null +++ b/libsrc/atari/ser_stat_stddrv.s @@ -0,0 +1,22 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .ifdef __ATARIXL__ + .import _atrxrdev_ser + .else + .import _atrrdev_ser + .endif + +.rodata + + .ifdef __ATARIXL__ +_ser_static_stddrv := _atrxrdev_ser + .else +_ser_static_stddrv := _atrrdev_ser + .endif diff --git a/libsrc/atari/ser_stddrv.s b/libsrc/atari/ser_stddrv.s new file mode 100644 index 000000000..e6f4ccd48 --- /dev/null +++ b/libsrc/atari/ser_stddrv.s @@ -0,0 +1,18 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: + .ifdef __ATARIXL__ + .asciiz "atrxrdev.ser" + .else + .asciiz "atrrdev.ser" + .endif diff --git a/libsrc/atmos/ser_stat_stddrv.s b/libsrc/atmos/ser_stat_stddrv.s new file mode 100644 index 000000000..2b4373695 --- /dev/null +++ b/libsrc/atmos/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _atmos_acia_ser + +.rodata + +_ser_static_stddrv := _atmos_acia_ser diff --git a/libsrc/atmos/ser_stddrv.s b/libsrc/atmos/ser_stddrv.s new file mode 100644 index 000000000..71e33115a --- /dev/null +++ b/libsrc/atmos/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "atmos-acia.ser" diff --git a/libsrc/c128/ser_stat_stddrv.s b/libsrc/c128/ser_stat_stddrv.s new file mode 100644 index 000000000..8b0732703 --- /dev/null +++ b/libsrc/c128/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _c128_swlink_ser + +.rodata + +_ser_static_stddrv := _c128_swlink_ser diff --git a/libsrc/c128/ser_stddrv.s b/libsrc/c128/ser_stddrv.s new file mode 100644 index 000000000..63f73cadd --- /dev/null +++ b/libsrc/c128/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "c128_swlink.ser" diff --git a/libsrc/c64/ser_stat_stddrv.s b/libsrc/c64/ser_stat_stddrv.s new file mode 100644 index 000000000..327abbe5f --- /dev/null +++ b/libsrc/c64/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _c64_swlink_ser + +.rodata + +_ser_static_stddrv := _c64_swlink_ser diff --git a/libsrc/c64/ser_stddrv.s b/libsrc/c64/ser_stddrv.s new file mode 100644 index 000000000..5b00b7642 --- /dev/null +++ b/libsrc/c64/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "c64_swlink.ser" diff --git a/libsrc/cbm510/ser_stat_stddrv.s b/libsrc/cbm510/ser_stat_stddrv.s new file mode 100644 index 000000000..a872f19b9 --- /dev/null +++ b/libsrc/cbm510/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _cbm510_std_ser + +.rodata + +_ser_static_stddrv := _cbm510_std_ser diff --git a/libsrc/cbm510/ser_stddrv.s b/libsrc/cbm510/ser_stddrv.s new file mode 100644 index 000000000..ed785f914 --- /dev/null +++ b/libsrc/cbm510/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "cbm510-std.ser" diff --git a/libsrc/cbm610/ser_stat_stddrv.s b/libsrc/cbm610/ser_stat_stddrv.s new file mode 100644 index 000000000..643a74c7d --- /dev/null +++ b/libsrc/cbm610/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _cbm610_std_ser + +.rodata + +_ser_static_stddrv := _cbm610_std_ser diff --git a/libsrc/cbm610/ser_stddrv.s b/libsrc/cbm610/ser_stddrv.s new file mode 100644 index 000000000..83702c1ef --- /dev/null +++ b/libsrc/cbm610/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "cbm610-std.ser" diff --git a/libsrc/lynx/ser_stat_stddrv.s b/libsrc/lynx/ser_stat_stddrv.s new file mode 100644 index 000000000..37f481c47 --- /dev/null +++ b/libsrc/lynx/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _lynx_comlynx_ser + +.rodata + +_ser_static_stddrv := _lynx_comlynx_ser diff --git a/libsrc/plus4/ser_stat_stddrv.s b/libsrc/plus4/ser_stat_stddrv.s new file mode 100644 index 000000000..f35b09232 --- /dev/null +++ b/libsrc/plus4/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _plus4_stdser_ser + +.rodata + +_ser_static_stddrv := _plus4_stdser_ser diff --git a/libsrc/plus4/ser_stddrv.s b/libsrc/plus4/ser_stddrv.s new file mode 100644 index 000000000..f308d5f40 --- /dev/null +++ b/libsrc/plus4/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "plus4-stdser.ser" From 1daa445310a5054a63e4985340f6760111e3e097 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Thu, 22 Dec 2022 21:50:38 +0100 Subject: [PATCH 041/520] Fixed recently introduced addressing mode bug. --- libsrc/apple2/ser/a2.ssc.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index e0cd94597..d49bf3526 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -199,7 +199,7 @@ SER_OPEN: asl asl asl - adc Offset ; Assume carry to be clear + adc #Offset ; Assume carry to be clear tax ; Check if the handshake setting is valid From de30a57c0c9e6186049e2659f88ee395bad25b8b Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Fri, 23 Dec 2022 15:24:28 +0100 Subject: [PATCH 042/520] Added minimalistic terminal program. So far there was no sample code at all making use of the serial drivers. --- samples/Makefile | 8 +++++ samples/terminal.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 samples/terminal.c diff --git a/samples/Makefile b/samples/Makefile index 2aa637844..114bbdf6a 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -171,6 +171,7 @@ EXELIST_apple2 = \ multdemo \ ovrldemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -186,6 +187,7 @@ EXELIST_atari = \ multdemo \ ovrldemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -203,6 +205,7 @@ EXELIST_atmos = \ hello \ mandelbrot \ sieve \ + terminal \ tgidemo EXELIST_bbc = \ @@ -219,6 +222,7 @@ EXELIST_c64 = \ multdemo \ ovrldemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -231,6 +235,7 @@ EXELIST_c128 = \ mandelbrot \ mousedemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -247,6 +252,7 @@ EXELIST_cbm510 = \ gunzip65 \ hello \ mousedemo \ + terminal \ tinyshell \ sieve @@ -255,6 +261,7 @@ EXELIST_cbm610 = \ checkversion \ gunzip65 \ hello \ + terminal \ tinyshell \ sieve @@ -314,6 +321,7 @@ EXELIST_plus4 = \ enumdevdir \ gunzip65 \ hello \ + terminal \ tinyshell \ sieve diff --git a/samples/terminal.c b/samples/terminal.c new file mode 100644 index 000000000..51973f7a3 --- /dev/null +++ b/samples/terminal.c @@ -0,0 +1,76 @@ +/* +** Minimalistic terminal program. +** +** Makes use of the serial drivers. +** +** 2022-12-23, Oliver Schmidt (ol.sc@web.de) +** +*/ + + + +#include <cc65.h> +#include <conio.h> +#include <stdio.h> +#include <stdlib.h> +#include <serial.h> + + +static void check (const char* msg, unsigned char err) +{ + if (err == SER_ERR_OK) { + return; + } + + printf ("%s:0x%02x\n", msg, err); + if (doesclrscrafterexit ()) { + cgetc (); + } + exit (1); +} + + +void main (void) +{ + const struct ser_params par = { + SER_BAUD_9600, + SER_BITS_8, + SER_STOP_1, + SER_PAR_NONE, + SER_HS_HW + }; + + check ("ser_install", ser_install (ser_static_stddrv)); + + check ("ser_open", ser_open (&par)); + + atexit ((void (*)) ser_close); + + printf ("Serial Port: 9600-8-1-N RTS/CTS\n" + "Simple Term: Press ESC for exit\n"); + + while (1) + { + char chr; + + if (kbhit ()) + { + chr = cgetc (); + + if (chr == CH_ESC) { + putchar ('\n'); + return; + } + + if (ser_put (chr) == SER_ERR_OK) { + putchar (chr); + } else { + putchar ('\a'); + } + } + + if (ser_get (&chr) == SER_ERR_OK) { + putchar (chr); + } + } +} From a8c6409689d91c35b647bc566b380c6766635cd9 Mon Sep 17 00:00:00 2001 From: Rutger van Bergen <rbergen@xs4all.nl> Date: Mon, 26 Dec 2022 19:54:00 +0100 Subject: [PATCH 043/520] Delete kimHello --- samples/kim1/kimHello | Bin 2789 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 samples/kim1/kimHello diff --git a/samples/kim1/kimHello b/samples/kim1/kimHello deleted file mode 100644 index 5842567557dc9f321409d269970f08832b736d86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2789 zcmbVOTWl2989p<!moqbGW`|^LuiKKGv;}Gss1s;#q)H_X&?OpGQAIB5O9zD|Yicfy zTZvX`id4^PyYa5P4=B(o(lGL@m@VsitJ2o0P$RXt9weHRms}Kq2*Qh942&^glRj+! zGd2QM>PwHj|K(i1^Ur@f{@6caBR^#e^evV4CFt9Pc8bVHbVBy0T$R2rJ0ktL?4Nib zo8J=YZ<8)^P!)bIYN9})f5+@!{>KkCxqxB*K%{?2S`KP<wmO;E>arRkDPJQrCHdD} z0@!q1Hct~T-8U=R>2Yzg!)n|iY!z*Y=9&Z-6I^_pQ(G)ZbVziBX1ZQ%rU7*b56R8W zzS-W9rvhdCYxE5<>*1Q5z-yyl4%@htlkG^!b8X~<mOOr*@3BrTk{gTU_!6=FC6{FO zFOm^q#(ZD)GPQ1C^niF{p0K4Gw+LIXz}^PY#xxBqZs^)riVI-(5T;$23X+G-@8E}& zBF;MTi8MMsO^U{1-cA=f=wFg{XQ7L;OrhIHC##t$_-kg$e<Ym#iFlnh_PT7Ddn;6f zHDPj!-D!2P`D2+}r`<UM?(3Vzw&Hb7%eER<!Zq}G!ijs`RaEpcoXDJDPE<~mDx#+e zeO;_G)s3H-CP&lfj3p~r!@z5tg2U*KMDtgYk6iWLIdabU92PdPcx8@+-NBO}TqmIs zP!MjAFd2M>$(5S~j+uJQr9m%C)In)>Rzec2U&e!Iy&9tvVtJlaZjs>yGJKl==tlO% zXy-%Q-io#@kV>@SXK-OY><E4wt_t>rI55LlFu+v5UA_}tI20RRB<@m$XfCNhAl?vz z*O?A!QC=cGD&HgK$#}2_4)wDQE^2_eqDvcKzUV$#xl8D8#8IhNJ$09aiQy$4dPkw( zl=z_otb=D4E^yChsu-eC#o(0SAX^8=PTeG7SGHipjkp+>v<0Hw1`NNKE{b^vuV5Tv znu{uT$PBp0Kg`wzgN$(7hz7RuOMb#sIwHcIJEJ=fgQ2gno@@ial?cm!4FLHT1GeOY z!%SYuxB&&ugE=eeD@s|{Dk-h8Y)Hr00-4jb#%*%)dP*BlRCH}PrS&!D$#q?;H*OI= z3-PV*Y?p7563}_lm?KQ0-4gw(1Y*cVz%Lsb_;HnHO=UC3a>z!Ia6;a4+Lw-ui%@Ig zkOU<cCjCxVH2(}`^Nx*uV#~l4;Hum7+qU7LnNVxusxrmyw&a$|!j{2qT9**(c(Hy) zB_r_{VPtwz^)raL$m#>rtWm_oNnsLM9_pKo!Oem-DMU)*zQWrOa$|I>LVuZn7Le%? z!UWJnm~h)AlO{pNp$>ErstQ_C2IlG~GW5Lci}Xqo&O<{IsGxu;$pD32=9iL}+H7^u zX5Ac5j#V=pCz8c#<{VdCbOL2z2H8-l$nMUYA0*3<Xg01qszJI4P23|e**7Ro)Xfi* zpvKrdWD#}|H&~`uWhcprOfMv9h~aKMKumGspgW5BNP@ngf)yFCBHiG?jYl=;000d3 zVCHcYzLy?V0Kf4&GB!@kTA~FGYY7&&3|b*-p)d|V$!Z}!D*>*P;ACapa#1FSvZb6* z&s&+D?S3H(nb42wp2(RkdQ!xtyanM{j&+s}CmhL>cq0*kl&UCStRl-ccnebss!a+S zJt%Vx8nZ>ej%(7W@t(q-^TfCu$86Q5BTRGDiQ8&bDH==WUvbG;?Qd{URcR?;zsmbC z4U@d`%(pQB_~2~nBi_H)ifg6yXN{5Aw9&#vHg|I3iVDpgfT0N4Cl1P2l~9^uOCG6d z7E8FOWU<eAh^_Em?;=-2(YP7$*^#Re{}kwY%hpTb#zG2AKQ=0d>mX%~9jU$gh?>da zx+muh+bHcd_U(naBAfN}TFNrF$OU;6*YpA&(j9C*FWWjl@4yFytL@Z|2d7hZs<bz? zkDt9(_jC)EBmj;k?(hj76I+I$+1UJBayYFunq=(v{3-NMO~~v|Yw!+5%?OeJf{XbI zchncuQ)vx?>I>$1-l@<i6yqekzYr8&Jiwrhqb#o>(MIr=3XX6;Y7of>)5}T24^F|l z#REB0Q0p@VWjUUc3X4su;S==xk5T8bo?iq>h60VneFaoFhFh3@+LGBvk?&8ZvrTj~ zod>kj9bD`H(c&56f^khH#8d2EOPyi2k6HY?_zEwPFiratJt9;w?x)ao*&IO5cuGx$ zbhY&;LUdG#9%~=jl4I0T;p03v)~}W7XLV1SPK{t&DoyM47A_Up6<f5T<?+~A9TG?9 ziE{%cp@OdRe>9>}Y<xz>E)&`trCnTRNqAu{5T<!Jv;P7Cv9$jpDPJT|ZdvGuFm;hQ zViT1=BmM>WMvyKOpFlOxSER-TGIo*tPY<r<-w8g89H@W;HBi8Lms+y9OO3qePp@=Q zbkaqk!u{~?mVA!geXlo+b8XSu_0vN+x|<I49{_e>4?Xl|Zg7xp|L(5s-+PK48p!P) z%xzQ@q-1jg0|)7=2j3jn^90wGUHRPyemKbG-lw10wD}v)Zs~jO`ES3lZTpT5&pf?( z%jUjK&u;ERNDzsv7+YU**j^a5Vv)0G$Y^<N;seB&UVeGam+ub|p{?7trB~e_0y?y^ z{r_eUKQBl?f`CNfzro%izb5Yz6QS}Xsr-YK&y&hONpYgxE`PMlJ|x4R5ZZ1#Z8l>e JbXnmD{{hxi-7)|G From 37954dffe78074b9d8c80ef8253fd3f8dcb83f56 Mon Sep 17 00:00:00 2001 From: Rutger van Bergen <rbergen@xs4all.nl> Date: Mon, 26 Dec 2022 20:26:29 +0100 Subject: [PATCH 044/520] Specify supported tests --- targettest/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/targettest/Makefile b/targettest/Makefile index 7bcbc95f0..4d989d0df 100644 --- a/targettest/Makefile +++ b/targettest/Makefile @@ -693,6 +693,17 @@ EXELIST_bbc = \ EXELIST_lunix = \ notavailable +# omitted: arg-test clock-test clock cpeek-test conio cprintf cursor deb dir-test +# em-test exec-test1 exec-test2 fileio-test ft getopt-test heaptest joy-test +# mouse-test mul-test posixio-test rename-test scanf-test seek ser-test strdup-test +# stroserror-test uname-test +EXELIST_kim1 = \ + minimal \ + div-test \ + moddiv-test \ + strnlen \ + strqtok-test + # Unlisted targets will try to build everything. # That lets us learn what they cannot build, and what settings # we need to use for programs that can be built and run. From 817d129be8a076744a4d2d8d04e0d682f5d99fad Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Thu, 29 Dec 2022 19:18:00 +0200 Subject: [PATCH 045/520] Add support for 4 pixels per plane --- src/sp65/pcx.c | 171 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 30 deletions(-) diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index d721671b3..489c65c5d 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -153,11 +153,12 @@ static PCXHeader* ReadPCXHeader (FILE* F, const char* Name) P->Compressed, Name); } /* We support: - ** - one plane with either 1 or 8 bits per pixel - ** - three planes with 8 bits per pixel - ** - four planes with 8 bits per pixel (does this exist?) - */ + * - one plane with either 1, 4 or 8 bits per pixel + * - three planes with 8 bits per pixel + * - four planes with 8 bits per pixel (does this exist?) + */ if (!((P->BPP == 1 && P->Planes == 1) || + (P->BPP == 4 && P->Planes == 1) || (P->BPP == 8 && (P->Planes == 1 || P->Planes == 3 || P->Planes == 4)))) { /* We could support others, but currently we don't */ Error ("Unsupported PCX format: %u planes, %u bpp in PCX file '%s'", @@ -204,11 +205,14 @@ static void DumpPCXHeader (const PCXHeader* P, const char* Name) static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) /* Read one (possibly compressed) plane from the file */ { - if (P->Compressed) { + unsigned i; + if (P->Compressed) { /* Uncompress RLE data */ - unsigned Remaining = P->Width; - while (Remaining) { + signed Remaining = P->BytesPerPlane; + signed WidthCounter = P->Width; + + while (Remaining > 0) { unsigned char C; @@ -224,21 +228,111 @@ static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) } /* Write the data to the buffer */ - if (C > Remaining) { - C = Remaining; - } - memset (L, B, C); - - /* Bump counters */ - L += C; - Remaining -= C; - + switch (P->BPP) { + default: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = B; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + case 4: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = B >> 4; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = B & 15; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + case 2: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = (B >> 6) & 3; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 4) & 3; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 2) & 3; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = B & 3; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + case 1: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = (B >> 7) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 6) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 5) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 4) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 3) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 2) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 1) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = B & 1; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + } } } else { - /* Just read one line */ - ReadData (F, L, P->Width); - + if (P->BPP == 4) { + printf("Not implemented\n"); + } else { + ReadData (F, L, P->Width); + } } } @@ -309,25 +403,42 @@ Bitmap* ReadPCXFile (const Collection* A) } } else { - /* One plane with 8bpp is indexed */ - for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { + if (P->BPP == 4) { + /* One plane with 8bpp is indexed */ + for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { - /* Read the plane */ - ReadPlane (F, P, L); + /* Read the plane */ + ReadPlane (F, P, L); - /* Create pixels */ - for (X = 0; X < P->Width; ++X, ++Px) { - if (L[X] > MaxIdx) { - MaxIdx = L[X]; + /* Create pixels */ + for (X = 0; X < P->Width; ++X, ++Px) { + if (L[X] > MaxIdx) { + MaxIdx = L[X]; + } + Px->Index = L[X]; + } + } + } else { + /* One plane with 8bpp is indexed */ + for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { + + /* Read the plane */ + ReadPlane (F, P, L); + + /* Create pixels */ + for (X = 0; X < P->Width; ++X, ++Px) { + if (L[X] > MaxIdx) { + MaxIdx = L[X]; + } + Px->Index = L[X]; } - Px->Index = L[X]; } } } /* One plane means we have a palette which is either part of the header - ** or follows. - */ + * or follows. + */ if (P->PalInfo == 0) { /* Create the monochrome palette */ From e953c1aa082a154b991388650bef4a0e46bd76b0 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Thu, 29 Dec 2022 19:28:16 +0200 Subject: [PATCH 046/520] Add support for 4 pixels per plane --- src/sp65/pcx.c | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index 489c65c5d..4beda045c 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -230,109 +230,109 @@ static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) /* Write the data to the buffer */ switch (P->BPP) { default: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = B; + *L = B; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; case 4: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = B >> 4; + *L = B >> 4; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = B & 15; + *L = B & 15; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; case 2: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = (B >> 6) & 3; + *L = (B >> 6) & 3; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 4) & 3; + *L = (B >> 4) & 3; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 2) & 3; + *L = (B >> 2) & 3; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = B & 3; + *L = B & 3; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; case 1: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = (B >> 7) & 1; + *L = (B >> 7) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 6) & 1; + *L = (B >> 6) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 5) & 1; + *L = (B >> 5) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 4) & 1; + *L = (B >> 4) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 3) & 1; + *L = (B >> 3) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 2) & 1; + *L = (B >> 2) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 1) & 1; + *L = (B >> 1) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = B & 1; + *L = B & 1; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; - } + } } } else { /* Just read one line */ if (P->BPP == 4) { - printf("Not implemented\n"); - } else { + printf("Not implemented\n"); + } else { ReadData (F, L, P->Width); - } + } } } From 05766d4bfecddc4e4cd4d468c945ee142a5c163d Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Thu, 29 Dec 2022 19:40:00 +0200 Subject: [PATCH 047/520] Add support for 4 pixels per plane --- src/sp65/pcx.c | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index 4beda045c..519f3bd58 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -230,109 +230,109 @@ static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) /* Write the data to the buffer */ switch (P->BPP) { default: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = B; + *L = B; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; case 4: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = B >> 4; + *L = B >> 4; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = B & 15; + *L = B & 15; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; case 2: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = (B >> 6) & 3; + *L = (B >> 6) & 3; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 4) & 3; + *L = (B >> 4) & 3; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 2) & 3; + *L = (B >> 2) & 3; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = B & 3; + *L = B & 3; L += 1; WidthCounter -= 1; } Remaining -= 1; - } + } break; case 1: - for (i = 0; i < C; i++) { + for (i = 0; i < C; i++) { if (WidthCounter > 0) { - *L = (B >> 7) & 1; + *L = (B >> 7) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 6) & 1; + *L = (B >> 6) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 5) & 1; + *L = (B >> 5) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 4) & 1; + *L = (B >> 4) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 3) & 1; + *L = (B >> 3) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 2) & 1; + *L = (B >> 2) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = (B >> 1) & 1; + *L = (B >> 1) & 1; L += 1; WidthCounter -= 1; } if (WidthCounter > 0) { - *L = B & 1; + *L = B & 1; L += 1; WidthCounter -= 1; } Remaining -= 1; - } - break; } + break; + } } } else { /* Just read one line */ if (P->BPP == 4) { - printf("Not implemented\n"); - } else { + printf("Not implemented\n"); + } else { ReadData (F, L, P->Width); - } + } } } From 5ec5050af2cb6b9e51c3c5482aaf67be3f22c1d4 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Thu, 29 Dec 2022 21:48:28 +0200 Subject: [PATCH 048/520] Remove redundant code --- src/sp65/pcx.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index 519f3bd58..607253c59 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -403,35 +403,18 @@ Bitmap* ReadPCXFile (const Collection* A) } } else { - if (P->BPP == 4) { - /* One plane with 8bpp is indexed */ - for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { + /* One plane with 8bpp is indexed */ + for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { - /* Read the plane */ - ReadPlane (F, P, L); + /* Read the plane */ + ReadPlane (F, P, L); - /* Create pixels */ - for (X = 0; X < P->Width; ++X, ++Px) { - if (L[X] > MaxIdx) { - MaxIdx = L[X]; - } - Px->Index = L[X]; - } - } - } else { - /* One plane with 8bpp is indexed */ - for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { - - /* Read the plane */ - ReadPlane (F, P, L); - - /* Create pixels */ - for (X = 0; X < P->Width; ++X, ++Px) { - if (L[X] > MaxIdx) { - MaxIdx = L[X]; - } - Px->Index = L[X]; + /* Create pixels */ + for (X = 0; X < P->Width; ++X, ++Px) { + if (L[X] > MaxIdx) { + MaxIdx = L[X]; } + Px->Index = L[X]; } } } From 9cb06672602c8d563d021baa7cdc3f871f67668f Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Thu, 29 Dec 2022 21:53:50 +0200 Subject: [PATCH 049/520] Use same style in comments --- src/sp65/pcx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index 607253c59..18a60276d 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -153,10 +153,10 @@ static PCXHeader* ReadPCXHeader (FILE* F, const char* Name) P->Compressed, Name); } /* We support: - * - one plane with either 1, 4 or 8 bits per pixel - * - three planes with 8 bits per pixel - * - four planes with 8 bits per pixel (does this exist?) - */ + ** - one plane with either 1, 4 or 8 bits per pixel + ** - three planes with 8 bits per pixel + ** - four planes with 8 bits per pixel (does this exist?) + **/ if (!((P->BPP == 1 && P->Planes == 1) || (P->BPP == 4 && P->Planes == 1) || (P->BPP == 8 && (P->Planes == 1 || P->Planes == 3 || P->Planes == 4)))) { @@ -420,8 +420,8 @@ Bitmap* ReadPCXFile (const Collection* A) } /* One plane means we have a palette which is either part of the header - * or follows. - */ + ** or follows. + **/ if (P->PalInfo == 0) { /* Create the monochrome palette */ From ebd13810307768b422626d91253018e515d8ba06 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Thu, 29 Dec 2022 21:55:25 +0200 Subject: [PATCH 050/520] Use same style in comments --- src/sp65/pcx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index 18a60276d..e6d99b30b 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -156,7 +156,7 @@ static PCXHeader* ReadPCXHeader (FILE* F, const char* Name) ** - one plane with either 1, 4 or 8 bits per pixel ** - three planes with 8 bits per pixel ** - four planes with 8 bits per pixel (does this exist?) - **/ + */ if (!((P->BPP == 1 && P->Planes == 1) || (P->BPP == 4 && P->Planes == 1) || (P->BPP == 8 && (P->Planes == 1 || P->Planes == 3 || P->Planes == 4)))) { @@ -421,7 +421,7 @@ Bitmap* ReadPCXFile (const Collection* A) /* One plane means we have a palette which is either part of the header ** or follows. - **/ + */ if (P->PalInfo == 0) { /* Create the monochrome palette */ From aed6591b19262c10d13517f7049fad25d03c2d27 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 12:16:46 +0200 Subject: [PATCH 051/520] Add new method GetBitmapBPP as we need it for sprite math --- src/sp65/bitmap.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sp65/bitmap.h b/src/sp65/bitmap.h index bf5e60559..e94ec71fd 100644 --- a/src/sp65/bitmap.h +++ b/src/sp65/bitmap.h @@ -75,6 +75,9 @@ struct Bitmap { unsigned Width; unsigned Height; + /* Bits per pixels */ + unsigned BPP; + /* Palette for indexed bitmap types, otherwise NULL */ Palette* Pal; @@ -179,6 +182,17 @@ INLINE unsigned GetBitmapColors (const Bitmap* B) # define GetBitmapColors(B) ((B)->Pal? (B)->Pal->Count : (1U << 24)) #endif +#if defined(HAVE_INLINE) +INLINE unsigned GetBitmapBPP (const Bitmap* B) +/* Get the bits per pixel of the converted sprite + */ +{ + return B->BPP; +} +#else +# define GetBitmapBPP(B) ((B)->BPP +#endif + /* End of bitmap.h */ From b4aa853e6fa15835a0f8cf862cd9639bc7e9dfb2 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 12:21:49 +0200 Subject: [PATCH 052/520] Add fixed Lynx sprite generation --- src/sp65/lynxsprite.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sp65/lynxsprite.h b/src/sp65/lynxsprite.h index fe686ec8e..e700b4f46 100644 --- a/src/sp65/lynxsprite.h +++ b/src/sp65/lynxsprite.h @@ -54,9 +54,9 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A); -/* Generate binary output in packed Lynx sprite format for the bitmap B. The output -** is stored in a string buffer (which is actually a dynamic char array) and -** returned. +/* Generate binary output in packed Lynx sprite format for the bitmap B. +** The output is stored in a string buffer (which is actually a dynamic +** char array) and returned. */ From f6b3bdda21f10b8078217a7679c9b36f441581ea Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 12:36:49 +0200 Subject: [PATCH 053/520] Add fixed Lynx sprite generation --- src/sp65/lynxsprite.c | 785 ++++++++++++++++++++++++++++-------------- 1 file changed, 529 insertions(+), 256 deletions(-) diff --git a/src/sp65/lynxsprite.c b/src/sp65/lynxsprite.c index 4d7669faf..dca42b061 100644 --- a/src/sp65/lynxsprite.c +++ b/src/sp65/lynxsprite.c @@ -88,12 +88,18 @@ static enum Mode GetMode (const Collection* A) } -static unsigned GetActionPointX (const Collection* A) +static unsigned GetActionPointX (const Bitmap* B, const Collection* A) /* Return the sprite mode from the attribute collection A */ { /* Check for a action point x attribute */ const char* ActionPointX = GetAttrVal (A, "ax"); if (ActionPointX) { + if (strcmp (ActionPointX, "mid") == 0) { + return GetBitmapWidth (B) / 2; + } + if (strcmp (ActionPointX, "max") == 0) { + return GetBitmapWidth (B) - 1; + } return atoi(ActionPointX); } else { return 0; @@ -101,12 +107,18 @@ static unsigned GetActionPointX (const Collection* A) } -static unsigned GetActionPointY (const Collection* A) +static unsigned GetActionPointY (const Bitmap* B, const Collection* A) /* Return the sprite mode from the attribute collection A */ { /* Check for a action point y attribute */ const char* ActionPointY = GetAttrVal (A, "ay"); if (ActionPointY) { + if (strcmp (ActionPointY, "mid") == 0) { + return GetBitmapHeight (B) / 2; + } + if (strcmp (ActionPointY, "max") == 0) { + return GetBitmapHeight (B) - 1; + } return atoi(ActionPointY); } else { return 0; @@ -125,6 +137,124 @@ static unsigned GetEdgeIndex (const Collection* A) } } +static unsigned GetQuadrant (const Collection* A) +/* Return the sprite mode from the attribute collection A */ +{ + /* Get index for edge color in shaped mode */ + const char* Quadrant = GetAttrVal (A, "quadrant"); + if (Quadrant) { + return atoi(Quadrant); + } else { + return 0; + } +} + +static void OptimizePenpal (const Bitmap* B, char *PenPal) +/* Create an optimal Penpal */ +{ + char usage[16]; + unsigned I, J, Val; + + memset(usage, 0, sizeof(usage)); + for (J = 0; J < GetBitmapHeight (B); J++) { + for (I = 0; I < GetBitmapWidth (B); I++) { + Val = GetPixel (B, I, J).Index; + if (Val < 16) { + usage[Val] = 1; + } + } + } + J = 0; + for (I = 0; I < 16; I++) { + if (usage[I]) { + switch (I) { + case 0: + PenPal[J] = '0'; + break; + case 1: + PenPal[J] = '1'; + break; + case 2: + PenPal[J] = '2'; + break; + case 3: + PenPal[J] = '3'; + break; + case 4: + PenPal[J] = '4'; + break; + case 5: + PenPal[J] = '5'; + break; + case 6: + PenPal[J] = '6'; + break; + case 7: + PenPal[J] = '7'; + break; + case 8: + PenPal[J] = '8'; + break; + case 9: + PenPal[J] = '9'; + break; + case 10: + PenPal[J] = 'a'; + break; + case 11: + PenPal[J] = 'b'; + break; + case 12: + PenPal[J] = 'c'; + break; + case 13: + PenPal[J] = 'd'; + break; + case 14: + PenPal[J] = 'e'; + break; + case 15: + PenPal[J] = 'f'; + break; + } + J++; + } + } + while (J < 16) { + PenPal[J] = 0; + J++; + } + /* printf("Penpal %s\n", PenPal); */ +} + +static unsigned GetPenpal (const Bitmap* B, const Collection* A, char *PenPal) +/* Return the penpal from the attribute collection A */ +{ + const char* Pen = GetAttrVal (A, "pen"); + if (Pen) { + if (strcmp (Pen, "opt") == 0) { + /* So we need to optimize the penpal and colour depth */ + OptimizePenpal (B, PenPal); + } else { + strncpy(PenPal, Pen, 17); + } + return 1; + } + return 0; +} + +static unsigned GetBPP (const Collection* A) +/* Return the sprite depth from the attribute collection A */ +{ + /* Get index for edge color in shaped mode */ + const char* BPP = GetAttrVal (A, "bpp"); + if (BPP) { + return atoi(BPP); + } else { + return 0; + } +} + static char OutBuffer[512]; /* The maximum size is 508 pixels */ static unsigned char OutIndex; @@ -140,26 +270,16 @@ static void AssembleByte(unsigned bits, char val) return; } /* handle end of line */ - if (bits == 8) { + if (bits == 7) { if (bit_counter != 8) { byte <<= bit_counter; OutBuffer[OutIndex++] = byte; if (!OutIndex) { Error ("Sprite is too large for the Lynx"); } - if (byte & 0x1) { - OutBuffer[OutIndex++] = byte; - if (!OutIndex) { - Error ("Sprite is too large for the Lynx"); - } - } - } - return; - } - /* handle end of line for literal */ - if (bits == 7) { - if (bit_counter != 8) { - byte <<= bit_counter; + } else { + /* Add pad byte */ + byte = 0; OutBuffer[OutIndex++] = byte; if (!OutIndex) { Error ("Sprite is too large for the Lynx"); @@ -189,28 +309,78 @@ static void AssembleByte(unsigned bits, char val) } while (--bits); } -static unsigned char ChoosePackagingMode(signed len, signed index, char ColorBits, char LineBuffer[512]) +static unsigned char AnalyseNextChunks(signed *newlen, signed len, char data[32], char ColorBits) { - --len; - if (!len) { - return 0; + char longest = 1; + char prev = 255; + char count = 0; + char index = 0; + char lindex = 0; + int i; + int literal_cost; + int packed_cost; + + for (i = 0; i < len; i++) { + index = index + 1; + if (data[i] == prev) { + count = count + 1; + if (count >= longest) { + longest = count; + lindex = index - count; + } + } else { + prev = data[i]; + count = 1; + } } - if (LineBuffer[index] != LineBuffer[index + 1]) { - return 0; + if (longest == 1) { + if (len > 16) { + *newlen = 16; + } else { + *newlen = len; + } + return 'L'; } - if (ColorBits > 2) { - return 1; + if ((lindex > 0) && (lindex + longest > 15)) { + /* We cannot pack the stride in this packet */ + *newlen = lindex; + return 'A'; } - if (LineBuffer[index] != LineBuffer[index + 2]) { - return 0; + /* Cost till end of area */ + literal_cost = 5 + lindex * ColorBits + longest * ColorBits; + packed_cost = 5 + lindex * ColorBits + 5 + ColorBits; + if (packed_cost < literal_cost) { + if (lindex == 0) { + /* Use packed data */ + if (longest > 16) { + *newlen = 16; + } else { + *newlen = longest; + } + return 'P'; + } + /* We had a good find, but it was not at the start of the line */ + *newlen = lindex; + return 'A'; } - if (ColorBits > 1) { - return 1; + /* There is no point in packing - use literal */ + if (len > 16) { + *newlen = 16; + } else { + *newlen = len; } - if (LineBuffer[index] != LineBuffer[index + 3]) { - return 0; + return 'L'; +} + +static unsigned char GetNextChunk(signed *newlen, signed len, char data[32], char ColorBits) +{ + char oper = 'A'; + + while (oper == 'A') { + oper = AnalyseNextChunks(newlen, len, data, ColorBits); + len = *newlen; } - return 1; + return oper; /* The packet type is now P or L and the length is in newlen */ } static void WriteOutBuffer(StrBuf *D) @@ -235,27 +405,25 @@ static void WriteOutBuffer(StrBuf *D) static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512], int len, int LastOpaquePixel) { /* -** The data starts with a byte count. It tells the number of bytes on this -** line + 1. -** Special case is a count of 1. It will change to next quadrant. -** Other special case is 0. It will end the sprite. -** -** Ordinary data packet. These are bits in a stream. -** 1=literal 0=packed -** 4 bit count (+1) -** for literal you put "count" values -** for packed you repeat the value "count" times -** Never use packed mode for one pixel -** If the last bit on a line is 1 you need to add a byte of zeroes -** A sequence 00000 ends a scan line -** -** All data is high nybble first -*/ + * The data starts with a byte count. It tells the number of bytes on this + * line + 1. + * Special case is a count of 1. It will change to next quadrant. + * Other special case is 0. It will end the sprite. + * + * Ordinary data packet. These are bits in a stream. + * 1=literal 0=packed + * 4 bit count (+1) + * for literal you put "count" values + * for packed you repeat the value "count" times + * Never use packed mode for one pixel + * If the last bit on a line is in use you need to add a byte of zeroes + * A sequence 00000 ends a scan line + * + * All data is high nybble first + */ unsigned char V = 0; signed i; signed count; - unsigned char differ[16]; - unsigned char *d_ptr; AssembleByte(0, 0); switch (M) { @@ -270,100 +438,46 @@ static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, WriteOutBuffer(D); break; case smPacked: + case smShaped: + if (M == smShaped) { + if (LastOpaquePixel > -1) { + if (LastOpaquePixel < len - 1) { + len = LastOpaquePixel + 1; + } + } else { + len = 0; + } + } i = 0; while (len) { - if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) { + signed analyselen; + analyselen = len; + if (analyselen > 32) { + analyselen = 32; + } + if (GetNextChunk(&count, analyselen, LineBuffer + i, ColorBits) == 'P') { /* Make runlength packet */ V = LineBuffer[i]; - ++i; - --len; - count = 0; - do { - ++count; - ++i; - --len; - } while (V == LineBuffer[i] && len && count != 15); - - AssembleByte(5, count); - AssembleByte(ColorBits, V); + i += count; + len -= count; + AssembleByte(5, count-1); + AssembleByte(ColorBits, V & ColorMask); } else { /* Make packed literal packet */ - d_ptr = differ; - V = LineBuffer[i++]; - *d_ptr++ = V; - --len; - count = 0; - while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) { - V = LineBuffer[i++]; - *d_ptr++ = V; - ++count; - --len; - } - - AssembleByte(5, count | 0x10); - d_ptr = differ; + AssembleByte(5, (count-1) | 0x10); do { - AssembleByte(ColorBits, *d_ptr++); - } while (--count >= 0); - + AssembleByte(ColorBits, LineBuffer[i]); + i++; + len--; + } while (--count > 0); } } - AssembleByte(8, 0); + /* Force EOL for shaped? AssembleByte(5, 0); */ + AssembleByte(7, 0); /* Write the buffer to file */ WriteOutBuffer(D); break; - - case smShaped: - if (LastOpaquePixel > -1) { - if (LastOpaquePixel < len - 1) { - len = LastOpaquePixel + 1; - } - i = 0; - while (len) { - if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) { - /* Make runlength packet */ - V = LineBuffer[i]; - ++i; - --len; - count = 0; - do { - ++count; - ++i; - --len; - } while (V == LineBuffer[i] && len && count != 15); - - AssembleByte(5, count); - AssembleByte(ColorBits, V); - - } else { - /* Make packed literal packet */ - d_ptr = differ; - V = LineBuffer[i++]; - *d_ptr++ = V; - --len; - count = 0; - while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) { - V = LineBuffer[i++]; - *d_ptr++ = V; - ++count; - --len; - } - - AssembleByte(5, count | 0x10); - d_ptr = differ; - do { - AssembleByte(ColorBits, *d_ptr++); - } while (--count >= 0); - - } - } - AssembleByte(5, 0); - AssembleByte(8, 0); - /* Write the buffer to file */ - WriteOutBuffer(D); - } - break; } } @@ -373,10 +487,10 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) ** returned. ** ** The Lynx will draw 4 quadrants: -** - Down right -** - Up right -** - Up left -** - Down left +** 0 - Down right +** 1 - Up right +** 2 - Up left +** 3 - Down left ** ** The sprite will end with a byte 0. */ @@ -388,13 +502,24 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) char ColorBits; char ColorMask; char EdgeIndex; + char Quadrant; + char quad; + char BPP; + /* The default mapping is 1:1 plus extra colours become 0 */ + char Map[17] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0}; + signed PenColors; + char PenPal[18]; + signed Val; /* Get EdgeIndex */ EdgeIndex = GetEdgeIndex (A); + /* Get Quadrant for starting the draw process */ + Quadrant = GetQuadrant (A) & 3; + /* Action point of the sprite */ - OX = GetActionPointX (A); - OY = GetActionPointY (A); + OX = GetActionPointX (B, A); + OY = GetActionPointY (B, A); if (OX >= GetBitmapWidth (B)) { Error ("Action point X cannot be larger than bitmap width"); } @@ -410,145 +535,293 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) /* Get the sprite mode */ M = GetMode (A); - /* Now check if bitmap indexes are ok */ - if (GetBitmapColors (B) > 16) { - Error ("Too many colors for a Lynx sprite"); + /* Now check how to do the mapping */ + if (GetPenpal (B, A, &PenPal[0])) { + signed I; + + /* Modify the map by content of PenPal */ + PenColors = strlen(PenPal); + for (I = 0; I < PenColors; I++) { + switch (PenPal[I]) { + case '0': + Map[0] = I; + break; + case '1': + Map[1] = I; + break; + case '2': + Map[2] = I; + break; + case '3': + Map[3] = I; + break; + case '4': + Map[4] = I; + break; + case '5': + Map[5] = I; + break; + case '6': + Map[6] = I; + break; + case '7': + Map[7] = I; + break; + case '8': + Map[8] = I; + break; + case '9': + Map[9] = I; + break; + case 'a': + case 'A': + Map[10] = I; + break; + case 'b': + case 'B': + Map[11] = I; + break; + case 'c': + case 'C': + Map[12] = I; + break; + case 'd': + case 'D': + Map[13] = I; + break; + case 'e': + case 'E': + Map[14] = I; + break; + case 'f': + case 'F': + Map[15] = I; + break; + /* The X is reserved as transparency. This allows for shaped sprites */ + case 'x': + case 'X': + Map[16] = I; + break; + } + } + } else { + PenColors = GetBitmapColors (B); } ColorBits = 4; - ColorMask = 0x0f; - if (GetBitmapColors (B) < 9) { + if (PenColors < 9) { ColorBits = 3; - ColorMask = 0x07; } - if (GetBitmapColors (B) < 5) { + if (PenColors < 5) { ColorBits = 2; - ColorMask = 0x03; } - if (GetBitmapColors (B) < 3) { + if (PenColors < 3) { ColorBits = 1; - ColorMask = 0x01; } + BPP = GetBPP (A); + if (BPP > 0) { + ColorBits = BPP; + } + switch (ColorBits) { + case 1: + ColorMask = 0x01; + break; + case 2: + ColorMask = 0x03; + break; + case 3: + ColorMask = 0x07; + break; + default: + case 4: + ColorMask = 0x0f; + break; + } + /* B->BPP = ColorBits; */ /* Create the output buffer and resize it to the required size. */ D = NewStrBuf (); SB_Realloc (D, 63); - /* Convert the image for quadrant bottom right */ - for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + for (quad = 0; quad < 4; quad++) { + switch ((Quadrant + quad) & 3) { + case 0: + /* Convert the image for quadrant bottom right */ + for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - /* Fill the LineBuffer for easier optimisation */ - for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + LineBuffer[i] = Map[Val] & ColorMask; - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == 0) && (OX == 0)) { + /* Trivial case only one quadrant */ - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OY == 0)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + case 1: + /* Convert the image for quadrant top right */ + for (Y = OY - 1; Y >= 0; --Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ + + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + + LineBuffer[i] = Map[Val] & ColorMask; + + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } + + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + + if ((OY == GetBitmapHeight (B) - 1) && (OX == 0)) { + /* Trivial case only one quadrant */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OX == 0)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + case 2: + /* Convert the image for quadrant top left */ + for (Y = OY - 1; Y >= 0; --Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ + + /* Fill the LineBuffer for easier optimisation */ + for (X = OX - 1; X >= 0; --X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + + LineBuffer[i] = Map[Val] & ColorMask; + + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } + + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == GetBitmapHeight (B) - 1) && (OX == GetBitmapWidth (B) - 1)) { + /* Trivial case only one quadrant */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OY == GetBitmapHeight (B) - 1)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + case 3: + /* Convert the image for quadrant bottom left */ + for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ + + /* Fill the LineBuffer for easier optimisation */ + for (X = OX - 1; X >= 0; --X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + + LineBuffer[i] = Map[Val] & ColorMask; + + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } + + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == 0) && (OX == GetBitmapWidth (B) - 1)) { + /* Trivial case only one quadrant */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OX == GetBitmapWidth (B) - 1)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + } + if (quad < 3) { + /* Next quadrant */ + SB_AppendChar (D, 1); + } else { + /* End sprite */ + SB_AppendChar (D, 0); + } } - if ((OY == 0) && (OX == 0)) { - /* Trivial case only one quadrant */ - - /* Mark end of sprite */ - SB_AppendChar (D, 0); - - /* Return the converted bitmap */ - return D; - } - - /* Next quadrant */ - SB_AppendChar (D, 1); - - /* Convert the image for quadrant top right */ - for (Y = OY - 1; Y >= 0; --Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ - - /* Fill the LineBuffer for easier optimisation */ - for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { - - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; - - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } - - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } - - if (OX == 0) { - /* Special case only two quadrants */ - - /* Mark end of sprite */ - SB_AppendChar (D, 0); - - /* Return the converted bitmap */ - return D; - } - - /* Next quadrant */ - SB_AppendChar (D, 1); - - /* Convert the image for quadrant top left */ - for (Y = OY - 1; Y >= 0; --Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ - - /* Fill the LineBuffer for easier optimisation */ - for (X = OX - 1; X >= 0; --X) { - - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; - - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } - - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } - - /* Next quadrant */ - SB_AppendChar (D, 1); - - /* Convert the image for quadrant bottom left */ - for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ - - /* Fill the LineBuffer for easier optimisation */ - for (X = OX - 1; X >= 0; --X) { - - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; - - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } - - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } - - /* End sprite */ - SB_AppendChar (D, 0); - /* Return the converted bitmap */ return D; } From c2e9e5961d898e6f0237d40e70853e16d3deac4c Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 12:57:50 +0200 Subject: [PATCH 054/520] Add fixed Lynx sprite generation --- src/sp65/lynxsprite.c | 458 +++++++++++++++++++++--------------------- 1 file changed, 229 insertions(+), 229 deletions(-) diff --git a/src/sp65/lynxsprite.c b/src/sp65/lynxsprite.c index dca42b061..566edacbe 100644 --- a/src/sp65/lynxsprite.c +++ b/src/sp65/lynxsprite.c @@ -96,10 +96,10 @@ static unsigned GetActionPointX (const Bitmap* B, const Collection* A) if (ActionPointX) { if (strcmp (ActionPointX, "mid") == 0) { return GetBitmapWidth (B) / 2; - } + } if (strcmp (ActionPointX, "max") == 0) { return GetBitmapWidth (B) - 1; - } + } return atoi(ActionPointX); } else { return 0; @@ -115,10 +115,10 @@ static unsigned GetActionPointY (const Bitmap* B, const Collection* A) if (ActionPointY) { if (strcmp (ActionPointY, "mid") == 0) { return GetBitmapHeight (B) / 2; - } + } if (strcmp (ActionPointY, "max") == 0) { return GetBitmapHeight (B) - 1; - } + } return atoi(ActionPointY); } else { return 0; @@ -235,10 +235,10 @@ static unsigned GetPenpal (const Bitmap* B, const Collection* A, char *PenPal) if (strcmp (Pen, "opt") == 0) { /* So we need to optimize the penpal and colour depth */ OptimizePenpal (B, PenPal); - } else { - strncpy(PenPal, Pen, 17); + } else { + strncpy(PenPal, Pen, 17); } - return 1; + return 1; } return 0; } @@ -322,52 +322,52 @@ static unsigned char AnalyseNextChunks(signed *newlen, signed len, char data[32] for (i = 0; i < len; i++) { index = index + 1; - if (data[i] == prev) { + if (data[i] == prev) { count = count + 1; - if (count >= longest) { + if (count >= longest) { longest = count; - lindex = index - count; - } - } else { - prev = data[i]; - count = 1; - } + lindex = index - count; + } + } else { + prev = data[i]; + count = 1; + } } if (longest == 1) { - if (len > 16) { - *newlen = 16; - } else { - *newlen = len; - } - return 'L'; + if (len > 16) { + *newlen = 16; + } else { + *newlen = len; + } + return 'L'; } if ((lindex > 0) && (lindex + longest > 15)) { /* We cannot pack the stride in this packet */ *newlen = lindex; - return 'A'; + return 'A'; } /* Cost till end of area */ literal_cost = 5 + lindex * ColorBits + longest * ColorBits; packed_cost = 5 + lindex * ColorBits + 5 + ColorBits; if (packed_cost < literal_cost) { if (lindex == 0) { - /* Use packed data */ - if (longest > 16) { - *newlen = 16; - } else { - *newlen = longest; + /* Use packed data */ + if (longest > 16) { + *newlen = 16; + } else { + *newlen = longest; } - return 'P'; - } - /* We had a good find, but it was not at the start of the line */ - *newlen = lindex; - return 'A'; + return 'P'; + } + /* We had a good find, but it was not at the start of the line */ + *newlen = lindex; + return 'A'; } /* There is no point in packing - use literal */ if (len > 16) { - *newlen = 16; + *newlen = 16; } else { - *newlen = len; + *newlen = len; } return 'L'; } @@ -378,7 +378,7 @@ static unsigned char GetNextChunk(signed *newlen, signed len, char data[32], cha while (oper == 'A') { oper = AnalyseNextChunks(newlen, len, data, ColorBits); - len = *newlen; + len = *newlen; } return oper; /* The packet type is now P or L and the length is in newlen */ } @@ -439,27 +439,27 @@ static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, break; case smPacked: case smShaped: - if (M == smShaped) { + if (M == smShaped) { if (LastOpaquePixel > -1) { if (LastOpaquePixel < len - 1) { len = LastOpaquePixel + 1; - } + } } else { len = 0; } } i = 0; while (len) { - signed analyselen; - analyselen = len; - if (analyselen > 32) { + signed analyselen; + analyselen = len; + if (analyselen > 32) { analyselen = 32; - } + } if (GetNextChunk(&count, analyselen, LineBuffer + i, ColorBits) == 'P') { /* Make runlength packet */ V = LineBuffer[i]; - i += count; - len -= count; + i += count; + len -= count; AssembleByte(5, count-1); AssembleByte(ColorBits, V & ColorMask); @@ -468,8 +468,8 @@ static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, AssembleByte(5, (count-1) | 0x10); do { AssembleByte(ColorBits, LineBuffer[i]); - i++; - len--; + i++; + len--; } while (--count > 0); } } @@ -537,75 +537,75 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) /* Now check how to do the mapping */ if (GetPenpal (B, A, &PenPal[0])) { - signed I; + signed I; - /* Modify the map by content of PenPal */ - PenColors = strlen(PenPal); - for (I = 0; I < PenColors; I++) { - switch (PenPal[I]) { + /* Modify the map by content of PenPal */ + PenColors = strlen(PenPal); + for (I = 0; I < PenColors; I++) { + switch (PenPal[I]) { case '0': - Map[0] = I; - break; + Map[0] = I; + break; case '1': - Map[1] = I; - break; + Map[1] = I; + break; case '2': - Map[2] = I; - break; + Map[2] = I; + break; case '3': - Map[3] = I; - break; + Map[3] = I; + break; case '4': - Map[4] = I; - break; + Map[4] = I; + break; case '5': - Map[5] = I; - break; + Map[5] = I; + break; case '6': - Map[6] = I; - break; + Map[6] = I; + break; case '7': - Map[7] = I; - break; + Map[7] = I; + break; case '8': - Map[8] = I; - break; + Map[8] = I; + break; case '9': - Map[9] = I; - break; + Map[9] = I; + break; case 'a': case 'A': - Map[10] = I; - break; + Map[10] = I; + break; case 'b': case 'B': - Map[11] = I; - break; + Map[11] = I; + break; case 'c': case 'C': - Map[12] = I; - break; + Map[12] = I; + break; case 'd': case 'D': - Map[13] = I; - break; + Map[13] = I; + break; case 'e': case 'E': - Map[14] = I; - break; + Map[14] = I; + break; case 'f': case 'F': - Map[15] = I; - break; - /* The X is reserved as transparency. This allows for shaped sprites */ + Map[15] = I; + break; + /* The X is reserved as transparency. This allows for shaped sprites */ case 'x': case 'X': - Map[16] = I; - break; - } - } + Map[16] = I; + break; + } + } } else { - PenColors = GetBitmapColors (B); + PenColors = GetBitmapColors (B); } ColorBits = 4; if (PenColors < 9) { @@ -643,183 +643,183 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) SB_Realloc (D, 63); for (quad = 0; quad < 4; quad++) { - switch ((Quadrant + quad) & 3) { - case 0: - /* Convert the image for quadrant bottom right */ - for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + switch ((Quadrant + quad) & 3) { + case 0: + /* Convert the image for quadrant bottom right */ + for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - /* Fill the LineBuffer for easier optimisation */ - for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { - /* Fetch next bit into byte buffer */ - Val = GetPixel (B, X, Y).Index; - if (Val > 16) Val = 16; - LineBuffer[i] = Map[Val] & ColorMask; + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + LineBuffer[i] = Map[Val] & ColorMask; - if (Val != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } - if ((OY == 0) && (OX == 0)) { - /* Trivial case only one quadrant */ + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == 0) && (OX == 0)) { + /* Trivial case only one quadrant */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - if ((quad == 1) && (OY == 0)) { - /* Special case only two quadrants */ + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OY == 0)) { + /* Special case only two quadrants */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - break; - case 1: - /* Convert the image for quadrant top right */ - for (Y = OY - 1; Y >= 0; --Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + /* Return the converted bitmap */ + return D; + } + break; + case 1: + /* Convert the image for quadrant top right */ + for (Y = OY - 1; Y >= 0; --Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - /* Fill the LineBuffer for easier optimisation */ - for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { - /* Fetch next bit into byte buffer */ - Val = GetPixel (B, X, Y).Index; - if (Val > 16) Val = 16; + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; - LineBuffer[i] = Map[Val] & ColorMask; + LineBuffer[i] = Map[Val] & ColorMask; - if (Val != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } - if ((OY == GetBitmapHeight (B) - 1) && (OX == 0)) { - /* Trivial case only one quadrant */ + if ((OY == GetBitmapHeight (B) - 1) && (OX == 0)) { + /* Trivial case only one quadrant */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - if ((quad == 1) && (OX == 0)) { - /* Special case only two quadrants */ + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OX == 0)) { + /* Special case only two quadrants */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - break; - case 2: - /* Convert the image for quadrant top left */ - for (Y = OY - 1; Y >= 0; --Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + /* Return the converted bitmap */ + return D; + } + break; + case 2: + /* Convert the image for quadrant top left */ + for (Y = OY - 1; Y >= 0; --Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - /* Fill the LineBuffer for easier optimisation */ - for (X = OX - 1; X >= 0; --X) { - /* Fetch next bit into byte buffer */ - Val = GetPixel (B, X, Y).Index; - if (Val > 16) Val = 16; + /* Fill the LineBuffer for easier optimisation */ + for (X = OX - 1; X >= 0; --X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; - LineBuffer[i] = Map[Val] & ColorMask; + LineBuffer[i] = Map[Val] & ColorMask; - if (Val != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } - if ((OY == GetBitmapHeight (B) - 1) && (OX == GetBitmapWidth (B) - 1)) { - /* Trivial case only one quadrant */ + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == GetBitmapHeight (B) - 1) && (OX == GetBitmapWidth (B) - 1)) { + /* Trivial case only one quadrant */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - if ((quad == 1) && (OY == GetBitmapHeight (B) - 1)) { - /* Special case only two quadrants */ + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OY == GetBitmapHeight (B) - 1)) { + /* Special case only two quadrants */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - break; - case 3: - /* Convert the image for quadrant bottom left */ - for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + /* Return the converted bitmap */ + return D; + } + break; + case 3: + /* Convert the image for quadrant bottom left */ + for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - /* Fill the LineBuffer for easier optimisation */ - for (X = OX - 1; X >= 0; --X) { - /* Fetch next bit into byte buffer */ - Val = GetPixel (B, X, Y).Index; - if (Val > 16) Val = 16; + /* Fill the LineBuffer for easier optimisation */ + for (X = OX - 1; X >= 0; --X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; - LineBuffer[i] = Map[Val] & ColorMask; + LineBuffer[i] = Map[Val] & ColorMask; - if (Val != EdgeIndex) { - LastOpaquePixel = i; - } - ++i; - } + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } - if ((OY == 0) && (OX == GetBitmapWidth (B) - 1)) { - /* Trivial case only one quadrant */ + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == 0) && (OX == GetBitmapWidth (B) - 1)) { + /* Trivial case only one quadrant */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - if ((quad == 1) && (OX == GetBitmapWidth (B) - 1)) { - /* Special case only two quadrants */ + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OX == GetBitmapWidth (B) - 1)) { + /* Special case only two quadrants */ - /* Mark end of sprite */ - SB_AppendChar (D, 0); + /* Mark end of sprite */ + SB_AppendChar (D, 0); - /* Return the converted bitmap */ - return D; - } - break; - } - if (quad < 3) { + /* Return the converted bitmap */ + return D; + } + break; + } + if (quad < 3) { /* Next quadrant */ SB_AppendChar (D, 1); - } else { + } else { /* End sprite */ SB_AppendChar (D, 0); - } + } } /* Return the converted bitmap */ From 4117e94ed59e9a8c47f05eddad27bd746fba6d42 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 14:44:18 +0200 Subject: [PATCH 055/520] Add palette extraction functions --- src/sp65/lynxpalette.c | 90 +++++++++++++++++++++++++++++++++++ src/sp65/lynxpalette.h | 66 ++++++++++++++++++++++++++ src/sp65/main.c | 64 +++++++++++++++++++++++-- src/sp65/palconv.c | 104 +++++++++++++++++++++++++++++++++++++++++ src/sp65/palconv.h | 72 ++++++++++++++++++++++++++++ 5 files changed, 391 insertions(+), 5 deletions(-) create mode 100644 src/sp65/lynxpalette.c create mode 100644 src/sp65/lynxpalette.h create mode 100644 src/sp65/palconv.c create mode 100644 src/sp65/palconv.h diff --git a/src/sp65/lynxpalette.c b/src/sp65/lynxpalette.c new file mode 100644 index 000000000..e822fa5ee --- /dev/null +++ b/src/sp65/lynxpalette.c @@ -0,0 +1,90 @@ +/*****************************************************************************/ +/* */ +/* lynxpalette.c */ +/* */ +/* Lynx palette backend for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include <stdlib.h> + +/* common */ +#include "attrib.h" +#include "print.h" + +/* sp65 */ +#include "attr.h" +#include "error.h" +#include "palette.h" +#include "lynxpalette.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + +StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A) +/* Generate binary output in Lynx palette format for the bitmap B. The output +** is stored in a string buffer (which is actually a dynamic char array) and +** returned. +** +*/ +{ + StrBuf* D; + Palette* P; + unsigned I; + + P = GetBitmapPalette (B); + D = NewStrBuf (); + for (I = 0; I < 16; ++I) { + + /* Get the color entry */ + const Color* C = P->Entries + I; + + /* Add the green component */ + SB_AppendChar (D, C->G >> 4); + } + for (I = 0; I < 16; ++I) { + + /* Get the color entry */ + const Color* C = P->Entries + I; + + /* Add the blue,red component */ + SB_AppendChar (D, (C->B & 0xF0) | (C->R >> 4)); + } + + /* Return the converted palette */ + return D; +} + diff --git a/src/sp65/lynxpalette.h b/src/sp65/lynxpalette.h new file mode 100644 index 000000000..3ba4526fb --- /dev/null +++ b/src/sp65/lynxpalette.h @@ -0,0 +1,66 @@ +/*****************************************************************************/ +/* */ +/* lynxpalette.h */ +/* */ +/* Lynx palette format backend for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LYNXPALETTE_H +#define LYNXPALETTE_H + + + +/* common */ +#include "coll.h" +#include "strbuf.h" + +/* sp65 */ +#include "bitmap.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A); +/* Generate binary output in Lynx palette format for the bitmap B. The output + * is stored in a string buffer (which is actually a dynamic char array) and + * returned. + */ + + + +/* End of lynxpalette.h */ + +#endif + + + diff --git a/src/sp65/main.c b/src/sp65/main.c index 1dda696d6..62dec0952 100644 --- a/src/sp65/main.c +++ b/src/sp65/main.c @@ -1,6 +1,6 @@ /*****************************************************************************/ /* */ -/* main.c */ +/* main.c */ /* */ /* Main program of the sp65 sprite and bitmap utility */ /* */ @@ -47,6 +47,7 @@ /* sp65 */ #include "attr.h" #include "convert.h" +#include "palconv.h" #include "error.h" #include "input.h" #include "output.h" @@ -68,10 +69,13 @@ static Bitmap* C; /* Output data from convertion */ static StrBuf* D; +/* Output data from palconv */ +static StrBuf* E; + /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ @@ -88,11 +92,11 @@ static void Usage (void) " -lc\t\t\t\tList all possible conversions\n" " -r file[,attrlist]\t\tRead an input file\n" " -v\t\t\t\tIncrease verbosity\n" + " -p tgt,file[,attrlist]\t\tWrite the palette to a file\n" " -w file[,attrlist]\t\tWrite the output to a file\n" "\n" "Long options:\n" " --convert-to fmt[,attrlist]\tConvert into target format\n" - " --dump-palette\t\tDump palette as table\n" " --help\t\t\tHelp (this text)\n" " --list-conversions\t\tList all possible conversions\n" " --pop\t\t\t\tRestore the original loaded image\n" @@ -100,6 +104,7 @@ static void Usage (void) " --slice x,y,w,h\t\tGenerate a slice from the loaded bitmap\n" " --verbose\t\t\tIncrease verbosity\n" " --version\t\t\tPrint the version number and exit\n" + " --palette tgt,file[,attrlist]\tWrite the palette to a file\n" " --write file[,attrlist]\tWrite the output to a file\n", ProgName); } @@ -137,6 +142,21 @@ static void SetOutputData (StrBuf* N) } +static void SetPalOutputData (StrBuf* N) +/* Delete the old output data and replace it by the given one. The new one +** may be NULL to clear it. +*/ +{ + /* Delete the old output data */ + if (E != 0) { + FreeStrBuf (E); + } + + /* Set the new one */ + E = N; +} + + static void OptConvertTo (const char* Opt attribute ((unused)), const char* Arg) /* Convert the bitmap into a target format */ @@ -282,15 +302,45 @@ static void OptVerbose (const char* Opt attribute ((unused)), static void OptVersion (const char* Opt attribute ((unused)), - const char* Arg attribute ((unused))) + const char* Arg attribute ((unused))) /* Print the assembler version */ { fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ()); - exit(EXIT_SUCCESS); } +static void OptPalette (const char* Opt attribute ((unused)), const char* Arg) +/* Write an output file */ +{ + static const char* const NameList[] = { + "target", "name", "format" + }; + + + /* Parse the argument */ + Collection* A = ParseAttrList (Arg, NameList, 2); + + /* We must have a bitmap ... */ + if (C == 0) { + Error ("No bitmap"); + } + + /* ... which must be indexed */ + if (!BitmapIsIndexed (C)) { + Error ("Current bitmap is not indexed"); + } + + /* Convert the palette */ + SetPalOutputData (PaletteTo (C, A)); + + /* Write the file */ + WriteOutputFile (E, A, C); + + /* Delete the attribute list */ + FreeAttrList (A); +} + static void OptWrite (const char* Opt attribute ((unused)), const char* Arg) /* Write an output file */ { @@ -381,6 +431,10 @@ int main (int argc, char* argv []) OptVerbose (Arg, 0); break; + case 'p': + OptPalette (Arg, GetArg (&I, 2)); + break; + case 'w': OptWrite (Arg, GetArg (&I, 2)); break; diff --git a/src/sp65/palconv.c b/src/sp65/palconv.c new file mode 100644 index 000000000..e92f3c22e --- /dev/null +++ b/src/sp65/palconv.c @@ -0,0 +1,104 @@ +/*****************************************************************************/ +/* */ +/* palconv.c */ +/* */ +/* Color palette conversions for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include <string.h> +#include <stdlib.h> + +/* common */ +#include "check.h" +#include "xmalloc.h" + +/* sp65 */ +#include "attr.h" +#include "error.h" +#include "palette.h" +#include "lynxpalette.h" + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + +/* Type of the entry in the palette table */ +typedef struct PaletteMapEntry PaletteMapEntry; +struct PaletteMapEntry { + const char* Format; + StrBuf* (*PaletteFunc) (const Bitmap*, const Collection*); +}; + +/* Converter table, alphabetically sorted */ +static const PaletteMapEntry PaletteMap[] = { + { "lynx-palette", GenLynxPalette }, +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + +static int Compare (const void* Key, const void* MapEntry) +/* Compare function for bsearch */ +{ + return strcmp (Key, ((const PaletteMapEntry*) MapEntry)->Format); +} + + + +StrBuf* PaletteTo (const Bitmap* B, const Collection* A) +/* Convert the palette of bitmap B into some sort of other binary format. +** The output is stored in a string buffer (which is actually a dynamic +** char array) and returned. The actual output format is taken from the +** "format" attribute in the attribute collection A. +*/ +{ + const PaletteMapEntry* E; + + /* Get the format to convert to */ + const char* Format = NeedAttrVal (A, "target", "palette"); + + /* Search for the matching converter */ + E = bsearch (Format, + PaletteMap, + sizeof (PaletteMap) / sizeof (PaletteMap[0]), + sizeof (PaletteMap[0]), + Compare); + if (E == 0) { + Error ("No such target format: '%s'", Format); + } + + /* Do the conversion */ + return E->PaletteFunc (B, A); +} + diff --git a/src/sp65/palconv.h b/src/sp65/palconv.h new file mode 100644 index 000000000..693281c20 --- /dev/null +++ b/src/sp65/palconv.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* */ +/* palconv.h */ +/* */ +/* Color palette conversions for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef PALCONV_H +#define PALCONV_H + + + +#include <stdio.h> + +/* common */ +#include "coll.h" +#include "strbuf.h" + +/* sp65 */ +#include "bitmap.h" + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + +StrBuf* PaletteTo (const Bitmap* B, const Collection* A); +/* Convert the palette of bitmap B into some sort of other binary format. +** The output is stored in a string buffer (which is actually a dynamic char +** array) and returned. The actual output format is taken from the "target" +** attribute in the attribute collection A. +*/ + +void ListPaletteTargets (FILE* F); +/* Output a list of palette targets */ + +/* End of palette.h */ + +#endif + + + From f85951ab2497dec2bac535ae569382b20e89d9fe Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 15:06:22 +0200 Subject: [PATCH 056/520] Add to build --- src/sp65.vcxproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sp65.vcxproj b/src/sp65.vcxproj index 1b7a18427..a9f0919a3 100644 --- a/src/sp65.vcxproj +++ b/src/sp65.vcxproj @@ -62,9 +62,11 @@ <ClCompile Include="sp65\geosicon.c" /> <ClCompile Include="sp65\input.c" /> <ClCompile Include="sp65\koala.c" /> + <ClCompile Include="sp65\lynxpalette.c" /> <ClCompile Include="sp65\lynxsprite.c" /> <ClCompile Include="sp65\main.c" /> <ClCompile Include="sp65\output.c" /> + <ClCompile Include="sp65\palconv.c" /> <ClCompile Include="sp65\palette.c" /> <ClCompile Include="sp65\pcx.c" /> <ClCompile Include="sp65\raw.c" /> @@ -84,8 +86,10 @@ <ClInclude Include="sp65\geosicon.h" /> <ClInclude Include="sp65\input.h" /> <ClInclude Include="sp65\koala.h" /> + <ClInclude Include="sp65\lynxpalette.h" /> <ClInclude Include="sp65\lynxsprite.h" /> <ClInclude Include="sp65\output.h" /> + <ClInclude Include="sp65\palconv.h" /> <ClInclude Include="sp65\palette.h" /> <ClInclude Include="sp65\pcx.h" /> <ClInclude Include="sp65\pixel.h" /> @@ -95,4 +99,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> From b6d06288e0e918dbad2b724caca5a2b0a4ba1e83 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 15:20:54 +0200 Subject: [PATCH 057/520] Fix warnings --- src/sp65/lynxpalette.c | 7 +++++-- src/sp65/lynxpalette.h | 9 +++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sp65/lynxpalette.c b/src/sp65/lynxpalette.c index e822fa5ee..b084af26e 100644 --- a/src/sp65/lynxpalette.c +++ b/src/sp65/lynxpalette.c @@ -62,10 +62,13 @@ StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A) */ { StrBuf* D; - Palette* P; + const Palette* P = GetBitmapPalette (B); + const char* Format = GetAttrVal(A, "format"); unsigned I; - P = GetBitmapPalette (B); + if (Format == 0) { + /* No format specified */ + } D = NewStrBuf (); for (I = 0; I < 16; ++I) { diff --git a/src/sp65/lynxpalette.h b/src/sp65/lynxpalette.h index 3ba4526fb..805db0d74 100644 --- a/src/sp65/lynxpalette.h +++ b/src/sp65/lynxpalette.h @@ -52,15 +52,12 @@ StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A); /* Generate binary output in Lynx palette format for the bitmap B. The output - * is stored in a string buffer (which is actually a dynamic char array) and - * returned. - */ +** is stored in a string buffer (which is actually a dynamic char array) and +** returned. +*/ /* End of lynxpalette.h */ #endif - - - From 878a20ab59b8ba5104220dc4bcc08927816ae6d4 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Fri, 30 Dec 2022 15:27:17 +0200 Subject: [PATCH 058/520] Fix dangling space --- src/sp65/lynxpalette.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp65/lynxpalette.h b/src/sp65/lynxpalette.h index 805db0d74..b4e3defe3 100644 --- a/src/sp65/lynxpalette.h +++ b/src/sp65/lynxpalette.h @@ -51,7 +51,7 @@ StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A); -/* Generate binary output in Lynx palette format for the bitmap B. The output +/* Generate binary output in Lynx palette format for the bitmap B. The output ** is stored in a string buffer (which is actually a dynamic char array) and ** returned. */ From 56a414593aad29ddc2f2cff1ffc9205d297db79c Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <cand@gmx.com> Date: Tue, 8 Nov 2022 19:04:46 +0200 Subject: [PATCH 059/520] Teach da65 about table units --- doc/da65.sgml | 5 +++++ src/da65/attrtab.h | 5 ++++- src/da65/data.c | 2 +- src/da65/infofile.c | 32 ++++++++++++++++++++++++++++++++ src/da65/scanner.h | 1 + 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/doc/da65.sgml b/doc/da65.sgml index aa6ad40bf..3a01ce4f5 100644 --- a/doc/da65.sgml +++ b/doc/da65.sgml @@ -522,6 +522,11 @@ following attributes are recognized: </descrip> + <tag><tt>UNIT</tt></tag> + Split the table into sections of this size. For example, if you have a + ByteTable of size 48, but it has logical groups of size 16, specifying + 16 for UNIT adds newlines after every 16 bytes. UNIT is always in bytes. + <tag><tt>ADDRMODE</tt></tag> When disassembling 65816 code, this specifies the M and X flag states for this range. It's a string argument of the form "mx", capital letters diff --git a/src/da65/attrtab.h b/src/da65/attrtab.h index 37143c0d1..4a0ea8225 100644 --- a/src/da65/attrtab.h +++ b/src/da65/attrtab.h @@ -71,6 +71,9 @@ typedef enum attr_t { atSegmentEnd = 0x0200, /* Segment end */ atSegmentStart = 0x0400, /* Segment start */ + /* Table unit separator */ + atTableUnit = 0x0800, + /* 65816 addressing mode */ atMem8 = 0x1000, /* M flag enabled, 8-bit */ atMem16 = 0x2000, /* M flag disabled, 16-bit */ @@ -79,7 +82,7 @@ typedef enum attr_t { atStyleMask = 0x000F, /* Output style */ atLabelMask = 0x00F0, /* Label information */ - atSegmentMask = 0x0F00, /* Segment information */ + atSegmentMask = 0x0700, /* Segment information */ at65816Mask = 0xF000, /* 65816 information */ } attr_t; diff --git a/src/da65/data.c b/src/da65/data.c index 7355e60d1..f85cd327d 100644 --- a/src/da65/data.c +++ b/src/da65/data.c @@ -70,7 +70,7 @@ static unsigned GetSpan (attr_t Style) if ((Attr & atStyleMask) != Style) { break; } - if ((Attr & (atSegmentStart | atSegmentEnd))) { + if ((Attr & (atSegmentStart | atSegmentEnd | atTableUnit))) { break; } ++Count; diff --git a/src/da65/infofile.c b/src/da65/infofile.c index 923cc53c9..fbf367cc9 100644 --- a/src/da65/infofile.c +++ b/src/da65/infofile.c @@ -528,6 +528,7 @@ static void RangeSection (void) { "START", INFOTOK_START }, { "TYPE", INFOTOK_TYPE }, { "ADDRMODE", INFOTOK_ADDRMODE }, + { "UNIT", INFOTOK_UNIT }, }; static const IdentTok TypeDefs[] = { @@ -552,6 +553,7 @@ static void RangeSection (void) tName = 0x08, tComment = 0x10, tAddrMode = 0x20, + tUnit = 0x40, tNeeded = (tStart | tEnd | tType) }; unsigned Attributes = tNone; @@ -564,6 +566,7 @@ static void RangeSection (void) char* Name = 0; char* Comment = 0; unsigned MemberSize = 0; + unsigned Unit = 0; /* Skip the token */ @@ -682,6 +685,15 @@ static void RangeSection (void) InfoNextTok (); break; + case INFOTOK_UNIT: + AddAttr ("UNIT", &Attributes, tUnit); + InfoNextTok (); + InfoAssureInt (); + InfoRangeCheck (0x0002, 0xFFFF); + Unit = InfoIVal; + InfoNextTok (); + break; + default: Internal ("Unexpected token: %u", InfoTok); } @@ -705,6 +717,26 @@ static void RangeSection (void) } } + /* Only tables support unit sizes */ + if ((Attributes & tUnit) && + Type != atAddrTab && + Type != atByteTab && + Type != atDByteTab && + Type != atDWordTab && + Type != atRtsTab && + Type != atTextTab && + Type != atWordTab) { + InfoError ("Only table types support unit size"); + } + + /* Mark each unit separator */ + if (Attributes & tUnit) { + unsigned i; + for (i = Start; i < End; i += Unit) { + MarkAddr(i, atTableUnit); + } + } + /* Start must be less than end */ if (Start > End) { InfoError ("Start value must not be greater than end value"); diff --git a/src/da65/scanner.h b/src/da65/scanner.h index 60648a40c..ce76d4a98 100644 --- a/src/da65/scanner.h +++ b/src/da65/scanner.h @@ -91,6 +91,7 @@ typedef enum token_t { INFOTOK_END, INFOTOK_TYPE, INFOTOK_ADDRMODE, + INFOTOK_UNIT, INFOTOK_CODE, INFOTOK_BYTETAB, From e2c6d06b0d5be4f77deb3994c601f362ae1326f1 Mon Sep 17 00:00:00 2001 From: karri <karri@sipo.fi> Date: Wed, 4 Jan 2023 12:14:37 +0200 Subject: [PATCH 060/520] Update dosc --- doc/sp65.sgml | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/doc/sp65.sgml b/doc/sp65.sgml index 255d7a552..e1bcdd558 100644 --- a/doc/sp65.sgml +++ b/doc/sp65.sgml @@ -44,6 +44,7 @@ Short options: -lc List all possible conversions -r file[,attrlist] Read an input file -v Increase verbosity + -p tgt,file[,attrlist] Write the palette to a file -w file[,attrlist] Write the output to a file Long options: @@ -56,6 +57,7 @@ Long options: --slice x,y,w,h Generate a slice from the loaded bitmap --verbose Increase verbosity --version Print the version number and exit + --palette tgt,file{,attrlist] Write the palette to a file --write file[,attrlist] Write the output to a file --------------------------------------------------------------------------- </verb></tscreen> @@ -124,6 +126,13 @@ attribute lists see <ref id="attr-lists" name="below">. bugfixes, please include the version number. + <label id="option--palette"> + <tag><tt>-p, --palette target,filename[,attrlist]</tt></tag> + + Write the palette of the input bitmap to a file in a format suitable of + the target. + + <label id="option--write"> <tag><tt>-w, --write filename[,attrlist]</tt></tag> @@ -265,6 +274,7 @@ of a sprite is roughly 508 pixels but in reality the Lynx screen is only 160 by 102 pixels which makes very large sprites useless. The number per pixels is taken from the number of colors of the input bitmap. +You can also force the number of pens used in the conversion. There are a few attributes that you can give to the conversion software. @@ -273,7 +283,7 @@ There are a few attributes that you can give to the conversion software. <tag/mode/ The first is what kind of encoding to use for the sprite. The attribute for this is called "mode" and the possible values are "literal", "packed" or - "transparent". The default is "packed" if no mode is specified. + "shaped". The default is "packed" if no mode is specified. The "literal" is a totally literal mode with no packing. In this mode the number of pixels per scanline will be a multiple of 8 both right and left from @@ -290,10 +300,26 @@ There are a few attributes that you can give to the conversion software. using run-length encoding and literal coding mixed for optimisation to produce a small sprite. - The last encoding mode "transparent" is like packed. But here we know that - the index 0 will be transparent so we can clip off all 0 pixels from the left - and right edge of the sprite. This will produce the smallest sprite possible - on the Lynx. The sprite is not rectangular anymore. + The last encoding mode "shaped" is like packed. But we can stop the conversion + to the right abd left edge when we get the first "edge" colour. If no edge + colour is specified we stop at the first index 0 colour. + If your edge index is outside the range 0..15 then your sprite can use all + the colours in the defined palette. + This will also produce the smallest sprite possible on the Lynx. The sprite + is not rectangular anymore. + + <tag/edge/ + This keyword is only meaningful for shaped sprites. By default it is 0. + The shaped sprite outer edge is defined by the colour index "edge". + + <tag/pen/ + This keyword defines the order the colours in the original bitmap is + mapped to the Lynx sprite. The length of the pen also defines the depth + of the generated sprite. + If you want to create a 1 BPP sprite you can define the two indices used + in the sprite like pen=34. Now areas in colour index 3 will be mapped as 0. + Areas in colour index 4 will be mapped as 1. + The default pen=0123456789abcdef. <tag/ax/ The sprite is painted around the Anchor point. The anchor point x can be @@ -301,7 +327,9 @@ There are a few attributes that you can give to the conversion software. painting the sprite in location 10,20 will set the left edge of the sprite 10 pixels from the left of the Lynx screen. When the sprite is scaled by hardware the anchor point stays in place and the sprite grows or shrinks - around the anchor point. The default value is 0 (left). + around the anchor point. You can also define the location using the words + "mid" for the center or "max" for the right edge. + The default value is 0 (left). <tag/ay/ The sprite is painted around the Anchor point. The anchor point y can be @@ -309,7 +337,8 @@ There are a few attributes that you can give to the conversion software. painting the sprite in location 10,20 will set the top of the sprite 20 pixels from the top of the Lynx screen. When the sprite is scaled by hardware the anchor point stays in place and the sprite grows or shrinks - around the anchor point. The default value is 0 (top). + around the anchor point. You can also define the location using the words + "mid" for the center or "max" for the bottom. The default value is 0 (top). </descrip> From 7ea2d8ab180ba38ba05952862034acc5aaccf109 Mon Sep 17 00:00:00 2001 From: Rutger van Bergen <rbergen@xs4all.nl> Date: Wed, 4 Jan 2023 19:05:08 +0100 Subject: [PATCH 061/520] Address review comment --- include/kim1.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/kim1.h b/include/kim1.h index bb616f7a0..dae246944 100644 --- a/include/kim1.h +++ b/include/kim1.h @@ -46,13 +46,6 @@ /* Hardware */ /*****************************************************************************/ - -// Todo (davepl) -// -// #include <_6530.h> -// #define RIOT3 (*(struct __6530*)0x1700) // U25 -// #define RIOT2 (*(struct __6530*)0x1740) // U28 - /*****************************************************************************/ /* Code */ /*****************************************************************************/ From f9024d132a33ffb62257fbb2bb84b63436eb8996 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:12:58 -0800 Subject: [PATCH 062/520] add sound command this behives the same as BASIC sound(voice, pitch, distortion, volume) --- include/atari.h | 6 ++++++ libsrc/atari/sound.s | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 libsrc/atari/sound.s diff --git a/include/atari.h b/include/atari.h index deae8fdf5..e31463991 100644 --- a/include/atari.h +++ b/include/atari.h @@ -235,6 +235,12 @@ extern void __fastcall__ _scroll (signed char numlines); /* numlines < 0 scrolls down */ +/*****************************************************************************/ +/* Sound function */ +/*****************************************************************************/ + +extern void __fastcall__ sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); /* plays a sound in the specidied voice, to silence call with in other params*/ + /*****************************************************************************/ /* Misc. functions */ /*****************************************************************************/ diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s new file mode 100644 index 000000000..53cb717ff --- /dev/null +++ b/libsrc/atari/sound.s @@ -0,0 +1,43 @@ +; +; Mariano Domínguez +; 2022-12-4 +; +; atari lib +; + .include "atari.inc" + + .export _sound + .import popa +; play sound, arguments: voice, pitch, distortion, volume. same as BASIC +.proc _sound + sta STORE2 ;save volume + jsr popa ;get distortion + sta STORE1 ;save distortion + jsr popa ;get pitch + pha ;save in stack + jsr popa ;get voice + + asl a ;adjust voice *2 for offset in x + tax + pla ;get pitch from stack + sta AUDF1,x ; store pitch + + lda #0 + sta AUDCTL + lda #3 + stx SKCTL ; init sound + + lda STORE1 ;get distortion + asl a ;ignore the high nibble + asl a + asl a + asl a + clc ; setup for adding volume + adc STORE2 ; add volume + sta AUDC1,x ; volume + distortion in control channel + rts +.endproc +; reserve 2 bytes for temp storage + .bss +STORE1: .res 1 +STORE2: .res 1 \ No newline at end of file From fa05294054a441be49d247d1a72a9cf53d2de9d6 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:16:58 -0800 Subject: [PATCH 063/520] add newline for style --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 53cb717ff..94b7a2d8a 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -40,4 +40,4 @@ ; reserve 2 bytes for temp storage .bss STORE1: .res 1 -STORE2: .res 1 \ No newline at end of file +STORE2: .res 1 From 30df733c3156893e8f3b113718ed393750cb8daa Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:20:16 -0800 Subject: [PATCH 064/520] remove dangling spaces --- libsrc/atari/sound.s | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 94b7a2d8a..eb90662ad 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -5,7 +5,6 @@ ; atari lib ; .include "atari.inc" - .export _sound .import popa ; play sound, arguments: voice, pitch, distortion, volume. same as BASIC @@ -15,18 +14,15 @@ sta STORE1 ;save distortion jsr popa ;get pitch pha ;save in stack - jsr popa ;get voice - - asl a ;adjust voice *2 for offset in x + jsr popa ;get voice + asl a ;adjust voice *2 for offset in x tax - pla ;get pitch from stack - sta AUDF1,x ; store pitch - + pla ;get pitch from stack + sta AUDF1,x ; store pitch lda #0 sta AUDCTL lda #3 - stx SKCTL ; init sound - + stx SKCTL ;init sound lda STORE1 ;get distortion asl a ;ignore the high nibble asl a From 29a80065cca2e36639d773f773ddf799f5808969 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:27:54 -0800 Subject: [PATCH 065/520] fix tab indentation --- libsrc/atari/sound.s | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index eb90662ad..756d0e02d 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -9,31 +9,31 @@ .import popa ; play sound, arguments: voice, pitch, distortion, volume. same as BASIC .proc _sound - sta STORE2 ;save volume - jsr popa ;get distortion - sta STORE1 ;save distortion - jsr popa ;get pitch - pha ;save in stack - jsr popa ;get voice - asl a ;adjust voice *2 for offset in x - tax - pla ;get pitch from stack - sta AUDF1,x ; store pitch - lda #0 - sta AUDCTL - lda #3 - stx SKCTL ;init sound - lda STORE1 ;get distortion - asl a ;ignore the high nibble - asl a - asl a - asl a - clc ; setup for adding volume - adc STORE2 ; add volume - sta AUDC1,x ; volume + distortion in control channel - rts + sta STORE2 ;save volume + jsr popa ;get distortion + sta STORE1 ;save distortion + jsr popa ;get pitch + pha ;save in stack + jsr popa ;get voice + asl a ;adjust voice *2 for offset in x + tax + pla ;get pitch from stack + sta AUDF1,x ;store pitch + lda #0 + sta AUDCTL + lda #3 + stx SKCTL ;init sound + lda STORE1 ;get distortion + asl a ;ignore the high nibble + asl a + asl a + asl a + clc ;setup for adding volume + adc STORE2 ;add volume + sta AUDC1,x ;volume + distortion in control channel + rts .endproc -; reserve 2 bytes for temp storage + ;reserve 2 bytes for temp storage .bss -STORE1: .res 1 -STORE2: .res 1 +STORE1: .res 1 +STORE2: .res 1 From 4e8b18c035775344519fde47b2571027abe2944c Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:29:33 -0800 Subject: [PATCH 066/520] missing tab --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 756d0e02d..880af3ee7 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -34,6 +34,6 @@ rts .endproc ;reserve 2 bytes for temp storage - .bss + .bss STORE1: .res 1 STORE2: .res 1 From 85f657b35e2c141ebccc1d99445c4f2ca4a50e56 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:30:31 -0800 Subject: [PATCH 067/520] missing space --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 880af3ee7..0fc1dad07 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -16,7 +16,7 @@ pha ;save in stack jsr popa ;get voice asl a ;adjust voice *2 for offset in x - tax + tax pla ;get pitch from stack sta AUDF1,x ;store pitch lda #0 From 658c1ad71195e7cfd5544dabfa2dc7408454f30b Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:33:40 -0800 Subject: [PATCH 068/520] only missing space --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 0fc1dad07..90fed6f70 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -8,7 +8,7 @@ .export _sound .import popa ; play sound, arguments: voice, pitch, distortion, volume. same as BASIC -.proc _sound +.proc _sound sta STORE2 ;save volume jsr popa ;get distortion sta STORE1 ;save distortion From 8c97b54f5d5391322692436bb854913bbf02c957 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:34:43 -0800 Subject: [PATCH 069/520] more trailing spaces :( --- libsrc/atari/sound.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 90fed6f70..80f89a0c4 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -25,8 +25,8 @@ stx SKCTL ;init sound lda STORE1 ;get distortion asl a ;ignore the high nibble - asl a - asl a + asl a + asl a asl a clc ;setup for adding volume adc STORE2 ;add volume From c3e1ae3478bc848a9ab16219aee4e8fd1c9d3424 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Thu, 5 Jan 2023 21:36:44 -0800 Subject: [PATCH 070/520] last trailing space --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 80f89a0c4..9b0984ad7 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -28,7 +28,7 @@ asl a asl a asl a - clc ;setup for adding volume + clc ;setup for adding volume adc STORE2 ;add volume sta AUDC1,x ;volume + distortion in control channel rts From 2a961a870b2486bf1a4edd2eb4e00faf57fe1064 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Fri, 6 Jan 2023 09:56:15 -0800 Subject: [PATCH 071/520] add target test --- targettest/atari/Makefile | 5 ++++- targettest/atari/sound.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 targettest/atari/sound.c diff --git a/targettest/atari/Makefile b/targettest/atari/Makefile index dd4f6078f..d5b4d9593 100644 --- a/targettest/atari/Makefile +++ b/targettest/atari/Makefile @@ -39,6 +39,7 @@ EXELIST_atari = \ multi.xex \ ostype.xex \ scrcode.com \ + sound.xex \ sys.xex ifneq ($(EXELIST_$(SYS)),) @@ -74,7 +75,8 @@ scrcode.com: scrcode.s $(CL) -t atari -C atari-asm.cfg -o scrcode.com scrcode.s sys.xex: sys.c $(CL) -t atari -o sys.xex sys.c - +sound.xex: sound.c + $(CL) -t atari -o sound.xex sound.c clean: @$(DEL) charmapping.xex 2>$(NULLDEV) @$(DEL) defdev.xex 2>$(NULLDEV) @@ -85,3 +87,4 @@ clean: @$(DEL) scrcode.o 2>$(NULLDEV) @$(DEL) scrcode.com 2>$(NULLDEV) @$(DEL) sys.xex 2>$(NULLDEV) + @$(DEL) sound.xex 2>$(NULLDEV) diff --git a/targettest/atari/sound.c b/targettest/atari/sound.c new file mode 100644 index 000000000..e0cec7f3b --- /dev/null +++ b/targettest/atari/sound.c @@ -0,0 +1,20 @@ +/* +** testprogram printing the default device +** +** January 6 2023 Mariano Domínguez +*/ + +#include <stdio.h> +#include <conio.h> +#include <atari.h> +#include <cc65.h> + +int main(void) +{ + int i=0; + printf("playing sound \n"); + sound(1,121,10,15); + for(i=0;i<90;i++); + sound(1,0,0,0); + return 0; +} From 196962adac43b17a4df99da6f27bc801ae0e2fa7 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Fri, 6 Jan 2023 10:54:16 -0800 Subject: [PATCH 072/520] remove lasy pesky space --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 9b0984ad7..95431bbb4 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -11,7 +11,7 @@ .proc _sound sta STORE2 ;save volume jsr popa ;get distortion - sta STORE1 ;save distortion + sta STORE1 ;save distortion jsr popa ;get pitch pha ;save in stack jsr popa ;get voice From bf9fb6dcdb3717abb25dbd0dad6935f5a6d0e30d Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Fri, 6 Jan 2023 11:23:33 -0800 Subject: [PATCH 073/520] increase delay in sound test --- targettest/atari/sound.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targettest/atari/sound.c b/targettest/atari/sound.c index e0cec7f3b..66d0ee3e3 100644 --- a/targettest/atari/sound.c +++ b/targettest/atari/sound.c @@ -14,7 +14,7 @@ int main(void) int i=0; printf("playing sound \n"); sound(1,121,10,15); - for(i=0;i<90;i++); + for(i=0;i<9000;i++); sound(1,0,0,0); return 0; } From e4d9dbf0c005951d39e8013659772aa8ba3b69be Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 8 Jan 2023 17:47:04 +0100 Subject: [PATCH 074/520] Wrong file "geover.cvt" See line 106 --- samples/geos/Makefile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 1fc49a873..7b78b1064 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -94,37 +94,37 @@ bitmap.c: logo.pcx $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap bitmap-demo.cvt: bitmap.c bitmap-demores.grc bitmap-demo.c - $(CL) -t $(SYS) -O -o bitmap-demo.cvt -m bitmap-demo.map bitmap-demores.grc bitmap-demo.c + $(CL) -t $(SYS) -O -o $@ -m bitmap-demo.map bitmap-demores.grc bitmap-demo.c filesel.cvt: fileselres.grc filesel.c - $(CL) -t $(SYS) -O -o filesel.cvt -m filesel.map fileselres.grc filesel.c + $(CL) -t $(SYS) -O -o $@ -m filesel.map fileselres.grc filesel.c geosconio.cvt: geosconiores.grc geosconio.c - $(CL) -t $(SYS) -O -o geosconio.cvt -m geosconio.map geosconiores.grc geosconio.c + $(CL) -t $(SYS) -O -o $@ -m geosconio.map geosconiores.grc geosconio.c geosver.cvt: geosverres.grc geosver.c - $(CL) -t $(SYS) -O -o geover.cvt -m geosver.map geosverres.grc geosver.c + $(CL) -t $(SYS) -O -o $@ -m geosver.map geosverres.grc geosver.c getid.cvt: getidres.grc getid.c - $(CL) -t $(SYS) -O -o getid.cvt -m getid.map getidres.grc getid.c + $(CL) -t $(SYS) -O -o $@ -m getid.map getidres.grc getid.c hello1.cvt: hello1res.grc hello1.c - $(CL) -t $(SYS) -O -o hello1.cvt -m hello1.map hello1res.grc hello1.c + $(CL) -t $(SYS) -O -o $@ -m hello1.map hello1res.grc hello1.c hello2.cvt: hello2res.grc hello2.c - $(CL) -t $(SYS) -O -o hello2.cvt -m hello2.map hello2res.grc hello2.c + $(CL) -t $(SYS) -O -o $@ -m hello2.map hello2res.grc hello2.c overlay-demo.cvt: overlay-demores.grc overlay-demo.c - $(CL) -t $(SYS) -O -o overlay-demo.cvt -m overlay-demo.map overlay-demores.grc overlay-demo.c + $(CL) -t $(SYS) -O -o $@ -m overlay-demo.map overlay-demores.grc overlay-demo.c rmvprot.cvt: rmvprotres.grc rmvprot.c - $(CL) -t $(SYS) -O -o rmvprot.cvt -m rmvprot.map rmvprotres.grc rmvprot.c + $(CL) -t $(SYS) -O -o $@ -m rmvprot.map rmvprotres.grc rmvprot.c vector-demo.cvt: vector-demores.grc vector-demo.c - $(CL) -t $(SYS) -O -o vector-demo.cvt -m vector-demo.map vector-demores.grc vector-demo.c + $(CL) -t $(SYS) -O -o $@ -m vector-demo.map vector-demores.grc vector-demo.c yesno.cvt: yesnores.grc yesno.c - $(CL) -t $(SYS) -O -o yesno.cvt -m yesno.map yesnores.grc yesno.c + $(CL) -t $(SYS) -O -o $@ -m yesno.map yesnores.grc yesno.c clean: From 27ecb555eab378f9c7aacabd5d1a302a042becca Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <marianod@wepay.com> Date: Tue, 10 Jan 2023 11:16:36 -0800 Subject: [PATCH 075/520] Rename sound to _sound. remove extra comments. --- include/atari.h | 2 +- libsrc/atari/sound.s | 4 ++-- targettest/atari/sound.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/atari.h b/include/atari.h index e31463991..08b671ff4 100644 --- a/include/atari.h +++ b/include/atari.h @@ -239,7 +239,7 @@ extern void __fastcall__ _scroll (signed char numlines); /* Sound function */ /*****************************************************************************/ -extern void __fastcall__ sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); /* plays a sound in the specidied voice, to silence call with in other params*/ +extern void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); /*****************************************************************************/ /* Misc. functions */ diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 95431bbb4..a8b712770 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -5,10 +5,10 @@ ; atari lib ; .include "atari.inc" - .export _sound + .export __sound .import popa ; play sound, arguments: voice, pitch, distortion, volume. same as BASIC -.proc _sound +.proc __sound sta STORE2 ;save volume jsr popa ;get distortion sta STORE1 ;save distortion diff --git a/targettest/atari/sound.c b/targettest/atari/sound.c index 66d0ee3e3..d1c50e1b4 100644 --- a/targettest/atari/sound.c +++ b/targettest/atari/sound.c @@ -1,5 +1,5 @@ /* -** testprogram printing the default device +** Test program for _sound for atari ** ** January 6 2023 Mariano Domínguez */ @@ -13,8 +13,8 @@ int main(void) { int i=0; printf("playing sound \n"); - sound(1,121,10,15); + _sound(1,121,10,15); //voice, pitch, distortion, volume for(i=0;i<9000;i++); - sound(1,0,0,0); + _sound(1,0,0,0); //silencing, same as Atari Basic return 0; } From 42c39c6bfcdc111b927c0f82895f11071e0ed6d0 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Tue, 10 Jan 2023 11:26:12 -0800 Subject: [PATCH 076/520] remove trailing spacew, use correct user --- include/atari.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/atari.h b/include/atari.h index 08b671ff4..04cacab33 100644 --- a/include/atari.h +++ b/include/atari.h @@ -239,7 +239,7 @@ extern void __fastcall__ _scroll (signed char numlines); /* Sound function */ /*****************************************************************************/ -extern void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); +extern void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); /*****************************************************************************/ /* Misc. functions */ From 8ea0dfe45395f9357eb7e523044344d09db490e5 Mon Sep 17 00:00:00 2001 From: Matt Seabold <matt.seabold@gmail.com> Date: Wed, 11 Jan 2023 20:18:51 -0500 Subject: [PATCH 077/520] Allow line_bynumber to return more than one result --- src/dbginfo/dbginfo.c | 54 +++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c index fdebe6910..c8db5bea6 100644 --- a/src/dbginfo/dbginfo.c +++ b/src/dbginfo/dbginfo.c @@ -4736,14 +4736,18 @@ static SpanInfoListEntry* FindSpanInfoByAddr (const SpanInfoList* L, cc65_addr A -static LineInfo* FindLineInfoByLine (const Collection* LineInfos, cc65_line Line) -/* Find the LineInfo for a given line number. The function returns the line -** info or NULL if none was found. +static int FindLineInfoByLine (const Collection* LineInfos, cc65_line Line, + unsigned *Index) +/* Find the LineInfo for a given line number. The function returns true if the +** name was found. In this case, Index contains the index of the first item +** that matches. If the item wasn't found, the function returns false and +** Index contains the insert position for Name. */ { /* Do a binary search */ int Lo = 0; int Hi = (int) CollCount (LineInfos) - 1; + int Found = 0; while (Lo <= Hi) { /* Mid of range */ @@ -4755,16 +4759,20 @@ static LineInfo* FindLineInfoByLine (const Collection* LineInfos, cc65_line Line /* Found? */ if (Line > CurItem->Line) { Lo = Cur + 1; - } else if (Line < CurItem->Line) { - Hi = Cur - 1; } else { - /* Found */ - return CurItem; + Hi = Cur - 1; + /* Since we may have duplicates, repeat the search until we've + ** the first item that has a match. + */ + if(Line == CurItem->Line) { + Found = 1; + } } } - /* Not found */ - return 0; + /* Pass back the index. This is also the insert position */ + *Index = Lo; + return Found; } @@ -6134,6 +6142,9 @@ const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo Handle, unsigned FileId, const FileInfo* F; cc65_lineinfo* D; LineInfo* L = 0; + unsigned I; + unsigned Index; + unsigned Count; /* Check the parameter */ assert (Handle != 0); @@ -6150,18 +6161,31 @@ const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo Handle, unsigned FileId, F = CollAt (&Info->FileInfoById, FileId); /* Search in the file for the given line */ - L = FindLineInfoByLine (&F->LineInfoByLine, Line); - - /* Bail out if we didn't find the line */ - if (L == 0) { + if(!FindLineInfoByLine (&F->LineInfoByLine, Line, &Index)) { + /* Not found */ return 0; } + /* Index contains the first position. Count how many lines with this number + ** we have. Skip the first one, since we have at least one. + */ + Count = 1; + + while ((unsigned) Index + Count < CollCount( &F->LineInfoByLine)) { + L = CollAt (&F->LineInfoByLine, (unsigned) Index + Count); + if (L->Line != Line) { + break; + } + ++Count; + } + /* Prepare the struct we will return to the caller */ - D = new_cc65_lineinfo (1); + D = new_cc65_lineinfo (Count); /* Copy the data */ - CopyLineInfo (D->data, L); + for (I = 0; I < Count; ++I) { + CopyLineInfo (D->data + I, CollAt (&F->LineInfoByLine, Index++)); + } /* Return the allocated struct */ return D; From a5a8225a58addbd8e1532d378163786cb2ebafa7 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sat, 14 Jan 2023 13:49:16 +0100 Subject: [PATCH 078/520] Create disk of GEOS samples You don't need to convert anymore. --- samples/geos/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 7b78b1064..03f6b8cdc 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -80,6 +80,14 @@ EXELIST_geos-apple = \ ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) + +define samples-geos +c1541 -attach $(0).d64 -geoswrite $(1); +endef + +samples-geos: $(EXELIST_$(SYS)) + c1541 -format "$@,01" d64 $@.d64 + $(foreach tool,$(EXELIST_$(SYS)),$(call samples-geos,$(tool))) else samples: ifeq ($(MAKELEVEL),0) From 6236e828504bde60b0206fe6e710dfa34bcd7feb Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Sun, 15 Jan 2023 17:00:05 -0800 Subject: [PATCH 079/520] change storage to pager zero fix bug that kills keyboard. --- libsrc/atari/sound.s | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index a8b712770..746ebc2bb 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -7,11 +7,13 @@ .include "atari.inc" .export __sound .import popa + .importzp tmp1,tmp2 + ; play sound, arguments: voice, pitch, distortion, volume. same as BASIC .proc __sound - sta STORE2 ;save volume + sta tmp2 ;save volume jsr popa ;get distortion - sta STORE1 ;save distortion + sta tmp1 ;save distortion jsr popa ;get pitch pha ;save in stack jsr popa ;get voice @@ -22,18 +24,14 @@ lda #0 sta AUDCTL lda #3 - stx SKCTL ;init sound - lda STORE1 ;get distortion + sta SKCTL ;init sound + lda tmp1 ;get distortion asl a ;ignore the high nibble asl a asl a asl a clc ;setup for adding volume - adc STORE2 ;add volume + adc tmp2 ;add volume sta AUDC1,x ;volume + distortion in control channel rts .endproc - ;reserve 2 bytes for temp storage - .bss -STORE1: .res 1 -STORE2: .res 1 From 40656ffb6e829da3abace9503020de5e8754a6d3 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Sun, 15 Jan 2023 17:05:24 -0800 Subject: [PATCH 080/520] more style --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 746ebc2bb..0c1e80db5 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -7,7 +7,7 @@ .include "atari.inc" .export __sound .import popa - .importzp tmp1,tmp2 + .importzp tmp1,tmp2 ; play sound, arguments: voice, pitch, distortion, volume. same as BASIC .proc __sound From 67564d83c8a549be4ad963037dd2e1038c023ce6 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Mon, 16 Jan 2023 18:01:11 +0100 Subject: [PATCH 081/520] libsrc/atari/sound.s: fix indentation --- libsrc/atari/sound.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 0c1e80db5..0d2362318 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -31,7 +31,7 @@ asl a asl a clc ;setup for adding volume - adc tmp2 ;add volume + adc tmp2 ;add volume sta AUDC1,x ;volume + distortion in control channel rts .endproc From c6d183c31b72dd710adaf35d4d6773165be077f1 Mon Sep 17 00:00:00 2001 From: Mariano Dominguez <mariano.dm@gmail.com> Date: Mon, 16 Jan 2023 19:09:01 -0800 Subject: [PATCH 082/520] add documentation for _sound. --- doc/atari.sgml | 1 + doc/funcref.sgml | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/doc/atari.sgml b/doc/atari.sgml index 2e2db0402..3057cd8a6 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -332,6 +332,7 @@ See the <url url="funcref.html" name="function reference"> for declaration and u <item>_scroll <item>_setcolor <item>_setcolor_low +<item>_sound <item>waitvsync </itemize> diff --git a/doc/funcref.sgml b/doc/funcref.sgml index a8593ebb5..2df86289e 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -126,6 +126,7 @@ function. <!-- <item><ref id="_scroll" name="_scroll"> --> <!-- <item><ref id="_setcolor" name="_setcolor"> --> <!-- <item><ref id="_setcolor_low" name="_setcolor_low"> --> +<item><ref id="_sound" name="_sound"> <item><ref id="get_ostype" name="get_ostype"> <!-- <item><ref id="get_tv" name="get_tv"> --> </itemize> @@ -1108,6 +1109,43 @@ considered random to a certain degree. </descrip> </quote> +<sect1>_sound<label id="_sound"><p> +<quote> +<descrip> +<tag/Function/Use the speaker to produce sound in a specified voice. (Atari only) +<tag/Header/<tt/<ref id="atari.h" name="atari.h">/ +<tag/Declaration/<tt/void _sound (unsigned char voice, unsigned char pitch, unsigned char distortion, unsigned char volume);/ +<tag/Description/The function produces a sound with the specified parameters. using any of the 4 availble oscillators (voices) controlled by POKEY chip. Sound is non cpu-blocking and it keeps oscillating until program sends 0 in all the other parameters. + +<tag/Notes/<itemize> +<item>The function is specific to the Atari 8 bit. +<item> Voice can be any of 0-3 different sound channels. +<item> Pitch goes from 0-255 (about 125 Hz to 32 Khz). +<item> Distortion (0-14) uses poly dividers to reshape wave in order to create a noise effect. Use 10 for a "pure" square-wave sound. +<item>Volume (0-15) is the intensity for the wave. +<item>Extra bits in those parameters will be ignored. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/Example/ +<verb> + +#include <stdio.h> +int main(void) +{ + int i=0; + unsigned char j; + printf("playing sound \n"); + for (j=0; j<144; j++) { + _sound(1,144-j,10,8); //change the pitch for voice 1 + for (i=0; i<50; i++); //pause for sound duration + } + _sound(1,0,0,0); //use zero in other parameters to stop the sound + return 0; +} + +</verb> +</descrip> +</quote> <sect1>_stroserror<label id="_stroserror"><p> @@ -6908,7 +6946,6 @@ switching the CPU into single clock mode. </descrip> </quote> - <sect1>srand<label id="srand"><p> <quote> From 9ee0c835e5f7698f1376b58d38a6cfd5f1514bfc Mon Sep 17 00:00:00 2001 From: Matt Seabold <matt.seabold@gmail.com> Date: Tue, 17 Jan 2023 22:15:27 -0500 Subject: [PATCH 083/520] Update documentation for line_bynumber --- src/dbginfo/dbginfo.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dbginfo/dbginfo.h b/src/dbginfo/dbginfo.h index 38d891e7c..95aae837c 100644 --- a/src/dbginfo/dbginfo.h +++ b/src/dbginfo/dbginfo.h @@ -258,7 +258,8 @@ const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo handle, unsigned source_id, cc65_line line); /* Return line information for a source file/line number combination. The -** function returns NULL if no line information was found. +** function returns NULL if no line information was found, otherwise a list +** of line infos. */ const cc65_lineinfo* cc65_line_bysource (cc65_dbginfo Handle, unsigned source_id); From 3360734487a08e32e473476b29c5ea4c6207c752 Mon Sep 17 00:00:00 2001 From: Matt Seabold <matt.seabold@gmail.com> Date: Tue, 17 Jan 2023 22:17:23 -0500 Subject: [PATCH 084/520] Update comment to match in source file --- src/dbginfo/dbginfo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c index c8db5bea6..fee41012c 100644 --- a/src/dbginfo/dbginfo.c +++ b/src/dbginfo/dbginfo.c @@ -6135,7 +6135,8 @@ const cc65_lineinfo* cc65_line_byid (cc65_dbginfo Handle, unsigned Id) const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo Handle, unsigned FileId, cc65_line Line) /* Return line information for a source file/line number combination. The -** function returns NULL if no line information was found. +** function returns NULL if no line information was found, otherwise a list +** of line infos. */ { const DbgInfo* Info; From 60f56c43c769f39089f5005f736a06aacd393224 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Wed, 18 Jan 2023 11:55:52 +0100 Subject: [PATCH 085/520] some documentation fixes --- doc/funcref.sgml | 11 +++++++---- libsrc/atari/sound.s | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 2df86289e..6418723b5 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -1114,18 +1114,21 @@ considered random to a certain degree. <descrip> <tag/Function/Use the speaker to produce sound in a specified voice. (Atari only) <tag/Header/<tt/<ref id="atari.h" name="atari.h">/ -<tag/Declaration/<tt/void _sound (unsigned char voice, unsigned char pitch, unsigned char distortion, unsigned char volume);/ -<tag/Description/The function produces a sound with the specified parameters. using any of the 4 availble oscillators (voices) controlled by POKEY chip. Sound is non cpu-blocking and it keeps oscillating until program sends 0 in all the other parameters. +<tag/Declaration/<tt/void __fastcall__ _sound (unsigned char voice, unsigned char pitch, unsigned char distortion, unsigned char volume);/ +<tag/Description/The function produces a sound with the specified parameters using any of the 4 available oscillators (voices) controlled by the POKEY chip. Sound is non cpu-blocking and it keeps oscillating until program sends 0 in all the other parameters. <tag/Notes/<itemize> +<item>The function is available only as a fastcall function; so, it may be used +only in the presence of a prototype. <item>The function is specific to the Atari 8 bit. <item> Voice can be any of 0-3 different sound channels. <item> Pitch goes from 0-255 (about 125 Hz to 32 Khz). <item> Distortion (0-14) uses poly dividers to reshape wave in order to create a noise effect. Use 10 for a "pure" square-wave sound. <item>Volume (0-15) is the intensity for the wave. <item>Extra bits in those parameters will be ignored. +<item>Parameters are the same as for the AtariBASIC SOUND statement. </itemize> -<tag/Availability/cc65 (not all platforms) +<tag/Availability/cc65 (<tt/atari/ and <tt/atarixl/ platforms) <tag/Example/ <verb> @@ -4186,7 +4189,7 @@ be used in presence of a prototype. <tag/Example/<verb> chdir (getdevicedir (device, buf, sizeof buf)); </verb> -cf. <tt/samples/enumdevdir.c/ +cf. <tt/samples/enumdevdir.c/ </descrip> </quote> diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s index 0d2362318..a8d11fd82 100644 --- a/libsrc/atari/sound.s +++ b/libsrc/atari/sound.s @@ -2,14 +2,16 @@ ; Mariano Domínguez ; 2022-12-4 ; -; atari lib +; this file provides an equivalent to the BASIC SOUND function +; +; void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); ; .include "atari.inc" .export __sound .import popa .importzp tmp1,tmp2 -; play sound, arguments: voice, pitch, distortion, volume. same as BASIC +; play sound, arguments: voice, pitch, distortion, volume .proc __sound sta tmp2 ;save volume jsr popa ;get distortion From ed909dba1d819f324f2cf77eb9862946b64474f2 Mon Sep 17 00:00:00 2001 From: tomas <93524600+TommiRommi@users.noreply.github.com> Date: Fri, 27 Jan 2023 10:52:20 +0000 Subject: [PATCH 086/520] fixed spelling mistake there was a spelling mistake in one of pointer warnings --- src/cc65/typeconv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 18a9f4dfa..683cb2356 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -238,10 +238,10 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) ** void pointers, just with warnings. */ if (Result.C == TC_PTR_SIGN_DIFF) { - /* Specific warning for pointee signedness difference */ + /* Specific warning for pointer signess difference */ if (IS_Get (&WarnPointerSign)) { TypeCompatibilityDiagnostic (NewType, Expr->Type, - 0, "Pointer conversion to '%s' from '%s' changes pointee signedness"); + 0, "Pointer conversion to '%s' from '%s' changes pointer signess"); } } else if ((Result.C <= TC_PTR_INCOMPATIBLE || (Result.F & TCF_INCOMPATIBLE_QUAL) != 0)) { From 1d73c8d2592f0153138f3c5706d24abd293f70b5 Mon Sep 17 00:00:00 2001 From: tomas <93524600+TommiRommi@users.noreply.github.com> Date: Fri, 27 Jan 2023 16:53:36 +0000 Subject: [PATCH 087/520] Update typeconv.c --- src/cc65/typeconv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 683cb2356..f77ec3951 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -238,10 +238,10 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) ** void pointers, just with warnings. */ if (Result.C == TC_PTR_SIGN_DIFF) { - /* Specific warning for pointer signess difference */ + /* Specific warning for pointer signedness difference */ if (IS_Get (&WarnPointerSign)) { TypeCompatibilityDiagnostic (NewType, Expr->Type, - 0, "Pointer conversion to '%s' from '%s' changes pointer signess"); + 0, "Pointer conversion to '%s' from '%s' changes pointer signedness"); } } else if ((Result.C <= TC_PTR_INCOMPATIBLE || (Result.F & TCF_INCOMPATIBLE_QUAL) != 0)) { From 1764b7aca9cbcf822649e5c69694c1f4158dab64 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 28 Jan 2023 12:29:05 +0100 Subject: [PATCH 088/520] fix for #1941, taken from https://github.com/acqn/cc65/commit/22d435b68966b86929d3c393f3bbc1593b261cee --- src/cc65/shiftexpr.c | 37 +++++++++++++++++--------------- test/val/bug1941-shift-by-zero.c | 28 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 test/val/bug1941-shift-by-zero.c diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index b8fb70434..1224bfecb 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -169,27 +169,21 @@ void ShiftExpr (struct ExprDesc* Expr) } } - /* If the shift count is zero, nothing happens. If the left hand - ** side is a constant, the result is constant. - */ - if (Expr2.IVal == 0 || lconst) { - - /* Set the type */ + /* If the left hand side is a constant, the result is constant */ + if (lconst) { + /* Set the result type */ Expr->Type = ResultType; - if (lconst) { - - /* Evaluate the result */ - switch (Tok) { - case TOK_SHL: Expr->IVal <<= Expr2.IVal; break; - case TOK_SHR: Expr->IVal >>= Expr2.IVal; break; - default: /* Shutup gcc */ break; - } - - /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr, 1); + /* Evaluate the result */ + switch (Tok) { + case TOK_SHL: Expr->IVal <<= Expr2.IVal; break; + case TOK_SHR: Expr->IVal >>= Expr2.IVal; break; + default: /* Shutup gcc */ break; } + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr, 1); + /* Result is already got, remove the generated code */ RemoveCode (&Mark1); @@ -197,6 +191,15 @@ void ShiftExpr (struct ExprDesc* Expr) continue; } + /* If the shift count is zero, nothing happens */ + if (Expr2.IVal == 0) { + /* Result is already got, remove the pushing code */ + RemoveCode (&Mark2); + + /* Be sure to mark the value as in the primary */ + goto MakeRVal; + } + /* If we're shifting an integer or unsigned to the right, the lhs ** has a quasi-const address, and the shift count is larger than 8, ** we can load just the high byte as a char with the correct diff --git a/test/val/bug1941-shift-by-zero.c b/test/val/bug1941-shift-by-zero.c new file mode 100644 index 000000000..eaeba814f --- /dev/null +++ b/test/val/bug1941-shift-by-zero.c @@ -0,0 +1,28 @@ +/* Bug 1941 - Bitwise shift char types by 0 count results in out-of-range access */ + +#include <stdio.h> +#include <stdint.h> + +uint8_t foo = 42U; /* "Low byte" */ +uint8_t goo = 1U; /* "High byte" - you need it to reproduce the issue */ +int16_t bar = 256; /* ...or just do it with this */ + +_Static_assert (sizeof (foo >> 0) == sizeof (int), "Shift result should be int-promoted"); +_Static_assert (sizeof ((int8_t)bar << 0) == sizeof (int), "Shift result should be int-promoted"); + +unsigned failures; + +int main(void) +{ + if (foo >> 0 != foo) { + ++failures; + printf("foo failed\n"); + } + + if ((int8_t)bar << 0 != (int8_t)bar) { + ++failures; + printf("bar failed\n"); + } + + return failures; +} From 8ac5e2f7b6292b594cdde0bcfe66b426eb8c5741 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 28 Jan 2023 15:08:00 +0100 Subject: [PATCH 089/520] fix devicestr call, so related posix function work on cbm targets --- libsrc/cbm/initcwd.s | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libsrc/cbm/initcwd.s b/libsrc/cbm/initcwd.s index d5e5b9296..7064c16e1 100644 --- a/libsrc/cbm/initcwd.s +++ b/libsrc/cbm/initcwd.s @@ -9,8 +9,6 @@ .import pusha0, tosudiva0 .importzp sreg, ptr1, ptr2 - .macpack generic - initcwd: lda #<__cwd ldx #>__cwd @@ -27,15 +25,20 @@ devicestr: lda #10 jsr tosudiva0 ldy #0 - lda sreg - beq @L0 ; >=10 - add #'0' + tax ; result of the division (lsb) + beq @L0 ; < 10 + + clc + adc #'0' sta (ptr2),y iny -@L0: lda ptr1 ; rem - add #'0' +@L0: + lda sreg ; reminder of the division + clc + adc #'0' sta (ptr2),y iny - lda #0 + + lda #0 ; terminating 0 sta (ptr2),y rts From 727b3288d67ffc0642f2ef5b424a3e4cd4b39c00 Mon Sep 17 00:00:00 2001 From: Spiro Trikaliotis <spiro.trikaliotis@gmx.de> Date: Fri, 17 Feb 2023 17:22:55 +0100 Subject: [PATCH 090/520] Remove .exe from file output Whenever a tool like ld65 wants to output its name into a log file, it uses the name of command-line parameter 0. However, this parameter also includes the .exe suffix if it is on Windows. This patch removes the .exe suffix, so that the output is clean and consistent across platforms. This fixes #1990. --- src/common/cmdline.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/common/cmdline.c b/src/common/cmdline.c index 4de79a419..6b24a75fb 100644 --- a/src/common/cmdline.c +++ b/src/common/cmdline.c @@ -181,6 +181,17 @@ void InitCmdLine (int* aArgCount, char*** aArgVec, const char* aProgName) /* Use the default */ ProgName = aProgName; } + else { + /* remove .exe extension, if there is any + ** + ** Note: This creates a new string that is + ** never free()d. + ** As this is exactly only string, and it + ** lives for the whole lifetime of the tool, + ** this is not an issue. + */ + ProgName = MakeFilename (ProgName, ""); + } } /* Make a CmdLine struct */ From 78263cd24b301406f9567f3e0a4fa03488445656 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 17 Feb 2023 23:48:32 -0500 Subject: [PATCH 091/520] Fix OptStackOps label migration case for toscmpbool, add test case --- src/cc65/coptstop.c | 2 ++ test/val/bug1989.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 test/val/bug1989.c diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 4d0151c55..8b2821dcd 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1153,6 +1153,8 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) /* Save lhs into zeropage */ AddStoreLhsA (D); + /* AddStoreLhsA may have moved the OpIndex, recalculate insertion point to prevent label migration. */ + D->IP = D->OpIndex + 1; /* cmp */ X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); diff --git a/test/val/bug1989.c b/test/val/bug1989.c new file mode 100644 index 000000000..b3e3d4c8d --- /dev/null +++ b/test/val/bug1989.c @@ -0,0 +1,40 @@ + +/* bug #1989 - OptStackOps Opt_a_toscmpbool bypassed a comparison, discovered in 544a49c */ + +#include <stdlib.h> + +unsigned char i,r,j; + +void fail() // for the r=0 case, the == comparison was getting jumped over by OptStackOps +{ + if ((i & 0x1f) == (r ? 0 : 16)) j -=8; +} + +void pass() +{ + if ((i & 0x1f) == (unsigned char)(r ? 0 : 16)) j -= 8; +} + +void test(unsigned char ti, unsigned char tr, unsigned char tj) +{ + unsigned char rj; + i = ti; + r = tr; + j = tj; + pass(); + rj = j; + i = ti; + r = tr; + j = tj; + fail(); + if (j != rj) exit(1); +} + +int main(void) +{ + test( 1,0,33); + test( 0,0,33); + test( 1,1,33); + test(16,1,33); + return 0; +} From 4e411e8526df0c627c5ab0e6338853a82a51c0b2 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 18 Feb 2023 00:13:34 -0500 Subject: [PATCH 092/520] Opt_a_tosicmp likely has a bug which will incorrectly migrate a label on a replaced op in one case. --- src/cc65/coptstop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 8b2821dcd..ae40a55e9 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1208,6 +1208,8 @@ static unsigned Opt_a_tosicmp (StackOpData* D) /* RHS src is not directly comparable */ X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); InsertEntry (D, X, D->Rhs.A.ChgIndex + 1); + /* RHS insertion may have moved the OpIndex, recalculate insertion point to prevent label migration. */ + D->IP = D->OpIndex + 1; /* Cmp with stored RHS */ X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); From 4d30333099d84dc9266bd6cc3febbdb559e7a5e6 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 19 Feb 2023 05:31:46 -0500 Subject: [PATCH 093/520] sim65 path length safety for PVOpen --- src/sim65/paravirt.c | 13 ++++++++++--- src/sim65/paravirt.h | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index db4120326..9e5c28432 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -63,6 +63,7 @@ /* sim65 */ #include "6502.h" +#include "error.h" #include "memory.h" #include "paravirt.h" @@ -166,7 +167,7 @@ static void PVArgs (CPURegs* Regs) static void PVOpen (CPURegs* Regs) { - char Path[1024]; + char Path[PVOPEN_PATH_SIZE]; int OFlag = O_INITIAL; int OMode = 0; unsigned RetVal, I = 0; @@ -183,9 +184,15 @@ static void PVOpen (CPURegs* Regs) } do { - Path[I] = MemReadByte (Name++); + if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) { + break; + } + ++I; + if (I >= PVOPEN_PATH_SIZE) { + Error("PVOpen path too long at address $%04X",Name); + } } - while (Path[I++]); + while (1); Print (stderr, 2, "PVOpen (\"%s\", $%04X)\n", Path, Flags); diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index bfa38e047..3badb50ea 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -47,6 +47,9 @@ #define PARAVIRT_BASE 0xFFF4 /* Lowest address used by a paravirtualization hook */ +#define PVOPEN_PATH_SIZE 1024 +/* Maximum path size supported by PVOpen */ + /*****************************************************************************/ From 5c12659cf499343deff1d81c0c4ac235a74192c3 Mon Sep 17 00:00:00 2001 From: compyx <b.wassink@ziggo.nl> Date: Sun, 19 Feb 2023 14:01:14 +0100 Subject: [PATCH 094/520] make avail: check for presence of binaries before creating symbolic links To avoid creating broken symlinks, first check if the binaries exists in bin/ and exit when they don't, with a message to first run `make`. --- src/Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Makefile b/src/Makefile index 8356d0001..a37fa5811 100644 --- a/src/Makefile +++ b/src/Makefile @@ -110,11 +110,21 @@ $(RM) /usr/local/bin/$(prog) endef # UNAVAIL_recipe +define AVAIL_check_prog + +@if [ ! -e ../bin/$(prog) ]; then \ + echo "failed to locate $(prog), please run make first"; \ + false; \ +fi + +endef # AVAIL_check_prog + install: $(INSTALL) -d $(DESTDIR)$(bindir) $(INSTALL) ../bin/* $(DESTDIR)$(bindir) avail: + $(foreach prog,$(PROGS),$(AVAIL_check_prog)) $(foreach prog,$(PROGS),$(AVAIL_recipe)) unavail: From 2ac055383f4619cea09988aa79c2b4324c1bfb40 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 19 Feb 2023 08:14:04 -0500 Subject: [PATCH 095/520] ld65 fix overwrite segments adding wrong padding or causing internal errors --- src/ld65/config.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ld65/config.c b/src/ld65/config.c index c22ced1ef..c101bd1c6 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -1945,6 +1945,10 @@ unsigned CfgProcess (void) /* Remember the start address before handling this segment */ unsigned long StartAddr = Addr; + /* For computing FillLevel */ + unsigned long FillLevel; + unsigned long FillAdded = 0; + /* Take note of "overwrite" segments and make sure there are no ** other segment types following them in current memory region. */ @@ -2081,14 +2085,19 @@ unsigned CfgProcess (void) /* Increment the fill level of the memory area; and, check for an ** overflow. */ - M->FillLevel = Addr + S->Seg->Size - M->Start; - if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { + FillLevel = Addr + S->Seg->Size - M->Start; + if (FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { ++Overflows; M->Flags |= MF_OVERFLOW; CfgWarning (GetSourcePos (M->LI), "Segment '%s' overflows memory area '%s' by %lu byte%c", GetString (S->Name), GetString (M->Name), - M->FillLevel - M->Size, (M->FillLevel - M->Size == 1) ? ' ' : 's'); + FillLevel - M->Size, (FillLevel - M->Size == 1) ? ' ' : 's'); + } + if (FillLevel > M->FillLevel) { + /* Regular segments increase FillLevel. Overwrite segments may increase but not decrease FillLevel. */ + FillAdded = FillLevel - M->FillLevel; + M->FillLevel = FillLevel; } /* If requested, define symbols for the start and size of the @@ -2107,13 +2116,14 @@ unsigned CfgProcess (void) Addr += S->Seg->Size; /* If this segment will go out to the file, or its place - ** in the file will be filled, then increase the file size, - ** unless it's an OVERWRITE segment. + ** in the file will be filled, then increase the file size. + ** An OVERWRITE segment will only increase the size if it overlapped some of the fill area. */ if (S->Load == M && - ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0) && - (S->Flags & SF_OVERWRITE) == 0) { - M->F->Size += Addr - StartAddr; + ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0)) { + M->F->Size += (!(S->Flags & SF_OVERWRITE)) ? + (Addr - StartAddr) : + FillAdded; } } From 4fc19a3d4c27d193297e928171f85ab361ac2ae7 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 19 Feb 2023 08:37:07 -0500 Subject: [PATCH 096/520] ld65 overwrite segment should pad with fill value if the desired seek position is beyond the end of the file so far --- src/ld65/bin.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ld65/bin.c b/src/ld65/bin.c index bd822cc23..15b534f66 100644 --- a/src/ld65/bin.c +++ b/src/ld65/bin.c @@ -193,8 +193,16 @@ static void BinWriteMem (BinDesc* D, MemoryArea* M) NewAddr += M->Start; } if (DoWrite || (M->Flags & MF_FILL) != 0) { - /* Seek in "overwrite" segments */ if (S->Flags & SF_OVERWRITE) { + /* Seek in "overwrite" segments. Fill if the seek position has not been reached yet. */ + unsigned long FileLength; + unsigned long SeekTarget = NewAddr - M->Start + M->FileOffs; + fseek (D->F, 0, SEEK_END); + FileLength = ftell (D->F); + if (SeekTarget > FileLength) { + WriteMult (D->F, M->FillVal, SeekTarget - FileLength); + PrintNumVal ("SF_OVERWRITE", SeekTarget - FileLength); + } fseek (D->F, NewAddr - M->Start + M->FileOffs, SEEK_SET); } else { WriteMult (D->F, M->FillVal, NewAddr-Addr); From 5bc8ff98de9e7d942b7920645ce1a162d93af3ab Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 19 Feb 2023 09:01:17 -0500 Subject: [PATCH 097/520] error if computed memory size is negative --- src/ld65/config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ld65/config.c b/src/ld65/config.c index c22ced1ef..6606df7e3 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -1936,6 +1936,11 @@ unsigned CfgProcess (void) GetString (M->Name)); } M->Size = GetExprVal (M->SizeExpr); + if (M->Size >= 0x80000000) { + CfgError (GetSourcePos (M->LI), + "Size of memory area '%s' is negative: %ld", + GetString (M->Name), (long)M->Size); + } /* Walk through the segments in this memory area */ for (J = 0; J < CollCount (&M->SegList); ++J) { From 3df5b9f41faabcbff982713db485d91cc8ac9a6c Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 19 Feb 2023 09:30:07 -0500 Subject: [PATCH 098/520] relocate dasm test start-addr to avoid producing negative memory area size with none.cfg (conflicted with stack position at $8000) --- test/dasm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dasm/Makefile b/test/dasm/Makefile index d9ac7ac21..e84560ad2 100644 --- a/test/dasm/Makefile +++ b/test/dasm/Makefile @@ -30,7 +30,7 @@ ISEQUAL = ../../testwrk/isequal$(EXE) CC = gcc CFLAGS = -O2 -START = --start-addr 0x8000 +START = --start-addr 0x7000 .PHONY: all clean From 68ce335f59b9d6d69b6d348ec1e764162021bdbe Mon Sep 17 00:00:00 2001 From: compyx <b.wassink@ziggo.nl> Date: Sun, 19 Feb 2023 19:17:45 +0100 Subject: [PATCH 099/520] Replace shell code with GNU Make conditionals and functions Use `ifeq` to provide two rules for the `avail` target: one that reports an error if any of the symlink targets are missing and one that installs the symlinks for the targets if they're all present. --- src/Makefile | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index a37fa5811..034a2230f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -110,22 +110,16 @@ $(RM) /usr/local/bin/$(prog) endef # UNAVAIL_recipe -define AVAIL_check_prog - -@if [ ! -e ../bin/$(prog) ]; then \ - echo "failed to locate $(prog), please run make first"; \ - false; \ -fi - -endef # AVAIL_check_prog - install: $(INSTALL) -d $(DESTDIR)$(bindir) $(INSTALL) ../bin/* $(DESTDIR)$(bindir) avail: - $(foreach prog,$(PROGS),$(AVAIL_check_prog)) +ifneq ($(patsubst %,../bin/%,$(PROGS)),$(wildcard $(patsubst %,../bin/%,$(PROGS)))) + $(error executables are missing, please run make first) +else $(foreach prog,$(PROGS),$(AVAIL_recipe)) +endif unavail: $(foreach prog,$(PROGS),$(UNAVAIL_recipe)) From 864eecf7c3764741855801de3228f4363b4d69fa Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 20 Feb 2023 20:08:58 -0500 Subject: [PATCH 100/520] ca16 doc for A8/A18/I8/I16: use "assume" instead of "switch" to avoid implying that a hardware state is changed. See: #1759 --- doc/ca65.sgml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 19fd3aa2a..c84c57353 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2019,7 +2019,7 @@ Here's a list of all control commands and a description, what they do: <sect1><tt>.A16</tt><label id=".A16"><p> - Valid only in 65816 mode. Switch the accumulator to 16 bit. + Valid only in 65816 mode. Assume the accumulator is 16 bit. Note: This command will not emit any code, it will tell the assembler to create 16 bit operands for immediate accumulator addressing mode. @@ -2029,7 +2029,7 @@ Here's a list of all control commands and a description, what they do: <sect1><tt>.A8</tt><label id=".A8"><p> - Valid only in 65816 mode. Switch the accumulator to 8 bit. + Valid only in 65816 mode. Assume the accumulator is 8 bit. Note: This command will not emit any code, it will tell the assembler to create 8 bit operands for immediate accu addressing mode. @@ -3036,7 +3036,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH <sect1><tt>.I16</tt><label id=".I16"><p> - Valid only in 65816 mode. Switch the index registers to 16 bit. + Valid only in 65816 mode. Assume the index registers are 16 bit. Note: This command will not emit any code, it will tell the assembler to create 16 bit operands for immediate operands. @@ -3047,7 +3047,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH <sect1><tt>.I8</tt><label id=".I8"><p> - Valid only in 65816 mode. Switch the index registers to 8 bit. + Valid only in 65816 mode. Assume the index registers are 8 bit. Note: This command will not emit any code, it will tell the assembler to create 8 bit operands for immediate operands. From f1134cc5f18aef555e3c8573d0fa62b17f3908a0 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 20 Feb 2023 20:42:09 -0500 Subject: [PATCH 101/520] ca65 improve error for unclosed scopes See: #1779 --- src/ca65/symtab.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ca65/symtab.c b/src/ca65/symtab.c index afe7b3ad6..a01d14bd8 100644 --- a/src/ca65/symtab.c +++ b/src/ca65/symtab.c @@ -570,7 +570,18 @@ void SymCheck (void) /* Check for open scopes */ if (CurrentScope->Parent != 0) { - Error ("Local scope was not closed"); + if (CurrentScope->Label) { + /* proc has a label indicating the line it was opened. */ + LIError (&CurrentScope->Label->DefLines, + "Local proc '%s' was not closed", + GetString (CurrentScope->Name)); + } else { + /* scope has no label to track a line number, uses end-of-document line instead. + ** Anonymous scopes will reveal their internal automatic name. + */ + Error ("Local scope '%s' was not closed", + GetString (CurrentScope->Name)); + } } /* First pass: Walk through all symbols, checking for undefined's and From 45d0d603495b7f2181301ed4db4acdc98d7a4e6b Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:24:26 -0500 Subject: [PATCH 102/520] --warnings-as-errors for ca65 and ld65 --- doc/ca65.sgml | 8 +++++++ doc/ld65.sgml | 8 +++++++ src/ca65/global.c | 1 + src/ca65/global.h | 1 + src/ca65/main.c | 56 +++++++++++++++++++++++++++++----------------- src/ld65/error.c | 14 ++++++++++++ src/ld65/error.h | 11 +++++++++ src/ld65/global.c | 23 ++++++++++--------- src/ld65/global.h | 23 ++++++++++--------- src/ld65/main.c | 14 ++++++++++++ src/ld65/scanner.c | 3 +++ 11 files changed, 119 insertions(+), 43 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 19fd3aa2a..da094359c 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -125,6 +125,7 @@ Long options: --target sys Set the target system --verbose Increase verbosity --version Print the assembler version + --warnings-as-errors Treat warnings as errors --------------------------------------------------------------------------- </verb></tscreen> @@ -359,6 +360,13 @@ Here is a description of all the command line options: warning level is 1, and it would probably be silly to set it to something lower. + + <label id="option--warnings-as-errors"> + <tag><tt>--warnings-as-errors</tt></tag> + + An error will be generated if any warnings were produced. + + </descrip> <p> diff --git a/doc/ld65.sgml b/doc/ld65.sgml index 307caeaa4..1ad04b395 100644 --- a/doc/ld65.sgml +++ b/doc/ld65.sgml @@ -90,6 +90,7 @@ Long options: --start-group Start a library group --target sys Set the target system --version Print the linker version + --warnings-as-errors Treat warnings as errors --------------------------------------------------------------------------- </verb></tscreen> @@ -330,6 +331,13 @@ Here is a description of all of the command-line options: directory, in the list of directories specified using <tt/--obj-path/, in directories given by environment variables, and in a built-in default directory. + + <label id="option--warnings-as-errors"> + <tag><tt>--warnings-as-errors</tt></tag> + + An error will be generated if any warnings were produced. + + </descrip> diff --git a/src/ca65/global.c b/src/ca65/global.c index a216fc406..81ff68a79 100644 --- a/src/ca65/global.c +++ b/src/ca65/global.c @@ -67,6 +67,7 @@ unsigned char LineCont = 0; /* Allow line continuation */ unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ unsigned char RelaxChecks = 0; /* Relax a few assembler checks */ unsigned char StringEscapes = 0; /* Allow C-style escapes in strings */ +unsigned char WarningsAsErrors = 0; /* Error if any warnings */ /* Emulation features */ unsigned char DollarIsPC = 0; /* Allow the $ symbol as current PC */ diff --git a/src/ca65/global.h b/src/ca65/global.h index 77d90beca..26877032e 100644 --- a/src/ca65/global.h +++ b/src/ca65/global.h @@ -69,6 +69,7 @@ extern unsigned char LineCont; /* Allow line continuation */ extern unsigned char LargeAlignment; /* Don't warn about large alignments */ extern unsigned char RelaxChecks; /* Relax a few assembler checks */ extern unsigned char StringEscapes; /* Allow C-style escapes in strings */ +extern unsigned char WarningsAsErrors; /* Error if any warnings */ /* Emulation features */ extern unsigned char DollarIsPC; /* Allow the $ symbol as current PC */ diff --git a/src/ca65/main.c b/src/ca65/main.c index 4146aaf11..9710924bf 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -656,6 +656,15 @@ static void OptVersion (const char* Opt attribute ((unused)), +static void OptWarningsAsErrors (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Generate an error if any warnings occur */ +{ + WarningsAsErrors = 1; +} + + + static void DoPCAssign (void) /* Start absolute code */ { @@ -919,27 +928,28 @@ int main (int argc, char* argv []) { /* Program long options */ static const LongOpt OptTab[] = { - { "--auto-import", 0, OptAutoImport }, - { "--bin-include-dir", 1, OptBinIncludeDir }, - { "--cpu", 1, OptCPU }, - { "--create-dep", 1, OptCreateDep }, - { "--create-full-dep", 1, OptCreateFullDep }, - { "--debug", 0, OptDebug }, - { "--debug-info", 0, OptDebugInfo }, - { "--feature", 1, OptFeature }, - { "--help", 0, OptHelp }, - { "--ignore-case", 0, OptIgnoreCase }, - { "--include-dir", 1, OptIncludeDir }, - { "--large-alignment", 0, OptLargeAlignment }, - { "--list-bytes", 1, OptListBytes }, - { "--listing", 1, OptListing }, - { "--memory-model", 1, OptMemoryModel }, - { "--pagelength", 1, OptPageLength }, - { "--relax-checks", 0, OptRelaxChecks }, - { "--smart", 0, OptSmart }, - { "--target", 1, OptTarget }, - { "--verbose", 0, OptVerbose }, - { "--version", 0, OptVersion }, + { "--auto-import", 0, OptAutoImport }, + { "--bin-include-dir", 1, OptBinIncludeDir }, + { "--cpu", 1, OptCPU }, + { "--create-dep", 1, OptCreateDep }, + { "--create-full-dep", 1, OptCreateFullDep }, + { "--debug", 0, OptDebug }, + { "--debug-info", 0, OptDebugInfo }, + { "--feature", 1, OptFeature }, + { "--help", 0, OptHelp }, + { "--ignore-case", 0, OptIgnoreCase }, + { "--include-dir", 1, OptIncludeDir }, + { "--large-alignment", 0, OptLargeAlignment }, + { "--list-bytes", 1, OptListBytes }, + { "--listing", 1, OptListing }, + { "--memory-model", 1, OptMemoryModel }, + { "--pagelength", 1, OptPageLength }, + { "--relax-checks", 0, OptRelaxChecks }, + { "--smart", 0, OptSmart }, + { "--target", 1, OptTarget }, + { "--verbose", 0, OptVerbose }, + { "--version", 0, OptVersion }, + { "--warnings-as-errors", 0, OptWarningsAsErrors }, }; /* Name of the global name space */ @@ -1144,6 +1154,10 @@ int main (int argc, char* argv []) SegDump (); } + if (WarningCount > 0 && WarningsAsErrors) { + Error("Warnings as errors"); + } + /* If we didn't have an errors, finish off the line infos */ DoneLineInfo (); diff --git a/src/ld65/error.c b/src/ld65/error.c index 20dfd4537..344e4c1b3 100644 --- a/src/ld65/error.c +++ b/src/ld65/error.c @@ -46,6 +46,17 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Statistics */ +unsigned WarningCount = 0; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -66,6 +77,9 @@ void Warning (const char* Format, ...) fprintf (stderr, "%s: Warning: %s\n", ProgName, SB_GetConstBuf (&S)); SB_Done (&S); + + /* Count warnings */ + ++WarningCount; } diff --git a/src/ld65/error.h b/src/ld65/error.h index 75b8e0bc1..1285ca725 100644 --- a/src/ld65/error.h +++ b/src/ld65/error.h @@ -43,6 +43,17 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Statistics */ +extern unsigned WarningCount; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ diff --git a/src/ld65/global.c b/src/ld65/global.c index 8f43232fe..93dd65958 100644 --- a/src/ld65/global.c +++ b/src/ld65/global.c @@ -43,19 +43,20 @@ -const char* OutputName = "a.out"; /* Name of output file */ -unsigned OutputNameUsed = 0; /* Output name was used by %O */ +const char* OutputName = "a.out"; /* Name of output file */ +unsigned OutputNameUsed = 0; /* Output name was used by %O */ -unsigned ModuleId = 0; /* Id for o65 module */ +unsigned ModuleId = 0; /* Id for o65 module */ /* Start address */ -unsigned char HaveStartAddr = 0; /* Start address not given */ -unsigned long StartAddr = 0x200; /* Start address */ +unsigned char HaveStartAddr = 0; /* Start address not given */ +unsigned long StartAddr = 0x200; /* Start address */ -unsigned char VerboseMap = 0; /* Verbose map file */ -unsigned char AllowMultDef = 0; /* Allow multiple definitions */ -unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ +unsigned char VerboseMap = 0; /* Verbose map file */ +unsigned char AllowMultDef = 0; /* Allow multiple definitions */ +unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ +unsigned char WarningsAsErrors = 0; /* Error if any warnings */ -const char* MapFileName = 0; /* Name of the map file */ -const char* LabelFileName = 0; /* Name of the label file */ -const char* DbgFileName = 0; /* Name of the debug file */ +const char* MapFileName = 0; /* Name of the map file */ +const char* LabelFileName = 0; /* Name of the label file */ +const char* DbgFileName = 0; /* Name of the debug file */ diff --git a/src/ld65/global.h b/src/ld65/global.h index a923f6de5..ad85daf0a 100644 --- a/src/ld65/global.h +++ b/src/ld65/global.h @@ -44,21 +44,22 @@ -extern const char* OutputName; /* Name of output file */ -extern unsigned OutputNameUsed; /* Output name was used by %O */ +extern const char* OutputName; /* Name of output file */ +extern unsigned OutputNameUsed; /* Output name was used by %O */ -extern unsigned ModuleId; /* Id for o65 module */ +extern unsigned ModuleId; /* Id for o65 module */ -extern unsigned char HaveStartAddr; /* True if start address was given */ -extern unsigned long StartAddr; /* Start address */ +extern unsigned char HaveStartAddr; /* True if start address was given */ +extern unsigned long StartAddr; /* Start address */ -extern unsigned char VerboseMap; /* Verbose map file */ -extern unsigned char AllowMultDef; /* Allow multiple definitions */ -extern unsigned char LargeAlignment; /* Don't warn about large alignments */ +extern unsigned char VerboseMap; /* Verbose map file */ +extern unsigned char AllowMultDef; /* Allow multiple definitions */ +extern unsigned char LargeAlignment; /* Don't warn about large alignments */ +extern unsigned char WarningsAsErrors; /* Error if any warnings */ -extern const char* MapFileName; /* Name of the map file */ -extern const char* LabelFileName; /* Name of the label file */ -extern const char* DbgFileName; /* Name of the debug file */ +extern const char* MapFileName; /* Name of the map file */ +extern const char* LabelFileName; /* Name of the label file */ +extern const char* DbgFileName; /* Name of the debug file */ diff --git a/src/ld65/main.c b/src/ld65/main.c index f2415a914..70e9c84d1 100644 --- a/src/ld65/main.c +++ b/src/ld65/main.c @@ -559,6 +559,15 @@ static void OptVersion (const char* Opt attribute ((unused)), +static void OptWarningsAsErrors (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Generate an error if any warnings occur */ +{ + WarningsAsErrors = 1; +} + + + static void OptMultDef (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* Set flag to allow multiple definitions of a global symbol */ @@ -637,6 +646,7 @@ static void ParseCommandLine(void) { "--start-group", 0, CmdlOptStartGroup }, { "--target", 1, CmdlOptTarget }, { "--version", 0, OptVersion }, + { "--warnings-as-errors", 0, OptWarningsAsErrors }, }; unsigned I; @@ -845,6 +855,10 @@ int main (int argc, char* argv []) (MemoryAreaOverflows > 1) ? 's' : ' '); } + if (WarningCount > 0 && WarningsAsErrors) { + Error("Warnings as errors"); + } + /* Create the output file */ CfgWriteTarget (); diff --git a/src/ld65/scanner.c b/src/ld65/scanner.c index 718951aa5..5f53e5765 100644 --- a/src/ld65/scanner.c +++ b/src/ld65/scanner.c @@ -95,6 +95,9 @@ void CfgWarning (const FilePos* Pos, const char* Format, ...) Warning ("%s:%u: %s", GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf)); SB_Done (&Buf); + + /* Count warnings */ + ++WarningCount; } From 99ecd9b69d698ad555d01bae5e3622ec1ca07f7b Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 00:53:55 -0500 Subject: [PATCH 103/520] Document expected linker configuration and special segments used by cc65 --- doc/cc65-intern.sgml | 73 +++++++++++++++++++++++++++++++++++++++++++- doc/index.sgml | 2 +- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/doc/cc65-intern.sgml b/doc/cc65-intern.sgml index 8e36578b5..904cee070 100644 --- a/doc/cc65-intern.sgml +++ b/doc/cc65-intern.sgml @@ -6,7 +6,8 @@ <abstract> Internal details of cc65 code generation, -such as calling assembly functions from C. +such as the expected linker configuration, +and calling assembly functions from C. </abstract> <!-- Table of contents --> @@ -16,6 +17,76 @@ such as calling assembly functions from C. +<sect>Linker configuration<p> + +The C libraries and code generation depend directly on a suitable linker configuration. +There are premade configuration files in the <tt/cfg// directory, normally chosen by the +linker's selected target. These can be used as a template for customization. + +The C libraries depend on several special segments to be defined in your linker configuration. +Generated code will also use some of them by default. +Some platform libraries have additional special segments. + +Memory areas are free to be defined in a way that is appropriate to each platform, +and the segments they contain are used as a layer of semantics and abstraction, +to allow much of the reorganization to be done with the linker config, +rather than requiring platform-specific code source changes. + +<sect1><tt/ZEROPAGE/ segment<p> + +Used by the C library and generated code for efficient internal and temporary state storage, +also called "pseudo-registers". + +<sect1><tt/STARTUP/ segment<p> + +Used by each platform instance of the C library in <tt/crt0.s/ to contain the entry point +of the program. + +The startup module will export <tt/__STARTUP__ : absolute = 1/ to force the linker to +always include <tt/crt0.s/ from the library. + +<sect1><tt/CODE/ segment<p> + +The default segment for generated code, and most C library code will be located here. + +Use <tt/#pragma code-name/ to redirect generated code to another segment. + +<sect1><tt/BSS/ segment<p> + +Used for uninitialized variables. +Originally an acronym for "Block Started by Symbol", but the meaning of this is now obscure. + +Use <tt/#pragma bss-name/ to redirect uninitialized variables to another segment. + +<sect1><tt/DATA/ segment<p> + +Used for initialized variables. + +On some platforms, this may be initialized as part of the program loading process, +but on others it may have a separate <tt/LOAD/ and <tt/RUN/ address, +allowing <tt/copydata/ to copy the initialization from the loaded location +into their run destination in RAM. + +Use <tt/#pragma data-name/ to redirect initialized variables to another segment. + +<sect1><tt/RODATA/ segment<p> + +Used for read-only (constant) data. + +Use <tt/#pragma rodata-name/ to redirect constant data to another segment. + +<sect1><tt/FEATURES/ table<p> + +This currently defines table locations for the <tt/CONDES/ +constructor, destructor, and interruptor features. +Some platform libraries use these. + +The constructors will be called with <tt/initlib/ at startup, +and the destructors with <tt/donelib/ at program exit. +Interruptors are called with <tt/callirq/. + + + <sect>Calling assembly functions from C<p> <sect1>Calling conventions<p> diff --git a/doc/index.sgml b/doc/index.sgml index bfce63486..727364028 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -59,7 +59,7 @@ Contains hints on creating the most effective code with cc65. <tag><htmlurl url="cc65-intern.html" name="cc65-intern.html"></tag> - Describes internal details of cc65, such as calling conventions. + Describes internal details of cc65: linker configuration, calling conventions, etc. <tag><htmlurl url="using-make.html" name="using-make.html"></tag> Build programs, using the GNU Make utility. From ccf3994e3b6f669d18ce83329adc1998a04923fe Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 04:00:34 -0500 Subject: [PATCH 104/520] ca65 jsr/jmp/rts will not promote to jsl/jml/rtl by default, but can still be enabled with new feature long_jsr_jmp_rts --- doc/ca65.sgml | 23 ++++++++++++++++-- src/ca65/feature.c | 2 ++ src/ca65/feature.h | 1 + src/ca65/global.c | 1 + src/ca65/global.h | 1 + src/ca65/instr.c | 60 ++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 79 insertions(+), 9 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 19fd3aa2a..276a2b697 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -183,7 +183,7 @@ Here is a description of all the command line options: Enable an emulation feature. This is identical as using <tt/.FEATURE/ in the source with two exceptions: Feature names must be lower case, and - each feature must be specified by using an extra <tt/--feature/ option, + each feature must be specified by using a separate <tt/--feature/ option, comma separated lists are not allowed. See the discussion of the <tt><ref id=".FEATURE" name=".FEATURE"></tt> @@ -2825,6 +2825,23 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH overridden. When using this feature, you may also get into trouble if later versions of the assembler define new keywords starting with a dot. + <tag><tt>long_jsr_jmp_rts</tt><label id="long_jsr_jmp_rts"></tag> + + Affects 65816 mode only. + + Allows <tt>jsr</tt> and <tt>jmp</tt> to produce long jumps if the target + address has been previously declared in a <tt>far</tt> segment, + or imported as <tt>far</tt>. + Otherwise <tt>jsl</tt> and <tt>jml</tt> must be used instead. + + Also allows <tt><ref id=".SMART" name=".SMART"></tt> to convert <tt>rts</tt> + to a long return <tt>rtl</tt> when the enclosing scope or memory model + indicates returning from a <tt>far</tt> procedure. + + This permits compatibility with the old behavior of this assembler, or other + assemblers which similarly allowed <tt>jsr</tt> and <tt>jmp</tt> to be used + this way. + <tag><tt>loose_char_term</tt><label id="loose_char_term"></tag> Accept single quotes as well as double quotes as terminators for char @@ -3994,7 +4011,9 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" the assembler cannot trace the execution flow this may lead to false results in some cases. If in doubt, use the <tt/.Inn/ and <tt/.Ann/ instructions to tell the assembler about the current settings. - <item>In 65816 mode, replace a <tt/RTS/ instruction by <tt/RTL/ if it is + <item>In 65816 mode, if the <tt><ref id="long_jsr_jmp_rts" + name="long_jsr_jmp_rts"></tt> feature is enabled, + smart mode will replace a <tt/RTS/ instruction by <tt/RTL/ if it is used within a procedure declared as <tt/far/, or if the procedure has no explicit address specification, but it is <tt/far/ because of the memory model used. diff --git a/src/ca65/feature.c b/src/ca65/feature.c index b11345338..754b645cf 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -66,6 +66,7 @@ static const char* const FeatureKeys[FEAT_COUNT] = { "addrsize", "bracket_as_indirect", "string_escapes", + "long_jsr_jmp_rts", }; @@ -125,6 +126,7 @@ feature_t SetFeature (const StrBuf* Key) case FEAT_ADDRSIZE: AddrSize = 1; break; case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = 1; break; case FEAT_STRING_ESCAPES: StringEscapes = 1; break; + case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = 1; break; default: /* Keep gcc silent */ break; } diff --git a/src/ca65/feature.h b/src/ca65/feature.h index 876f3c4a8..a29ee0172 100644 --- a/src/ca65/feature.h +++ b/src/ca65/feature.h @@ -68,6 +68,7 @@ typedef enum { FEAT_ADDRSIZE, FEAT_BRACKET_AS_INDIRECT, FEAT_STRING_ESCAPES, + FEAT_LONG_JSR_JMP_RTS, /* Special value: Number of features available */ FEAT_COUNT diff --git a/src/ca65/global.c b/src/ca65/global.c index a216fc406..962f368db 100644 --- a/src/ca65/global.c +++ b/src/ca65/global.c @@ -67,6 +67,7 @@ unsigned char LineCont = 0; /* Allow line continuation */ unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ unsigned char RelaxChecks = 0; /* Relax a few assembler checks */ unsigned char StringEscapes = 0; /* Allow C-style escapes in strings */ +unsigned char LongJsrJmpRts = 0; /* Allow JSR/JMP/RTS as alias for JSL/JML/RTL */ /* Emulation features */ unsigned char DollarIsPC = 0; /* Allow the $ symbol as current PC */ diff --git a/src/ca65/global.h b/src/ca65/global.h index 77d90beca..c92291ad3 100644 --- a/src/ca65/global.h +++ b/src/ca65/global.h @@ -69,6 +69,7 @@ extern unsigned char LineCont; /* Allow line continuation */ extern unsigned char LargeAlignment; /* Don't warn about large alignments */ extern unsigned char RelaxChecks; /* Relax a few assembler checks */ extern unsigned char StringEscapes; /* Allow C-style escapes in strings */ +extern unsigned char LongJsrJmpRts; /* Allow JSR/JMP/RTS as alias for JSL/JML/RTL */ /* Emulation features */ extern unsigned char DollarIsPC; /* Allow the $ symbol as current PC */ diff --git a/src/ca65/instr.c b/src/ca65/instr.c index d0d7ce64c..745072a09 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -120,9 +120,21 @@ static void PutJMP (const InsDesc* Ins); ** to check for this case and is otherwise identical to PutAll. */ +static void PutJMP816 (const InsDesc* Ins); +/* Handle the JMP instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JMP. +** Note that JMP [abs] and JML [abs] are always both permitted for instruction $DC, +** because the [] notation for long indirection makes the generated instruction unambiguous. +*/ + +static void PutJSR816 (const InsDesc* Ins); +/* Handle the JSR instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JSR. +*/ + static void PutRTS (const InsDesc* Ins attribute ((unused))); /* Handle the RTS instruction for the 816. In smart mode emit a RTL opcode if -** the enclosing scope is FAR. +** the enclosing scope is FAR, but only if the long_jsr_jmp_rts feature applies. */ static void PutAll (const InsDesc* Ins); @@ -758,9 +770,9 @@ static const struct { { "INX", 0x0000001, 0xe8, 0, PutAll }, { "INY", 0x0000001, 0xc8, 0, PutAll }, { "JML", 0x4000010, 0x5c, 1, PutAll }, - { "JMP", 0x4010818, 0x4c, 6, PutAll }, + { "JMP", 0x4010818, 0x4c, 6, PutJMP816 }, { "JSL", 0x0000010, 0x20, 7, PutAll }, - { "JSR", 0x0010018, 0x20, 7, PutAll }, + { "JSR", 0x0010018, 0x20, 7, PutJSR816 }, { "LDA", 0x0b8f6fc, 0xa0, 0, PutAll }, { "LDX", 0x0c0030c, 0xa2, 1, PutAll }, { "LDY", 0x0c0006c, 0xa0, 1, PutAll }, @@ -1627,12 +1639,46 @@ static void PutJMP (const InsDesc* Ins) -static void PutRTS (const InsDesc* Ins attribute ((unused))) -/* Handle the RTS instruction for the 816. In smart mode emit a RTL opcode if -** the enclosing scope is FAR. +static void PutJMP816 (const InsDesc* Ins) +/* Handle the JMP instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JMP. +** Note that JMP [abs] and JML [abs] are always both permitted for instruction $DC, +** because the [] notation for long indirection makes the generated instruction unambiguous. */ { - if (SmartMode && CurrentScope->AddrSize == ADDR_SIZE_FAR) { + if (LongJsrJmpRts) { + PutJMP (Ins); + } else { + InsDesc InsAbs = *Ins; + InsAbs.AddrMode &= ~(AM65_ABS_LONG); + PutJMP (&InsAbs); + } +} + + + +static void PutJSR816 (const InsDesc* Ins) +/* Handle the JSR instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JSR. +*/ +{ + if (LongJsrJmpRts) { + PutAll (Ins); + } else { + InsDesc InsAbs = *Ins; + InsAbs.AddrMode &= ~(AM65_ABS_LONG); + PutJMP (&InsAbs); + } +} + + + +static void PutRTS (const InsDesc* Ins attribute ((unused))) +/* Handle the RTS instruction for the 816. In smart mode emit a RTL opcode if +** the enclosing scope is FAR, but only if the long_jsr_jmp_rts feature applies. +*/ +{ + if (LongJsrJmpRts && SmartMode && CurrentScope->AddrSize == ADDR_SIZE_FAR) { Emit0 (0x6B); /* RTL */ } else { Emit0 (0x60); /* RTS */ From 4b29d257d9c1aaa256b5bf1537daf9eb1e18a8ee Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 04:38:17 -0500 Subject: [PATCH 105/520] fix dasm test65816 which relied on JMP automatic promotion, add test of new long_jsr_jmp_rts and smart --- test/dasm/test65816.s | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/dasm/test65816.s b/test/dasm/test65816.s index 3d764fb6e..1b447d0fe 100644 --- a/test/dasm/test65816.s +++ b/test/dasm/test65816.s @@ -151,10 +151,11 @@ BVC LABEL BVS LABEL BRL LABEL JMP $1234 -JMP $FEDCBA +JML $FEDCBA JMP ($1234) JMP ($1234,X) JMP [$1234] +JML [$1234] ; alternative to JMP [] JSL $123456 JSR $1234 JSR ($1234,X) @@ -271,3 +272,15 @@ BIT #$5432 LDA #$5432 LDX #$5432 LDY #$5432 + +; test of smart and long_jsr_jmp_rts +.smart +.proc short_rts : far +RTS ; not promoted to RTL +.endproc +.feature long_jsr_jmp_rts +JSR $FEDCBA ; promoted to JSL +JMP $FEDCBA ; promoted to JML +.proc long_rts : far +RTS ; promoted to RTL +.endproc From 2cdab999f54a8c6047657507e2e8a5f0a9242c0e Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 05:22:28 -0500 Subject: [PATCH 106/520] dasm test extend 65816.info to include added code --- test/dasm/65816.info | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dasm/65816.info b/test/dasm/65816.info index 2a3394680..a6d836688 100644 --- a/test/dasm/65816.info +++ b/test/dasm/65816.info @@ -3,5 +3,5 @@ GLOBAL { }; RANGE { START $8000; END $8229; ADDRMODE "MX"; TYPE Code;}; -RANGE { START $822a; END $824b; ADDRMODE "mx"; TYPE Code;}; +RANGE { START $822a; END $825b; ADDRMODE "mx"; TYPE Code;}; From 13f1d37403017a3b399eb9bc2c61e6613a1ebdfa Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 06:48:46 -0500 Subject: [PATCH 107/520] suppress spurious "large alignment" warning when the combined alignment is not larger than any of the explictly requested ones --- doc/ca65.sgml | 13 +++++++------ src/ca65/segment.c | 2 +- src/ld65/segments.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 19fd3aa2a..f2f7e94c0 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2112,14 +2112,15 @@ Here's a list of all control commands and a description, what they do: </verb></tscreen> the assembler will force a segment alignment to the least common multiple of - 15, 18 and 251 - which is 22590. To protect the user against errors, the - assembler will issue a warning when the combined alignment exceeds 256. The - command line option <tt><ref id="option--large-alignment" - name="--large-alignment"></tt> will disable this warning. + 15, 18 and 251 - which is 22590. To protect the user against errors, when the + combined alignment is larger than the explicitly requested alignments, + and also exceeds 256, the assembler will issue a warning. The command line + option <tt><ref id="option--large-alignment" name="--large-alignment"></tt> + will disable this warning. Please note that with alignments that are a power of two (which were the - only alignments possible in older versions of the assembler), the problem is - less severe, because the least common multiple of powers to the same base is + only alignments possible in older versions of the assembler) will not cause + a warning, because the least common multiple of powers to the same base is always the larger one. diff --git a/src/ca65/segment.c b/src/ca65/segment.c index 26b568711..aadc35009 100644 --- a/src/ca65/segment.c +++ b/src/ca65/segment.c @@ -306,7 +306,7 @@ void SegAlign (unsigned long Alignment, int FillVal) ActiveSeg->Align = CombinedAlignment; /* Output a warning for larger alignments if not suppressed */ - if (CombinedAlignment >= LARGE_ALIGNMENT && !LargeAlignment) { + if (CombinedAlignment >= LARGE_ALIGNMENT && CombinedAlignment > ActiveSeg->Align && CombinedAlignment > Alignment && !LargeAlignment) { Warning (0, "Combined alignment is suspiciously large (%lu)", CombinedAlignment); } diff --git a/src/ld65/segments.c b/src/ld65/segments.c index 10d2cda2c..255b8ccd1 100644 --- a/src/ld65/segments.c +++ b/src/ld65/segments.c @@ -230,7 +230,7 @@ Section* ReadSection (FILE* F, ObjData* O) "%lu. Last module requiring alignment was '%s'.", GetString (Name), Alignment, MAX_ALIGNMENT, GetObjFileName (O)); - } else if (Alignment >= LARGE_ALIGNMENT && !LargeAlignment) { + } else if (Alignment >= LARGE_ALIGNMENT && Alignment > S->Alignment && Alignment > Sec->Alignment && !LargeAlignment) { Warning ("Combined alignment for segment '%s' is suspiciously " "large (%lu). Last module requiring alignment was '%s'.", GetString (Name), Alignment, GetObjFileName (O)); From 23cc295b08661bab604237582acd5bf5773dd6d3 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 07:32:38 -0500 Subject: [PATCH 108/520] improve description of large-alignment warning --- doc/ca65.sgml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index f2f7e94c0..062b6d0c7 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2114,14 +2114,13 @@ Here's a list of all control commands and a description, what they do: the assembler will force a segment alignment to the least common multiple of 15, 18 and 251 - which is 22590. To protect the user against errors, when the combined alignment is larger than the explicitly requested alignments, - and also exceeds 256, the assembler will issue a warning. The command line + the assembler will issue a warning if it also exceeds 256. The command line option <tt><ref id="option--large-alignment" name="--large-alignment"></tt> will disable this warning. - Please note that with alignments that are a power of two (which were the - only alignments possible in older versions of the assembler) will not cause - a warning, because the least common multiple of powers to the same base is - always the larger one. + Please note that with only alignments that are a power of two, a warning will + never occur, because the least common multiple of powers to the same base is + always simply the larger one. From ba038e921fcd7c224ea144d68af6043afafde537 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:06:21 -0500 Subject: [PATCH 109/520] jmp (abs) page wrapping should be an error, not a warning, also only applies to 6502 CPU --- src/ca65/instr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index d0d7ce64c..503304b5b 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1605,11 +1605,12 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if (A.AddrModeBit & AM65_ABS_IND) { + if (A.AddrModeBit & AM65_ABS_IND && CPU < CPU_65SC02) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise - ** things will go weird later. + ** things will go weird later. This only affects the 6502 CPU, + ** and was corrected in 65C02 and later CPUs in this family. */ ExprNode* E = GenNE (GenByteExpr (CloneExpr (A.Expr)), 0xFF); @@ -1617,7 +1618,7 @@ static void PutJMP (const InsDesc* Ins) unsigned Msg = GetStringId ("\"jmp (abs)\" across page border"); /* Generate the assertion */ - AddAssertion (E, ASSERT_ACT_WARN, Msg); + AddAssertion (E, ASSERT_ACT_ERROR, Msg); } /* No error, output code */ From fedfc3443de7adacb290cc33adb4b8fb63739e41 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Fri, 24 Feb 2023 02:12:19 +0100 Subject: [PATCH 110/520] fix for issue #1973 (printf("%c", '\0') doesn't _write '\0' to fd 0) --- libsrc/common/_printf.s | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index 840d42127..a0074583e 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -502,10 +502,10 @@ DoFormat: ; It is a character jsr GetIntArg ; Get the argument (promoted to int) - sta Buf ; Place it as zero terminated string... - lda #0 - sta Buf+1 ; ...into the buffer - jmp HaveArg ; Done + sta Buf ; Place it into the buffer + ldx #0 + lda #1 ; Buffer length is 1 + jmp HaveArg1 ; Is it an integer? @@ -671,6 +671,7 @@ HaveArg: lda Str ldx Str+1 jsr _strlen ; Get length of argument +HaveArg1: ; Jumped into here from %c handling sta ArgLen stx ArgLen+1 From da150aeeac5e030d34a208bf3efabdaaac23e81b Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:00:58 -0500 Subject: [PATCH 111/520] allow .feature to both enable and disable --- doc/ca65.sgml | 14 ++++++++----- src/ca65/feature.c | 50 +++++++++++++++++++--------------------------- src/ca65/feature.h | 6 ++---- src/ca65/main.c | 11 ++++++---- src/ca65/pseudo.c | 25 +++++++++++++++-------- 5 files changed, 56 insertions(+), 50 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b65d337bb..02ed5ad8a 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2746,15 +2746,19 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH This directive may be used to enable one or more compatibility features of the assembler. While the use of <tt/.FEATURE/ should be avoided when possible, it may be useful when porting sources written for other - assemblers. There is no way to switch a feature off, once you have - enabled it, so using + assemblers. After the feature name an optional '+' or '-' may specify whether + to enable or disable the feature (enable if omitted). Multiple features may be + enabled, separated by commas. Examples: <tscreen><verb> - .FEATURE xxx + ; enable c_comments + .feature c_comments + .feature c_comments + + ; enable force_range, disable underline_in_numbers, enable labels_without_colons + .feature force_range, underline_in_numbers -, labels_without_colons + + .feature force_range +, underline_in_numbers off, labels_without_colons on </verb></tscreen> - will enable the feature until end of assembly is reached. - The following features are available: <descrip> diff --git a/src/ca65/feature.c b/src/ca65/feature.c index 754b645cf..41177d66b 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -98,38 +98,30 @@ feature_t FindFeature (const StrBuf* Key) -feature_t SetFeature (const StrBuf* Key) -/* Find the feature and set the corresponding flag if the feature is known. -** In any case, return the feature found. An invalid Key will return -** FEAT_UNKNOWN. +void SetFeature (feature_t Feature, unsigned char On) +/* Set the corresponding feature flag if Feature is valid. */ { - /* Map the string to an enum value */ - feature_t Feature = FindFeature (Key); - /* Set the flags */ switch (Feature) { - case FEAT_DOLLAR_IS_PC: DollarIsPC = 1; break; - case FEAT_LABELS_WITHOUT_COLONS: NoColonLabels = 1; break; - case FEAT_LOOSE_STRING_TERM: LooseStringTerm = 1; break; - case FEAT_LOOSE_CHAR_TERM: LooseCharTerm = 1; break; - case FEAT_AT_IN_IDENTIFIERS: AtInIdents = 1; break; - case FEAT_DOLLAR_IN_IDENTIFIERS: DollarInIdents = 1; break; - case FEAT_LEADING_DOT_IN_IDENTIFIERS: LeadingDotInIdents= 1; break; - case FEAT_ORG_PER_SEG: OrgPerSeg = 1; break; - case FEAT_PC_ASSIGNMENT: PCAssignment = 1; break; - case FEAT_MISSING_CHAR_TERM: MissingCharTerm = 1; break; - case FEAT_UBIQUITOUS_IDENTS: UbiquitousIdents = 1; break; - case FEAT_C_COMMENTS: CComments = 1; break; - case FEAT_FORCE_RANGE: ForceRange = 1; break; - case FEAT_UNDERLINE_IN_NUMBERS: UnderlineInNumbers= 1; break; - case FEAT_ADDRSIZE: AddrSize = 1; break; - case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = 1; break; - case FEAT_STRING_ESCAPES: StringEscapes = 1; break; - case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = 1; break; - default: /* Keep gcc silent */ break; + case FEAT_DOLLAR_IS_PC: DollarIsPC = On; break; + case FEAT_LABELS_WITHOUT_COLONS: NoColonLabels = On; break; + case FEAT_LOOSE_STRING_TERM: LooseStringTerm = On; break; + case FEAT_LOOSE_CHAR_TERM: LooseCharTerm = On; break; + case FEAT_AT_IN_IDENTIFIERS: AtInIdents = On; break; + case FEAT_DOLLAR_IN_IDENTIFIERS: DollarInIdents = On; break; + case FEAT_LEADING_DOT_IN_IDENTIFIERS: LeadingDotInIdents= On; break; + case FEAT_ORG_PER_SEG: OrgPerSeg = On; break; + case FEAT_PC_ASSIGNMENT: PCAssignment = On; break; + case FEAT_MISSING_CHAR_TERM: MissingCharTerm = On; break; + case FEAT_UBIQUITOUS_IDENTS: UbiquitousIdents = On; break; + case FEAT_C_COMMENTS: CComments = On; break; + case FEAT_FORCE_RANGE: ForceRange = On; break; + case FEAT_UNDERLINE_IN_NUMBERS: UnderlineInNumbers= On; break; + case FEAT_ADDRSIZE: AddrSize = On; break; + case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = On; break; + case FEAT_STRING_ESCAPES: StringEscapes = On; break; + case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = On; break; + default: break; } - - /* Return the value found */ - return Feature; } diff --git a/src/ca65/feature.h b/src/ca65/feature.h index a29ee0172..8eeb62e6f 100644 --- a/src/ca65/feature.h +++ b/src/ca65/feature.h @@ -87,10 +87,8 @@ feature_t FindFeature (const StrBuf* Key); ** feature is invalid, return FEAT_UNKNOWN. */ -feature_t SetFeature (const StrBuf* Key); -/* Find the feature and set the corresponding flag if the feature is known. -** In any case, return the feature found. An invalid Key will return -** FEAT_UNKNOWN. +void SetFeature (feature_t Feature, unsigned char On); +/* Set the corresponding feature flag if Feature is valid. */ diff --git a/src/ca65/main.c b/src/ca65/main.c index 9710924bf..7183ff046 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -489,12 +489,15 @@ static void OptDebugInfo (const char* Opt attribute ((unused)), static void OptFeature (const char* Opt attribute ((unused)), const char* Arg) /* Set an emulation feature */ { - /* Make a string buffer from Arg */ - StrBuf Feature; + /* Make a string buffer from Arg and use it to find the feature. */ + StrBuf StrFeature; + feature_t Feature = FindFeature (SB_InitFromString (&StrFeature, Arg)); - /* Set the feature, check for errors */ - if (SetFeature (SB_InitFromString (&Feature, Arg)) == FEAT_UNKNOWN) { + /* Enable the feature, check for errors */ + if (Feature == FEAT_UNKNOWN) { AbEnd ("Illegal emulation feature: '%s'", Arg); + } else { + SetFeature (Feature, 1); } } diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index 843f5b9d2..1877512d5 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -1023,7 +1023,10 @@ static void DoFatal (void) static void DoFeature (void) /* Switch the Feature option */ { - /* Allow a list of comma separated keywords */ + feature_t Feature; + unsigned char On; + + /* Allow a list of comma separated feature keywords with optional +/- or ON/OFF */ while (1) { /* We expect an identifier */ @@ -1034,18 +1037,24 @@ static void DoFeature (void) /* Make the string attribute lower case */ LocaseSVal (); - - /* Set the feature and check for errors */ - if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) { + Feature = FindFeature(&CurTok.SVal); + if (Feature == FEAT_UNKNOWN) { /* Not found */ ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal); return; - } else { - /* Skip the keyword */ - NextTok (); + } + NextTok (); + + /* Optional +/- or ON/OFF */ + On = 1; + if (CurTok.Tok != TOK_COMMA && !TokIsSep (CurTok.Tok)) { + SetBoolOption(&On); } - /* Allow more than one keyword */ + /* Apply feature setting. */ + SetFeature (Feature, On); + + /* Allow more than one feature separated by commas. */ if (CurTok.Tok == TOK_COMMA) { NextTok (); } else { From 230230819eb8a10be5eda01ee646acd8ce57c796 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 24 Feb 2023 19:35:38 -0500 Subject: [PATCH 112/520] document the undocumented test/asm/listing behaviours --- test/asm/listing/readme.txt | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/test/asm/listing/readme.txt b/test/asm/listing/readme.txt index e43f2008a..d6280b954 100644 --- a/test/asm/listing/readme.txt +++ b/test/asm/listing/readme.txt @@ -6,22 +6,38 @@ The name of a test is everything in the form <test>.s. The following reference files can be added: -- <test>.bin-ref: +- ref/<test>.bin-ref: This is a reference for the resulting binary. The binary as binary tested against this file. If they are not equal, the test fails. -- <test>.list-ref +- ref/<test>.list-ref This is a reference for the resulting listing output This file *must* have the first line of the listing removed, as that contains a ca65 version string, and almost always this will be changed! +- ref/<test>.err-ref + This is a reference for the resulting ca65 stdout (>) output. + +- ref/<test>.err2-ref + This is a reference for the resulting ca65 stderr (2>) output. + +- ref/<test>.ld65err-ref + This is a reference for the resutling ld65 stdout (>) output. + +- ref/<test>.ld65err2-ref + This is a reference for the resulting ld65 stderr (2>) output. + +The following control files can be added to control the tests. +These files are empty (contents ignored), and only the filename is used: + +- control/<test>.err + Test is expected to produce an error. + +- control/<test>.no-ld65 + Skip the ld65 step. + Note that the resulting .bin file is generated twice: Once with no listing file, and once with listing file. This way, one can find out if the listing file generation changes anything with the resulting binary output. - - -TODO: -- add the possibility to test for specific error output that are to be - expected From c2bc40f2138152c333021cff7d60931d24380f18 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 24 Feb 2023 20:00:56 -0500 Subject: [PATCH 113/520] test for all ca65 .feature options --- test/asm/listing/108-long-rts.s | 8 ++ test/asm/listing/ref/108-long-rts.bin-ref | 1 + test/asm/val/feature.s | 147 ++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 test/asm/listing/108-long-rts.s create mode 100644 test/asm/listing/ref/108-long-rts.bin-ref create mode 100644 test/asm/val/feature.s diff --git a/test/asm/listing/108-long-rts.s b/test/asm/listing/108-long-rts.s new file mode 100644 index 000000000..a8bf860cd --- /dev/null +++ b/test/asm/listing/108-long-rts.s @@ -0,0 +1,8 @@ +; test of long-rts promotion + +.p816 +.feature long_jsr_jmp_rts +.smart + +.proc farproc : far + rts ; should be $6B (RTL) and not $60 (RTS) +.endproc diff --git a/test/asm/listing/ref/108-long-rts.bin-ref b/test/asm/listing/ref/108-long-rts.bin-ref new file mode 100644 index 000000000..23fa7d31a --- /dev/null +++ b/test/asm/listing/ref/108-long-rts.bin-ref @@ -0,0 +1 @@ +k \ No newline at end of file diff --git a/test/asm/val/feature.s b/test/asm/val/feature.s new file mode 100644 index 000000000..4428cf4c2 --- /dev/null +++ b/test/asm/val/feature.s @@ -0,0 +1,147 @@ +; a simple test of every .feature + +.export _main + +.segment "ZEROPAGE" +zplabel: + +.segment "CODE" +abslabel: + +; exit with 0 + +_main: + ; if any feature needs a runtime test, + ; it can be added here. + lda #0 + tax + rts + + +.feature addrsize + +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" +.feature addrsize - + + +.feature at_in_identifiers on +ident@with@at: + rts +.feature at_in_identifiers off + + +.feature bracket_as_indirect + lda [$82],y +.feature bracket_as_indirect- + + +.feature c_comments + lda zplabel /* comment */ + /* comment */ +/* multiline +** comment +*/ +.feature c_comments - + + +.feature dollar_in_identifiers +ident$with$dollar: + rts +.feature dollar_in_identifiers - + + +.feature dollar_is_pc +.assert $ = *, error, "dollar_is_pc failure" +.feature dollar_is_pc - + + +.feature force_range + lda #-1 +.feature force_range - + + +.feature labels_without_colons +labelwithoutcolon + jmp labelwithoutcolon +.feature labels_without_colons - + + +.feature leading_dot_in_identifiers +.identifierwithdot: + rts +.feature leading_dot_in_identifiers - + + +.feature long_jsr_jmp_rts +.p816 + ; long addresses require alternate instruction names JSL, JML without this feature + jsr $123456 + jmp $123456 +; smart + far + long_jsr_jmp_rts will promote rts to rtl +.smart + +.proc long_rts : far + rts ; should become RTL ($6B) instead of RTS ($60) + ; the emitted opcode is not verified by this test, + ; see test/asm/listing/108-long-rts +.endproc +.smart - +.p02 +.feature long_jsr_jmp_rts - + + +.feature loose_char_term +.byte 'a' +.byte "a" +.feature loose_char_term - + + +.feature loose_string_term +.asciiz "string" +.asciiz 'string' +.feature loose_string_term - + + +.feature missing_char_term + lda #'a +.feature missing_char_term - + + +.feature org_per_seg +.segment "RODATA" +.org $5678 +.assert * = $5678, error, "org_per_seg failed" +.segment "CODE" +.org $9ABC +.assert * = $9ABC, error, "org_per_seg failed" +.segment "RODATA" +.assert * = $5678, error, "org_per_seg failed" +.reloc +.segment "CODE" +.assert * = $9ABC, error, "org_per_seg failed" +.reloc +.feature org_per_seg - + + +.feature pc_assignment +* = $1234 +.assert * = $1234, error, "pc_assignment failed" +.reloc +.feature pc_assignment - + + +.feature string_escapes +.asciiz "quote:\"" +.feature string_escapes - + + +.feature ubiquitous_idents +.macro bit + brk +.endmacro + bit +.feature ubiquitous_idents - + + +.feature underline_in_numbers +.byte %10_10_10_10 +.feature underline_in_numbers - From cd8fa3906631c6ba4dda3f37077eb0770daf769a Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 24 Feb 2023 22:40:29 -0500 Subject: [PATCH 114/520] optional BRK signature on all 6502 CPUs, not just 65816 (also COP) --- src/ca65/instr.c | 16 ++++---- test/asm/listing/109-brk-signature.s | 36 ++++++++++++++++++ .../asm/listing/ref/109-brk-signature.bin-ref | Bin 0 -> 28 bytes 3 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 test/asm/listing/109-brk-signature.s create mode 100644 test/asm/listing/ref/109-brk-signature.bin-ref diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 745072a09..2ed5ba734 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -181,7 +181,7 @@ static const struct { { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -252,7 +252,7 @@ static const struct { { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -342,7 +342,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x12, 0, PutPCRel8 }, /* DTV */ - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -418,7 +418,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -510,7 +510,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -622,7 +622,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BSR", 0x0040000, 0x63, 0, PutPCRel4510 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, @@ -756,7 +756,7 @@ static const struct { { "CLI", 0x0000001, 0x58, 0, PutAll }, { "CLV", 0x0000001, 0xb8, 0, PutAll }, { "CMP", 0x0b8f6fc, 0xc0, 0, PutAll }, - { "COP", 0x0000004, 0x02, 6, PutAll }, + { "COP", 0x0000005, 0x02, 6, PutAll }, { "CPA", 0x0b8f6fc, 0xc0, 0, PutAll }, /* == CMP */ { "CPX", 0x0c0000c, 0xe0, 1, PutAll }, { "CPY", 0x0c0000c, 0xc0, 1, PutAll }, @@ -909,7 +909,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0000005, 0x00, 6, PutAll }, { "BSR", 0x0020000, 0x44, 0, PutPCRel8 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, diff --git a/test/asm/listing/109-brk-signature.s b/test/asm/listing/109-brk-signature.s new file mode 100644 index 000000000..b150e088a --- /dev/null +++ b/test/asm/listing/109-brk-signature.s @@ -0,0 +1,36 @@ +; test of optional signature byte for BRK on all 6502-derived CPUs +; and also COP on 65C816 + +.setcpu "6502" +brk ; 1 byte +brk 0 ; 2 bytes +brk $60 ; 2 bytes + +.setcpu "6502X" +brk +brk $60 + +.setcpu "6502DTV" +brk +brk $60 + +.setcpu "65SC02" +brk +brk $60 + +.setcpu "65816" +brk +brk $60 +cop +cop $60 +; WDM is a NOP that gives +2 PC, probably not useful to make its signature byte optional +;wdm +wdm $60 + +.setcpu "4510" +brk +brk $60 + +.setcpu "HuC6280" +brk +brk $60 diff --git a/test/asm/listing/ref/109-brk-signature.bin-ref b/test/asm/listing/ref/109-brk-signature.bin-ref new file mode 100644 index 0000000000000000000000000000000000000000..0f46d29fd39f8d79bd403ca35bd6eb6aa7ced41c GIT binary patch literal 28 TcmZQzU|>i<ModfzPEY{=EA|7Y literal 0 HcmV?d00001 From 3f8252311efaae97cae48b6acb2e76d1a4dcf14f Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 24 Feb 2023 23:00:30 -0500 Subject: [PATCH 115/520] 65C816 document brk optional signature, and mvn/mvp syntax --- doc/ca65.sgml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b65d337bb..d4c002408 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -439,6 +439,14 @@ The assembler accepts <tt><ref id=".P4510" name=".P4510"></tt> command was given). </itemize> +On 6502-derived platforms the <tt/BRK/ instruction has an optional signature +byte. If omitted, the assembler will only produce only 1 byte. + +<tscreen><verb> + brk ; 1-byte: $00 + brk $34 ; 2-bytes: $00 $34 +</verb></tscreen> + <sect1>65816 mode<p> @@ -456,6 +464,17 @@ mnemonics: <item><tt>TSA</tt> is an alias for <tt>TSC</tt> </itemize> +The <tt/MVN/ and <tt/MVP/ instructions accept two different argument forms. +Either two bank bytes may be given with a <tt/#/ prefix, +or two far addresses whose high byte will be used. + +<tscreen><verb> + mvn #^src, #^dst ; bank of src to bank of dst + mvn src, dst ; bank of src to bank of dst + mvp #$12, #$78 ; bank $12 to $78 + mvp $123456, $789ABC ; bank $12 to $78 +</verb></tscreen> + <sect1>6502X mode<label id="6502X-mode"><p> From 3d41a5b51647fbc1c840d2d8167c76456faa3dec Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 25 Feb 2023 08:23:47 -0500 Subject: [PATCH 116/520] allow immedite style syntax variation for BRK signature byte --- doc/ca65.sgml | 1 + src/ca65/instr.c | 20 +++++++++--------- test/asm/listing/109-brk-signature.s | 9 ++++++++ .../asm/listing/ref/109-brk-signature.bin-ref | Bin 28 -> 46 bytes 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index d4c002408..1bef0b551 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -445,6 +445,7 @@ byte. If omitted, the assembler will only produce only 1 byte. <tscreen><verb> brk ; 1-byte: $00 brk $34 ; 2-bytes: $00 $34 + brk #$34 ; 2-bytes: $00 $34 </verb></tscreen> diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 2ed5ba734..b72be6711 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -181,7 +181,7 @@ static const struct { { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -252,7 +252,7 @@ static const struct { { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -342,7 +342,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x12, 0, PutPCRel8 }, /* DTV */ - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -418,7 +418,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -510,7 +510,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -622,7 +622,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BSR", 0x0040000, 0x63, 0, PutPCRel4510 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, @@ -747,7 +747,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BRL", 0x0040000, 0x82, 0, PutPCRel16 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, @@ -756,7 +756,7 @@ static const struct { { "CLI", 0x0000001, 0x58, 0, PutAll }, { "CLV", 0x0000001, 0xb8, 0, PutAll }, { "CMP", 0x0b8f6fc, 0xc0, 0, PutAll }, - { "COP", 0x0000005, 0x02, 6, PutAll }, + { "COP", 0x0800005, 0x02, 6, PutAll }, { "CPA", 0x0b8f6fc, 0xc0, 0, PutAll }, /* == CMP */ { "CPX", 0x0c0000c, 0xe0, 1, PutAll }, { "CPY", 0x0c0000c, 0xc0, 1, PutAll }, @@ -833,7 +833,7 @@ static const struct { { "TYA", 0x0000001, 0x98, 0, PutAll }, { "TYX", 0x0000001, 0xbb, 0, PutAll }, { "WAI", 0x0000001, 0xcb, 0, PutAll }, - { "WDM", 0x0000004, 0x42, 6, PutAll }, + { "WDM", 0x0800004, 0x42, 6, PutAll }, { "XBA", 0x0000001, 0xeb, 0, PutAll }, { "XCE", 0x0000001, 0xfb, 0, PutAll } } @@ -909,7 +909,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BSR", 0x0020000, 0x44, 0, PutPCRel8 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, diff --git a/test/asm/listing/109-brk-signature.s b/test/asm/listing/109-brk-signature.s index b150e088a..e9c23dd63 100644 --- a/test/asm/listing/109-brk-signature.s +++ b/test/asm/listing/109-brk-signature.s @@ -5,32 +5,41 @@ brk ; 1 byte brk 0 ; 2 bytes brk $60 ; 2 bytes +brk #$60 ; 2 bytes .setcpu "6502X" brk brk $60 +brk #$60 .setcpu "6502DTV" brk brk $60 +brk #$60 .setcpu "65SC02" brk brk $60 +brk #$60 .setcpu "65816" brk brk $60 +brk #$60 cop cop $60 +cop #$60 ; WDM is a NOP that gives +2 PC, probably not useful to make its signature byte optional ;wdm wdm $60 +wdm #$60 .setcpu "4510" brk brk $60 +brk #$60 .setcpu "HuC6280" brk brk $60 +brk #$60 diff --git a/test/asm/listing/ref/109-brk-signature.bin-ref b/test/asm/listing/ref/109-brk-signature.bin-ref index 0f46d29fd39f8d79bd403ca35bd6eb6aa7ced41c..0eaa09faa5e113f3fe15fcb8e23837523fe04edc 100644 GIT binary patch literal 46 YcmZQzU|>i9LM#LmQvy?h6A&UK0hde%O8@`> literal 28 TcmZQzU|>i<ModfzPEY{=EA|7Y From e87325033d04955ea871e367f015b7ec508ae481 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 25 Feb 2023 12:39:36 -0500 Subject: [PATCH 117/520] Fix .endmacro in a .define in a macro body --- src/ca65/macro.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 4812b12c4..16d916c88 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -391,6 +391,7 @@ void MacDef (unsigned Style) Macro* M; TokNode* N; int HaveParams; + int DefineActive = 0; /* We expect a macro name here */ if (CurTok.Tok != TOK_IDENT) { @@ -491,8 +492,8 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, only .endmacro is allowed */ - if (CurTok.Tok == TOK_ENDMACRO) { + /* In classic macros, only .endmacro is allowed, but ignore it if it is in a .define */ + if (CurTok.Tok == TOK_ENDMACRO && !DefineActive) { /* Done */ break; } @@ -573,6 +574,13 @@ void MacDef (unsigned Style) } ++M->TokCount; + /* Mark if .define has been read until end of line has been reached */ + if (CurTok.Tok == TOK_DEFINE) { + DefineActive = 1; + } else if (TokIsSep(CurTok.Tok)) { + DefineActive = 0; + } + /* Read the next token */ NextTok (); } @@ -582,7 +590,7 @@ void MacDef (unsigned Style) NextTok (); } - /* Reset the Incomplete flag now that parsing is done */ +/* Reset the Incomplete flag now that parsing is done */ M->Incomplete = 0; Done: From 429e90dffdc326bbb23a332ea3b7b11dc67f8c67 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 25 Feb 2023 12:42:26 -0500 Subject: [PATCH 118/520] Fix .endmacro in a .define in a macro body --- src/ca65/macro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 16d916c88..24cdcad38 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -590,7 +590,7 @@ void MacDef (unsigned Style) NextTok (); } -/* Reset the Incomplete flag now that parsing is done */ + /* Reset the Incomplete flag now that parsing is done */ M->Incomplete = 0; Done: From 0a05f78d47c8a6ea20b97fd570d0f5db054aa387 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 25 Feb 2023 13:38:02 -0500 Subject: [PATCH 119/520] Fix .endmacro not at the start of the line. --- src/ca65/macro.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 24cdcad38..c09b2412f 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -391,7 +391,7 @@ void MacDef (unsigned Style) Macro* M; TokNode* N; int HaveParams; - int DefineActive = 0; + int LastTokWasSep = 0; /* We expect a macro name here */ if (CurTok.Tok != TOK_IDENT) { @@ -493,7 +493,7 @@ void MacDef (unsigned Style) /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { /* In classic macros, only .endmacro is allowed, but ignore it if it is in a .define */ - if (CurTok.Tok == TOK_ENDMACRO && !DefineActive) { + if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ break; } @@ -574,12 +574,8 @@ void MacDef (unsigned Style) } ++M->TokCount; - /* Mark if .define has been read until end of line has been reached */ - if (CurTok.Tok == TOK_DEFINE) { - DefineActive = 1; - } else if (TokIsSep(CurTok.Tok)) { - DefineActive = 0; - } + /* Save if last token was a separator to know if .endmacro is valid */ + LastTokWasSep = TokIsSep(CurTok.Tok); /* Read the next token */ NextTok (); From 4b29615f2bad008cc5a7b015616883a4e837972b Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 25 Feb 2023 13:49:19 -0500 Subject: [PATCH 120/520] Fix .endmacro not at the start of the line. Allow empty macro --- src/ca65/macro.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index c09b2412f..fa90009b5 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -480,6 +480,7 @@ void MacDef (unsigned Style) */ if (Style == MAC_STYLE_CLASSIC) { ConsumeSep (); + LastTokWasSep = 1; } else if (HaveParams) { ConsumeRParen (); } From 7d894fbe04f64aa0917404412cc5b14a1c970818 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 25 Feb 2023 13:52:13 -0500 Subject: [PATCH 121/520] Fix .endmacro not at the start of the line. Fix comment --- src/ca65/macro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index fa90009b5..92a0f6e9a 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -493,7 +493,7 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, only .endmacro is allowed, but ignore it if it is in a .define */ + /* In classic macros, only .endmacro is allowed, but ignore it if it is not at the start of the line */ if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ break; From ffa83c32a4d4cb2f7bd7749aa3e2766c370e60a7 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 26 Feb 2023 20:03:41 +0100 Subject: [PATCH 122/520] clean-up of driver return codes --- Contributing.md | 17 +++++++- libsrc/apple2/emd/a2.auxmem.s | 3 +- libsrc/apple2/joy/a2.stdjoy.s | 5 ++- libsrc/apple2/mou/a2.stdmou.s | 4 +- libsrc/apple2/ser/a2.ssc.s | 43 +++++++++++-------- libsrc/atari/joy/atrmj8.s | 3 +- libsrc/atari/joy/atrstd.s | 3 +- libsrc/atari/mou/atrjoy.s | 5 ++- libsrc/atari/mou/atrst.s | 5 ++- libsrc/atari/mou/atrtt.s | 5 ++- libsrc/atari/ser/atrrdev.s | 35 ++++++++------- libsrc/atari5200/joy/atr5200std.s | 3 +- libsrc/atari7800/joy/atari7800-stdjoy.s | 5 ++- libsrc/atmos/joy/atmos-ijk.s | 7 +-- libsrc/atmos/joy/atmos-pase.s | 3 +- libsrc/atmos/ser/atmos-acia.s | 28 ++++++------ libsrc/atmos/tgi/atmos-228-200-3.s | 3 +- libsrc/c128/emd/c128-efnram.s | 7 +-- libsrc/c128/emd/c128-georam.s | 8 ++-- libsrc/c128/emd/c128-ifnram.s | 7 +-- libsrc/c128/emd/c128-ram.s | 3 +- libsrc/c128/emd/c128-ram2.s | 3 +- libsrc/c128/emd/c128-ramcart.s | 9 ++-- libsrc/c128/emd/c128-reu.s | 5 ++- libsrc/c128/emd/c128-vdc.s | 5 ++- libsrc/c128/joy/c128-ptvjoy.s | 5 ++- libsrc/c128/joy/c128-stdjoy.s | 5 ++- libsrc/c128/mou/c128-1351.s | 5 ++- libsrc/c128/mou/c128-inkwell.s | 3 +- libsrc/c128/mou/c128-joy.s | 5 ++- libsrc/c128/mou/c128-pot.s | 5 ++- libsrc/c128/ser/c128-swlink.s | 39 +++++++++-------- libsrc/c16/emd/c16-ram.s | 9 ++-- libsrc/c64/emd/c64-65816.s | 9 ++-- libsrc/c64/emd/c64-c256k.s | 9 ++-- libsrc/c64/emd/c64-dqbb.s | 9 ++-- libsrc/c64/emd/c64-georam.s | 9 ++-- libsrc/c64/emd/c64-isepic.s | 9 ++-- libsrc/c64/emd/c64-kerberos.s | 9 ++-- libsrc/c64/emd/c64-ram.s | 5 ++- libsrc/c64/emd/c64-ramcart.s | 8 ++-- libsrc/c64/emd/c64-reu.s | 6 ++- libsrc/c64/emd/c64-vdc.s | 9 ++-- libsrc/c64/emd/dtv-himem.s | 9 ++-- libsrc/c64/joy/c64-hitjoy.s | 5 ++- libsrc/c64/joy/c64-numpad.s | 10 +++-- libsrc/c64/joy/c64-ptvjoy.s | 5 ++- libsrc/c64/joy/c64-stdjoy.s | 5 ++- libsrc/c64/mou/c64-1351.s | 9 ++-- libsrc/c64/mou/c64-inkwell.s | 5 ++- libsrc/c64/mou/c64-joy.s | 9 ++-- libsrc/c64/mou/c64-pot.s | 9 ++-- libsrc/c64/ser/c64-swlink.s | 39 +++++++++-------- libsrc/cbm510/emd/cbm510-ram.s | 5 ++- libsrc/cbm510/joy/cbm510-std.s | 5 ++- libsrc/cbm510/mou/cbm510-inkwl.s | 5 ++- libsrc/cbm510/mou/cbm510-joy.s | 7 +-- libsrc/cbm510/ser/cbm510-std.s | 34 ++++++++------- libsrc/cbm610/emd/cbm610-ram.s | 5 ++- libsrc/cbm610/ser/cbm610-std.s | 31 +++++++------ libsrc/creativision/joy/creativision-stdjoy.s | 3 +- libsrc/cx16/joy/cx16-std.s | 5 ++- libsrc/cx16/mou/cx16-std.s | 7 +-- libsrc/gamate/joy/gamate-stdjoy.s | 5 ++- libsrc/geos-cbm/emd/geos-vdc.s | 5 ++- libsrc/geos-cbm/joy/geos-stdjoy.s | 5 ++- libsrc/joystick/joy_unload.s | 7 ++- libsrc/lynx/joy/lynx-stdjoy.s | 5 ++- libsrc/lynx/ser/lynx-comlynx.s | 33 +++++++------- libsrc/mouse/mouse_unload.s | 7 ++- libsrc/nes/joy/nes-stdjoy.s | 3 +- libsrc/pce/joy/pce-stdjoy.s | 5 ++- libsrc/pet/joy/pet-ptvjoy.s | 5 ++- libsrc/plus4/joy/plus4-stdjoy.s | 5 ++- libsrc/plus4/ser/plus4-stdser.s | 34 ++++++++------- libsrc/serial/ser_unload.s | 10 ++--- libsrc/supervision/joy/supervision-stdjoy.s | 5 ++- libsrc/telestrat/joy/telestrat.s | 5 ++- libsrc/vic20/emd/vic20-georam.s | 8 ++-- libsrc/vic20/emd/vic20-rama.s | 9 ++-- libsrc/vic20/joy/vic20-ptvjoy.s | 5 ++- libsrc/vic20/joy/vic20-stdjoy.s | 5 ++- 82 files changed, 438 insertions(+), 322 deletions(-) diff --git a/Contributing.md b/Contributing.md index 74e6ead17..eb26e920b 100644 --- a/Contributing.md +++ b/Contributing.md @@ -134,7 +134,22 @@ You can refer to Annex B of the ISO C99 standard ([here](https://www.open-std.or * Hexadecimal number constants should be used except where decimal or binary numbers make much more sense in that constant's context. * Hexadecimal letters should be upper-case. * When you set two registers or two memory locations to an immediate 16-bit zero, you should use the expressions ```#<$0000``` and ```#>$0000``` (they make it obvious where you are putting the lower and upper bytes). -* If a function is declared to return a char-sized value, it actually must return an integer-sized value. (When cc65 promotes a returned value, it sometimes assumes that the value already is an integer.) +* If a function is declared to return a char-sized value, it actually must return an integer-sized value. (When cc65 promotes a returned value, it sometimes assumes that the value already is an integer.) This must be done in one of the following ways: +<pre> + lda #RETURN_VALUE + ldx #0 ; return value is char +</pre> +or, if the value is 0, you can use: +<pre> + lda #RETURN_VALUE + .assert RETURN_VALUE = 0 + tax +</pre> +sometimes jumping to return0 could save a byte: +<pre> + .assert RETURN_VALUE = 0 + jmp return 0 +</pre> * Functions, that are intended for a platform's system library, should be optimized as much as possible. * Sometimes, there must be a trade-off between size and speed. If you think that a library function won't be used often, then you should make it small. Otherwise, you should make it fast. * Comments that are put on the right side of instructions must be aligned (start in the same character columns). diff --git a/libsrc/apple2/emd/a2.auxmem.s b/libsrc/apple2/emd/a2.auxmem.s index 5ef2564b1..c582e9eec 100644 --- a/libsrc/apple2/emd/a2.auxmem.s +++ b/libsrc/apple2/emd/a2.auxmem.s @@ -73,7 +73,8 @@ INSTALL: and #$f0 cmp #$80 bne @L1 - lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + txa rts @L1: lda #EM_ERR_NO_DEVICE ; rts diff --git a/libsrc/apple2/joy/a2.stdjoy.s b/libsrc/apple2/joy/a2.stdjoy.s index 558013910..11be52eb4 100644 --- a/libsrc/apple2/joy/a2.stdjoy.s +++ b/libsrc/apple2/joy/a2.stdjoy.s @@ -71,8 +71,9 @@ INSTALL: stx gettype+2 gettype:jsr $0000 sta ostype - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; Fall through ; UNINSTALL routine. Is called before the driver is removed from memory. diff --git a/libsrc/apple2/mou/a2.stdmou.s b/libsrc/apple2/mou/a2.stdmou.s index dfc69a942..c54c09d34 100644 --- a/libsrc/apple2/mou/a2.stdmou.s +++ b/libsrc/apple2/mou/a2.stdmou.s @@ -133,8 +133,8 @@ next: inc ptr1+1 bcc :+ ; Mouse firmware not found - lda #<MOUSE_ERR_NO_DEVICE - ldx #>MOUSE_ERR_NO_DEVICE + lda #MOUSE_ERR_NO_DEVICE + ldx #0 ; return value is char rts ; Check Pascal 1.1 Firmware Protocol ID bytes diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index d49bf3526..6ad9c3825 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -168,8 +168,9 @@ SER_CLOSE: sta ACIA_CMD,x ; Done, return an error code -: lda #<SER_ERR_OK - tax ; A is zero +: lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax stx Index ; Mark port as closed rts @@ -256,23 +257,24 @@ SER_OPEN: ; Done stx Index ; Mark port as open - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Device (hardware) not found -NoDevice:lda #<SER_ERR_NO_DEVICE - ldx #>SER_ERR_NO_DEVICE +NoDevice:lda #SER_ERR_NO_DEVICE + ldx #0 ; return value is char rts ; Invalid parameter -InvParam:lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED +InvParam:lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available -InvBaud:lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL +InvBaud:lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -292,8 +294,8 @@ SER_GET: : lda RecvFreeCnt ; (25) cmp #$FF bne :+ - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -336,8 +338,8 @@ SER_PUT: ; Put byte into send buffer & send : ldy SendFreeCnt bne :+ - lda #<SER_ERR_OVERFLOW - ldx #>SER_ERR_OVERFLOW + lda #SER_ERR_OVERFLOW + ldx #0 ; return value is char rts : ldy SendTail @@ -346,7 +348,8 @@ SER_PUT: dec SendFreeCnt lda #$FF ; TryHard = true jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -359,7 +362,8 @@ SER_STATUS: lda ACIA_STATUS,x ldx #$00 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -379,11 +383,12 @@ SER_IOCTL: bcs :+ stx Slot - tax ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts -: lda #<SER_ERR_INV_IOCTL - ldx #>SER_ERR_INV_IOCTL +: lda #SER_ERR_INV_IOCTL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/atari/joy/atrmj8.s b/libsrc/atari/joy/atrmj8.s index 3a26c381d..daf11651d 100644 --- a/libsrc/atari/joy/atrmj8.s +++ b/libsrc/atari/joy/atrmj8.s @@ -69,7 +69,8 @@ INSTALL: lda #$34 sta PACTL lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari/joy/atrstd.s b/libsrc/atari/joy/atrstd.s index fd1d99d31..1a124192f 100644 --- a/libsrc/atari/joy/atrstd.s +++ b/libsrc/atari/joy/atrstd.s @@ -62,7 +62,8 @@ JOY_COUNT = 4 ; Number of joysticks we support INSTALL: lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s index 3ea428576..a93c7de13 100644 --- a/libsrc/atari/mou/atrjoy.s +++ b/libsrc/atari/mou/atrjoy.s @@ -137,9 +137,10 @@ INSTALL: ldx YPos+1 jsr CMOVEY -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s index 5d8a5cd12..626b7a8f7 100644 --- a/libsrc/atari/mou/atrst.s +++ b/libsrc/atari/mou/atrst.s @@ -268,9 +268,10 @@ INSTALL: and #$0f sta old_porta_vbi -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s index 61963aa61..b1e53935e 100644 --- a/libsrc/atari/mou/atrtt.s +++ b/libsrc/atari/mou/atrtt.s @@ -132,9 +132,10 @@ INSTALL: ldx YPos+1 jsr CMOVEY -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/atari/ser/atrrdev.s b/libsrc/atari/ser/atrrdev.s index 3a7bc21c2..1d2b98e8d 100644 --- a/libsrc/atari/ser/atrrdev.s +++ b/libsrc/atari/ser/atrrdev.s @@ -135,8 +135,8 @@ my_CIOV: .code invbaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char openerr: rts @@ -229,8 +229,9 @@ SER_OPEN: jsr my_CIOV bmi cioerr - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts inverr: jmp my___inviocb @@ -240,8 +241,8 @@ cioerr: jsr my_fddecusage ; decrement usage counter of fd as open failed init_err: - ldx #0 lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ;---- open the device @@ -313,8 +314,9 @@ SER_CLOSE: stx rshand+1 inx stx cm_run -@done: lda #<SER_ERR_OK - ldx #>SER_ERR_OK +@done: lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -365,16 +367,16 @@ SER_GET: rts @nix_da:lda #SER_ERR_NO_DATA - ldx #0 + ldx #0 ; return value is char rts ser_error: lda #SER_ERR_OVERFLOW ; there is no large selection of serial error codes... :-/ - ldx #0 + ldx #0 ; return value is char rts ni_err: lda #SER_ERR_NOT_OPEN - ldx #0 + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -427,8 +429,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -456,8 +458,8 @@ search: lda HATABS,y ; get device name ; R: device not found, return error - lda #<SER_ERR_NO_DEVICE - ldx #0 + lda #SER_ERR_NO_DEVICE + ldx #0 ; return value is char rts ; R: device found, initialize jump table into main program @@ -554,8 +556,9 @@ found: lda ptr3 pla sta ptr3 - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts diff --git a/libsrc/atari5200/joy/atr5200std.s b/libsrc/atari5200/joy/atr5200std.s index fb7946bea..980428d13 100644 --- a/libsrc/atari5200/joy/atr5200std.s +++ b/libsrc/atari5200/joy/atr5200std.s @@ -47,7 +47,8 @@ INSTALL: lda #$04 ; enable POT input from the joystick ports, see section "GTIA" in sta CONSOL ; http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari7800/joy/atari7800-stdjoy.s b/libsrc/atari7800/joy/atari7800-stdjoy.s index d76e1d105..59f656ada 100644 --- a/libsrc/atari7800/joy/atari7800-stdjoy.s +++ b/libsrc/atari7800/joy/atari7800-stdjoy.s @@ -62,8 +62,9 @@ INSTALL: sty SWCHB ; enable 2-button 7800 controller 2: pull pin 6 (INPT4) high reset: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atmos/joy/atmos-ijk.s b/libsrc/atmos/joy/atmos-ijk.s index 6e75a3e0b..c2bdd67ab 100644 --- a/libsrc/atmos/joy/atmos-ijk.s +++ b/libsrc/atmos/joy/atmos-ijk.s @@ -55,11 +55,12 @@ INSTALL: lda VIA::PRA and #%00100000 bne ijkPresent - lda #<JOY_ERR_NO_DEVICE + lda #JOY_ERR_NO_DEVICE .byte $2C ; Skip next opcode ijkPresent: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atmos/joy/atmos-pase.s b/libsrc/atmos/joy/atmos-pase.s index fd64901c9..d9982cafe 100644 --- a/libsrc/atmos/joy/atmos-pase.s +++ b/libsrc/atmos/joy/atmos-pase.s @@ -58,7 +58,8 @@ temp2: .byte $00 INSTALL: lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index f84b66a0a..f679125d1 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -141,8 +141,9 @@ SER_CLOSE: sta ACIA::CMD,x ; Done, return an error code -: lda #<SER_ERR_OK - tax ; A is zero +: lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax stx Index ; Mark port as closed rts @@ -205,8 +206,9 @@ SER_OPEN: ; Done stx Index ; Mark port as open - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter @@ -235,8 +237,8 @@ SER_GET: : lda RecvFreeCnt ; (25) cmp #$FF bne :+ - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -277,8 +279,8 @@ SER_PUT: ; Put byte into send buffer & send : ldy SendFreeCnt bne :+ - lda #<SER_ERR_OVERFLOW - ldx #>SER_ERR_OVERFLOW + lda #SER_ERR_OVERFLOW + ldx #0 ; return value is char rts : ldy SendTail @@ -287,7 +289,8 @@ SER_PUT: dec SendFreeCnt lda #$FF ; TryHard = true jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -299,7 +302,8 @@ SER_STATUS: lda ACIA::STATUS ldx #$00 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -308,8 +312,8 @@ SER_STATUS: ; Must return an SER_ERR_xx code in a/x. SER_IOCTL: - lda #<SER_ERR_INV_IOCTL - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/atmos/tgi/atmos-228-200-3.s b/libsrc/atmos/tgi/atmos-228-200-3.s index 98d2cef96..466f6922a 100644 --- a/libsrc/atmos/tgi/atmos-228-200-3.s +++ b/libsrc/atmos/tgi/atmos-228-200-3.s @@ -215,7 +215,8 @@ SETPALETTE: jsr PAPER ldy #1 jsr flipcolor - dey ; TGI_ERR_OK + .assert TGI_ERR_OK = 0, error + dey sty ERROR sty PARAM1+1 jmp INK diff --git a/libsrc/c128/emd/c128-efnram.s b/libsrc/c128/emd/c128-efnram.s index 788c73e0f..909c90048 100644 --- a/libsrc/c128/emd/c128-efnram.s +++ b/libsrc/c128/emd/c128-efnram.s @@ -87,16 +87,17 @@ INSTALL: cli cmp tmp1 beq @ram_present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @ram_present: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-georam.s b/libsrc/c128/emd/c128-georam.s index 7511c6841..ecf12f6cd 100644 --- a/libsrc/c128/emd/c128-georam.s +++ b/libsrc/c128/emd/c128-georam.s @@ -120,16 +120,16 @@ INSTALL: bne @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @setok: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts check: diff --git a/libsrc/c128/emd/c128-ifnram.s b/libsrc/c128/emd/c128-ifnram.s index 01a4fdf8e..c51b775b2 100644 --- a/libsrc/c128/emd/c128-ifnram.s +++ b/libsrc/c128/emd/c128-ifnram.s @@ -87,16 +87,17 @@ INSTALL: cli cmp tmp1 beq @ram_present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @ram_present: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-ram.s b/libsrc/c128/emd/c128-ram.s index 3fc52c9cc..0ae504b84 100644 --- a/libsrc/c128/emd/c128-ram.s +++ b/libsrc/c128/emd/c128-ram.s @@ -68,8 +68,9 @@ INSTALL: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-ram2.s b/libsrc/c128/emd/c128-ram2.s index 7d2703fa5..92e72700a 100644 --- a/libsrc/c128/emd/c128-ram2.s +++ b/libsrc/c128/emd/c128-ram2.s @@ -107,8 +107,9 @@ INSTALL: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-ramcart.s b/libsrc/c128/emd/c128-ramcart.s index e72d053ac..c58c1cd1b 100644 --- a/libsrc/c128/emd/c128-ramcart.s +++ b/libsrc/c128/emd/c128-ramcart.s @@ -97,13 +97,14 @@ INSTALL: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @notpresent: @readonly: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-reu.s b/libsrc/c128/emd/c128-reu.s index 84e7cb695..8228a0517 100644 --- a/libsrc/c128/emd/c128-reu.s +++ b/libsrc/c128/emd/c128-reu.s @@ -126,8 +126,9 @@ size_found: pagecount_ok: stx pagecount sty pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts ; common REU setup for size check diff --git a/libsrc/c128/emd/c128-vdc.s b/libsrc/c128/emd/c128-vdc.s index accb82154..8d0b77fd2 100644 --- a/libsrc/c128/emd/c128-vdc.s +++ b/libsrc/c128/emd/c128-vdc.s @@ -121,8 +121,9 @@ INSTALL: lda vdc_cset_save jsr vdcputreg @keep64kBit: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts test64k: diff --git a/libsrc/c128/joy/c128-ptvjoy.s b/libsrc/c128/joy/c128-ptvjoy.s index 180f7667d..0a1c53587 100644 --- a/libsrc/c128/joy/c128-ptvjoy.s +++ b/libsrc/c128/joy/c128-ptvjoy.s @@ -53,8 +53,9 @@ JOY_COUNT = 4 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/joy/c128-stdjoy.s b/libsrc/c128/joy/c128-stdjoy.s index bf2e2fea7..ee04374ee 100644 --- a/libsrc/c128/joy/c128-stdjoy.s +++ b/libsrc/c128/joy/c128-stdjoy.s @@ -57,8 +57,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/mou/c128-1351.s b/libsrc/c128/mou/c128-1351.s index 79ccbe0de..76e28d9f7 100644 --- a/libsrc/c128/mou/c128-1351.s +++ b/libsrc/c128/mou/c128-1351.s @@ -194,9 +194,10 @@ INSTALL: sta (ptr3),y cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/c128/mou/c128-inkwell.s b/libsrc/c128/mou/c128-inkwell.s index b8e71bbb1..2aac7d32d 100644 --- a/libsrc/c128/mou/c128-inkwell.s +++ b/libsrc/c128/mou/c128-inkwell.s @@ -228,9 +228,10 @@ INSTALL: jsr MoveX cli -; Done, return zero. +; Done lda #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error tax rts diff --git a/libsrc/c128/mou/c128-joy.s b/libsrc/c128/mou/c128-joy.s index 065674dc0..d809db526 100644 --- a/libsrc/c128/mou/c128-joy.s +++ b/libsrc/c128/mou/c128-joy.s @@ -195,9 +195,10 @@ INSTALL: sta (ptr3),y cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/c128/mou/c128-pot.s b/libsrc/c128/mou/c128-pot.s index e582d64fb..1cbe4aa18 100644 --- a/libsrc/c128/mou/c128-pot.s +++ b/libsrc/c128/mou/c128-pot.s @@ -195,9 +195,10 @@ INSTALL: sta (ptr3),y cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s index 3337e2668..7d36eb5bc 100644 --- a/libsrc/c128/ser/c128-swlink.s +++ b/libsrc/c128/ser/c128-swlink.s @@ -187,8 +187,9 @@ SetNMI: sta NMIVec ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -264,22 +265,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -300,8 +302,9 @@ SER_CLOSE: ; Return OK - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -322,8 +325,8 @@ SER_GET: @L1: lda RecvFreeCnt ; (25) cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -370,7 +373,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -379,7 +382,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -392,7 +396,8 @@ SER_STATUS: lda ACIA_STATUS ldx #0 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -402,8 +407,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c16/emd/c16-ram.s b/libsrc/c16/emd/c16-ram.s index a8083aca4..937019974 100644 --- a/libsrc/c16/emd/c16-ram.s +++ b/libsrc/c16/emd/c16-ram.s @@ -77,12 +77,13 @@ INSTALL: ldx #$FF stx curpage ; Invalidate the current page - inx ; X = 0 - txa ; A = X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa rts -nomem: ldx #>EM_ERR_NO_DEVICE - lda #<EM_ERR_NO_DEVICE +nomem: ldx #EM_ERR_NO_DEVICE + lda #0 ; return value is char ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-65816.s b/libsrc/c64/emd/c64-65816.s index 39f323d28..ce9491fbe 100644 --- a/libsrc/c64/emd/c64-65816.s +++ b/libsrc/c64/emd/c64-65816.s @@ -120,13 +120,14 @@ INSTALL: dex @noextradex: stx bankcount - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @not_present: cli - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/emd/c64-c256k.s b/libsrc/c64/emd/c64-c256k.s index 79706e8fb..5a4bc54c3 100644 --- a/libsrc/c64/emd/c64-c256k.s +++ b/libsrc/c64/emd/c64-c256k.s @@ -158,13 +158,14 @@ INSTALL: jsr restore_data cpy #$01 beq @present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @present: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-dqbb.s b/libsrc/c64/emd/c64-dqbb.s index 986c5939d..6a63b3baa 100644 --- a/libsrc/c64/emd/c64-dqbb.s +++ b/libsrc/c64/emd/c64-dqbb.s @@ -147,13 +147,14 @@ INSTALL: jsr restore_data cpy #$01 beq @present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @present: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-georam.s b/libsrc/c64/emd/c64-georam.s index 97f1a7cc4..0116fe8ea 100644 --- a/libsrc/c64/emd/c64-georam.s +++ b/libsrc/c64/emd/c64-georam.s @@ -121,16 +121,17 @@ INSTALL: bne @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + .assert EM_ERR_OK = 0, error + tax rts @setok: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts check: diff --git a/libsrc/c64/emd/c64-isepic.s b/libsrc/c64/emd/c64-isepic.s index 3764443e2..2b7949757 100644 --- a/libsrc/c64/emd/c64-isepic.s +++ b/libsrc/c64/emd/c64-isepic.s @@ -76,13 +76,14 @@ INSTALL: beq @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @setok: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-kerberos.s b/libsrc/c64/emd/c64-kerberos.s index 30183362f..20be4e409 100644 --- a/libsrc/c64/emd/c64-kerberos.s +++ b/libsrc/c64/emd/c64-kerberos.s @@ -82,13 +82,14 @@ INSTALL: cmp #$AA bne @notpresent - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; use rts from UNINSTALL below ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-ram.s b/libsrc/c64/emd/c64-ram.s index 5355b552d..cdd7b8965 100644 --- a/libsrc/c64/emd/c64-ram.s +++ b/libsrc/c64/emd/c64-ram.s @@ -65,8 +65,9 @@ window: .res 256 ; Memory "window" INSTALL: ldx #$FF stx curpage ; Invalidate the current page - inx ; X = 0 - txa ; A = X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-ramcart.s b/libsrc/c64/emd/c64-ramcart.s index 8998bb6d6..a99f25b4f 100644 --- a/libsrc/c64/emd/c64-ramcart.s +++ b/libsrc/c64/emd/c64-ramcart.s @@ -98,13 +98,13 @@ INSTALL: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @notpresent: @readonly: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-reu.s b/libsrc/c64/emd/c64-reu.s index 07ac1fbed..832e66f51 100644 --- a/libsrc/c64/emd/c64-reu.s +++ b/libsrc/c64/emd/c64-reu.s @@ -127,8 +127,9 @@ size_found: pagecount_ok: stx pagecount sty pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts ; common REU setup for size check @@ -152,6 +153,7 @@ reu_size_check_common: nodevice: lda #EM_ERR_NO_DEVICE + .assert EM_ERR_OK = 0, error ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-vdc.s b/libsrc/c64/emd/c64-vdc.s index 2448f09d4..60fbccbbf 100644 --- a/libsrc/c64/emd/c64-vdc.s +++ b/libsrc/c64/emd/c64-vdc.s @@ -87,8 +87,8 @@ INSTALL: bne @L0 iny bne @L0 - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE +; ldx #0 ; return value is char rts @present: @@ -131,8 +131,9 @@ INSTALL: sta pagecount stx pagecount+1 @endok: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts test64k: diff --git a/libsrc/c64/emd/dtv-himem.s b/libsrc/c64/emd/dtv-himem.s index 6dde874f7..4d19b19d5 100644 --- a/libsrc/c64/emd/dtv-himem.s +++ b/libsrc/c64/emd/dtv-himem.s @@ -93,15 +93,16 @@ INSTALL: ; DTV not found - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @present: ldx #$FF stx curpage+1 ; Invalidate curpage - inx ; X = 0 - txa ; A/X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/joy/c64-hitjoy.s b/libsrc/c64/joy/c64-hitjoy.s index 3b4a0b909..a9d454fd0 100644 --- a/libsrc/c64/joy/c64-hitjoy.s +++ b/libsrc/c64/joy/c64-hitjoy.s @@ -59,8 +59,9 @@ temp4: .byte 0 ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/joy/c64-numpad.s b/libsrc/c64/joy/c64-numpad.s index 0ccdc4fcd..f6839d6cf 100644 --- a/libsrc/c64/joy/c64-numpad.s +++ b/libsrc/c64/joy/c64-numpad.s @@ -100,12 +100,14 @@ masktable: ; INSTALL: - lda #JOY_ERR_OK ; Assume we have a joystick - ldx VIC_CLK_128 ; Test for a C128 - cpx #$FF + lda #JOY_ERR_OK ; Assume we have a "joystick" + .assert JOY_ERR_OK = 0, error + tax ; Set high byte + ldy VIC_CLK_128 ; Test for a C128 + cpy #$FF bne @C128 ; Jump if we have one lda #JOY_ERR_NO_DEVICE ; No C128 -> no numpad -@C128: ldx #0 ; Set high byte +@C128: ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/joy/c64-ptvjoy.s b/libsrc/c64/joy/c64-ptvjoy.s index a772fb5f6..30466b2c2 100644 --- a/libsrc/c64/joy/c64-ptvjoy.s +++ b/libsrc/c64/joy/c64-ptvjoy.s @@ -52,8 +52,9 @@ JOY_COUNT = 4 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/joy/c64-stdjoy.s b/libsrc/c64/joy/c64-stdjoy.s index d11093fba..511032507 100644 --- a/libsrc/c64/joy/c64-stdjoy.s +++ b/libsrc/c64/joy/c64-stdjoy.s @@ -56,8 +56,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/mou/c64-1351.s b/libsrc/c64/mou/c64-1351.s index a7d042c7b..ce0f18803 100644 --- a/libsrc/c64/mou/c64-1351.s +++ b/libsrc/c64/mou/c64-1351.s @@ -152,9 +152,10 @@ INSTALL: jsr CMOVEY cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -307,8 +308,8 @@ INFO: jsr POS ; Must return an error code in a/x. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/mou/c64-inkwell.s b/libsrc/c64/mou/c64-inkwell.s index 9c876a7c8..d2f14a6f0 100644 --- a/libsrc/c64/mou/c64-inkwell.s +++ b/libsrc/c64/mou/c64-inkwell.s @@ -168,6 +168,7 @@ INSTALL: ; Done, return zero. lda #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error tax rts @@ -319,8 +320,8 @@ INFO: jsr POS ; Must return an error code in .XA. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/mou/c64-joy.s b/libsrc/c64/mou/c64-joy.s index f2a501000..5ee1b4f84 100644 --- a/libsrc/c64/mou/c64-joy.s +++ b/libsrc/c64/mou/c64-joy.s @@ -156,9 +156,10 @@ INSTALL: jsr CMOVEY cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -312,8 +313,8 @@ INFO: jsr POS ; Must return an error code in a/x. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/mou/c64-pot.s b/libsrc/c64/mou/c64-pot.s index 102ca351c..9bdf24f62 100644 --- a/libsrc/c64/mou/c64-pot.s +++ b/libsrc/c64/mou/c64-pot.s @@ -139,9 +139,10 @@ INSTALL: jsr CMOVEY cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -297,8 +298,8 @@ INFO: jsr POS ; Must return an error code in a/x. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s index 597cf1dd6..81c9916a6 100644 --- a/libsrc/c64/ser/c64-swlink.s +++ b/libsrc/c64/ser/c64-swlink.s @@ -161,8 +161,9 @@ SetNMI: sta NMIVec ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -238,22 +239,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -274,8 +276,9 @@ SER_CLOSE: ; Return OK - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -296,8 +299,8 @@ SER_GET: @L1: lda RecvFreeCnt ; (25) cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -344,7 +347,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -353,7 +356,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -366,7 +370,8 @@ SER_STATUS: lda ACIA_STATUS ldx #0 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -376,8 +381,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm510/emd/cbm510-ram.s b/libsrc/cbm510/emd/cbm510-ram.s index f724c7360..6cc319b7f 100644 --- a/libsrc/cbm510/emd/cbm510-ram.s +++ b/libsrc/cbm510/emd/cbm510-ram.s @@ -81,8 +81,9 @@ INSTALL: sbc #$00 sta pagecount -@L1: lda #<EM_ERR_OK - ldx #>EM_ERR_OK +@L1: lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cbm510/joy/cbm510-std.s b/libsrc/cbm510/joy/cbm510-std.s index 4e47fc1a0..f7cbb2cdc 100644 --- a/libsrc/cbm510/joy/cbm510-std.s +++ b/libsrc/cbm510/joy/cbm510-std.s @@ -57,8 +57,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cbm510/mou/cbm510-inkwl.s b/libsrc/cbm510/mou/cbm510-inkwl.s index 91bc52fcd..ea6d95934 100644 --- a/libsrc/cbm510/mou/cbm510-inkwl.s +++ b/libsrc/cbm510/mou/cbm510-inkwl.s @@ -175,6 +175,7 @@ INSTALL: ; Done, return zero. lda #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error tax rts @@ -331,8 +332,8 @@ INFO: jsr POS ; Must return an error code in .XA. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm510/mou/cbm510-joy.s b/libsrc/cbm510/mou/cbm510-joy.s index 8aa3a778e..4daa49272 100644 --- a/libsrc/cbm510/mou/cbm510-joy.s +++ b/libsrc/cbm510/mou/cbm510-joy.s @@ -140,7 +140,8 @@ INSTALL: ; Done, return zero. - ldx #>MOUSE_ERR_OK + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -315,8 +316,8 @@ POS: ldy #MOUSE_POS::XCOORD ; Structure offset ; Must return an error code in .XA. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm510/ser/cbm510-std.s b/libsrc/cbm510/ser/cbm510-std.s index 64f613cd5..cc58c1233 100644 --- a/libsrc/cbm510/ser/cbm510-std.s +++ b/libsrc/cbm510/ser/cbm510-std.s @@ -148,8 +148,9 @@ SER_CLOSE: ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -217,22 +218,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -253,8 +255,8 @@ SER_GET: @L1: lda RecvFreeCnt cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -301,7 +303,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -310,7 +312,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -328,7 +331,8 @@ SER_STATUS: sta (ptr1,x) lda IndReg sta ExecReg - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -338,8 +342,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm610/emd/cbm610-ram.s b/libsrc/cbm610/emd/cbm610-ram.s index 5c67df7a4..5aa43b0c2 100644 --- a/libsrc/cbm610/emd/cbm610-ram.s +++ b/libsrc/cbm610/emd/cbm610-ram.s @@ -81,8 +81,9 @@ INSTALL: sbc #$00 sta pagecount -@L1: lda #<EM_ERR_OK - ldx #>EM_ERR_OK +@L1: lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cbm610/ser/cbm610-std.s b/libsrc/cbm610/ser/cbm610-std.s index 7cdb285bd..f7ddde935 100644 --- a/libsrc/cbm610/ser/cbm610-std.s +++ b/libsrc/cbm610/ser/cbm610-std.s @@ -149,8 +149,9 @@ SER_CLOSE: ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -218,22 +219,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -254,8 +256,8 @@ SER_GET: @L1: lda RecvFreeCnt cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -302,7 +304,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -311,7 +313,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -339,8 +342,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/creativision/joy/creativision-stdjoy.s b/libsrc/creativision/joy/creativision-stdjoy.s index 73b0c249f..43f9a2b40 100644 --- a/libsrc/creativision/joy/creativision-stdjoy.s +++ b/libsrc/creativision/joy/creativision-stdjoy.s @@ -59,7 +59,8 @@ JOY_RIGHT = $08 ; INSTALL: lda #JOY_ERR_OK - ldx #>$0000 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Fall through ; ------------------------------------------------------------------------ diff --git a/libsrc/cx16/joy/cx16-std.s b/libsrc/cx16/joy/cx16-std.s index a40fcb061..5def55511 100644 --- a/libsrc/cx16/joy/cx16-std.s +++ b/libsrc/cx16/joy/cx16-std.s @@ -55,8 +55,9 @@ JOY_COUNT = $05 ; Number of joysticks we support ; Must return a JOY_ERR_xx code in .XA . INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cx16/mou/cx16-std.s b/libsrc/cx16/mou/cx16-std.s index 3af7d2eb3..f211815de 100644 --- a/libsrc/cx16/mou/cx16-std.s +++ b/libsrc/cx16/mou/cx16-std.s @@ -139,7 +139,8 @@ INSTALL: ; Done, return zero - ldx #>MOUSE_ERR_OK + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -300,8 +301,8 @@ INFO: jsr BUTTONS ; Will not touch ptr1 ; specific data in ptr1, and the ioctl code in A. ; Must return an error code in .XA . -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char ; rts ; Fall through ;---------------------------------------------------------------------------- diff --git a/libsrc/gamate/joy/gamate-stdjoy.s b/libsrc/gamate/joy/gamate-stdjoy.s index 8f927cdf5..514f92db4 100644 --- a/libsrc/gamate/joy/gamate-stdjoy.s +++ b/libsrc/gamate/joy/gamate-stdjoy.s @@ -47,8 +47,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/geos-cbm/emd/geos-vdc.s b/libsrc/geos-cbm/emd/geos-vdc.s index 27316e1a0..2e7d19c03 100644 --- a/libsrc/geos-cbm/emd/geos-vdc.s +++ b/libsrc/geos-cbm/emd/geos-vdc.s @@ -125,8 +125,9 @@ INSTALL: pla sta $01 plp - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts test64k: diff --git a/libsrc/geos-cbm/joy/geos-stdjoy.s b/libsrc/geos-cbm/joy/geos-stdjoy.s index 2787cb594..a3fd4ffc8 100644 --- a/libsrc/geos-cbm/joy/geos-stdjoy.s +++ b/libsrc/geos-cbm/joy/geos-stdjoy.s @@ -53,8 +53,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/joystick/joy_unload.s b/libsrc/joystick/joy_unload.s index 25d54ff02..f52b7a2c1 100644 --- a/libsrc/joystick/joy_unload.s +++ b/libsrc/joystick/joy_unload.s @@ -10,7 +10,7 @@ .include "modload.inc" .import joy_clear_ptr - .import return0 + .import return0, return1 @@ -31,7 +31,6 @@ _joy_unload: jmp return0 ; Return JOY_ERR_OK no_driver: - tax ; X = 0 pla ; Remove pushed junk - lda #JOY_ERR_NO_DRIVER - rts + .assert JOY_ERR_NO_DRIVER = 1, error + jmp return1 ; Return JOY_ERR_NO_DRIVER diff --git a/libsrc/lynx/joy/lynx-stdjoy.s b/libsrc/lynx/joy/lynx-stdjoy.s index c81a97dbf..45eb8ab4e 100644 --- a/libsrc/lynx/joy/lynx-stdjoy.s +++ b/libsrc/lynx/joy/lynx-stdjoy.s @@ -58,8 +58,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index ded862eaa..8aa3c838e 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -75,8 +75,9 @@ SER_UNINSTALL: SER_CLOSE: ; Disable interrupts ; Done, return an error code - lda #<SER_ERR_OK - ldx #>SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -190,8 +191,8 @@ SER_OPEN: cmp #SER_BAUD_134_5 beq setbaudrate - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts setprescaler: stx TIM4CTLA @@ -238,12 +239,13 @@ checkhs: lda contrl ora #RxIntEnable|ResetErr sta SERCTL - lda #<SER_ERR_OK - ldx #>SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts invparameter: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -255,8 +257,8 @@ SER_GET: lda RxPtrIn cmp RxPtrOut bne GetByte - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts GetByte: ldy RxPtrOut @@ -277,8 +279,8 @@ SER_PUT: ina cmp TxPtrOut bne PutByte - lda #<SER_ERR_OVERFLOW - ldx #>SER_ERR_OVERFLOW + lda #SER_ERR_OVERFLOW + ldx #0 ; return value is char rts PutByte: ldy TxPtrIn @@ -296,7 +298,8 @@ PutByte: sta TxDone plp @L1: - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -317,8 +320,8 @@ SER_STATUS: ; Must return an SER_ERR_xx code in a/x. SER_IOCTL: - lda #<SER_ERR_INV_IOCTL - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/mouse/mouse_unload.s b/libsrc/mouse/mouse_unload.s index 8c9018484..ecd7846ab 100644 --- a/libsrc/mouse/mouse_unload.s +++ b/libsrc/mouse/mouse_unload.s @@ -8,7 +8,7 @@ .include "mouse-kernel.inc" .include "modload.inc" - .import return0 + .import return0, return1 @@ -29,7 +29,6 @@ _mouse_unload: jmp return0 ; Return MOUSE_ERR_OK no_driver: - tax ; X = 0 pla ; Remove pushed junk - lda #<MOUSE_ERR_NO_DRIVER - rts + .assert MOUSE_ERR_NO_DRIVER = 1, error + jmp return1 ; Return MOUSE_ERR_NO_DRIVER diff --git a/libsrc/nes/joy/nes-stdjoy.s b/libsrc/nes/joy/nes-stdjoy.s index 63caf364b..0d0e7d9ac 100644 --- a/libsrc/nes/joy/nes-stdjoy.s +++ b/libsrc/nes/joy/nes-stdjoy.s @@ -53,7 +53,8 @@ JOY_COUNT = 2 ; Number of joysticks we support INSTALL: lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/pce/joy/pce-stdjoy.s b/libsrc/pce/joy/pce-stdjoy.s index 2de3d0c4c..dc8576c87 100644 --- a/libsrc/pce/joy/pce-stdjoy.s +++ b/libsrc/pce/joy/pce-stdjoy.s @@ -50,8 +50,9 @@ padbuffer: .res JOY_COUNT ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/pet/joy/pet-ptvjoy.s b/libsrc/pet/joy/pet-ptvjoy.s index c098072fb..ee14c95c8 100644 --- a/libsrc/pet/joy/pet-ptvjoy.s +++ b/libsrc/pet/joy/pet-ptvjoy.s @@ -51,8 +51,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/plus4/joy/plus4-stdjoy.s b/libsrc/plus4/joy/plus4-stdjoy.s index e8e85fedc..86f080dae 100644 --- a/libsrc/plus4/joy/plus4-stdjoy.s +++ b/libsrc/plus4/joy/plus4-stdjoy.s @@ -58,8 +58,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index bb44a4cf9..fbdc61b2e 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -157,8 +157,9 @@ SER_CLOSE: ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -225,22 +226,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -261,8 +263,8 @@ SER_GET: @L1: lda RecvFreeCnt ; (25) cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -309,7 +311,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -318,7 +320,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -331,7 +334,8 @@ SER_STATUS: lda ACIA_STATUS ldx #0 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -341,8 +345,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ; Run into IRQ instead ;---------------------------------------------------------------------------- diff --git a/libsrc/serial/ser_unload.s b/libsrc/serial/ser_unload.s index bf7201255..7cd0f7125 100644 --- a/libsrc/serial/ser_unload.s +++ b/libsrc/serial/ser_unload.s @@ -10,7 +10,7 @@ .include "modload.inc" .import ser_clear_ptr - .import return0 + .import return0, return1 @@ -28,10 +28,10 @@ _ser_unload: tax pla ; Get pointer to driver jsr _mod_free ; Free the driver - jmp return0 ; Return SER_ERR_OK + .assert SER_ERR_OK = 0, error + jmp return0 no_driver: - tax ; X = 0 pla ; Remove pushed junk - lda #<SER_ERR_NO_DRIVER - rts + .assert SER_ERR_NO_DRIVER = 1, error + jmp return1 diff --git a/libsrc/supervision/joy/supervision-stdjoy.s b/libsrc/supervision/joy/supervision-stdjoy.s index ef790ec0b..2469abf31 100644 --- a/libsrc/supervision/joy/supervision-stdjoy.s +++ b/libsrc/supervision/joy/supervision-stdjoy.s @@ -46,8 +46,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/telestrat/joy/telestrat.s b/libsrc/telestrat/joy/telestrat.s index 0f5d28651..7472ab187 100644 --- a/libsrc/telestrat/joy/telestrat.s +++ b/libsrc/telestrat/joy/telestrat.s @@ -54,8 +54,9 @@ INSTALL: sta VIA2::PRB ; We could detect joysticks because with previous command bit0,1,2,3,4 should be set to 1 after ; But if some one press fire or press direction, we could reach others values which could break joystick detection. - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/vic20/emd/vic20-georam.s b/libsrc/vic20/emd/vic20-georam.s index a960e9a1a..4e3121c05 100644 --- a/libsrc/vic20/emd/vic20-georam.s +++ b/libsrc/vic20/emd/vic20-georam.s @@ -119,16 +119,16 @@ INSTALL: bne @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @setok: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts check: diff --git a/libsrc/vic20/emd/vic20-rama.s b/libsrc/vic20/emd/vic20-rama.s index 133c3974b..4264e2caf 100644 --- a/libsrc/vic20/emd/vic20-rama.s +++ b/libsrc/vic20/emd/vic20-rama.s @@ -71,12 +71,13 @@ INSTALL: ldx #$FF stx curpage ; Invalidate the current page - inx ; X = 0 - txa ; A = X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa rts -nomem: ldx #>EM_ERR_NO_DEVICE - lda #<EM_ERR_NO_DEVICE +nomem: ldx #0 ; return value is char + lda #EM_ERR_NO_DEVICE ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/vic20/joy/vic20-ptvjoy.s b/libsrc/vic20/joy/vic20-ptvjoy.s index 496653e9d..c29d1d5ed 100644 --- a/libsrc/vic20/joy/vic20-ptvjoy.s +++ b/libsrc/vic20/joy/vic20-ptvjoy.s @@ -53,8 +53,9 @@ JOY_COUNT = 3 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/vic20/joy/vic20-stdjoy.s b/libsrc/vic20/joy/vic20-stdjoy.s index ee8dc93d7..b3de8766c 100644 --- a/libsrc/vic20/joy/vic20-stdjoy.s +++ b/libsrc/vic20/joy/vic20-stdjoy.s @@ -57,8 +57,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ From 993054c9d300a222e3a7abbf9d7217fa4e47e4b3 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Wed, 1 Mar 2023 11:58:42 -0500 Subject: [PATCH 123/520] Fix .endmacro not at the start of the line. .ENDMACRO error with line number of macro definition start --- src/ca65/macro.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 92a0f6e9a..84f0e8bcd 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -364,7 +364,7 @@ static void FreeMacExp (MacExp* E) -static void MacSkipDef (unsigned Style) +static void MacSkipDef (unsigned Style, FilePos Pos) /* Skip a macro definition */ { if (Style == MAC_STYLE_CLASSIC) { @@ -375,7 +375,7 @@ static void MacSkipDef (unsigned Style) if (CurTok.Tok != TOK_EOF) { SkipUntilSep (); } else { - Error ("'.ENDMACRO' expected"); + PError (&Pos, "'.ENDMACRO' expected"); } } else { /* Skip until end of line */ @@ -391,19 +391,26 @@ void MacDef (unsigned Style) Macro* M; TokNode* N; int HaveParams; - int LastTokWasSep = 0; + + /* Remember if we are at the beginning of the line. If the macro name + ** and parameters pass then this will be set, so set it now */ + int LastTokWasSep = 1; + + /* Save the position of the start of the macro definition to allow + ** using Perror to display the error if .ENDMACRO isn't found */ + FilePos Pos = CurTok.Pos; /* We expect a macro name here */ if (CurTok.Tok != TOK_IDENT) { Error ("Identifier expected"); - MacSkipDef (Style); + MacSkipDef (Style, Pos); return; } else if (!UbiquitousIdents && FindInstruction (&CurTok.SVal) >= 0) { /* The identifier is a name of a 6502 instruction, which is not ** allowed if not explicitly enabled. */ Error ("Cannot use an instruction as macro name"); - MacSkipDef (Style); + MacSkipDef (Style, Pos); return; } @@ -412,7 +419,7 @@ void MacDef (unsigned Style) /* Macro is already defined */ Error ("A macro named '%m%p' is already defined", &CurTok.SVal); /* Skip tokens until we reach the final .endmacro */ - MacSkipDef (Style); + MacSkipDef (Style, Pos); return; } @@ -480,7 +487,6 @@ void MacDef (unsigned Style) */ if (Style == MAC_STYLE_CLASSIC) { ConsumeSep (); - LastTokWasSep = 1; } else if (HaveParams) { ConsumeRParen (); } @@ -493,14 +499,14 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, only .endmacro is allowed, but ignore it if it is not at the start of the line */ + /* In classic macros, only .endmacro is allowed, but do no exit the macro definition if not at the start of a line */ if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ break; } /* May not have end of file in a macro definition */ if (CurTok.Tok == TOK_EOF) { - Error ("'.ENDMACRO' expected"); + PError (&Pos, "'.ENDMACRO' expected"); goto Done; } } else { From 1c60bc50098ac981f05131eb4e85556f0cd0a5be Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Wed, 1 Mar 2023 21:47:50 -0500 Subject: [PATCH 124/520] Fix .endmacro not at the start of the line. Fix style, add doc., add tests --- doc/ca65.sgml | 14 +++++++++++++- src/ca65/macro.c | 35 ++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b65d337bb..b19a66a2d 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2522,7 +2522,19 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH <sect1><tt>.ENDMAC, .ENDMACRO</tt><label id=".ENDMACRO"><p> - Marks the end of a macro definition. + Marks the end of a macro definition. Note, <tt>.ENDMACRO</tt> should be on + its own line to successfully end the macro definition. It is possible to use + <tt><ref id=".DEFINE" name=".DEFINE"></tt> to create a symbol that references + <tt>.ENDMACRO</tt> without ending the macro definition. + + Example: + + <tscreen><verb> + .macro new_mac + .define startmac .macro + .define endmac .endmacro + .endmacro + </verb></tscreen> See: <tt><ref id=".DELMACRO" name=".DELMACRO"></tt>, <tt><ref id=".EXITMACRO" name=".EXITMACRO"></tt>, diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 84f0e8bcd..72436fdec 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -364,7 +364,7 @@ static void FreeMacExp (MacExp* E) -static void MacSkipDef (unsigned Style, FilePos Pos) +static void MacSkipDef (unsigned Style) /* Skip a macro definition */ { if (Style == MAC_STYLE_CLASSIC) { @@ -375,7 +375,7 @@ static void MacSkipDef (unsigned Style, FilePos Pos) if (CurTok.Tok != TOK_EOF) { SkipUntilSep (); } else { - PError (&Pos, "'.ENDMACRO' expected"); + Error ("'.ENDMACRO' expected"); } } else { /* Skip until end of line */ @@ -390,27 +390,32 @@ void MacDef (unsigned Style) { Macro* M; TokNode* N; + FilePos Pos; int HaveParams; + int LastTokWasSep; - /* Remember if we are at the beginning of the line. If the macro name - ** and parameters pass then this will be set, so set it now */ - int LastTokWasSep = 1; + /* For classic macros, remember if we are at the beginning of the line. + ** If the macro name and parameters pass our checks then we will be on a + ** new line, so set it now + */ + LastTokWasSep = 1; /* Save the position of the start of the macro definition to allow - ** using Perror to display the error if .ENDMACRO isn't found */ - FilePos Pos = CurTok.Pos; + ** using Perror to display the error if .ENDMACRO isn't found + */ + Pos = CurTok.Pos; /* We expect a macro name here */ if (CurTok.Tok != TOK_IDENT) { Error ("Identifier expected"); - MacSkipDef (Style, Pos); + MacSkipDef (Style); return; } else if (!UbiquitousIdents && FindInstruction (&CurTok.SVal) >= 0) { /* The identifier is a name of a 6502 instruction, which is not ** allowed if not explicitly enabled. */ Error ("Cannot use an instruction as macro name"); - MacSkipDef (Style, Pos); + MacSkipDef (Style); return; } @@ -419,7 +424,7 @@ void MacDef (unsigned Style) /* Macro is already defined */ Error ("A macro named '%m%p' is already defined", &CurTok.SVal); /* Skip tokens until we reach the final .endmacro */ - MacSkipDef (Style, Pos); + MacSkipDef (Style); return; } @@ -499,14 +504,16 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, only .endmacro is allowed, but do no exit the macro definition if not at the start of a line */ + /* In classic macros, if .endmacro is not at the start of the line + ** it will be added to the macro definition instead of closing it. + */ if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ break; } /* May not have end of file in a macro definition */ if (CurTok.Tok == TOK_EOF) { - PError (&Pos, "'.ENDMACRO' expected"); + PError (&Pos, "'.ENDMACRO' expected for macro '%m%p'", &M->Name); goto Done; } } else { @@ -581,7 +588,9 @@ void MacDef (unsigned Style) } ++M->TokCount; - /* Save if last token was a separator to know if .endmacro is valid */ + /* Save if last token was a separator to know if .endmacro is at + ** the start of a line + */ LastTokWasSep = TokIsSep(CurTok.Tok); /* Read the next token */ From 0b71d1cfff0bf3fd7d329701e231fb9b0bbb818a Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Wed, 1 Mar 2023 21:55:14 -0500 Subject: [PATCH 125/520] Fix .endmacro not at the start of the line. Fix last commit --- doc/ca65.sgml | 2 +- src/ca65/macro.c | 18 +++++++++--------- test/asm/err/bug2013.s | 6 ++++++ test/asm/val/bug2013.s | 30 ++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 test/asm/err/bug2013.s create mode 100644 test/asm/val/bug2013.s diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b19a66a2d..b68acb639 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2522,7 +2522,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH <sect1><tt>.ENDMAC, .ENDMACRO</tt><label id=".ENDMACRO"><p> - Marks the end of a macro definition. Note, <tt>.ENDMACRO</tt> should be on + Marks the end of a macro definition. Note, <tt>.ENDMACRO</tt> should be on its own line to successfully end the macro definition. It is possible to use <tt><ref id=".DEFINE" name=".DEFINE"></tt> to create a symbol that references <tt>.ENDMACRO</tt> without ending the macro definition. diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 72436fdec..6e059f421 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -394,14 +394,14 @@ void MacDef (unsigned Style) int HaveParams; int LastTokWasSep; - /* For classic macros, remember if we are at the beginning of the line. - ** If the macro name and parameters pass our checks then we will be on a - ** new line, so set it now + /* For classic macros, remember if we are at the beginning of the line. + ** If the macro name and parameters pass our checks then we will be on a + ** new line, so set it now */ LastTokWasSep = 1; - /* Save the position of the start of the macro definition to allow - ** using Perror to display the error if .ENDMACRO isn't found + /* Save the position of the start of the macro definition to allow + ** using Perror to display the error if .ENDMACRO isn't found */ Pos = CurTok.Pos; @@ -504,8 +504,8 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, if .endmacro is not at the start of the line - ** it will be added to the macro definition instead of closing it. + /* In classic macros, if .endmacro is not at the start of the line + ** it will be added to the macro definition instead of closing it. */ if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ @@ -588,8 +588,8 @@ void MacDef (unsigned Style) } ++M->TokCount; - /* Save if last token was a separator to know if .endmacro is at - ** the start of a line + /* Save if last token was a separator to know if .endmacro is at + ** the start of a line */ LastTokWasSep = TokIsSep(CurTok.Tok); diff --git a/test/asm/err/bug2013.s b/test/asm/err/bug2013.s new file mode 100644 index 000000000..8907675fb --- /dev/null +++ b/test/asm/err/bug2013.s @@ -0,0 +1,6 @@ +; for PR #2013 +; should produce error output: +; ... Error: '.ENDMACRO' expected for macro 'test' + +.macro test + nop .endmacro diff --git a/test/asm/val/bug2013.s b/test/asm/val/bug2013.s new file mode 100644 index 000000000..b6e9e37e6 --- /dev/null +++ b/test/asm/val/bug2013.s @@ -0,0 +1,30 @@ +; for PR #2013 + .import _exit + .export _main + + ; this macro is invalid, but should not cause an error (if it is never expanded) + .macro invalid + nop .endmacro + .endmacro + + .define temp_endmac .endmacro + .macro new_mac + .define startmac .macro + .define endmac .endmacro + temp_endmac + + .undefine temp_endmac + + new_mac + + startmac dex2 + dex + dex + endmac + +_main: + ldx #$02 + dex2 + ; x should be zero + txa + jmp _exit \ No newline at end of file From 98d2d9ee4e3d86c1d44d9a1d696d631031cdeffe Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Wed, 1 Mar 2023 21:59:00 -0500 Subject: [PATCH 126/520] Fix .endmacro not at the start of the line. Fix style --- test/asm/val/bug2013.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/asm/val/bug2013.s b/test/asm/val/bug2013.s index b6e9e37e6..cfb8efefb 100644 --- a/test/asm/val/bug2013.s +++ b/test/asm/val/bug2013.s @@ -27,4 +27,4 @@ _main: dex2 ; x should be zero txa - jmp _exit \ No newline at end of file + jmp _exit From 3f2129894be43d2840c28c5a33cd39c306984c1e Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Thu, 2 Mar 2023 15:12:30 -0500 Subject: [PATCH 127/520] Fix .endmacro not at the start of the line. Rename test files. --- test/asm/err/{bug2013.s => endmacro.s} | 0 test/asm/val/{bug2013.s => endmacro.s} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/asm/err/{bug2013.s => endmacro.s} (100%) rename test/asm/val/{bug2013.s => endmacro.s} (100%) diff --git a/test/asm/err/bug2013.s b/test/asm/err/endmacro.s similarity index 100% rename from test/asm/err/bug2013.s rename to test/asm/err/endmacro.s diff --git a/test/asm/val/bug2013.s b/test/asm/val/endmacro.s similarity index 100% rename from test/asm/val/bug2013.s rename to test/asm/val/endmacro.s From 90d5f41b377dfe810758c3cfdd0dd42d735a3bc7 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Thu, 2 Mar 2023 17:26:05 -0500 Subject: [PATCH 128/520] Consistent case for .endmacro in comments --- src/ca65/macro.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 6e059f421..d776411be 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -394,14 +394,14 @@ void MacDef (unsigned Style) int HaveParams; int LastTokWasSep; - /* For classic macros, remember if we are at the beginning of the line. - ** If the macro name and parameters pass our checks then we will be on a - ** new line, so set it now + /* For classic macros, remember if we are at the beginning of the line. + ** If the macro name and parameters pass our checks then we will be on a + ** new line, so set it now */ LastTokWasSep = 1; - /* Save the position of the start of the macro definition to allow - ** using Perror to display the error if .ENDMACRO isn't found + /* Save the position of the start of the macro definition to allow + ** using Perror to display the error if .endmacro isn't found */ Pos = CurTok.Pos; @@ -504,8 +504,8 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, if .endmacro is not at the start of the line - ** it will be added to the macro definition instead of closing it. + /* In classic macros, if .endmacro is not at the start of the line + ** it will be added to the macro definition instead of closing it. */ if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ @@ -588,8 +588,8 @@ void MacDef (unsigned Style) } ++M->TokCount; - /* Save if last token was a separator to know if .endmacro is at - ** the start of a line + /* Save if last token was a separator to know if .endmacro is at + ** the start of a line */ LastTokWasSep = TokIsSep(CurTok.Tok); From c092f57b73a92b6db55d43a4b4f649059d77bf2e Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Thu, 2 Mar 2023 17:31:55 -0500 Subject: [PATCH 129/520] Consistent case for .endmacro in comments, remove trailing spaces --- src/ca65/macro.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index d776411be..904c80756 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -394,13 +394,13 @@ void MacDef (unsigned Style) int HaveParams; int LastTokWasSep; - /* For classic macros, remember if we are at the beginning of the line. - ** If the macro name and parameters pass our checks then we will be on a - ** new line, so set it now + /* For classic macros, remember if we are at the beginning of the line. + ** If the macro name and parameters pass our checks then we will be on a + ** new line, so set it now */ LastTokWasSep = 1; - /* Save the position of the start of the macro definition to allow + /* Save the position of the start of the macro definition to allow ** using Perror to display the error if .endmacro isn't found */ Pos = CurTok.Pos; @@ -504,8 +504,8 @@ void MacDef (unsigned Style) while (1) { /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, if .endmacro is not at the start of the line - ** it will be added to the macro definition instead of closing it. + /* In classic macros, if .endmacro is not at the start of the line + ** it will be added to the macro definition instead of closing it. */ if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ @@ -588,8 +588,8 @@ void MacDef (unsigned Style) } ++M->TokCount; - /* Save if last token was a separator to know if .endmacro is at - ** the start of a line + /* Save if last token was a separator to know if .endmacro is at + ** the start of a line */ LastTokWasSep = TokIsSep(CurTok.Tok); From 016b03e3562666db24e5364b8034ad07314a96fc Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 7 Mar 2023 17:00:38 -0500 Subject: [PATCH 130/520] ca65 jmp (abs) wrap warning only applies to 6502, later CPUs do not have this bug --- src/ca65/instr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index b72be6711..7cbefaecf 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1617,11 +1617,12 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if (A.AddrModeBit & AM65_ABS_IND) { + if (A.AddrModeBit & AM65_ABS_IND && CPU < CPU_65SC02) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise - ** things will go weird later. + ** things will go weird later. This only affects the 6502 CPU, + ** and was corrected in 65C02 and later CPUs in this family. */ ExprNode* E = GenNE (GenByteExpr (CloneExpr (A.Expr)), 0xFF); From 8b818aac673b186a9a1558f1f6dfd7559f056f1f Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 7 Mar 2023 18:42:20 -0500 Subject: [PATCH 131/520] remove trailing space on segment "bytes" warning plural --- src/ld65/config.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ld65/config.c b/src/ld65/config.c index 38b4f96d0..6c1f6ad4c 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -2043,14 +2043,14 @@ unsigned CfgProcess (void) ++Overflows; if (S->Flags & SF_OFFSET) { CfgWarning (GetSourcePos (S->LI), - "Segment '%s' offset is too small in '%s' by %lu byte%c", + "Segment '%s' offset is too small in '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), - Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's'); + Addr - NewAddr, (Addr - NewAddr == 1) ? "" : "s"); } else { CfgWarning (GetSourcePos (S->LI), - "Segment '%s' start address is too low in '%s' by %lu byte%c", + "Segment '%s' start address is too low in '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), - Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's'); + Addr - NewAddr, (Addr - NewAddr == 1) ? "" : "s"); } } else { Addr = NewAddr; @@ -2095,9 +2095,9 @@ unsigned CfgProcess (void) ++Overflows; M->Flags |= MF_OVERFLOW; CfgWarning (GetSourcePos (M->LI), - "Segment '%s' overflows memory area '%s' by %lu byte%c", + "Segment '%s' overflows memory area '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), - FillLevel - M->Size, (FillLevel - M->Size == 1) ? ' ' : 's'); + FillLevel - M->Size, (FillLevel - M->Size == 1) ? "" : "s"); } if (FillLevel > M->FillLevel) { /* Regular segments increase FillLevel. Overwrite segments may increase but not decrease FillLevel. */ From 4732e937ad0aee2bdc2f693861e67eab263e6880 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 7 Mar 2023 18:44:56 -0500 Subject: [PATCH 132/520] overwrite segment tests asm/listing linker CFG support asm and asm/listing test documentation --- test/asm/listing/200-overwrite.cfg | 20 ++++++++ test/asm/listing/200-overwrite.s | 29 +++++++++++ test/asm/listing/201-overwrite-overflow.cfg | 12 +++++ test/asm/listing/201-overwrite-overflow.s | 13 +++++ test/asm/listing/Makefile | 16 ++++++ .../control/201-overwrite-overflow.err | 0 test/asm/listing/readme.txt | 2 + test/asm/listing/ref/200-overwrite.bin-ref | Bin 0 -> 30 bytes .../ref/201-overwrite-overflow.ld65err2-ref | 3 ++ test/asm/readme.txt | 46 ++++++++++-------- 10 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 test/asm/listing/200-overwrite.cfg create mode 100644 test/asm/listing/200-overwrite.s create mode 100644 test/asm/listing/201-overwrite-overflow.cfg create mode 100644 test/asm/listing/201-overwrite-overflow.s create mode 100644 test/asm/listing/control/201-overwrite-overflow.err create mode 100644 test/asm/listing/ref/200-overwrite.bin-ref create mode 100644 test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref diff --git a/test/asm/listing/200-overwrite.cfg b/test/asm/listing/200-overwrite.cfg new file mode 100644 index 000000000..b7b2fee49 --- /dev/null +++ b/test/asm/listing/200-overwrite.cfg @@ -0,0 +1,20 @@ +MEMORY +{ + A: start = 0, size = 8, file = %O, fill = yes, fillval = $33; + B: start = 8, size = 8, file = %O, fill = yes, fillval = $44; + C: start = 0, size = 8, file = %O, fill = yes, fillval = $55; + D: start = 8, size = 8, file = %O, fill = no, fillval = $66; +} +SEGMENTS +{ + A: load = A, type = ro; + B: load = B, type = ro; + C0: load = C, type = ro; + C1: load = C, type = ro, start = 5; + D: load = D, type = ro; + + AO: load = A, type = overwrite, start = 4; + BO: load = B, type = overwrite, start = 8+5; + CO: load = C, type = overwrite, start = 2; + DO: load = D, type = overwrite, start = 8+4; +} diff --git a/test/asm/listing/200-overwrite.s b/test/asm/listing/200-overwrite.s new file mode 100644 index 000000000..cd2349c6a --- /dev/null +++ b/test/asm/listing/200-overwrite.s @@ -0,0 +1,29 @@ +; verification of overwrite segment feature +; See: https://github.com/cc65/cc65/issues/1366 + +; A: full memory area which is overwritten to the end +.segment "A" +.byte 0,1,2,3,4,5,6,7 +.segment "AO" +.byte $24,$25,$26,$27 + +; B: incomplete memory area overwritten in the fill area +.segment "B" +.byte 0,1,2 +.segment "BO" +.byte $25,$26 + +; C: memory area with gap overwritten across the gap +.segment "C0" +.byte 0,1,2 +.segment "C1" +.byte 5,6,7 +.segment "CO" +.byte $22,$23,$24,$25 + +; D: incomplete memory area without fill, +; but overwrite extends past existing segments +.segment "D" +.byte 0,1,2 +.segment "DO" +.byte $24,$25 diff --git a/test/asm/listing/201-overwrite-overflow.cfg b/test/asm/listing/201-overwrite-overflow.cfg new file mode 100644 index 000000000..cada57d5a --- /dev/null +++ b/test/asm/listing/201-overwrite-overflow.cfg @@ -0,0 +1,12 @@ +MEMORY +{ + A: start = 0, size = 8, file = %O, fill = yes, fillval = $33; + B: start = 8, size = 8, file = %O, fill = yes, fillval = $44; +} +SEGMENTS +{ + A: load = A, type = ro; + B: load = B, type = ro; + AO: load = A, type = overwrite, start = 6; + BO: load = B, type = overwrite, start = 8+6; +} diff --git a/test/asm/listing/201-overwrite-overflow.s b/test/asm/listing/201-overwrite-overflow.s new file mode 100644 index 000000000..156388ab5 --- /dev/null +++ b/test/asm/listing/201-overwrite-overflow.s @@ -0,0 +1,13 @@ +; verification of overwrite segment overflow cases + +; error: overflow past end of A memory area +.segment "A" +.byte 0,1,2,3 +.segment "AO" +.byte $26,$27,$28 + +; error: overflow past end of B memory area +.segment "B" +.byte 0,1,2,3 +.segment "BO" +.byte $26,$27,$28 diff --git a/test/asm/listing/Makefile b/test/asm/listing/Makefile index 3c4c404af..204984b64 100644 --- a/test/asm/listing/Makefile +++ b/test/asm/listing/Makefile @@ -58,12 +58,20 @@ $(WORKDIR)/$1.bin: $1.s $(ISEQUAL) ifeq ($(wildcard control/$1.err),) $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2> $$(@:.bin=.err2) ifeq ($(wildcard control/$1.no-ld65),) +ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) +else + $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) +endif endif else $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2> $$(@:.bin=.err2) || $(TRUE) ifeq ($(wildcard control/$1.no-ld65),) +ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) +else + $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) +endif endif endif @@ -117,12 +125,20 @@ endif ifeq ($(wildcard control/$1.err),) $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2> $$(@:.bin=.list-err2) ifeq ($(wildcard control/$1.no-ld65),) +ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) +else + $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) +endif endif else $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2> $$(@:.bin=.list-err2) || $(TRUE) ifeq ($(wildcard control/$1.no-ld65),) +ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) +else + $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) +endif endif endif diff --git a/test/asm/listing/control/201-overwrite-overflow.err b/test/asm/listing/control/201-overwrite-overflow.err new file mode 100644 index 000000000..e69de29bb diff --git a/test/asm/listing/readme.txt b/test/asm/listing/readme.txt index d6280b954..5436b234a 100644 --- a/test/asm/listing/readme.txt +++ b/test/asm/listing/readme.txt @@ -4,6 +4,8 @@ Overall test: These testcases can be used to test different aspects of the assembler. The name of a test is everything in the form <test>.s. +If a custom linker configuration is needed, also include <test>.cfg. + The following reference files can be added: - ref/<test>.bin-ref: diff --git a/test/asm/listing/ref/200-overwrite.bin-ref b/test/asm/listing/ref/200-overwrite.bin-ref new file mode 100644 index 0000000000000000000000000000000000000000..b3a6f4f63cbcf5aa6c02e3980227eac3edd378f2 GIT binary patch literal 30 jcmZQzWMWoPRa0kRWO8v)RdZorR8m$^Wn%~O(^OOeDdYnM literal 0 HcmV?d00001 diff --git a/test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref b/test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref new file mode 100644 index 000000000..e372938ef --- /dev/null +++ b/test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref @@ -0,0 +1,3 @@ +ld65: Warning: 201-overwrite-overflow.cfg:3: Segment 'AO' overflows memory area 'A' by 1 byte +ld65: Warning: 201-overwrite-overflow.cfg:4: Segment 'BO' overflows memory area 'B' by 1 byte +ld65: Error: Cannot generate most of the files due to memory area overflows diff --git a/test/asm/readme.txt b/test/asm/readme.txt index c3198c12a..db7f64504 100644 --- a/test/asm/readme.txt +++ b/test/asm/readme.txt @@ -1,32 +1,38 @@ Assembler Testcases =================== -Opcode Tests: -------------- +cpudetect +--------- -these go into opcodes/. Refer to opcodes/readme.txt +Tests the --cpu command line option of ca65/ld65. +Refer to cpudetect/readme.txt -CPU Detect Tests ----------------- +opcodes +------- -these go into cpudetect/. Refer to cpudetect/readme.txt +Test of assembler opcodes for each CPU. +Refer to opcodes/readme.txt -Overall tests: --------------- - -These go into listing/. Refer to listing/readme.txt - -val: ----- - -Works very much like the /val directory used to test the compiler - individual -tests are run in the simulator and should exit with an exit code of 0 when they -pass, or either -1 or a number indicating what part of the test failed on error. - err: ---- -Works very much like the /err directory used to test the compiler - individual -tests are assembled and MUST NOT assemble without error. +Used to test assembler errors. These tests MUST NOT assemble without error. + + +listing: +-------- + +This is the most versatile assembler test form, allowing control customizations, +reference tests for binary output, stdout and error text ouput, error tests, +listings, custom linker configuration, etc. as needed. +Refer to listing/readme.txt + + +val: +---- + +Runtime assembly tests using sim65 that should exit with an exit code of 0 when +the pass, or either -1 or a number indicating what part of the test failed on +error. From 6be7c160133d4315686d49149029149e7106a958 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 7 Mar 2023 18:48:30 -0500 Subject: [PATCH 133/520] linkter test with custom config should not use none.lib --- test/asm/listing/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/asm/listing/Makefile b/test/asm/listing/Makefile index 204984b64..23aa3969c 100644 --- a/test/asm/listing/Makefile +++ b/test/asm/listing/Makefile @@ -61,7 +61,7 @@ ifeq ($(wildcard control/$1.no-ld65),) ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) else - $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) + $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) endif endif else @@ -70,7 +70,7 @@ ifeq ($(wildcard control/$1.no-ld65),) ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) else - $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) + $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) endif endif endif @@ -128,7 +128,7 @@ ifeq ($(wildcard control/$1.no-ld65),) ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) else - $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) + $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) endif endif else @@ -137,7 +137,7 @@ ifeq ($(wildcard control/$1.no-ld65),) ifeq ($(wildcard $1.cfg),) $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) else - $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) + $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) endif endif endif From 99220f60af8fca4817bde91a9dce8fb20e83e53f Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 7 Mar 2023 18:53:30 -0500 Subject: [PATCH 134/520] test/asm readme val description revision --- test/asm/readme.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/asm/readme.txt b/test/asm/readme.txt index db7f64504..49b530d1c 100644 --- a/test/asm/readme.txt +++ b/test/asm/readme.txt @@ -33,6 +33,6 @@ Refer to listing/readme.txt val: ---- -Runtime assembly tests using sim65 that should exit with an exit code of 0 when -the pass, or either -1 or a number indicating what part of the test failed on -error. +Runtime assembly tests using sim65 that should end with an exit code of 0 if +they pass. If they fail the exit code should be either -1, or a number +indicating what part of the test failed. From c8c6fc6a4064efb1a69a99fe75b37cdf5944f60f Mon Sep 17 00:00:00 2001 From: jede <jede@oric.org> Date: Fri, 17 Mar 2023 23:10:15 +0100 Subject: [PATCH 135/520] add xfseek --- asminc/telestrat.inc | 2 ++ libsrc/telestrat/lseek.s | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 libsrc/telestrat/lseek.s diff --git a/asminc/telestrat.inc b/asminc/telestrat.inc index 682696887..c57bd3de8 100644 --- a/asminc/telestrat.inc +++ b/asminc/telestrat.inc @@ -277,6 +277,8 @@ XRECLK = $3C ; Reset clock XCLCL = $3D ; Close clock XWRCLK = $3E ; Displays clock in the address in A & Y registers +XFSEEK = $3F ; Only in Orix + ; Sound primitives XSONPS = $40 ; Send data to PSG register (14 values) XOUPS = $42 ; Send Oups sound into PSG diff --git a/libsrc/telestrat/lseek.s b/libsrc/telestrat/lseek.s new file mode 100644 index 000000000..11d1fad33 --- /dev/null +++ b/libsrc/telestrat/lseek.s @@ -0,0 +1,39 @@ +; +; Jede (jede@oric.org), 2023-03-13 +; + +; off_t __fastcall__ lseek(int fd, off_t offset, int whence); + + .export _lseek + + .include "telestrat.inc" + .include "zeropage.inc" + + .import popax + +.proc _lseek + ; Save whence + sta tmp1 + ; Skip X + + ; Get offset and store + + jsr popax + sta tmp2 + stx tmp3 + + jsr popax + sta RESB + stx RESB+1 + + ; Get FD + jsr popax + ; Does not need X + sta RES ; Save FD + + lda tmp2 + ldy tmp3 + ldx tmp1 ; Get whence + BRK_TELEMON XFSEEK + rts +.endproc From fcbcbea4ad7653b279012b948b463bc6aa096080 Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Mon, 27 Mar 2023 09:23:40 -0400 Subject: [PATCH 136/520] Make some documentation fixes for KIM-1 platform. --- doc/kim1.sgml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/kim1.sgml b/doc/kim1.sgml index c3494b9b9..1387d3b19 100644 --- a/doc/kim1.sgml +++ b/doc/kim1.sgml @@ -45,8 +45,10 @@ system configuration before compiling and linking user programs. The ROMs and I/O areas are defined in the configuration files, as are most of the entry points for useful subroutines in the KIM-1 monitor ROM. cc65 generated programs compiled and linked using 4k config run in the memory range of $200 - $0FFF. The 60k config expands -this range to $DFFF. The starting memory location and entry point for running the program is -$200, so when the program is transferred to the KIM-1, it is executed by typing '200 G'. +this range to $DFFF. When using the 4k config the starting memory location and entry point +for running the program is $200, so when the program is transferred to the KIM-1, it is +executed by typing '200 G'. With the 60k config the default starting memory location and entry +point is $2000. Special locations: @@ -65,7 +67,7 @@ Special locations: <sect>Platform specific header files<p> -Programs containing KIM-1 code may use the <tt/kim.h/ header file. See the header file for more information. +Programs containing KIM-1 code may use the <tt/kim1.h/ header file. See the header file for more information. <sect>Loadable drivers<p> From 806ffe56757ee192d478565f9fb438f1e146ab08 Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Mon, 27 Mar 2023 17:56:25 -0400 Subject: [PATCH 137/520] Fixes and improvements to KIM-1 i/o routines. Tested on real KIM-1 hardware. read.s: - Remove commented out line. - Remove unused check for bell character. - Remove echo of newline (hardware always echoes entered characters). - This fixes gets() and fgets() so they return when CR is entered. write.s: - Fix check for adding return after linefeed (failed to work because OUTCHR changes A) - Remove unused check for bell character. kim1.inc: - Add symbol for monitor entry crt0.s: - Jump to KIM-1 monitor by address rather than using BRK (which relies on vector being set in RAM) --- asminc/kim1.inc | 2 +- libsrc/kim1/crt0.s | 6 ++---- libsrc/kim1/read.s | 7 +------ libsrc/kim1/write.s | 7 +++---- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/asminc/kim1.inc b/asminc/kim1.inc index 68f059490..2fb1117fa 100644 --- a/asminc/kim1.inc +++ b/asminc/kim1.inc @@ -15,7 +15,7 @@ OUTCHR := $1EA0 ; Output character INTCHR := $1E5A ; Input character without case conversion DUMPT := $1800 ; Dump memory to tape LOADT := $1873 ; Load memory from tape - +START := $1C4F ; Enter KIM-1 monitor ; --------------------------------------------------------------------------- ; System Memory diff --git a/libsrc/kim1/crt0.s b/libsrc/kim1/crt0.s index f1fee86c1..906b3b980 100644 --- a/libsrc/kim1/crt0.s +++ b/libsrc/kim1/crt0.s @@ -40,8 +40,6 @@ _init: cld ; Clear decimal mode jsr _main -; Back from main (this is also the _exit entry). There may be a more elegant way to -; return to the monitor on the KIM-1, but I don't know it! - -_exit: brk +; Back from main (this is also the _exit entry). Jumps to the KIM-1 monitor. +_exit: jmp START diff --git a/libsrc/kim1/read.s b/libsrc/kim1/read.s index 5566a9f27..dd178ee98 100644 --- a/libsrc/kim1/read.s +++ b/libsrc/kim1/read.s @@ -27,15 +27,10 @@ begin: dec ptr2 beq done ; If buffer full, return getch: jsr INTCHR ; Get character using Monitor ROM call - ;jsr OUTCHR ; Echo it and #$7F ; Clear top bit - cmp #$07 ; Check for '\a' - bne chkcr ; ...if BEL character - ;jsr BEEP ; Make beep sound TODO -chkcr: cmp #$0D ; Check for '\r' + cmp #$0D ; Check for '\r' bne putch ; ...if CR character lda #$0A ; Replace with '\n' - jsr OUTCHR ; and echo it putch: ldy #$00 ; Put char into return buffer sta (ptr1),y diff --git a/libsrc/kim1/write.s b/libsrc/kim1/write.s index 216f5031c..96bcc91d1 100644 --- a/libsrc/kim1/write.s +++ b/libsrc/kim1/write.s @@ -28,11 +28,10 @@ begin: dec ptr2 outch: ldy #0 lda (ptr1),y + pha ; Save A (changed by OUTCHR) jsr OUTCHR ; Send character using Monitor call - cmp #$07 ; Check for '\a' - bne chklf ; ...if BEL character -;jsr BEEP ; Make beep sound -chklf: cmp #$0A ; Check for 'n' + pla ; Restore A + cmp #$0A ; Check for '\n' bne next ; ...if LF character lda #$0D ; Add a carriage return jsr OUTCHR From 2dd558eae9cc31e794beeda3a3e31278ee971977 Mon Sep 17 00:00:00 2001 From: mooinglemur <mooinglemur@users.noreply.github.com> Date: Tue, 28 Mar 2023 21:18:02 -0700 Subject: [PATCH 138/520] cx16: update RAM constants for ROM ver R42 --- asminc/cx16.inc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/asminc/cx16.inc b/asminc/cx16.inc index 1a916ecdb..be63780c8 100644 --- a/asminc/cx16.inc +++ b/asminc/cx16.inc @@ -239,12 +239,12 @@ BASIC_BUF := $0200 ; Location of command-line BASIC_BUF_LEN = 81 ; Maximum length of command-line SCREEN_PTR := $0262 ; Pointer to current row on text screen (16 bits) -STATUS := $0289 ; Status from previous I/O operation -IN_DEV := $028D ; Current input device number -OUT_DEV := $028E ; Current output device number -FNAM_LEN := $0291 ; Length of filename -SECADR := $0293 ; Secondary address -DEVNUM := $0294 ; Device number +STATUS := $0287 ; Status from previous I/O operation +IN_DEV := $028B ; Current input device number +OUT_DEV := $028C ; Current output device number +FNAM_LEN := $028F ; Length of filename +SECADR := $0291 ; Secondary address +DEVNUM := $0292 ; Device number CURS_COLOR := $0373 ; Color under the cursor CHARCOLOR := $0376 ; Cursor's color nybbles (high: background, low: foreground) RVS := $0377 ; Reverse flag @@ -258,8 +258,8 @@ LLEN := $0386 ; Line length NLINES := $0387 ; Number of screen lines ; BASIC -VARTAB := $03E2 ; Pointer to start of BASIC variables -MEMSIZE := $03EA ; Pointer to highest BASIC RAM location (+1) +VARTAB := $03E1 ; Pointer to start of BASIC variables +MEMSIZE := $03E9 ; Pointer to highest BASIC RAM location (+1) ; --------------------------------------------------------------------------- ; Vector and other locations From 3a5fbd34da2daf0004a51fd24f853ebfb90e7307 Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Wed, 29 Mar 2023 18:40:10 -0400 Subject: [PATCH 139/520] Add KIM-1 functions to write to the 7-segment LED display and get keypresses from the keypad. Includes sample program illustrating how to use them. Tested on real KIM-1 hardware. --- asminc/kim1.inc | 4 +++ include/kim1.h | 13 ++++++++++ libsrc/kim1/getkey.s | 18 ++++++++++++++ libsrc/kim1/scandisplay.s | 21 ++++++++++++++++ samples/kim1/kimKeyDisp.c | 52 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 libsrc/kim1/getkey.s create mode 100644 libsrc/kim1/scandisplay.s create mode 100644 samples/kim1/kimKeyDisp.c diff --git a/asminc/kim1.inc b/asminc/kim1.inc index 2fb1117fa..81e83b83c 100644 --- a/asminc/kim1.inc +++ b/asminc/kim1.inc @@ -16,6 +16,10 @@ INTCHR := $1E5A ; Input character without case conversion DUMPT := $1800 ; Dump memory to tape LOADT := $1873 ; Load memory from tape START := $1C4F ; Enter KIM-1 monitor +SCANDS := $1F1F ; Scan 7-segment display +KEYIN := $1F40 ; Open up keyboard channel +GETKEY := $1F6A ; Return key from keyboard + ; --------------------------------------------------------------------------- ; System Memory diff --git a/include/kim1.h b/include/kim1.h index dae246944..99843f8f8 100644 --- a/include/kim1.h +++ b/include/kim1.h @@ -56,5 +56,18 @@ int __fastcall__ loadt (unsigned char); /* Write to tape */ int __fastcall__ dumpt (unsigned char, const void*, const void*); + +/* Write to 7-segment LED display. Due to hardware limitations it only +** displays briefly, so must be called repeatedly to update the +** display. +**/ +void __fastcall__ scandisplay(unsigned char left, unsigned char middle, unsigned char right); + +/* +** Get a keypress from the keypad. Returns $00-$0F(0-F), $10(AD), $11(DA), $12(+), +** $13(GO), $14(PC) or $15 for no keypress. +**/ +int __fastcall__ getkey(); + /* End of sym1.h */ #endif diff --git a/libsrc/kim1/getkey.s b/libsrc/kim1/getkey.s new file mode 100644 index 000000000..b36cd4b4c --- /dev/null +++ b/libsrc/kim1/getkey.s @@ -0,0 +1,18 @@ +; +; int __fastcall__ getkey(); +; + +.include "kim1.inc" + +.import popa + +.export _getkey + +.proc _getkey + + jsr KEYIN ; Open up keyboard channel + jsr GETKEY ; Get key code + ldx #0 ; MSB of return value is zero + rts + +.endproc diff --git a/libsrc/kim1/scandisplay.s b/libsrc/kim1/scandisplay.s new file mode 100644 index 000000000..0f46a5de4 --- /dev/null +++ b/libsrc/kim1/scandisplay.s @@ -0,0 +1,21 @@ +; +; void __fastcall__ scandisplay(unsigned char left, unsigned char middle, unsigned char right); +; + +.include "kim1.inc" + +.import popa + +.export _scandisplay + +.proc _scandisplay + + sta $F9 ; Rightmost display data + jsr popa + sta $FA ; Middle display data + jsr popa + sta $FB ; Leftmost display data + jsr SCANDS + rts + +.endproc diff --git a/samples/kim1/kimKeyDisp.c b/samples/kim1/kimKeyDisp.c new file mode 100644 index 000000000..63839d19e --- /dev/null +++ b/samples/kim1/kimKeyDisp.c @@ -0,0 +1,52 @@ +/* Example illustrating scandisplay() and getkey() functions. */ + +#include <stdio.h> +#include <kim1.h> + +int main (void) +{ + int i, j, k, l; + int last = 15; + + printf("\nKIM-1 Demo\n"); + + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + for (k = 0; k < 16; k++) { + scandisplay(i, j, k); + + l = getkey(); + + if (l != last) { + switch (l) { + case 0x0: case 0x1: case 0x2: case 0x3: + case 0x4: case 0x5: case 0x6: case 0x7: + case 0x8: case 0x9: case 0xa: case 0xb: + case 0xc: case 0xd: case 0xe: case 0xf: + printf("Key pressed: %X\n", l); + break; + case 0x10: + printf("Key pressed: AD\n"); + break; + case 0x11: + printf("Key pressed: DA\n"); + break; + case 0x12: + printf("Key pressed: +\n"); + break; + case 0x13: + printf("Key pressed: GO\n"); + break; + case 0x14: + printf("Key pressed: PC\n"); + break; + } + + last = l; + } + } + } + } + + return 0; +} From ed4edd908b8274a13ca1b8f988dfe5cdbb194d02 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Tue, 4 Apr 2023 00:40:33 +0200 Subject: [PATCH 140/520] fix copy'n'paste typo --- include/kim1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/kim1.h b/include/kim1.h index 99843f8f8..03c496223 100644 --- a/include/kim1.h +++ b/include/kim1.h @@ -69,5 +69,5 @@ void __fastcall__ scandisplay(unsigned char left, unsigned char middle, unsigned **/ int __fastcall__ getkey(); -/* End of sym1.h */ +/* End of kim1.h */ #endif From 19349acdcf731fe54d28f839385a4f431ee62d20 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Tue, 4 Apr 2023 00:57:52 +0200 Subject: [PATCH 141/520] fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 697132fde..e0e2c24e7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Core team members: * [Christian Groessler](https://github.com/groessler): Atari, Atari5200, and CreatiVision library Maintainer * [dqh](https://github.com/dqh-au): GHA help * [Greg King](https://github.com/greg-king5): all around hackery -* [groepaz](https://github.com/mrdudz): CBM libary, Project Maintainer +* [groepaz](https://github.com/mrdudz): CBM library, Project Maintainer * [Oliver Schmidt](https://github.com/oliverschmidt): Apple II library Maintainer External contributors: From 2a7533268cfcec42f217b7cf521a567da866e29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ola=20S=C3=B6der?= <rolfkopman@gmail.com> Date: Fri, 7 Apr 2023 23:44:30 +0200 Subject: [PATCH 142/520] Don't use hardcoded paths on Amiga Hardcoded paths don't make sense on AmigaOS, AROS and MorphOS. --- src/ca65/incpath.c | 2 +- src/cc65/incpath.c | 2 +- src/cl65/main.c | 2 +- src/ld65/filepath.c | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ca65/incpath.c b/src/ca65/incpath.c index 8ea67df6f..461f1ad79 100644 --- a/src/ca65/incpath.c +++ b/src/ca65/incpath.c @@ -75,7 +75,7 @@ void FinishIncludePaths (void) AddSubSearchPathFromEnv (IncSearchPath, "CC65_HOME", "asminc"); /* Add some compiled-in search paths if defined at compile time. */ -#if defined(CA65_INC) && !defined(_WIN32) +#if defined(CA65_INC) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (IncSearchPath, CA65_INC); #endif diff --git a/src/cc65/incpath.c b/src/cc65/incpath.c index 85f6e070b..3069079ac 100644 --- a/src/cc65/incpath.c +++ b/src/cc65/incpath.c @@ -76,7 +76,7 @@ void FinishIncludePaths (void) AddSubSearchPathFromEnv (SysIncSearchPath, "CC65_HOME", "include"); /* Add some compiled-in search paths if defined at compile time. */ -#if defined(CC65_INC) && !defined(_WIN32) +#if defined(CC65_INC) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (SysIncSearchPath, CC65_INC); #endif diff --git a/src/cl65/main.c b/src/cl65/main.c index 67e9444f4..553fb9ca6 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -1216,7 +1216,7 @@ static void OptPrintTargetPath (const char* Opt attribute ((unused)), SearchPaths* TargetPaths = NewSearchPath (); AddSubSearchPathFromEnv (TargetPaths, "CC65_HOME", "target"); -#if defined(CL65_TGT) && !defined(_WIN32) +#if defined(CL65_TGT) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (TargetPaths, CL65_TGT); #endif AddSubSearchPathFromBin (TargetPaths, "target"); diff --git a/src/ld65/filepath.c b/src/ld65/filepath.c index 1ceb2333e..c84fe6f46 100644 --- a/src/ld65/filepath.c +++ b/src/ld65/filepath.c @@ -88,13 +88,13 @@ void InitSearchPaths (void) AddSubSearchPathFromEnv (CfgDefaultPath, "CC65_HOME", "cfg"); /* Add some compiled-in search paths if defined at compile time. */ -#if defined(LD65_LIB) && !defined(_WIN32) +#if defined(LD65_LIB) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (LibDefaultPath, LD65_LIB); #endif -#if defined(LD65_OBJ) && !defined(_WIN32) +#if defined(LD65_OBJ) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (ObjDefaultPath, LD65_OBJ); #endif -#if defined(LD65_CFG) && !defined(_WIN32) +#if defined(LD65_CFG) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (CfgDefaultPath, LD65_CFG); #endif From 1f9594560ed0392bd0420dc79e1f586cc8c63218 Mon Sep 17 00:00:00 2001 From: Janne Johansson <icepic.dz@gmail.com> Date: Wed, 19 Apr 2023 09:15:03 +0200 Subject: [PATCH 143/520] Update lynxsprite.c Remove compiler warning about (signed) char wrapping around to -1 when set to 255. --- src/sp65/lynxsprite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp65/lynxsprite.c b/src/sp65/lynxsprite.c index 566edacbe..6bd9b03de 100644 --- a/src/sp65/lynxsprite.c +++ b/src/sp65/lynxsprite.c @@ -312,7 +312,7 @@ static void AssembleByte(unsigned bits, char val) static unsigned char AnalyseNextChunks(signed *newlen, signed len, char data[32], char ColorBits) { char longest = 1; - char prev = 255; + unsigned char prev = 255; char count = 0; char index = 0; char lindex = 0; From 613ee94f445463c76135c5cb9ebffad6b9027cae Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Sun, 30 Apr 2023 18:40:09 -0400 Subject: [PATCH 144/520] Source listing in bootstrap.s is incorrect. The OSI C1P alternative boot file format works, but the code in the source listing does not match the ASCII-coded hex translation (which is actually used). This is confusing to anyone trying to maintain the code. Also, the source code does not assemble when ASM is defined. With these changes the source file should correctly match what is used at run time. --- libsrc/osic1p/bootstrap.s | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index e88e257fd..292f98914 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -34,7 +34,7 @@ ram_top := __MAIN_START__ + __MAIN_SIZE__ .ifdef ASM - .include "osic1p.inc" + .include "screen-c1p-24x24.s" .macpack generic load := $08 ; private variables @@ -45,20 +45,22 @@ GETCHAR := $FFBF ; gets one character from ACIA FIRSTVISC = $85 ; Offset of first visible character in video RAM LINEDIST = $20 ; Offset in video RAM between two lines - ldy #<$0000 + ldy #<$00 lda #<load_addr ldx #>load_addr sta load stx load+1 - ldx #(<load_size) + 1 - stx count - ldx #(>load_size) + 1 - stx count+1 ; save size with each byte incremented separately + lda #<load_size + eor #$FF + sta count + lda #>load_size + eor #$FF + sta count+1 -L1: dec count +L1: inc count bnz L2 - dec count+1 + inc count+1 bze L3 L2: jsr GETCHAR ; (doesn't change .Y) sta (load),y @@ -70,7 +72,7 @@ L2: jsr GETCHAR ; (doesn't change .Y) lsr a and #8 - 1 ora #$10 ; eight arrow characters - sta SCRNBASE + FIRSTVISC + 2 * LINEDIST + 11 + sta C1P_SCR_BASE + FIRSTVISC + 2 * LINEDIST + 11 iny bnz L1 From 4d97e30b557172d32435c83b1cb41f8eb5106042 Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Sun, 30 Apr 2023 18:40:09 -0400 Subject: [PATCH 145/520] Source listing in bootstrap.s is incorrect. The OSI C1P alternative boot file format works, but the code in the source listing does not match the ASCII-coded hex translation (which is actually used). This is confusing to anyone trying to maintain the code. Also, the source code did not assemble when ASM is defined. Also removed use of branch macros and an unnecessary "<" operator. With these changes the source file should correctly match what is used at run time. --- libsrc/osic1p/bootstrap.s | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index e88e257fd..52ce31f84 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -34,7 +34,7 @@ ram_top := __MAIN_START__ + __MAIN_SIZE__ .ifdef ASM - .include "osic1p.inc" + .include "screen-c1p-24x24.s" .macpack generic load := $08 ; private variables @@ -45,21 +45,23 @@ GETCHAR := $FFBF ; gets one character from ACIA FIRSTVISC = $85 ; Offset of first visible character in video RAM LINEDIST = $20 ; Offset in video RAM between two lines - ldy #<$0000 + ldy #$00 lda #<load_addr ldx #>load_addr sta load stx load+1 - ldx #(<load_size) + 1 - stx count - ldx #(>load_size) + 1 - stx count+1 ; save size with each byte incremented separately + lda #<load_size + eor #$FF + sta count + lda #>load_size + eor #$FF + sta count+1 -L1: dec count - bnz L2 - dec count+1 - bze L3 +L1: inc count + bne L2 + inc count+1 + beq L3 L2: jsr GETCHAR ; (doesn't change .Y) sta (load),y @@ -70,12 +72,12 @@ L2: jsr GETCHAR ; (doesn't change .Y) lsr a and #8 - 1 ora #$10 ; eight arrow characters - sta SCRNBASE + FIRSTVISC + 2 * LINEDIST + 11 + sta C1P_SCR_BASE + FIRSTVISC + 2 * LINEDIST + 11 iny - bnz L1 + bne L1 inc load+1 - bnz L1 ; branch always + bne L1 ; branch always L3: jmp load_addr From 08223360d539aede5719696be9d287747950500b Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Tue, 2 May 2023 12:43:50 +0200 Subject: [PATCH 146/520] Update instr.c --- src/ca65/instr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 7cbefaecf..4f95ec75c 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1617,7 +1617,7 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if (A.AddrModeBit & AM65_ABS_IND && CPU < CPU_65SC02) { + if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02)) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise From 1c26b1cf1b90172f16f7f7b0eed7ab36baa49165 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 14:42:00 -0400 Subject: [PATCH 147/520] RHS primary integer promotion must happen after loading the primary, not before. See: #2060 --- src/cc65/expr.c | 3 ++- test/val/bug2060.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/val/bug2060.c diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 691010b0a..9460569ed 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3090,9 +3090,10 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) Expr->Type = Expr2.Type; } else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) { /* Integer addition */ - flags = typeadjust (Expr, &Expr2, 0); /* Load rhs into the primary */ LoadExpr (CF_NONE, &Expr2); + /* Adjust rhs primary if needed */ + flags = typeadjust (Expr, &Expr2, 0); } else { /* OOPS */ AddDone = -1; diff --git a/test/val/bug2060.c b/test/val/bug2060.c new file mode 100644 index 000000000..59b4774a4 --- /dev/null +++ b/test/val/bug2060.c @@ -0,0 +1,56 @@ +/* Test of bug: https://github.com/cc65/cc65/issues/2060 */ + +#include <stdio.h> + +#define W 320 + +unsigned long test1(unsigned char* p, unsigned long n) +{ + (void)p; + return n; +} + +unsigned long test0(unsigned char* p, int x, int y, unsigned char b) +{ + (void)b; + return test1(p, (long)y * W + x); +} + +#define TEST(ta,tb) \ + expect = (long)tb * W + ta; \ + result = test0(p,ta,tb,0x56); \ + printf("%4d * %3d + %4d = %08lx",tb,W,ta,result); \ + if (expect != result) { printf(" expected: %08lx\n",expect); ++fail; } \ + else printf("\n"); + +int main(void) +{ + unsigned char* p = (unsigned char*)0x1234; + unsigned long expect, result; + int fail = 0; + + TEST(1,3); + TEST(50,60); + TEST(99,88); + TEST(128,102); + TEST(129,102); + TEST(320,102); + /* Bug 2060 indicated failure when y > 102. + Because: (y * 320) > 32767 + The promotion of x from int to long had an incorrect high word, + because it was done before loading x into AX, rather than after. + */ + TEST(0,103); + TEST(150,170); + TEST(300,180); + /* x < 0 also fails because its high word sign extend is incorrect. */ + TEST(-100,50); + TEST(-49,99); + TEST(-300,-180); + /* This passed despite the bug, because y * 320 coincidentally had the + same high word. + */ + TEST(-1,-1); + + return fail; +} From 2c47ea45af0ad91a611c866b9f99a5c7eae4e5f1 Mon Sep 17 00:00:00 2001 From: Brad Smith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 18:18:10 -0400 Subject: [PATCH 148/520] bug895.c compliant token pasting syntax to remove warnings Gets rid of some unnecessary warning spam in the test log of lines like this: ``` bug895.c:95: Warning: Pasting formed "unsigned_long_14(", an invalid preprocessing token ``` --- test/val/bug895.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/val/bug895.c b/test/val/bug895.c index c4892d7b1..3c0331a6d 100644 --- a/test/val/bug895.c +++ b/test/val/bug895.c @@ -21,7 +21,7 @@ unsigned int uia, uib; unsigned long ula, ulb; #define OPTCMP8TEST_SINGLE(num,cmpop,asmprefix,vara,varb,b0,b1,a0,a1,typename,name) \ - typename name ## _ ## num ## (void) { \ + typename name ## _ ## num(void) { \ varb = b0; \ asm( asmprefix ); \ vara = a0; \ @@ -30,7 +30,7 @@ unsigned long ula, ulb; } #define OPTCMP8TEST_VERIFY(num,b,desc,printterm,name) \ - ASSERT_AreEqual(name ## _ ## num ##(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ")."); + ASSERT_AreEqual(name ## _ ## num(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ")."); /* Generates a set of comparison tests for one type and set of test values. ** name = a name for this test (no spaces) From e7046a02ff392d793647fccd76245ce1bc00c4cc Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 19:00:34 -0400 Subject: [PATCH 149/520] Disallow pass/return of 3-byte struct (#2022), document capability added in #1102. --- doc/cc65.sgml | 5 ++--- src/cc65/datatype.c | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 683249bda..2e99f3201 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -806,9 +806,8 @@ and the one defined by the ISO standard: <item> The datatypes "float" and "double" are not available. <p> -<item> C Functions may not return structs (or unions), and structs may not - be passed as parameters by value. However, struct assignment *is* - possible. +<item> C Functions may return structs (or unions) by value, but only of + 1, 2 or 4 byte sizes. <p> <item> Most of the C library is available with only the fastcall calling convention (<ref id="extension-fastcall" name="see below">). It means diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6907ee099..caa41a7a4 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -803,7 +803,6 @@ const Type* GetStructReplacementType (const Type* SType) switch (SizeOf (SType)) { case 1: NewType = type_uchar; break; case 2: NewType = type_uint; break; - case 3: /* FALLTHROUGH */ case 4: NewType = type_ulong; break; default: NewType = SType; break; } From af11d4d947d090419827907604d8751a3eb01411 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 19:21:21 -0400 Subject: [PATCH 150/520] Document that struct-param is default off since: 3129266 --- doc/cc65.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 2e99f3201..023204f4d 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -741,7 +741,7 @@ Here is a description of all the command line options: <tag><tt/return-type/</tag> Warn about no return statement in function returning non-void. <tag><tt/struct-param/</tag> - Warn when passing structs by value. + Warn when passing structs by value. (Disabled by default.) <tag><tt/unknown-pragma/</tag> Warn about #pragmas that aren't recognized by cc65. <tag><tt/unreachable-code/</tag> From bf22f94a436345334e81fe70d196a8e805b7a778 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 20:02:58 -0400 Subject: [PATCH 151/520] struct pass and return by value test --- test/val/struct-by-value.c | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 test/val/struct-by-value.c diff --git a/test/val/struct-by-value.c b/test/val/struct-by-value.c new file mode 100644 index 000000000..0e846c117 --- /dev/null +++ b/test/val/struct-by-value.c @@ -0,0 +1,144 @@ +/* Test of passing and returning structs by value. + Structs of 1, 2 and 4 bytes are supported. + Note that structs of 3 bytes are disabled, see: + https://github.com/cc65/cc65/issues/2022 +*/ + +int fail = 0; + +struct s1 { char a; }; +struct s2 { char a, b; }; +struct s3 { char a, b, c; }; +struct s4 { char a, b, c, d; }; + +const struct s1 c1 = { 1 }; +const struct s2 c2 = { 2, 3 }; +const struct s3 c3 = { 4, 5, 6 }; +const struct s4 c4 = { 7, 8, 9, 10 }; + +struct s1 return1() { return c1; } +struct s2 return2() { return c2; } +/*struct s3 return3() { return c3; }*/ +struct s4 return4() { return c4; } + +int compare1(struct s1 a, struct s1 b) +{ + if (a.a != b.a) return 1; + return 0; +} + +int compare2(struct s2 a, struct s2 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + return 0; +} + +/*int compare3(struct s3 a, struct s3 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + return 0; +}*/ + +int compare4(struct s4 a, struct s4 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + if (a.d != b.d) return 1; + return 0; +} + +int pass1(struct s1 p1) +{ + struct s1 a1; + a1 = p1; + if (a1.a != c1.a) return 1; + return 0; +} + +int pass2(struct s2 p2) +{ + struct s2 a2; + a2 = p2; + if (a2.a != c2.a) return 1; + if (a2.b != c2.b) return 1; + return 0; +} + +/*int pass3(struct s3 p3) +{ + struct s3 a3; + a3 = p3; + if (a3.a != c3.a) return 1; + if (a3.b != c3.b) return 1; + if (a3.c != c3.c) return 1; + return 0; +}*/ + +int pass4(struct s4 p4) +{ + struct s4 a4; + a4 = p4; + if (a4.a != c4.a) return 1; + if (a4.b != c4.b) return 1; + if (a4.c != c4.c) return 1; + if (a4.d != c4.d) return 1; + return 0; +} + +void reset(char* gg) +{ + char i; + for (i=0;i<5;++i) gg[i] = 128+i; +} + +int test(char* gg, char start) +{ + char i; + for (i=start;i<5;++i) + if (gg[i] != 128+i) return 1; + return 0; +} + +int main() +{ + /* Used to check #2022 bug condition of extra bytes being overwritten. */ + union + { + char gg[5]; + struct s1 g1; + struct s2 g2; + struct s3 g3; + struct s4 g4; + } guard; + + reset(guard.gg); + guard.g1 = return1(); + fail += compare1(guard.g1,c1); + fail += test(guard.gg,1); + + reset(guard.gg); + guard.g2 = return2(); + fail += compare2(guard.g2,c2); + fail += test(guard.gg,2); + + /*reset(guard.gg); + guard.g3 = return3(); + fail += compare3(guard.g3,c3); + fail += test(guard.gg,3);*/ + + reset(guard.gg); + guard.g4 = return4(); + fail += compare4(guard.g4,c4); + fail += test(guard.gg,4); + + fail += pass1(c1); + fail += pass2(c2); + /*fail += pass3(c3);*/ + fail += pass4(c4); + + return fail; +} From bf5b37a3b2b0a286e3cc14d1435f0ddbaa6cee4d Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 21:27:02 -0400 Subject: [PATCH 152/520] Error check for internal overflow of numerical constant See bug #2026 --- src/cc65/scanner.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 36fd1301b..055c02450 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -522,6 +522,7 @@ static void NumericConst (void) char C; unsigned DigitVal; unsigned long IVal; /* Value */ + int Overflow; /* Get the pp-number first, then parse on it */ CopyPPNumber (&Src); @@ -575,6 +576,7 @@ static void NumericConst (void) /* Since we now know the correct base, convert the input into a number */ SB_SetIndex (&Src, Index); IVal = 0; + Overflow = 0; while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) { DigitVal = HexVal (C); if (DigitVal >= Base) { @@ -582,9 +584,17 @@ static void NumericConst (void) SB_Clear (&Src); break; } - IVal = (IVal * Base) + DigitVal; + if ((((unsigned long)(IVal * Base)) / Base) != IVal) + Overflow = 1; + IVal = IVal * Base; + if (((unsigned long)(IVal + DigitVal)) < IVal) + Overflow = 1; + IVal += DigitVal; SB_Skip (&Src); } + if (Overflow) + Error ("Numerical constant \"%s\" too large for internal 32-bit representation", + SB_GetConstBuf (&Src)); /* Distinguish between integer and floating point constants */ if (!IsFloat) { From 409235aee65ce5fd807a50b32a2a4ba664aaab70 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 22:27:28 -0400 Subject: [PATCH 153/520] Optional warning for implicit constant conversion overflow --- doc/cc65.sgml | 2 ++ src/cc65/error.c | 2 ++ src/cc65/error.h | 1 + src/cc65/typeconv.c | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 683249bda..5a094571a 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -754,6 +754,8 @@ Here is a description of all the command line options: Warn about unused function parameters. <tag><tt/unused-var/</tag> Warn about unused variables. + <tag><tt/const-overflow/</tag> + Warn if numerical constant conversion implies overflow. (Disabled by default.) </descrip> The full list of available warning names can be retrieved by using the diff --git a/src/cc65/error.c b/src/cc65/error.c index 3f36d9e97..6ac3e594b 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -79,6 +79,7 @@ IntStack WarnUnusedLabel = INTSTACK(1); /* - unused labels */ IntStack WarnUnusedParam = INTSTACK(1); /* - unused parameters */ IntStack WarnUnusedVar = INTSTACK(1); /* - unused variables */ IntStack WarnUnusedFunc = INTSTACK(1); /* - unused functions */ +IntStack WarnConstOverflow = INTSTACK(0); /* - overflow conversion of numerical constants */ /* Map the name of a warning to the intstack that holds its state */ typedef struct WarnMapEntry WarnMapEntry; @@ -102,6 +103,7 @@ static WarnMapEntry WarnMap[] = { { &WarnUnusedLabel, "unused-label" }, { &WarnUnusedParam, "unused-param" }, { &WarnUnusedVar, "unused-var" }, + { &WarnConstOverflow, "const-overflow" }, }; Collection DiagnosticStrBufs; diff --git a/src/cc65/error.h b/src/cc65/error.h index 7fcb03467..83be8c782 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -76,6 +76,7 @@ extern IntStack WarnUnusedLabel; /* - unused labels */ extern IntStack WarnUnusedParam; /* - unused parameters */ extern IntStack WarnUnusedVar; /* - unused variables */ extern IntStack WarnUnusedFunc; /* - unused functions */ +extern IntStack WarnConstOverflow; /* - overflow conversion of numerical constants */ /* Forward */ struct StrBuf; diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index f77ec3951..49dfcc597 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -128,6 +128,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** internally already represented by a long. */ if (NewBits <= OldBits) { + unsigned long OldVal = Expr->IVal; /* Cut the value to the new size */ Expr->IVal &= (0xFFFFFFFFUL >> (32 - NewBits)); @@ -139,6 +140,10 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) Expr->IVal |= shl_l (~0UL, NewBits); } } + + if ((OldVal != Expr->IVal) && IS_Get (&WarnConstOverflow)) { + Warning ("Implicit conversion of constant overflows %d-bit destination", NewBits); + } } /* Do the integer constant <-> absolute address conversion if necessary */ From 65f773f5ee96d3d872fe6fd06fba079fa6b64c6a Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 01:01:21 -0400 Subject: [PATCH 154/520] Explicit z: should suppress "Suspicious address expression" warning #194 --- src/ca65/ea.h | 5 +++++ src/ca65/ea65.c | 2 ++ src/ca65/instr.c | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ca65/ea.h b/src/ca65/ea.h index d861e9a6c..487027c02 100644 --- a/src/ca65/ea.h +++ b/src/ca65/ea.h @@ -43,6 +43,10 @@ /*****************************************************************************/ +/* EffAddr Flags */ +#define EFFADDR_OVERRIDE_ZP 0x00000001UL + + /* GetEA result struct */ typedef struct EffAddr EffAddr; @@ -51,6 +55,7 @@ struct EffAddr { unsigned long AddrModeSet; /* Possible addressing modes */ struct ExprNode* Expr; /* Expression if any (NULL otherwise) */ unsigned Reg; /* Register number in sweet16 mode */ + unsigned long Flags; /* Other properties */ /* The following fields are used inside instr.c */ unsigned AddrMode; /* Actual addressing mode used */ diff --git a/src/ca65/ea65.c b/src/ca65/ea65.c index 275d90b56..5bd2ba82b 100644 --- a/src/ca65/ea65.c +++ b/src/ca65/ea65.c @@ -72,11 +72,13 @@ void GetEA (EffAddr* A) /* Clear the output struct */ A->AddrModeSet = 0; A->Expr = 0; + A->Flags = 0; /* Handle an addressing size override */ switch (CurTok.Tok) { case TOK_OVERRIDE_ZP: Restrictions = AM65_DIR | AM65_DIR_X | AM65_DIR_Y; + A->Flags |= EFFADDR_OVERRIDE_ZP; NextTok (); break; diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 4f95ec75c..89162c3c6 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1269,7 +1269,8 @@ static int EvalEA (const InsDesc* Ins, EffAddr* A) ExprNode* Left = A->Expr->Left; if ((A->Expr->Op == EXPR_BYTE0 || A->Expr->Op == EXPR_BYTE1) && Left->Op == EXPR_SYMBOL && - GetSymAddrSize (Left->V.Sym) != ADDR_SIZE_ZP) { + GetSymAddrSize (Left->V.Sym) != ADDR_SIZE_ZP && + !(A->Flags & EFFADDR_OVERRIDE_ZP)) { /* Output a warning */ Warning (1, "Suspicious address expression"); From 56c715af40ed1cad1a3b2a29de50e7ceaa9911e4 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 01:14:27 -0400 Subject: [PATCH 155/520] Error for struct/union with a duplicate member #2015 --- src/cc65/symtab.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 72a2ac007..d9270f604 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -793,6 +793,8 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) */ Error ("Redeclaration of enumerator constant '%s'", Sym->Name); Sym = 0; + } else if (Flags & SC_STRUCTFIELD) { + Error ("Duplicate member '%s'", Sym->Name); } } } From 4d698bf18c89e8426e4ba8a37e4b72d1d51ee7ba Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 03:05:14 -0400 Subject: [PATCH 156/520] Don't use a,x,y in macro parameter example, document why not. #392 --- doc/ca65.sgml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 360b0bab2..2f95a032e 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -4262,8 +4262,13 @@ macro actually takes in the definition. You may also leave intermediate parameters empty. Empty parameters are replaced by empty space (that is, they are removed when the macro is expanded). If you have a look at our macro definition above, you will see, that replacing the "addr" parameter -by nothing will lead to wrong code in most lines. To help you, writing -macros with a variable parameter list, there are some control commands: +by nothing will lead to wrong code in most lines. + +The names "a", "x" and "y" should be avoided for macro parameters, as these +will usually conflict with the 6502 registers. + +For writing macros with a variable parameter list, control commands are +available: <tt><ref id=".IFBLANK" name=".IFBLANK"></tt> tests the rest of the line and returns true, if there are any tokens on the remainder of the line. Since @@ -4274,15 +4279,15 @@ opposite. Look at this example: <tscreen><verb> -.macro ldaxy a, x, y -.ifnblank a - lda #a +.macro ldaxy i, j, k +.ifnblank i + lda #i .endif -.ifnblank x - ldx #x +.ifnblank j + ldx #j .endif -.ifnblank y - ldy #y +.ifnblank k + ldy #k .endif .endmacro </verb></tscreen> From 2b60adfa11ad6b348b70cc224337f1cac6b4c125 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 03:26:10 -0400 Subject: [PATCH 157/520] Document directives that use SetBoolOption "can" use +/- rather than "must" #1772 --- doc/ca65.sgml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 360b0bab2..72a133299 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2283,7 +2283,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH Switch on or off case sensitivity on identifiers. The default is off (that is, identifiers are case sensitive), but may be changed by the -i switch on the command line. - The command must be followed by a '+' or '-' character to switch the + The command can be followed by a '+' or '-' character to switch the option on or off respectively. Example: @@ -2432,7 +2432,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH Switch on or off debug info generation. The default is off (that is, the object file will not contain debug infos), but may be changed by the -g switch on the command line. - The command must be followed by a '+' or '-' character to switch the + The command can be followed by a '+' or '-' character to switch the option on or off respectively. Example: @@ -3380,7 +3380,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH Note: Line continuations do not work in a comment. A backslash at the end of a comment is treated as part of the comment and does not trigger line continuation. - The command must be followed by a '+' or '-' character to switch the + The command can be followed by a '+' or '-' character to switch the option on or off respectively. Example: @@ -3395,7 +3395,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH <sect1><tt>.LIST</tt><label id=".LIST"><p> - Enable output to the listing. The command must be followed by a boolean + Enable output to the listing. The command can be followed by a boolean switch ("on", "off", "+" or "-") and will enable or disable listing output. The option has no effect if the listing is not enabled by the command line @@ -4040,7 +4040,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" <sect1><tt>.SMART</tt><label id=".SMART"><p> - Switch on or off smart mode. The command must be followed by a '+' or '-' + Switch on or off smart mode. The command can be followed by a '+' or '-' character to switch the option on or off respectively. The default is off (that is, the assembler doesn't try to be smart), but this default may be changed by the -s switch on the command line. From 86e3a640d5f785311dea52a266ef16bf290d9e0c Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 05:03:11 -0400 Subject: [PATCH 158/520] Support for three line ending types: \r, \r\n, \n. #1894 --- src/ca65/scanner.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index bf0a85183..d32939646 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -112,6 +112,7 @@ struct CharSource { CharSource* Next; /* Linked list of char sources */ token_t Tok; /* Last token */ int C; /* Last character */ + int SkipN; /* For '\r\n' line endings, skip '\n\ if next */ const CharSourceFunctions* Func; /* Pointer to function table */ union { InputFile File; /* File data */ @@ -325,6 +326,7 @@ static void UseCharSource (CharSource* S) Source = S; /* Read the first character from the new file */ + S->SkipN = 0; S->Func->NextChar (S); /* Setup the next token so it will be skipped on the next call to @@ -386,6 +388,10 @@ static void IFNextChar (CharSource* S) while (1) { int N = fgetc (S->V.File.F); + if (N == '\n' && S->SkipN) + N = fgetc (S->V.File.F); + S->SkipN = 0; + if (N == EOF) { /* End of file. Accept files without a newline at the end */ if (SB_NotEmpty (&S->V.File.Line)) { @@ -401,9 +407,12 @@ static void IFNextChar (CharSource* S) /* Check for end of line */ } else if (N == '\n') { - /* End of line */ break; + } else if (N == '\r') { + /* End of line, skip '\n' if it's the next character */ + S->SkipN = 1; + break; /* Collect other stuff */ } else { From 456fa9f963dcba838a135416a5d52957b54c3ffb Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 05:36:37 -0400 Subject: [PATCH 159/520] cc65 document: both pass and return of structs are allowed --- doc/cc65.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 023204f4d..1476b40a3 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -806,8 +806,8 @@ and the one defined by the ISO standard: <item> The datatypes "float" and "double" are not available. <p> -<item> C Functions may return structs (or unions) by value, but only of - 1, 2 or 4 byte sizes. +<item> C Functions may pass and return structs (or unions) by value, but only + of 1, 2 or 4 byte sizes. <p> <item> Most of the C library is available with only the fastcall calling convention (<ref id="extension-fastcall" name="see below">). It means From 6ffc4004d7d7bbae4ba48aba53263545d651d750 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Wed, 3 May 2023 14:24:13 +0200 Subject: [PATCH 160/520] Revert "Forbid 3-byte struct pass/return by value, document struct value pass/return" --- doc/cc65.sgml | 7 +- src/cc65/datatype.c | 1 + test/val/struct-by-value.c | 144 ------------------------------------- 3 files changed, 5 insertions(+), 147 deletions(-) delete mode 100644 test/val/struct-by-value.c diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 1476b40a3..683249bda 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -741,7 +741,7 @@ Here is a description of all the command line options: <tag><tt/return-type/</tag> Warn about no return statement in function returning non-void. <tag><tt/struct-param/</tag> - Warn when passing structs by value. (Disabled by default.) + Warn when passing structs by value. <tag><tt/unknown-pragma/</tag> Warn about #pragmas that aren't recognized by cc65. <tag><tt/unreachable-code/</tag> @@ -806,8 +806,9 @@ and the one defined by the ISO standard: <item> The datatypes "float" and "double" are not available. <p> -<item> C Functions may pass and return structs (or unions) by value, but only - of 1, 2 or 4 byte sizes. +<item> C Functions may not return structs (or unions), and structs may not + be passed as parameters by value. However, struct assignment *is* + possible. <p> <item> Most of the C library is available with only the fastcall calling convention (<ref id="extension-fastcall" name="see below">). It means diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index caa41a7a4..6907ee099 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -803,6 +803,7 @@ const Type* GetStructReplacementType (const Type* SType) switch (SizeOf (SType)) { case 1: NewType = type_uchar; break; case 2: NewType = type_uint; break; + case 3: /* FALLTHROUGH */ case 4: NewType = type_ulong; break; default: NewType = SType; break; } diff --git a/test/val/struct-by-value.c b/test/val/struct-by-value.c deleted file mode 100644 index 0e846c117..000000000 --- a/test/val/struct-by-value.c +++ /dev/null @@ -1,144 +0,0 @@ -/* Test of passing and returning structs by value. - Structs of 1, 2 and 4 bytes are supported. - Note that structs of 3 bytes are disabled, see: - https://github.com/cc65/cc65/issues/2022 -*/ - -int fail = 0; - -struct s1 { char a; }; -struct s2 { char a, b; }; -struct s3 { char a, b, c; }; -struct s4 { char a, b, c, d; }; - -const struct s1 c1 = { 1 }; -const struct s2 c2 = { 2, 3 }; -const struct s3 c3 = { 4, 5, 6 }; -const struct s4 c4 = { 7, 8, 9, 10 }; - -struct s1 return1() { return c1; } -struct s2 return2() { return c2; } -/*struct s3 return3() { return c3; }*/ -struct s4 return4() { return c4; } - -int compare1(struct s1 a, struct s1 b) -{ - if (a.a != b.a) return 1; - return 0; -} - -int compare2(struct s2 a, struct s2 b) -{ - if (a.a != b.a) return 1; - if (a.b != b.b) return 1; - return 0; -} - -/*int compare3(struct s3 a, struct s3 b) -{ - if (a.a != b.a) return 1; - if (a.b != b.b) return 1; - if (a.c != b.c) return 1; - return 0; -}*/ - -int compare4(struct s4 a, struct s4 b) -{ - if (a.a != b.a) return 1; - if (a.b != b.b) return 1; - if (a.c != b.c) return 1; - if (a.d != b.d) return 1; - return 0; -} - -int pass1(struct s1 p1) -{ - struct s1 a1; - a1 = p1; - if (a1.a != c1.a) return 1; - return 0; -} - -int pass2(struct s2 p2) -{ - struct s2 a2; - a2 = p2; - if (a2.a != c2.a) return 1; - if (a2.b != c2.b) return 1; - return 0; -} - -/*int pass3(struct s3 p3) -{ - struct s3 a3; - a3 = p3; - if (a3.a != c3.a) return 1; - if (a3.b != c3.b) return 1; - if (a3.c != c3.c) return 1; - return 0; -}*/ - -int pass4(struct s4 p4) -{ - struct s4 a4; - a4 = p4; - if (a4.a != c4.a) return 1; - if (a4.b != c4.b) return 1; - if (a4.c != c4.c) return 1; - if (a4.d != c4.d) return 1; - return 0; -} - -void reset(char* gg) -{ - char i; - for (i=0;i<5;++i) gg[i] = 128+i; -} - -int test(char* gg, char start) -{ - char i; - for (i=start;i<5;++i) - if (gg[i] != 128+i) return 1; - return 0; -} - -int main() -{ - /* Used to check #2022 bug condition of extra bytes being overwritten. */ - union - { - char gg[5]; - struct s1 g1; - struct s2 g2; - struct s3 g3; - struct s4 g4; - } guard; - - reset(guard.gg); - guard.g1 = return1(); - fail += compare1(guard.g1,c1); - fail += test(guard.gg,1); - - reset(guard.gg); - guard.g2 = return2(); - fail += compare2(guard.g2,c2); - fail += test(guard.gg,2); - - /*reset(guard.gg); - guard.g3 = return3(); - fail += compare3(guard.g3,c3); - fail += test(guard.gg,3);*/ - - reset(guard.gg); - guard.g4 = return4(); - fail += compare4(guard.g4,c4); - fail += test(guard.gg,4); - - fail += pass1(c1); - fail += pass2(c2); - /*fail += pass3(c3);*/ - fail += pass4(c4); - - return fail; -} From 387d455cb45d7fc7249ffe1e1d927fb0300e897d Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Wed, 3 May 2023 11:16:22 -0400 Subject: [PATCH 161/520] Revised patch. Uses code in source listing. Tested on a real OSI C1P machine. --- libsrc/osic1p/bootstrap.s | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index 52ce31f84..0d8a74eb7 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -35,7 +35,6 @@ ram_top := __MAIN_START__ + __MAIN_SIZE__ .ifdef ASM .include "screen-c1p-24x24.s" - .macpack generic load := $08 ; private variables count := $0A @@ -51,16 +50,14 @@ LINEDIST = $20 ; Offset in video RAM between two lines sta load stx load+1 - lda #<load_size - eor #$FF - sta count - lda #>load_size - eor #$FF - sta count+1 + ldx #(<load_size) + 1 + stx count + ldx #(>load_size) + 1 + stx count+1 ; save size with each byte incremented separately -L1: inc count +L1: dec count bne L2 - inc count+1 + dec count+1 beq L3 L2: jsr GETCHAR ; (doesn't change .Y) sta (load),y @@ -114,18 +111,15 @@ CR = $0D hex2 >load_addr .byte CR, "85", CR, "08", CR .byte "86", CR, "09", CR - .byte "A9", CR - hex2 <load_size - .byte CR, "49", CR, "FF", CR - .byte "85", CR, "0A", CR - .byte "A9", CR - hex2 >load_size - .byte CR, "49", CR, "FF", CR - .byte "85", CR, "0B", CR - - .byte "E6", CR, "0A", CR + .byte "A2", CR + hex2 (<load_size) + 1 + .byte CR, "86", CR, "0A", CR + .byte "A2", CR + hex2 (>load_size) + 1 + .byte CR, "86", CR, "0B", CR + .byte "C6", CR, "0A", CR .byte "D0", CR, "04", CR - .byte "E6", CR, "0B", CR + .byte "C6", CR, "0B", CR .byte "F0", CR, "16", CR .byte "20", CR, "BF", CR, "FF", CR .byte "91", CR, "08", CR From 016008b6df5a1a0283a29ba1e18255e39a229dd5 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 11:57:50 -0400 Subject: [PATCH 162/520] ca65: Suppress '.size' error for multiply-defined symbols --- src/ca65/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ca65/main.c b/src/ca65/main.c index 7183ff046..d45201cad 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -858,7 +858,11 @@ static void OneLine (void) /* The line has switched the segment */ Size = 0; } - DefSizeOfSymbol (Sym, Size); + /* Suppress .size Symbol if this Symbol already has a multiply-defined error, + ** as it will only create its own additional unnecessary error. + */ + if ((Sym->Flags & SF_MULTDEF) == 0) + DefSizeOfSymbol (Sym, Size); } /* Line separator must come here */ From 4e6b94de5cf3e073897c610e09c372d9ac36e57c Mon Sep 17 00:00:00 2001 From: Brad Smith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 12:19:05 -0400 Subject: [PATCH 163/520] braces --- src/ca65/scanner.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index d32939646..add365e84 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -388,8 +388,9 @@ static void IFNextChar (CharSource* S) while (1) { int N = fgetc (S->V.File.F); - if (N == '\n' && S->SkipN) + if (N == '\n' && S->SkipN) { N = fgetc (S->V.File.F); + } S->SkipN = 0; if (N == EOF) { From 1f18ab218ed96d9d7eaf3aecb97d1a7764bb15fd Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 13:35:51 -0400 Subject: [PATCH 164/520] Improve struct size error message to include the detected size --- src/cc65/function.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/function.c b/src/cc65/function.c index 39f04843f..38a8f45aa 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -601,7 +601,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) ** We don't currently support this case. */ if (RType == Param->Type) { - Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); + Error ("Passing '%s' of this size (%d) by value is not supported", GetFullTypeName (Param->Type), SizeOf (RType)); } } From ae7a1416fe53de39b991c26a8dc2050a6e98fbe4 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Wed, 3 May 2023 20:02:12 +0200 Subject: [PATCH 165/520] Revert "Revert "Forbid 3-byte struct pass/return by value, document struct value pass/return"" --- doc/cc65.sgml | 7 +- src/cc65/datatype.c | 1 - test/val/struct-by-value.c | 144 +++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 test/val/struct-by-value.c diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 683249bda..1476b40a3 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -741,7 +741,7 @@ Here is a description of all the command line options: <tag><tt/return-type/</tag> Warn about no return statement in function returning non-void. <tag><tt/struct-param/</tag> - Warn when passing structs by value. + Warn when passing structs by value. (Disabled by default.) <tag><tt/unknown-pragma/</tag> Warn about #pragmas that aren't recognized by cc65. <tag><tt/unreachable-code/</tag> @@ -806,9 +806,8 @@ and the one defined by the ISO standard: <item> The datatypes "float" and "double" are not available. <p> -<item> C Functions may not return structs (or unions), and structs may not - be passed as parameters by value. However, struct assignment *is* - possible. +<item> C Functions may pass and return structs (or unions) by value, but only + of 1, 2 or 4 byte sizes. <p> <item> Most of the C library is available with only the fastcall calling convention (<ref id="extension-fastcall" name="see below">). It means diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6907ee099..caa41a7a4 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -803,7 +803,6 @@ const Type* GetStructReplacementType (const Type* SType) switch (SizeOf (SType)) { case 1: NewType = type_uchar; break; case 2: NewType = type_uint; break; - case 3: /* FALLTHROUGH */ case 4: NewType = type_ulong; break; default: NewType = SType; break; } diff --git a/test/val/struct-by-value.c b/test/val/struct-by-value.c new file mode 100644 index 000000000..0e846c117 --- /dev/null +++ b/test/val/struct-by-value.c @@ -0,0 +1,144 @@ +/* Test of passing and returning structs by value. + Structs of 1, 2 and 4 bytes are supported. + Note that structs of 3 bytes are disabled, see: + https://github.com/cc65/cc65/issues/2022 +*/ + +int fail = 0; + +struct s1 { char a; }; +struct s2 { char a, b; }; +struct s3 { char a, b, c; }; +struct s4 { char a, b, c, d; }; + +const struct s1 c1 = { 1 }; +const struct s2 c2 = { 2, 3 }; +const struct s3 c3 = { 4, 5, 6 }; +const struct s4 c4 = { 7, 8, 9, 10 }; + +struct s1 return1() { return c1; } +struct s2 return2() { return c2; } +/*struct s3 return3() { return c3; }*/ +struct s4 return4() { return c4; } + +int compare1(struct s1 a, struct s1 b) +{ + if (a.a != b.a) return 1; + return 0; +} + +int compare2(struct s2 a, struct s2 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + return 0; +} + +/*int compare3(struct s3 a, struct s3 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + return 0; +}*/ + +int compare4(struct s4 a, struct s4 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + if (a.d != b.d) return 1; + return 0; +} + +int pass1(struct s1 p1) +{ + struct s1 a1; + a1 = p1; + if (a1.a != c1.a) return 1; + return 0; +} + +int pass2(struct s2 p2) +{ + struct s2 a2; + a2 = p2; + if (a2.a != c2.a) return 1; + if (a2.b != c2.b) return 1; + return 0; +} + +/*int pass3(struct s3 p3) +{ + struct s3 a3; + a3 = p3; + if (a3.a != c3.a) return 1; + if (a3.b != c3.b) return 1; + if (a3.c != c3.c) return 1; + return 0; +}*/ + +int pass4(struct s4 p4) +{ + struct s4 a4; + a4 = p4; + if (a4.a != c4.a) return 1; + if (a4.b != c4.b) return 1; + if (a4.c != c4.c) return 1; + if (a4.d != c4.d) return 1; + return 0; +} + +void reset(char* gg) +{ + char i; + for (i=0;i<5;++i) gg[i] = 128+i; +} + +int test(char* gg, char start) +{ + char i; + for (i=start;i<5;++i) + if (gg[i] != 128+i) return 1; + return 0; +} + +int main() +{ + /* Used to check #2022 bug condition of extra bytes being overwritten. */ + union + { + char gg[5]; + struct s1 g1; + struct s2 g2; + struct s3 g3; + struct s4 g4; + } guard; + + reset(guard.gg); + guard.g1 = return1(); + fail += compare1(guard.g1,c1); + fail += test(guard.gg,1); + + reset(guard.gg); + guard.g2 = return2(); + fail += compare2(guard.g2,c2); + fail += test(guard.gg,2); + + /*reset(guard.gg); + guard.g3 = return3(); + fail += compare3(guard.g3,c3); + fail += test(guard.gg,3);*/ + + reset(guard.gg); + guard.g4 = return4(); + fail += compare4(guard.g4,c4); + fail += test(guard.gg,4); + + fail += pass1(c1); + fail += pass2(c2); + /*fail += pass3(c3);*/ + fail += pass4(c4); + + return fail; +} From 440c91fad96e6b94cdec1e4cf249dc0ca0d28fe6 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 14:11:30 -0400 Subject: [PATCH 166/520] braces for 1-line if --- src/ca65/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ca65/main.c b/src/ca65/main.c index d45201cad..fedbb0d4b 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -861,8 +861,9 @@ static void OneLine (void) /* Suppress .size Symbol if this Symbol already has a multiply-defined error, ** as it will only create its own additional unnecessary error. */ - if ((Sym->Flags & SF_MULTDEF) == 0) + if ((Sym->Flags & SF_MULTDEF) == 0) { DefSizeOfSymbol (Sym, Size); + } } /* Line separator must come here */ From 9a502c69dc9d3c4c29791b75d9f03796487e3cc7 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 16:46:59 -0400 Subject: [PATCH 167/520] fix tab, braces for 1-line if, Expr->Ival is signed --- src/cc65/error.c | 2 +- src/cc65/scanner.c | 9 ++++++--- src/cc65/typeconv.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cc65/error.c b/src/cc65/error.c index 6ac3e594b..39b067825 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -103,7 +103,7 @@ static WarnMapEntry WarnMap[] = { { &WarnUnusedLabel, "unused-label" }, { &WarnUnusedParam, "unused-param" }, { &WarnUnusedVar, "unused-var" }, - { &WarnConstOverflow, "const-overflow" }, + { &WarnConstOverflow, "const-overflow" }, }; Collection DiagnosticStrBufs; diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 055c02450..ec49d0e3c 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -584,17 +584,20 @@ static void NumericConst (void) SB_Clear (&Src); break; } - if ((((unsigned long)(IVal * Base)) / Base) != IVal) + if ((((unsigned long)(IVal * Base)) / Base) != IVal) { Overflow = 1; + } IVal = IVal * Base; - if (((unsigned long)(IVal + DigitVal)) < IVal) + if (((unsigned long)(IVal + DigitVal)) < IVal) { Overflow = 1; + } IVal += DigitVal; SB_Skip (&Src); } - if (Overflow) + if (Overflow) { Error ("Numerical constant \"%s\" too large for internal 32-bit representation", SB_GetConstBuf (&Src)); + } /* Distinguish between integer and floating point constants */ if (!IsFloat) { diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 49dfcc597..e1d95ff63 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -128,7 +128,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** internally already represented by a long. */ if (NewBits <= OldBits) { - unsigned long OldVal = Expr->IVal; + long OldVal = Expr->IVal; /* Cut the value to the new size */ Expr->IVal &= (0xFFFFFFFFUL >> (32 - NewBits)); From a109f475ed362729c6c5aa109cac6ba759e12330 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 23:49:40 -0400 Subject: [PATCH 168/520] Fix broken/incomplete floating point parsing - Fractional digit scale was broken - Base was partially ignored - Exponent sign was ignored - Exponent for hex float is 2 not 10 --- src/cc65/scanner.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 36fd1301b..634ec39bb 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -687,20 +687,21 @@ static void NumericConst (void) /* Check for a fractional part and read it */ if (SB_Peek (&Src) == '.') { - Double Scale; + Double Scale, ScaleDigit; /* Skip the dot */ SB_Skip (&Src); /* Read fractional digits */ - Scale = FP_D_Make (1.0); + ScaleDigit = FP_D_Div (FP_D_Make (1.0), FP_D_FromInt (Base)); + Scale = ScaleDigit; while (IsXDigit (SB_Peek (&Src)) && (DigitVal = HexVal (SB_Peek (&Src))) < Base) { /* Get the value of this digit */ - Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale); + Double FracVal = FP_D_Mul (FP_D_FromInt (DigitVal), Scale); /* Add it to the float value */ FVal = FP_D_Add (FVal, FracVal); - /* Scale base */ - Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal)); + /* Adjust Scale for next digit */ + Scale = FP_D_Mul (Scale, ScaleDigit); /* Skip the digit */ SB_Skip (&Src); } @@ -712,12 +713,15 @@ static void NumericConst (void) unsigned Digits; unsigned Exp; + int Sign; /* Skip the exponent notifier */ SB_Skip (&Src); /* Read an optional sign */ + Sign = 0; if (SB_Peek (&Src) == '-') { + Sign = 1; SB_Skip (&Src); } else if (SB_Peek (&Src) == '+') { SB_Skip (&Src); @@ -747,9 +751,11 @@ static void NumericConst (void) Warning ("Floating constant exponent is too large"); } - /* Scale the exponent and adjust the value accordingly */ + /* Scale the exponent and adjust the value accordingly. + ** Decimal exponents are base 10, hexadecimal exponents are base 2 (C99). + */ if (Exp) { - FVal = FP_D_Mul (FVal, FP_D_Make (pow (10, Exp))); + FVal = FP_D_Mul (FVal, FP_D_Make (pow ((Base == 16) ? 2.0 : 10.0, (Sign ? -1.0 : 1.0) * Exp))); } } From a686d1fa8e4a69cca3761411201874dce3a3059c Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 23:50:01 -0400 Subject: [PATCH 169/520] Allow unary +/- for floating point constants --- src/cc65/expr.c | 51 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 691010b0a..42b9cda53 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1901,31 +1901,46 @@ static void UnaryOp (ExprDesc* Expr) /* Get the expression */ hie10 (Expr); - /* We can only handle integer types */ - if (!IsClassInt (Expr->Type)) { - Error ("Argument must have integer type"); - ED_MakeConstAbsInt (Expr, 1); - } - /* Check for a constant numeric expression */ if (ED_IsConstAbs (Expr)) { - /* Value is numeric */ - switch (Tok) { - case TOK_MINUS: Expr->IVal = -Expr->IVal; break; - case TOK_PLUS: break; - case TOK_COMP: Expr->IVal = ~Expr->IVal; break; - default: Internal ("Unexpected token: %d", Tok); + + if (IsClassFloat (Expr->Type)) { + switch (Tok) { + case TOK_MINUS: Expr->V.FVal = FP_D_Sub(FP_D_Make(0.0),Expr->V.FVal); break; + case TOK_PLUS: break; + case TOK_COMP: Error ("Unary ~ operator not valid for floating point constant"); break; + default: Internal ("Unexpected token: %d", Tok); + } + } else { + if (!IsClassInt (Expr->Type)) { + Error ("Constant argument must have integer or float type"); + ED_MakeConstAbsInt (Expr, 1); + } + + /* Value is numeric */ + switch (Tok) { + case TOK_MINUS: Expr->IVal = -Expr->IVal; break; + case TOK_PLUS: break; + case TOK_COMP: Expr->IVal = ~Expr->IVal; break; + 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, 1); } - /* Adjust the type of the expression */ - Expr->Type = IntPromotion (Expr->Type); - - /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr, 1); - } else { unsigned Flags; + /* If not constant, we can only handle integer types */ + if (!IsClassInt (Expr->Type)) { + Error ("Non-constant argument must have integer type"); + ED_MakeConstAbsInt (Expr, 1); + } + /* Value is not constant */ LoadExpr (CF_NONE, Expr); From 52f0e6a29cb946da2fca88740012981de3814f51 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 2 May 2023 23:57:32 -0400 Subject: [PATCH 170/520] Allow floating point constants to be converted to integer (warning if loss of precision) --- src/cc65/typeconv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index f77ec3951..004072c03 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -123,6 +123,16 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** to handle sign extension correctly. */ + /* If this is a floating point constant, convert to integer, + ** and warn if precision is discarded. + */ + if (IsClassFloat (OldType) && IsClassInt (NewType)) { + long IVal = (long)Expr->V.FVal.V; + if (Expr->V.FVal.V != FP_D_FromInt(IVal).V) + Warning ("Floating point constant (%f) converted to integer loses precision (%d)",Expr->V.FVal.V,IVal); + Expr->IVal = IVal; + } + /* Check if the new datatype will have a smaller range. If it ** has a larger range, things are OK, since the value is ** internally already represented by a long. From 7ff74b2c472d5feff62e228a8eebb22d6ccdbc2b Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 00:06:09 -0400 Subject: [PATCH 171/520] Give a better error for unsupported floating point arithmetic, instead of internal "Precondition violated" error. --- src/cc65/datatype.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6907ee099..103a3a634 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -670,6 +670,10 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) ** floating point types are not (yet) supported. ** The integral promotions are performed on both operands. */ + if (IsClassFloat(lhst) || IsClassFloat(rhst)) { + Error ("Floating point arithmetic not supported."); + return type_long; + } lhst = IntPromotion (lhst); rhst = IntPromotion (rhst); From e3887d7ead27ac092045b0e54e639b288513b163 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 00:12:36 -0400 Subject: [PATCH 172/520] Test to demonstrate availability of floating point constants, document the possibility. --- doc/cc65.sgml | 3 +++ test/val/float-const-convert.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 test/val/float-const-convert.c diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 683249bda..af85a057f 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -805,6 +805,9 @@ and the one defined by the ISO standard: <itemize> <item> The datatypes "float" and "double" are not available. + Floating point constants may be used, though they will have to be + converted and stored into integer values. + Floating point arithmetic expressions are not supported. <p> <item> C Functions may not return structs (or unions), and structs may not be passed as parameters by value. However, struct assignment *is* diff --git a/test/val/float-const-convert.c b/test/val/float-const-convert.c new file mode 100644 index 000000000..729595e0f --- /dev/null +++ b/test/val/float-const-convert.c @@ -0,0 +1,14 @@ +/* Demonstrates that floating point constants are allowed in a limited way. + Value will be converted to an int, with a warning if precision is lost. */ + +int a = 3.0; +int b = 23.1; +int c = -5.0; + +int main(void) +{ + if (a != 3) return 1; + if (b != 23) return 2; + if (c != -5) return 3; + return 0; +} From 2ac9c6f51efd8a6a89e147d2f4a3de13696cbfcd Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 01:44:49 -0400 Subject: [PATCH 173/520] Suppress the floating point precision warning if an explicit cast is used --- src/cc65/typeconv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 004072c03..4e2249836 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -55,7 +55,7 @@ -static void DoConversion (ExprDesc* Expr, const Type* NewType) +static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) /* Emit code to convert the given expression to a new type. */ { const Type* OldType; @@ -128,8 +128,9 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) */ if (IsClassFloat (OldType) && IsClassInt (NewType)) { long IVal = (long)Expr->V.FVal.V; - if (Expr->V.FVal.V != FP_D_FromInt(IVal).V) + if ((Expr->V.FVal.V != FP_D_FromInt(IVal).V) && !Explicit) { Warning ("Floating point constant (%f) converted to integer loses precision (%d)",Expr->V.FVal.V,IVal); + } Expr->IVal = IVal; } @@ -293,7 +294,7 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Both types must be complete */ if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) { /* Do the actual conversion */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 0); } else { /* We should have already generated error elsewhere so that we ** could just silently fail here to avoid excess errors, but to @@ -340,7 +341,7 @@ void TypeCast (ExprDesc* Expr) ReplaceType (Expr, NewType); } else if (IsCastType (Expr->Type)) { /* Convert the value. The result has always the new type */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 1); } else { TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, "Cast to incompatible type '%s' from '%s'"); From dbdadaa3f3c647476723b5ed2bb80806e5543457 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 16:59:48 -0400 Subject: [PATCH 174/520] accidental tabs, printf long expectts explicit %ld --- src/cc65/datatype.c | 4 ++-- src/cc65/typeconv.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 103a3a634..9334db40b 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -671,8 +671,8 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) ** The integral promotions are performed on both operands. */ if (IsClassFloat(lhst) || IsClassFloat(rhst)) { - Error ("Floating point arithmetic not supported."); - return type_long; + Error ("Floating point arithmetic not supported."); + return type_long; } lhst = IntPromotion (lhst); rhst = IntPromotion (rhst); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 4e2249836..1003b3c90 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -129,7 +129,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) if (IsClassFloat (OldType) && IsClassInt (NewType)) { long IVal = (long)Expr->V.FVal.V; if ((Expr->V.FVal.V != FP_D_FromInt(IVal).V) && !Explicit) { - Warning ("Floating point constant (%f) converted to integer loses precision (%d)",Expr->V.FVal.V,IVal); + Warning ("Floating point constant (%f) converted to integer loses precision (%ld)",Expr->V.FVal.V,IVal); } Expr->IVal = IVal; } From 49bd5681136fc4afff5f46f796eeb777c0e691d1 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 17:55:02 -0400 Subject: [PATCH 175/520] error test for integer constant too large for internal representation --- test/err/huge-integer-constant.c | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/err/huge-integer-constant.c diff --git a/test/err/huge-integer-constant.c b/test/err/huge-integer-constant.c new file mode 100644 index 000000000..1f423347c --- /dev/null +++ b/test/err/huge-integer-constant.c @@ -0,0 +1,7 @@ +/* too big for internal integer representation */ +unsigned long huge = 4294967296; + +int main(void) +{ + return 0; +} From e3cb8dfb9be6e4f9244fdecba6610f6a72116763 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 19:27:37 -0400 Subject: [PATCH 176/520] Numerical constant scanner requires explicitly 32-bit sized type for cross-platform consistency --- src/cc65/scanner.c | 16 +++++++++++----- test/val/common.h | 1 + test/val/cq241.c | 19 +++++++++++++++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index ec49d0e3c..f747fb458 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -39,6 +39,7 @@ #include <errno.h> #include <ctype.h> #include <math.h> +#include <inttypes.h> /* common */ #include "chartype.h" @@ -151,6 +152,11 @@ static const struct Keyword { #define IT_ULONG 0x08 +/* Internal type for numeric constant scanning. +** Size must be explicit for cross-platform uniformity. +*/ +typedef uint32_t scan_t; + /*****************************************************************************/ /* code */ @@ -521,7 +527,7 @@ static void NumericConst (void) int IsFloat; char C; unsigned DigitVal; - unsigned long IVal; /* Value */ + scan_t IVal; /* Scanned value. */ int Overflow; /* Get the pp-number first, then parse on it */ @@ -584,19 +590,19 @@ static void NumericConst (void) SB_Clear (&Src); break; } - if ((((unsigned long)(IVal * Base)) / Base) != IVal) { + if (((scan_t)(IVal * Base) / Base) != IVal) { Overflow = 1; } IVal = IVal * Base; - if (((unsigned long)(IVal + DigitVal)) < IVal) { + if (((scan_t)(IVal + DigitVal)) < IVal) { Overflow = 1; } IVal += DigitVal; SB_Skip (&Src); } if (Overflow) { - Error ("Numerical constant \"%s\" too large for internal 32-bit representation", - SB_GetConstBuf (&Src)); + Error ("Numerical constant \"%s\" too large for internal %d-bit representation", + SB_GetConstBuf (&Src), (int)(sizeof(IVal)*8)); } /* Distinguish between integer and floating point constants */ diff --git a/test/val/common.h b/test/val/common.h index dada61a14..61da6c325 100644 --- a/test/val/common.h +++ b/test/val/common.h @@ -20,3 +20,4 @@ #define SIZEOF_LONG_32BIT #define UNSIGNED_CHARS #define UNSIGNED_BITFIELDS +#define INTEGER_CONSTANT_MAX_32BIT diff --git a/test/val/cq241.c b/test/val/cq241.c index 611b5a376..a6d6c5324 100644 --- a/test/val/cq241.c +++ b/test/val/cq241.c @@ -4,6 +4,14 @@ !!LICENCE!! own, freely distributeable for non-profit. read CPYRIGHT.LCC */ +/* INTEGER_CONSTANT_MAX_32BIT +** This suppresses constants longer than 32-bit, which are now an error: +** https://github.com/cc65/cc65/pull/2084 +** Because cc65's internal representation is implicitly/explicitly +** 32-bit in many places, values larger than this aren't representable, +** but also can't be checked for overflow once accepted. +*/ + #include "common.h" struct defs { @@ -62,7 +70,12 @@ long pow2(long n) { return s; } - long d[39], o[39], x[39]; +#ifndef INTEGER_CONSTANT_MAX_32BIT +#define CTCOUNT 39 +#else +#define CTCOUNT 36 +#endif + long d[CTCOUNT], o[CTCOUNT], x[CTCOUNT]; #ifndef NO_OLD_FUNC_DECL s241(pd0) @@ -212,13 +225,15 @@ int s241(struct defs *pd0) { d[33] = 1073741823; o[33] = 07777777777; x[33] = 0x3fffffff; d[34] = 1073741824; o[34] = 010000000000; x[34] = 0x40000000; d[35] = 4294967295; o[35] = 037777777777; x[35] = 0xffffffff; +#if CTCOUNT > 36 d[36] = 4294967296; o[36] = 040000000000; x[36] = 0x100000000; d[37] = 68719476735; o[37] = 0777777777777; x[37] = 0xfffffffff; d[38] = 68719476736; o[38] = 01000000000000; x[38] = 0x1000000000; +#endif /* WHEW! */ - for (j=0; j<39; j++){ + for (j=0; j<CTCOUNT; j++){ if ( g[j] != d[j] || d[j] != o[j] || o[j] != x[j]) { From 8e75e5b51a03d1df40e0f052d838dade6a0a6200 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 19:42:05 -0400 Subject: [PATCH 177/520] Suppress overflow warning when conversion is an explicit cast --- src/cc65/typeconv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index e1d95ff63..6bdb45b5f 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -55,7 +55,7 @@ -static void DoConversion (ExprDesc* Expr, const Type* NewType) +static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) /* Emit code to convert the given expression to a new type. */ { const Type* OldType; @@ -141,7 +141,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) } } - if ((OldVal != Expr->IVal) && IS_Get (&WarnConstOverflow)) { + if ((OldVal != Expr->IVal) && IS_Get (&WarnConstOverflow) && !Explicit) { Warning ("Implicit conversion of constant overflows %d-bit destination", NewBits); } } @@ -288,7 +288,7 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Both types must be complete */ if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) { /* Do the actual conversion */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 0); } else { /* We should have already generated error elsewhere so that we ** could just silently fail here to avoid excess errors, but to @@ -335,7 +335,7 @@ void TypeCast (ExprDesc* Expr) ReplaceType (Expr, NewType); } else if (IsCastType (Expr->Type)) { /* Convert the value. The result has always the new type */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 1); } else { TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, "Cast to incompatible type '%s' from '%s'"); From b5f255f9123dc898e494321406edd434b89a6d49 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 19:54:40 -0400 Subject: [PATCH 178/520] Test case for const-overflow warnings --- test/err/integer-const-overflow.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/err/integer-const-overflow.c diff --git a/test/err/integer-const-overflow.c b/test/err/integer-const-overflow.c new file mode 100644 index 000000000..37cc0f01e --- /dev/null +++ b/test/err/integer-const-overflow.c @@ -0,0 +1,20 @@ +/* Integer constant overflow warnings. */ + +/* Warnings as errors. */ +#pragma warn(error,on) + +/* Warn on const overflow */ +#pragma warn(const-overflow,on) + +unsigned char a = 256; +signed char b = 128; +unsigned char c = -129; +unsigned short int d = 0x00010000; +unsigned short int e = 0x80000000; +signed short int f = 32768L; +signed short int g = -32769L; + +int main(void) +{ + return 0; +} From 84eafb7f9c255d6a82a8062a6d8a8f3c0eb60a72 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Wed, 3 May 2023 21:09:03 -0400 Subject: [PATCH 179/520] err test for struct with duplicate member --- test/err/struct-duplicate-member.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/err/struct-duplicate-member.c diff --git a/test/err/struct-duplicate-member.c b/test/err/struct-duplicate-member.c new file mode 100644 index 000000000..30cd06207 --- /dev/null +++ b/test/err/struct-duplicate-member.c @@ -0,0 +1,17 @@ +/* Ensure that a duplicate member in a struct produces an error. +** https://github.com/cc65/cc65/issues/2015 +*/ + +struct bads { + int a; + int a; /* this is an error */ +}; + +union badu { + int a, a; /* also an error */ +}; + +int main(void) +{ + return 0; +} From 13f317e660b543ec6037e69c17f3226dbee3d171 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 09:18:33 +0200 Subject: [PATCH 180/520] Update Contributing.md --- Contributing.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Contributing.md b/Contributing.md index eb26e920b..3b355373c 100644 --- a/Contributing.md +++ b/Contributing.md @@ -74,10 +74,12 @@ color := $0787 The following is still very incomplete - if in doubt please look at existing sourcefiles and adapt to the existing style -* Your files should obey the C89 standard. +* Your files should generally obey the C89 standard, with a few C99 things (this is a bit similar to what cc65 itself supports). The exceptions are: + * use stdint.h for variables that require a certain bit size + * In printf-style functions use the PRIX64 (and similar) macros to deal with 64bit values (from inttypes.h) +This list is not necessarily complete - if in doubt, please ask. * We generally have a "no warnings" policy -* Warnings must not be hidden by using typecasts - fix the code instead - * In printf-style functions use the PRIX64 (and similar) macros to deal with 64bit values + * Warnings must not be hidden by using typecasts - fix the code instead * The normal indentation width should be four spaces. * You must use ANSI C comments (```/* */```); you must not use C++ comments (```//```). * When you add functions to an existing file, you should separate them by the same number of blank lines that separate the functions that already are in that file. From ca8201a314bc0b6539a49518c334cc297e651764 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Thu, 4 May 2023 05:44:20 -0400 Subject: [PATCH 181/520] Overflow test optimization suggested by kugelfuhr User CHAR_BIT instead of 8 --- src/cc65/scanner.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index f747fb458..54ce02158 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -590,19 +590,15 @@ static void NumericConst (void) SB_Clear (&Src); break; } - if (((scan_t)(IVal * Base) / Base) != IVal) { + if (((scan_t)(IVal * Base + DigitVal) / Base) != IVal) { Overflow = 1; } - IVal = IVal * Base; - if (((scan_t)(IVal + DigitVal)) < IVal) { - Overflow = 1; - } - IVal += DigitVal; + IVal = IVal * Base + DigitVal; SB_Skip (&Src); } if (Overflow) { Error ("Numerical constant \"%s\" too large for internal %d-bit representation", - SB_GetConstBuf (&Src), (int)(sizeof(IVal)*8)); + SB_GetConstBuf (&Src), (int)(sizeof(IVal)*CHAR_BIT)); } /* Distinguish between integer and floating point constants */ From 69f4cd184779925ca399823acc56e50dcff1dc29 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Thu, 4 May 2023 05:48:48 -0400 Subject: [PATCH 182/520] limits.h was apparently already included somewhere on windows but not linux --- src/cc65/scanner.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 54ce02158..ede77cb2c 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -40,6 +40,7 @@ #include <ctype.h> #include <math.h> #include <inttypes.h> +#include <limits.h> /* common */ #include "chartype.h" @@ -590,6 +591,7 @@ static void NumericConst (void) SB_Clear (&Src); break; } + /* Test result of adding digit for overflow. */ if (((scan_t)(IVal * Base + DigitVal) / Base) != IVal) { Overflow = 1; } From 0957c36115126f067d42ed5a74210993a6e1e710 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 14:19:27 +0200 Subject: [PATCH 183/520] try verbose dry run to see what it does :) --- .github/workflows/snapshot-on-push-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index e8be4400e..38158b384 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -111,7 +111,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - #git push -v + git push -n -v # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 45258d060dc614a2ed07a08711bb4341d19cdb19 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 14:47:02 +0200 Subject: [PATCH 184/520] lets see if this works --- .github/workflows/snapshot-on-push-master.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 38158b384..dc7e6007e 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -7,6 +7,10 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# suggested at https://jonathansoma.com/everything/git/github-actions-403-error/ +permissions: + contents: write + jobs: build_windows: name: Build (Windows) From 7c5595efbc008aa32645cdb12ad52b186e6e0c67 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 15:02:32 +0200 Subject: [PATCH 185/520] another try --- .github/workflows/snapshot-on-push-master.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index dc7e6007e..759446dc2 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -39,6 +39,8 @@ jobs: runs-on: ubuntu-latest steps: +# inspired by https://github.com/JuliaRegistries/TagBot/blob/master/example.yml + token: ${{ secrets.GITHUB_TOKEN }} - name: Install Dependencies shell: bash run: | From c0dd3b9d9ac9f5e19c6341ae23e75278b0cecf82 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 15:10:10 +0200 Subject: [PATCH 186/520] like this? awesome how everyone does something different :) --- .github/workflows/snapshot-on-push-master.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 759446dc2..933bc05ba 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -39,8 +39,6 @@ jobs: runs-on: ubuntu-latest steps: -# inspired by https://github.com/JuliaRegistries/TagBot/blob/master/example.yml - token: ${{ secrets.GITHUB_TOKEN }} - name: Install Dependencies shell: bash run: | @@ -108,6 +106,8 @@ jobs: repository: cc65/doc path: doc.git - name: Update the online documents. + with: + github_token: ${{ secrets.GITHUB_TOKEN }} run: | cd doc.git rm *.* From 681c51b37e3fa3be8d6518c40506fd62ef4dff6d Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 15:12:46 +0200 Subject: [PATCH 187/520] yawn --- .github/workflows/snapshot-on-push-master.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 933bc05ba..7baa69ba7 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -8,8 +8,13 @@ concurrency: cancel-in-progress: true # suggested at https://jonathansoma.com/everything/git/github-actions-403-error/ +# https://github.com/orgs/community/discussions/26694 permissions: + deployments: write contents: write + statuses: write + actions: write + checks: read jobs: build_windows: @@ -106,8 +111,6 @@ jobs: repository: cc65/doc path: doc.git - name: Update the online documents. - with: - github_token: ${{ secrets.GITHUB_TOKEN }} run: | cd doc.git rm *.* From 20f0427fa42278bac6790f3a1bc0d2d2c8b634e1 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 15:20:33 +0200 Subject: [PATCH 188/520] comment out the push again. who knows whats wrong. sigh --- .github/workflows/snapshot-on-push-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 7baa69ba7..75e93b0f8 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -120,7 +120,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - git push -n -v + #git push -n -v # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 99a0d64b935ad2c0aaa85d35d4c7cbb68d8d3b18 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 20:03:27 +0200 Subject: [PATCH 189/520] lets see if that token stuff works --- .github/workflows/snapshot-on-push-master.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 75e93b0f8..3a69ab44b 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -109,6 +109,8 @@ jobs: uses: actions/checkout@v3 with: repository: cc65/doc + # this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065 + token: ${{ secrets.DOC_PAT }} # use secret token instead of default path: doc.git - name: Update the online documents. run: | @@ -120,7 +122,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - #git push -n -v + git push -n -v # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 3536761110067f79231775d304f73dcccf42e355 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 20:30:28 +0200 Subject: [PATCH 190/520] another try --- .github/workflows/snapshot-on-push-master.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 3a69ab44b..c562ad0dd 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -9,12 +9,12 @@ concurrency: # suggested at https://jonathansoma.com/everything/git/github-actions-403-error/ # https://github.com/orgs/community/discussions/26694 -permissions: - deployments: write - contents: write - statuses: write - actions: write - checks: read +#permissions: +# deployments: write +# contents: write +# statuses: write +# actions: write +# checks: read jobs: build_windows: From 0369838f24aece6035321de618f8992c46e9ee91 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 20:45:35 +0200 Subject: [PATCH 191/520] bleh --- .github/workflows/snapshot-on-push-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index c562ad0dd..f5f690083 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -122,7 +122,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - git push -n -v + -git push -v # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 926d09a14d812509102f722232d6ffd88d18af1f Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 20:48:52 +0200 Subject: [PATCH 192/520] i'm not patient enough --- .github/workflows/snapshot-on-push-master.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index f5f690083..2b346f1d3 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -66,15 +66,15 @@ jobs: - name: Build the platform libraries. shell: bash run: make -j2 lib QUIET=1 - - name: Run the regression tests. - shell: bash - run: make test QUIET=1 - - name: Test that the samples can be built. - shell: bash - run: make -j2 samples - - name: Remove the output from the samples tests. - shell: bash - run: make -C samples clean +# - name: Run the regression tests. +# shell: bash +# run: make test QUIET=1 +# - name: Test that the samples can be built. +# shell: bash +# run: make -j2 samples +# - name: Remove the output from the samples tests. +# shell: bash +# run: make -C samples clean - name: Remove programs in util directory shell: bash run: make -C util clean From 17f58d934fd2a5bc7b709de36a718e0938cd48d5 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 20:52:50 +0200 Subject: [PATCH 193/520] AGAIN --- .github/workflows/snapshot-on-push-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 2b346f1d3..f5340495e 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -122,7 +122,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - -git push -v + git push -v # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From ea90c934d7fd1fccfce4302c2ed0426297ccbc1e Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 20:59:18 +0200 Subject: [PATCH 194/520] try gain with classic token --- .github/workflows/snapshot-on-push-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index f5340495e..d859054ec 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -122,7 +122,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - git push -v + git push -n -v # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 610dfbb41ca54b6d0fc8a4661b2f88d79b101ca5 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 21:07:23 +0200 Subject: [PATCH 195/520] try normal push, also try actions/upload-artifact@v3 --- .github/workflows/snapshot-on-push-master.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index d859054ec..20b1ea8f7 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -95,12 +95,12 @@ jobs: mv cc65.zip cc65-snapshot-win32.zip - name: Upload a 32-bit Snapshot Zip - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: cc65-snapshot-win32.zip path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: cc65-snapshot-win64.zip path: cc65-snapshot-win64.zip @@ -122,7 +122,7 @@ jobs: git config push.default simple git add -A git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - git push -n -v + git push # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 7053dac3a99625d169b25a1b7a479d1a17a87091 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 21:13:37 +0200 Subject: [PATCH 196/520] remove token from cc65/doc, reenable the tests again --- .github/workflows/snapshot-on-push-master.yml | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 20b1ea8f7..62d6c6adb 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -7,15 +7,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -# suggested at https://jonathansoma.com/everything/git/github-actions-403-error/ -# https://github.com/orgs/community/discussions/26694 -#permissions: -# deployments: write -# contents: write -# statuses: write -# actions: write -# checks: read - jobs: build_windows: name: Build (Windows) @@ -66,15 +57,15 @@ jobs: - name: Build the platform libraries. shell: bash run: make -j2 lib QUIET=1 -# - name: Run the regression tests. -# shell: bash -# run: make test QUIET=1 -# - name: Test that the samples can be built. -# shell: bash -# run: make -j2 samples -# - name: Remove the output from the samples tests. -# shell: bash -# run: make -C samples clean + - name: Run the regression tests. + shell: bash + run: make test QUIET=1 + - name: Test that the samples can be built. + shell: bash + run: make -j2 samples + - name: Remove the output from the samples tests. + shell: bash + run: make -C samples clean - name: Remove programs in util directory shell: bash run: make -C util clean @@ -110,6 +101,8 @@ jobs: with: repository: cc65/doc # this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065 + # - apparently only a "classic" token works here + # - the token must exist in the cc65/cc65 repo token: ${{ secrets.DOC_PAT }} # use secret token instead of default path: doc.git - name: Update the online documents. From 769b31637689c0ead7983cddf85b5ca019804f3b Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 21:27:48 +0200 Subject: [PATCH 197/520] lets see if this will not fail when there are no changes in the docs --- .github/workflows/snapshot-on-push-master.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 62d6c6adb..9883003fb 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -114,8 +114,8 @@ jobs: git config user.email "cc65.nomail@github.com" git config push.default simple git add -A - git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - git push + git diff-index --quiet HEAD || git commit -m "Updated from cc65 commit ${GITHUB_SHA}." + git diff-index --quiet HEAD || git push # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 4ef849cb81c2a00e8716d8827eb45e27e8575269 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 21:44:57 +0200 Subject: [PATCH 198/520] Force background image to snap left, adapted from https://github.com/cc65/doc/pull/1 --- doc/doc.css | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/doc.css b/doc/doc.css index e4c316e16..6bd538681 100644 --- a/doc/doc.css +++ b/doc/doc.css @@ -2,12 +2,14 @@ body { font-family: arial, helvetica, sans-serif; font-size: 100%; text-align: justify; - margin-left: 110px; - margin-top: 10px; - margin-right: 30px; - margin-bottom: 10px; + margin: 0px; + padding-left: 110px; + padding-top: 10px; + padding-right: 30px; + padding-bottom: 10px; background-image: url(doc.png); background-repeat: repeat-y; + background-position:left top; } h1, h2, h2 a:link, h2 a:active, h2 a:visited { From e228e4d65c3edd22488a90a8878fb6aa71f42a9f Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 21:58:37 +0200 Subject: [PATCH 199/520] try something else, again --- .github/workflows/snapshot-on-push-master.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 9883003fb..fb42bfcb3 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -114,8 +114,9 @@ jobs: git config user.email "cc65.nomail@github.com" git config push.default simple git add -A - git diff-index --quiet HEAD || git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - git diff-index --quiet HEAD || git push + if git commit -m "Updated from cc65 commit ${GITHUB_SHA}." ; then + git push + fi # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 71bb11bee120872d911b98e58ce869df60b3610f Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 22:09:22 +0200 Subject: [PATCH 200/520] make the commit message a url, also check if it really doesnt fail when there is nothing to commit --- .github/workflows/snapshot-on-push-master.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index fb42bfcb3..50f5cd296 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -114,7 +114,8 @@ jobs: git config user.email "cc65.nomail@github.com" git config push.default simple git add -A - if git commit -m "Updated from cc65 commit ${GITHUB_SHA}." ; then + # prevent failure when there is nothing to commit + if git commit -m "Updated from https://github.com/cc65/cc65/commit/${GITHUB_SHA}" ; then git push fi From 69fd3d79985f09b41edb59c83fdc7c9b0bdfe9dc Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Thu, 4 May 2023 22:21:36 +0200 Subject: [PATCH 201/520] tweak --- doc/doc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc.css b/doc/doc.css index 6bd538681..0f6e90d67 100644 --- a/doc/doc.css +++ b/doc/doc.css @@ -27,7 +27,7 @@ h1 { } h2 { - font-size: 160%; + font-size: 150%; text-shadow: 1px 1px 3px #303030; letter-spacing: 1px; margin-top: 2em; From cfc8a41a031c244ebf5162b08ac88817e7dd175e Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Thu, 4 May 2023 17:07:34 -0400 Subject: [PATCH 202/520] guard test to ensure 3-byte struct isn't re-enabled without evaluation by accident --- test/misc/Makefile | 5 ++ test/misc/struct-by-value.c | 156 ++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 test/misc/struct-by-value.c diff --git a/test/misc/Makefile b/test/misc/Makefile index d0b8979b0..c708b160b 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -163,6 +163,11 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref +# should not compile until 3-byte struct by value tests are re-enabled +$(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR) + $(if $(QUIET),echo misc/struct-by-value.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # the rest are tests that fail currently for one reason or another $(WORKDIR)/sitest.$1.$2.prg: sitest.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." diff --git a/test/misc/struct-by-value.c b/test/misc/struct-by-value.c new file mode 100644 index 000000000..fc44f8729 --- /dev/null +++ b/test/misc/struct-by-value.c @@ -0,0 +1,156 @@ +/* This test ensures that compilation fails if a 3-byte struct by value +** is attempted, to avoid re-introducting a bug by accident: +** https://github.com/cc65/cc65/issues/2022 +** When 3-byte structs are re-enabled, this test will compile, +** which should trigger a "misc" test failure. +** When this happens: +** Delete this comment from the top. +** Replace test/val/struct-by-value.c with this one. +** See: +** https://github.com/cc65/cc65/issues/2086 +*/ + +/* Test of passing and returning structs by value. + Structs of 1, 2, 3, 4 bytes are supported. + Note that structs of 3 bytes had a past issue: + https://github.com/cc65/cc65/issues/2022 +*/ + +int fail = 0; + +struct s1 { char a; }; +struct s2 { char a, b; }; +struct s3 { char a, b, c; }; +struct s4 { char a, b, c, d; }; + +const struct s1 c1 = { 1 }; +const struct s2 c2 = { 2, 3 }; +const struct s3 c3 = { 4, 5, 6 }; +const struct s4 c4 = { 7, 8, 9, 10 }; + +struct s1 return1() { return c1; } +struct s2 return2() { return c2; } +struct s3 return3() { return c3; } +struct s4 return4() { return c4; } + +int compare1(struct s1 a, struct s1 b) +{ + if (a.a != b.a) return 1; + return 0; +} + +int compare2(struct s2 a, struct s2 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + return 0; +} + +int compare3(struct s3 a, struct s3 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + return 0; +} + +int compare4(struct s4 a, struct s4 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + if (a.d != b.d) return 1; + return 0; +} + +int pass1(struct s1 p1) +{ + struct s1 a1; + a1 = p1; + if (a1.a != c1.a) return 1; + return 0; +} + +int pass2(struct s2 p2) +{ + struct s2 a2; + a2 = p2; + if (a2.a != c2.a) return 1; + if (a2.b != c2.b) return 1; + return 0; +} + +int pass3(struct s3 p3) +{ + struct s3 a3; + a3 = p3; + if (a3.a != c3.a) return 1; + if (a3.b != c3.b) return 1; + if (a3.c != c3.c) return 1; + return 0; +} + +int pass4(struct s4 p4) +{ + struct s4 a4; + a4 = p4; + if (a4.a != c4.a) return 1; + if (a4.b != c4.b) return 1; + if (a4.c != c4.c) return 1; + if (a4.d != c4.d) return 1; + return 0; +} + +void reset(char* gg) +{ + char i; + for (i=0;i<5;++i) gg[i] = 128+i; +} + +int test(char* gg, char start) +{ + char i; + for (i=start;i<5;++i) + if (gg[i] != 128+i) return 1; + return 0; +} + +int main() +{ + /* Used to check #2022 bug condition of extra bytes being overwritten. */ + union + { + char gg[5]; + struct s1 g1; + struct s2 g2; + struct s3 g3; + struct s4 g4; + } guard; + + reset(guard.gg); + guard.g1 = return1(); + fail += compare1(guard.g1,c1); + fail += test(guard.gg,1); + + reset(guard.gg); + guard.g2 = return2(); + fail += compare2(guard.g2,c2); + fail += test(guard.gg,2); + + reset(guard.gg); + guard.g3 = return3(); + fail += compare3(guard.g3,c3); + fail += test(guard.gg,3); + + reset(guard.gg); + guard.g4 = return4(); + fail += compare4(guard.g4,c4); + fail += test(guard.gg,4); + + fail += pass1(c1); + fail += pass2(c2); + fail += pass3(c3); + fail += pass4(c4); + + return fail; +} From e57c991de791a0677177cd2e68ab02a05414aa9f Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 10:56:43 -0400 Subject: [PATCH 203/520] master push workflow can include a docs snapshot --- .github/workflows/snapshot-on-push-master.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 50f5cd296..5b37e3645 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -118,9 +118,16 @@ jobs: if git commit -m "Updated from https://github.com/cc65/cc65/commit/${GITHUB_SHA}" ; then git push fi + - name: Package offline documents. + run: 7z a cc65-snapshot-docs.zip ./html/*.* + - name: Upload a Documents Snapshot Zip + uses: actions/upload-artifact@v3 + with: + name: cc65-snapshot-docs.zip + path: cc65-snapshot-docs.zip # enter secrets under "repository secrets" - - name: Upload snapshot to sourceforge + - name: Upload 32-bit Windows snapshot to sourceforge uses: nogsantos/scp-deploy@master with: src: cc65-snapshot-win32.zip @@ -129,5 +136,14 @@ jobs: port: ${{ secrets.SSH_PORT }} user: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} + - name: Upload documents snapshot to sourceforge + uses: nogsantos/scp-deploy@master + with: + src: cc65-snapshot-docs.zip + host: ${{ secrets.SSH_HOST }} + remote: ${{ secrets.SSH_DIR }} + port: ${{ secrets.SSH_PORT }} + user: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_KEY }} # TODO: Publish snapshot zip at https://github.com/cc65/cc65.github.io From 0cad5bef8169b634f288874e324f838a5da89884 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 10:58:18 -0400 Subject: [PATCH 204/520] include docs snapshot with pull request build so that PRs can preview it easily --- .github/workflows/build-on-pull-request.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 05d6a4a39..55be5db1e 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -43,6 +43,11 @@ jobs: - name: Build the document files. shell: bash run: make -j2 doc + - name: Upload a documents snapshot. + uses: actions/upload-artifact@v3 + with: + name: docs + path: ./html - name: Build 64-bit Windows versions of the tools. run: | make -C src clean From 8f356f5093e7c9bbae33fe5103e3812f73eb8c30 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 11:00:06 -0400 Subject: [PATCH 205/520] artifact upload should not end with .zip as it is appended automatically fixes ".zip.zip" artifact filenames --- .github/workflows/snapshot-on-push-master.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 5b37e3645..408bdbb63 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -88,12 +88,12 @@ jobs: - name: Upload a 32-bit Snapshot Zip uses: actions/upload-artifact@v3 with: - name: cc65-snapshot-win32.zip + name: cc65-snapshot-win32 path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip uses: actions/upload-artifact@v3 with: - name: cc65-snapshot-win64.zip + name: cc65-snapshot-win64 path: cc65-snapshot-win64.zip - name: Get the online documents repo. @@ -123,7 +123,7 @@ jobs: - name: Upload a Documents Snapshot Zip uses: actions/upload-artifact@v3 with: - name: cc65-snapshot-docs.zip + name: cc65-snapshot-docs path: cc65-snapshot-docs.zip # enter secrets under "repository secrets" From 9f3e47e9c93d1aa5b439a5ecf2f38ec0a5a41b4c Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 11:04:31 -0400 Subject: [PATCH 206/520] test/standard was never added to test makefile --- test/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Makefile b/test/Makefile index abc70d58f..22e425c9c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -22,6 +22,7 @@ continue: @$(MAKE) -C val all @$(MAKE) -C ref all @$(MAKE) -C err all + @$(MAKE) -C standard all @$(MAKE) -C misc all @$(MAKE) -C todo all @@ -31,6 +32,7 @@ mostlyclean: @$(MAKE) -C val clean @$(MAKE) -C ref clean @$(MAKE) -C err clean + @$(MAKE) -C standard clean @$(MAKE) -C misc clean @$(MAKE) -C todo clean From 5c20fb28123bd938c18618d321766d85b9ecb4fc Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 11:28:42 -0400 Subject: [PATCH 207/520] test/todo makefile uses testwrk/val by mistake --- test/todo/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/todo/Makefile b/test/todo/Makefile index 17561f8f4..062b899ce 100644 --- a/test/todo/Makefile +++ b/test/todo/Makefile @@ -31,7 +31,7 @@ CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) LD65 := $(if $(wildcard ../../bin/ld65*),..$S..$Sbin$Sld65,ld65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) -WORKDIR = ../../testwrk/val +WORKDIR = ../../testwrk/todo OPTIONS = g O Os Osi Osir Osr Oi Oir Or @@ -49,7 +49,7 @@ $(WORKDIR): define PRG_template $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) - $(if $(QUIET),echo val/$$*.$1.$2.prg) + $(if $(QUIET),echo todo/$$*.$1.$2.prg) $(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.prg=.s) $$< $(NULLERR) $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) From c662c7a36f6c31c15858e7656c652f864c9d5aaf Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 12:02:50 -0400 Subject: [PATCH 208/520] use diff-index to prevent commit instead of bash if preferred because the if suppresses all git commit errors, instead of the one error we need to suppress (commit with no changes) --- .github/workflows/snapshot-on-push-master.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 50f5cd296..cba5b89dd 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -115,9 +115,8 @@ jobs: git config push.default simple git add -A # prevent failure when there is nothing to commit - if git commit -m "Updated from https://github.com/cc65/cc65/commit/${GITHUB_SHA}" ; then - git push - fi + git diff-index --quiet HEAD || git commit -m "Updated from https://github.com/cc65/cc65/commit/${GITHUB_SHA}" + git push # enter secrets under "repository secrets" - name: Upload snapshot to sourceforge From 1c58b302d8857928832931dfb2fd23a7c448919a Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Fri, 5 May 2023 12:31:19 -0400 Subject: [PATCH 209/520] Bugfix for the .ISMNEMONIC, .ISMNEM builtin function --- src/ca65/expr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca65/expr.c b/src/ca65/expr.c index 812b6e90c..5dcf5ca71 100644 --- a/src/ca65/expr.c +++ b/src/ca65/expr.c @@ -496,7 +496,7 @@ static ExprNode* FuncIsMnemonic (void) /* Skip the name */ NextTok (); - return GenLiteralExpr (Instr > 0); + return GenLiteralExpr (Instr >= 0); } From 17706208e80de82dff33a6e5feef53d330de4d9d Mon Sep 17 00:00:00 2001 From: Jeff Tranter <tranter@pobox.com> Date: Fri, 5 May 2023 18:02:42 -0400 Subject: [PATCH 210/520] Add support for 48x12 video mode on Challenger 1P. Tested on real C1P hardware. --- doc/osi.sgml | 13 +++++++++++++ libsrc/osic1p/extra/screen-c1p-48x12.s | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 libsrc/osic1p/extra/screen-c1p-48x12.s diff --git a/doc/osi.sgml b/doc/osi.sgml index eeaee4a97..62d466406 100644 --- a/doc/osi.sgml +++ b/doc/osi.sgml @@ -187,8 +187,21 @@ Currently the following extra screen configuration modules are implemented: <itemize> <item><tt>osic1p-screen-s3-32x28.o</tt>: 32 columns by 28 lines mode for Briel Superboard ///</item> +<item><tt>osic1p-screen-c1p-48x12.s</tt>: 48 columns by 12 lines mode +for Challenger 1P</item> </itemize> +On the Briel Superboard /// you enter 32 column mode by holding down +the BREAK key on powerup. + +On the Challenger 1P you can enable 48 column mode by writing a 1 to +bit 0 of address $D800, and writing a 0 to go back to 24 column mode. +You can use code like the following to do this: + +<tscreen><verb> +*(char*)0xd800 = 1; /* Switch to 48 column mode */ +</verb></tscreen> + <sect>Limitations<p> <sect1>stdio implementation<p> diff --git a/libsrc/osic1p/extra/screen-c1p-48x12.s b/libsrc/osic1p/extra/screen-c1p-48x12.s new file mode 100644 index 000000000..91a61338b --- /dev/null +++ b/libsrc/osic1p/extra/screen-c1p-48x12.s @@ -0,0 +1,16 @@ +; +; Implementation of screen-layout related functions for Challenger 1P in 48x12 mode. +; + + .include "../osiscreen.inc" + +C1P_SCR_BASE := $D000 ; Base of C1P video RAM +C1P_VRAM_SIZE = $0400 ; Size of C1P video RAM (1 kB) +C1P_SCR_WIDTH = $30 ; Screen width +C1P_SCR_HEIGHT = $0C ; Screen height +C1P_SCR_FIRSTCHAR = $8B ; Offset of cursor position (0, 0) from base + ; of video RAM +C1P_SCROLL_DIST = $40 ; Memory distance for scrolling by one line + +osi_screen_funcs C1P_SCR_BASE, C1P_VRAM_SIZE, C1P_SCR_FIRSTCHAR, \ + C1P_SCR_WIDTH, C1P_SCR_HEIGHT, C1P_SCROLL_DIST From 18570d18b811ed325972db70b8a21d3668e1e80a Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Fri, 5 May 2023 18:43:10 -0400 Subject: [PATCH 211/520] add test --- test/asm/err/ismnemonic.s | 808 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 808 insertions(+) create mode 100644 test/asm/err/ismnemonic.s diff --git a/test/asm/err/ismnemonic.s b/test/asm/err/ismnemonic.s new file mode 100644 index 000000000..438afed70 --- /dev/null +++ b/test/asm/err/ismnemonic.s @@ -0,0 +1,808 @@ +; Tests to ensure .ismnemonic is working correctly +; The .ismnemonic function calls FindInstruction internally, +; which is how the assembler detects all instructions +; +; Currently supported CPUs: +; "6502" +; "6502X" +; "6502DTV" +; "65SC02" +; "65C02" +; "4510" +; "huc6280" +; "65816" +; "sweet16" + +.macro test_Ismnemonic instr + .if .ismnemonic(instr) + ; do nothing + .else + .error .sprintf(".ISMNEMONIC failed for instruction: %s", .string(instr)) + .endif +.endmacro + +; there is no instruction table for "none", make sure 'adc' (common to all CPUs) and 'add' (sweet16) doesn't match +.setcpu "none" +.if .ismnemonic(adc) || .ismnemonic(add) + .error ".ISMNEMONIC with CPU set to 'none' should not match any instructions." +.endif + +.setcpu "6502" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "6502X" +test_Ismnemonic adc +test_Ismnemonic alr +test_Ismnemonic anc +test_Ismnemonic and +test_Ismnemonic ane +test_Ismnemonic arr +test_Ismnemonic asl +test_Ismnemonic axs +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dcp +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic isc +test_Ismnemonic jam +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic las +test_Ismnemonic lax +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic rla +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rra +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sax +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sha +test_Ismnemonic shx +test_Ismnemonic shy +test_Ismnemonic slo +test_Ismnemonic sre +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic tas +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "6502DTV" +test_Ismnemonic adc +test_Ismnemonic alr +test_Ismnemonic anc +test_Ismnemonic and +test_Ismnemonic ane +test_Ismnemonic arr +test_Ismnemonic asl +test_Ismnemonic axs +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic las +test_Ismnemonic lax +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic rla +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rra +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sac +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sha +test_Ismnemonic shx +test_Ismnemonic shy +test_Ismnemonic sir +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "65SC02" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "65C02" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bbr0 +test_Ismnemonic bbr1 +test_Ismnemonic bbr2 +test_Ismnemonic bbr3 +test_Ismnemonic bbr4 +test_Ismnemonic bbr5 +test_Ismnemonic bbr6 +test_Ismnemonic bbr7 +test_Ismnemonic bbs0 +test_Ismnemonic bbs1 +test_Ismnemonic bbs2 +test_Ismnemonic bbs3 +test_Ismnemonic bbs4 +test_Ismnemonic bbs5 +test_Ismnemonic bbs6 +test_Ismnemonic bbs7 +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rmb0 +test_Ismnemonic rmb1 +test_Ismnemonic rmb2 +test_Ismnemonic rmb3 +test_Ismnemonic rmb4 +test_Ismnemonic rmb5 +test_Ismnemonic rmb6 +test_Ismnemonic rmb7 +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic smb0 +test_Ismnemonic smb1 +test_Ismnemonic smb2 +test_Ismnemonic smb3 +test_Ismnemonic smb4 +test_Ismnemonic smb5 +test_Ismnemonic smb6 +test_Ismnemonic smb7 +test_Ismnemonic sta +test_Ismnemonic stp +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya +test_Ismnemonic wai + +.setcpu "4510" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic asr +test_Ismnemonic asw +test_Ismnemonic bbr0 +test_Ismnemonic bbr1 +test_Ismnemonic bbr2 +test_Ismnemonic bbr3 +test_Ismnemonic bbr4 +test_Ismnemonic bbr5 +test_Ismnemonic bbr6 +test_Ismnemonic bbr7 +test_Ismnemonic bbs0 +test_Ismnemonic bbs1 +test_Ismnemonic bbs2 +test_Ismnemonic bbs3 +test_Ismnemonic bbs4 +test_Ismnemonic bbs5 +test_Ismnemonic bbs6 +test_Ismnemonic bbs7 +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bsr +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cle +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic cpz +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dew +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic dez +test_Ismnemonic eom +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inw +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic inz +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lbcc +test_Ismnemonic lbcs +test_Ismnemonic lbeq +test_Ismnemonic lbmi +test_Ismnemonic lbne +test_Ismnemonic lbpl +test_Ismnemonic lbra +test_Ismnemonic lbvc +test_Ismnemonic lbvs +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic ldz +test_Ismnemonic lsr +test_Ismnemonic map +test_Ismnemonic neg +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic phd +test_Ismnemonic php +test_Ismnemonic phw +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic phz +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic plz +test_Ismnemonic rmb0 +test_Ismnemonic rmb1 +test_Ismnemonic rmb2 +test_Ismnemonic rmb3 +test_Ismnemonic rmb4 +test_Ismnemonic rmb5 +test_Ismnemonic rmb6 +test_Ismnemonic rmb7 +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic row +test_Ismnemonic rti +test_Ismnemonic rtn +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic see +test_Ismnemonic sei +test_Ismnemonic smb0 +test_Ismnemonic smb1 +test_Ismnemonic smb2 +test_Ismnemonic smb3 +test_Ismnemonic smb4 +test_Ismnemonic smb5 +test_Ismnemonic smb6 +test_Ismnemonic smb7 +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic tab +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic taz +test_Ismnemonic tba +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tsx +test_Ismnemonic tsy +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya +test_Ismnemonic tys +test_Ismnemonic tza + +.setcpu "HuC6280" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bbr0 +test_Ismnemonic bbr1 +test_Ismnemonic bbr2 +test_Ismnemonic bbr3 +test_Ismnemonic bbr4 +test_Ismnemonic bbr5 +test_Ismnemonic bbr6 +test_Ismnemonic bbr7 +test_Ismnemonic bbs0 +test_Ismnemonic bbs1 +test_Ismnemonic bbs2 +test_Ismnemonic bbs3 +test_Ismnemonic bbs4 +test_Ismnemonic bbs5 +test_Ismnemonic bbs6 +test_Ismnemonic bbs7 +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bsr +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic cla +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic clx +test_Ismnemonic cly +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic csh +test_Ismnemonic csl +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rmb0 +test_Ismnemonic rmb1 +test_Ismnemonic rmb2 +test_Ismnemonic rmb3 +test_Ismnemonic rmb4 +test_Ismnemonic rmb5 +test_Ismnemonic rmb6 +test_Ismnemonic rmb7 +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sax +test_Ismnemonic say +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic set +test_Ismnemonic smb0 +test_Ismnemonic smb1 +test_Ismnemonic smb2 +test_Ismnemonic smb3 +test_Ismnemonic smb4 +test_Ismnemonic smb5 +test_Ismnemonic smb6 +test_Ismnemonic smb7 +test_Ismnemonic st0 +test_Ismnemonic st1 +test_Ismnemonic st2 +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic sxy +test_Ismnemonic tai +test_Ismnemonic tam +test_Ismnemonic tam0 +test_Ismnemonic tam1 +test_Ismnemonic tam2 +test_Ismnemonic tam3 +test_Ismnemonic tam4 +test_Ismnemonic tam5 +test_Ismnemonic tam6 +test_Ismnemonic tam7 +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tdd +test_Ismnemonic tia +test_Ismnemonic tii +test_Ismnemonic tin +test_Ismnemonic tma +test_Ismnemonic tma0 +test_Ismnemonic tma1 +test_Ismnemonic tma2 +test_Ismnemonic tma3 +test_Ismnemonic tma4 +test_Ismnemonic tma5 +test_Ismnemonic tma6 +test_Ismnemonic tma7 +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tst +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "65816" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic brl +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cop +test_Ismnemonic cpa +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jml +test_Ismnemonic jmp +test_Ismnemonic jsl +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic mvn +test_Ismnemonic mvp +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pea +test_Ismnemonic pei +test_Ismnemonic per +test_Ismnemonic pha +test_Ismnemonic phb +test_Ismnemonic phd +test_Ismnemonic phk +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plb +test_Ismnemonic pld +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rep +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rtl +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sep +test_Ismnemonic sta +test_Ismnemonic stp +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic swa +test_Ismnemonic tad +test_Ismnemonic tas +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tcd +test_Ismnemonic tcs +test_Ismnemonic tda +test_Ismnemonic tdc +test_Ismnemonic trb +test_Ismnemonic tsa +test_Ismnemonic tsb +test_Ismnemonic tsc +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic txy +test_Ismnemonic tya +test_Ismnemonic tyx +test_Ismnemonic wai +test_Ismnemonic wdm +test_Ismnemonic xba +test_Ismnemonic xce + +.setcpu "sweet16" +test_Ismnemonic add +test_Ismnemonic bc +test_Ismnemonic bk +test_Ismnemonic bm +test_Ismnemonic bm1 +test_Ismnemonic bnc +test_Ismnemonic bnm1 +test_Ismnemonic bnz +test_Ismnemonic bp +test_Ismnemonic br +test_Ismnemonic bs +test_Ismnemonic bz +test_Ismnemonic cpr +test_Ismnemonic dcr +test_Ismnemonic inr +test_Ismnemonic ld +test_Ismnemonic ldd +test_Ismnemonic pop +test_Ismnemonic popd +test_Ismnemonic rs +test_Ismnemonic rtn +test_Ismnemonic set +test_Ismnemonic st +test_Ismnemonic std +test_Ismnemonic stp +test_Ismnemonic sub From c5cf32ac47836b770e73323c10a5aa6b5e8d1195 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Fri, 5 May 2023 18:50:44 -0400 Subject: [PATCH 212/520] add test - fix --- test/asm/{err => val}/ismnemonic.s | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/asm/{err => val}/ismnemonic.s (100%) diff --git a/test/asm/err/ismnemonic.s b/test/asm/val/ismnemonic.s similarity index 100% rename from test/asm/err/ismnemonic.s rename to test/asm/val/ismnemonic.s From 7994889213352a82c904efd06f5b82bbfdf193e7 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Fri, 5 May 2023 19:07:14 -0400 Subject: [PATCH 213/520] add test - fix again --- test/asm/val/ismnemonic.s | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/asm/val/ismnemonic.s b/test/asm/val/ismnemonic.s index 438afed70..4ba74c97f 100644 --- a/test/asm/val/ismnemonic.s +++ b/test/asm/val/ismnemonic.s @@ -13,18 +13,22 @@ ; "65816" ; "sweet16" +; count any errors: +ismnemonic_error .set 0 + +; macro to test an instruction .macro test_Ismnemonic instr .if .ismnemonic(instr) ; do nothing .else - .error .sprintf(".ISMNEMONIC failed for instruction: %s", .string(instr)) + ismnemonic_error .set ismnemonic_error + 1 .endif .endmacro ; there is no instruction table for "none", make sure 'adc' (common to all CPUs) and 'add' (sweet16) doesn't match .setcpu "none" .if .ismnemonic(adc) || .ismnemonic(add) - .error ".ISMNEMONIC with CPU set to 'none' should not match any instructions." + ismnemonic_error .set ismnemonic_error + 1 .endif .setcpu "6502" @@ -806,3 +810,17 @@ test_Ismnemonic st test_Ismnemonic std test_Ismnemonic stp test_Ismnemonic sub + + .setcpu "6502" + + .import _exit + .export _main + +_main: + .if ismnemonic_error + ldx #$01 + .else + ldx #$00 + .endif + txa + jmp _exit From dd0a2bf1bc78188b75a22003590f20a4fedac941 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Fri, 5 May 2023 19:10:16 -0400 Subject: [PATCH 214/520] add test - fix stlye --- test/asm/val/ismnemonic.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/asm/val/ismnemonic.s b/test/asm/val/ismnemonic.s index 4ba74c97f..a4534a9c4 100644 --- a/test/asm/val/ismnemonic.s +++ b/test/asm/val/ismnemonic.s @@ -815,7 +815,7 @@ test_Ismnemonic sub .import _exit .export _main - + _main: .if ismnemonic_error ldx #$01 From 8d048699ee6b33c68f06bc6d0a16e521a733a386 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 21:32:34 -0400 Subject: [PATCH 215/520] grc65 fix flawed text parsing Was using fseek(F,-1,SEEK_CUR) which is invalid for text files, behaviour unreliable across platforms. Added check for internal buffer overflow. --- src/grc65/main.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/grc65/main.c b/src/grc65/main.c index 349b5c110..ac654300d 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -850,8 +850,12 @@ static char *filterInput (FILE *F, char *tbl) /* loads file into buffer filtering it out */ int a, prevchar = -1, i = 0, bracket = 0, quote = 1; - for (;;) { - a = getc(F); + a = getc(F); + while (1) + { + if (i >= BLOODY_BIG_BUFFER) { + AbEnd ("File too large for internal parsing buffer (%d bytes).",BLOODY_BIG_BUFFER); + } if ((a == '\n') || (a == '\015')) a = ' '; if (a == ',' && quote) a = ' '; if (a == '\042') quote =! quote; @@ -873,13 +877,18 @@ static char *filterInput (FILE *F, char *tbl) if (a == ';' && quote) { do { a = getc (F); - } while (a != '\n'); - fseek (F, -1, SEEK_CUR); + } while (a != '\n' && a != EOF); + /* Don't discard this newline/EOF, continue to next loop. + ** A previous implementation used fseek(F,-1,SEEK_CUR), + ** which is invalid for text mode files, and was unreliable across platforms. + */ + continue; } else { tbl[i++] = a; prevchar = a; } } + a = getc(F); } if (bracket != 0) AbEnd ("There are unclosed brackets!"); From f2e7609046b4febb23a5726f2f4dbd1489aac929 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 21:45:57 -0400 Subject: [PATCH 216/520] sim65 cycles 32-bit range fix long is 64-bit on some platforms, making this inconsistent, added range check to catch overflow. reduced tests requesting 5 billion cycles to 2^32-1 so they can fun on 32-bit long sim65. --- src/sim65/main.c | 5 +++++ test/asm/val/Makefile | 3 ++- test/standard/Makefile | 3 ++- test/val/Makefile | 3 ++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sim65/main.c b/src/sim65/main.c index f2daf9295..9e371fd5d 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -36,6 +36,7 @@ #include <string.h> #include <stdlib.h> #include <errno.h> +#include <limits.h> /* common */ #include "abend.h" @@ -140,6 +141,10 @@ static void OptQuitXIns (const char* Opt attribute ((unused)), /* quit after MaxCycles cycles */ { MaxCycles = strtoul(Arg, NULL, 0); + /* Guard against overflow. */ + if (MaxCycles == ULONG_MAX && errno == ERANGE) { + Error("'-x parameter out of range. Max: %lu",ULONG_MAX); + } } static unsigned char ReadProgramFile (void) diff --git a/test/asm/val/Makefile b/test/asm/val/Makefile index 91dae9afd..49b6d5290 100644 --- a/test/asm/val/Makefile +++ b/test/asm/val/Makefile @@ -22,7 +22,8 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 5000000000 +# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. +SIM65FLAGS = -x 4294967295 CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) diff --git a/test/standard/Makefile b/test/standard/Makefile index 054623b79..9993ba699 100644 --- a/test/standard/Makefile +++ b/test/standard/Makefile @@ -22,7 +22,8 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 5000000000 -c +# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. +SIM65FLAGS = -x 4294967295 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) diff --git a/test/val/Makefile b/test/val/Makefile index a3722f7bf..8820e535a 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -24,7 +24,8 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 5000000000 -c +# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. +SIM65FLAGS = -x 4294967295 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) From 773716c32ae0f19a78264c511d89758600288dce Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 21:46:11 -0400 Subject: [PATCH 217/520] sim65 close(-1) crash fix test/val/constexpr.c relies on close(-1) to return -1 for some reason (comment says "abuse"), but on MSVC close(-1) is treated as a security issue and terminates the program instead of returning -1 simulating this desire for sim65, though constexpr.c may also warrant a review --- src/sim65/paravirt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 9e5c28432..0b16f89e9 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -242,7 +242,15 @@ static void PVClose (CPURegs* Regs) Print (stderr, 2, "PVClose ($%04X)\n", FD); - RetVal = close (FD); + if (FD != 0xFFFF) { + RetVal = close (FD); + } else { + /* test/val/constexpr.c "abuses" close, expecting close(-1) to return -1. + ** This behaviour is not the same on all target platforms. + ** MSVC's close treats it as a fatal error instead and terminates. + */ + RetVal = 0xFFFF; + } SetAX (Regs, RetVal); } From c03d00bc805ffdda6afe1866d6263e7eef098a39 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 21:46:42 -0400 Subject: [PATCH 218/520] sim65 suppress uninitialized variable warning the EOF check was protecting uninitialized Val2 but the compiler can't figure that out --- src/sim65/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sim65/main.c b/src/sim65/main.c index 9e371fd5d..27299168e 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -189,6 +189,7 @@ static unsigned char ReadProgramFile (void) } /* Get load address */ + Val2 = 0; /* suppress uninitialized variable warning */ if (((Val = fgetc(F)) == EOF) || ((Val2 = fgetc(F)) == EOF)) { Error ("'%s': Header missing load address", ProgramFile); From df749abbfba01458907fd954beb47e96756d4fe7 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 21:56:52 -0400 Subject: [PATCH 219/520] libtest target alternative to libs saves me about 20 minutes if I only want to run tests --- Makefile | 4 ++-- libsrc/Makefile | 8 +++++++- test/readme.txt | 6 +++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 909de81ec..29fcbbf96 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ mostlyclean clean: avail unavail bin: @$(MAKE) -C src --no-print-directory $@ -lib: +lib libtest: @$(MAKE) -C libsrc --no-print-directory $@ doc html info: @@ -43,7 +43,7 @@ util: checkstyle: @$(MAKE) -C .github/checks --no-print-directory $@ -# simple "test" target, only run regression tests for c64 target +# runs regression tests, requires libtest target libraries test: @$(MAKE) -C test --no-print-directory $@ diff --git a/libsrc/Makefile b/libsrc/Makefile index 627897d9b..732fa1d0e 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -39,6 +39,10 @@ TARGETS = apple2 \ sym1 \ telestrat +TARGETTEST = none \ + sim6502 \ + sim65c02 + DRVTYPES = emd \ joy \ mou \ @@ -53,7 +57,7 @@ OUTPUTDIRS := lib $(subst ../,,$(wildcard ../target/*/drv/*)) \ $(subst ../,,$(wildcard ../target/*/util)) -.PHONY: all mostlyclean clean install zip lib $(TARGETS) +.PHONY: all mostlyclean clean install zip lib libtest $(TARGETS) .SUFFIXES: @@ -81,6 +85,8 @@ datadir = $(PREFIX)/share/cc65 all lib: $(TARGETS) +libtest: $(TARGETTEST) + mostlyclean: $(call RMDIR,../libwrk) diff --git a/test/readme.txt b/test/readme.txt index 41d19aee3..d3f17148e 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -68,7 +68,11 @@ compiler is working as expected (when the tests behave as described): which will require additional changes to the makefile(s). -To run the tests use "make" in this (top) directory, the makefile should exit +These tests only require a subset of the platform libraries. In the (top) +directory above this one, "make libtest" can be used to build only those +libraries needed for testing, instead of "make lib". + +To run the tests use "make" in this (test) directory, the makefile should exit with no error. When a test failed you can use "make continue" to run further tests. From a022f7203dca1b7b4a00a2fdfff577b9f08ec90d Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Fri, 5 May 2023 22:05:10 -0400 Subject: [PATCH 220/520] workflow for manually dispatched Windows build and test --- .github/workflows/build-on-pull-request.yml | 4 ++ .github/workflows/snapshot-on-push-master.yml | 4 ++ .github/workflows/windows-test-manual.yml | 43 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 .github/workflows/windows-test-manual.yml diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 05d6a4a39..57f00751d 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -67,3 +67,7 @@ jobs: - name: Build app (release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release + + # The regression tests are currently too slow to run for this Windows build, + # but the "Windows Test Manual" workflow (windows-test-manual.yml) can by + # manually dispatched from the Actions menu to test as needed. diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 50f5cd296..43fcce0a8 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -29,6 +29,10 @@ jobs: - name: Build app (release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release + # The regression tests are currently too slow to run for this Windows build, + # but the "Windows Test Manual" workflow (windows-test-manual.yml) can by + # manually dispatched from the Actions menu to test as needed. + build_linux: name: Build, Test, and Snapshot (Linux) if: github.repository == 'cc65/cc65' diff --git a/.github/workflows/windows-test-manual.yml b/.github/workflows/windows-test-manual.yml new file mode 100644 index 000000000..854327726 --- /dev/null +++ b/.github/workflows/windows-test-manual.yml @@ -0,0 +1,43 @@ +name: Windows Test Manual +# Manually dispatched because it's much slower than the Linux test. + +on: + workflow_dispatch: + +jobs: + build_windows: + name: Build, Test (Windows MSVC) + runs-on: windows-latest + + steps: + - name: Git Setup + shell: bash + run: git config --global core.autocrlf input + + - name: Checkout source + uses: actions/checkout@v3 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.1 + + - name: Build app (MSVC debug) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug + + - name: Build app (MSVC release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release + + - name: Build utils (MinGW) + shell: cmd + run: make -j2 util + + - name: Build the platform libraries (make lib) + shell: cmd + run: make -j2 lib QUIET=1 + + - name: Run the regression tests (make test) + shell: cmd + run: make test QUIET=1 + + - name: Test that the samples can be built (make samples) + shell: cmd + run: make -j2 samples From 1df7ab0352a43546c6ece76830594a687681c938 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 6 May 2023 11:55:21 -0400 Subject: [PATCH 221/520] opening brace on same line as while other AbEnd messages don't end in . --- src/grc65/main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/grc65/main.c b/src/grc65/main.c index ac654300d..adce3dc47 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -851,10 +851,9 @@ static char *filterInput (FILE *F, char *tbl) int a, prevchar = -1, i = 0, bracket = 0, quote = 1; a = getc(F); - while (1) - { + while (1) { if (i >= BLOODY_BIG_BUFFER) { - AbEnd ("File too large for internal parsing buffer (%d bytes).",BLOODY_BIG_BUFFER); + AbEnd ("File too large for internal parsing buffer (%d bytes)",BLOODY_BIG_BUFFER); } if ((a == '\n') || (a == '\015')) a = ' '; if (a == ',' && quote) a = ' '; From 532681c9613af15b1993e08db12761855c9707c6 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 6 May 2023 12:06:06 -0400 Subject: [PATCH 222/520] braces were requested combining the two a = ' ' cases was requested --- src/grc65/main.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/grc65/main.c b/src/grc65/main.c index adce3dc47..7d31bfc52 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -855,12 +855,20 @@ static char *filterInput (FILE *F, char *tbl) if (i >= BLOODY_BIG_BUFFER) { AbEnd ("File too large for internal parsing buffer (%d bytes)",BLOODY_BIG_BUFFER); } - if ((a == '\n') || (a == '\015')) a = ' '; - if (a == ',' && quote) a = ' '; - if (a == '\042') quote =! quote; + if (((a == '\n') || (a == '\015')) || + (a == ',' && quote)) { + a = ' '; + } + if (a == '\042') { + quote =! quote; + } if (quote) { - if ((a == '{') || (a == '(')) bracket++; - if ((a == '}') || (a == ')')) bracket--; + if ((a == '{') || (a == '(')) { + bracket++; + } + if ((a == '}') || (a == ')')) { + bracket--; + } } if (a == EOF) { tbl[i] = '\0'; From fe35386b794b5359c726a5867b26f8dc58c26615 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 6 May 2023 12:56:34 -0400 Subject: [PATCH 223/520] add test - add overloading instruction test --- test/asm/val/ismnemonic.s | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test/asm/val/ismnemonic.s b/test/asm/val/ismnemonic.s index a4534a9c4..2d131e7a9 100644 --- a/test/asm/val/ismnemonic.s +++ b/test/asm/val/ismnemonic.s @@ -21,14 +21,34 @@ ismnemonic_error .set 0 .if .ismnemonic(instr) ; do nothing .else - ismnemonic_error .set ismnemonic_error + 1 + ismnemonic_error .set 1 .endif .endmacro +; test .feature ubiquitous_idents + + ; allow overloading mnemonics +.feature ubiquitous_idents + +.setcpu "6502" + +; make an adc macro +.macro adc +.endmacro + +; should not match +.if .ismnemonic(adc) + ismnemonic_error .set 1 +.endif + +.delmac adc + +; test all instructions: + ; there is no instruction table for "none", make sure 'adc' (common to all CPUs) and 'add' (sweet16) doesn't match .setcpu "none" .if .ismnemonic(adc) || .ismnemonic(add) - ismnemonic_error .set ismnemonic_error + 1 + ismnemonic_error .set 1 .endif .setcpu "6502" From 84f0ab322d4b056191fd09d8fbc792ba1e045453 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 6 May 2023 13:54:28 -0400 Subject: [PATCH 224/520] sim65: cycles does not increment 1 at a time, so some small overhead is needed in range check --- src/sim65/main.c | 11 ++++++++--- test/asm/val/Makefile | 2 +- test/standard/Makefile | 2 +- test/val/Makefile | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sim65/main.c b/src/sim65/main.c index 27299168e..d92d52ef6 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -64,6 +64,12 @@ const char* ProgramFile; /* exit simulator after MaxCycles Cycles */ unsigned long MaxCycles; +/* maximum number of cycles that can be tested, +** requires overhead for longest possible instruction, +** which should be 7, using 16 for safety. +*/ +#define MAXCYCLES_LIMIT (ULONG_MAX-16) + /* Header signature 'sim65' */ static const unsigned char HeaderSignature[] = { 0x73, 0x69, 0x6D, 0x36, 0x35 @@ -73,7 +79,6 @@ static const unsigned char HeaderSignature[] = { static const unsigned char HeaderVersion = 2; - /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -142,8 +147,8 @@ static void OptQuitXIns (const char* Opt attribute ((unused)), { MaxCycles = strtoul(Arg, NULL, 0); /* Guard against overflow. */ - if (MaxCycles == ULONG_MAX && errno == ERANGE) { - Error("'-x parameter out of range. Max: %lu",ULONG_MAX); + if (MaxCycles >= MAXCYCLES_LIMIT) { + Error("'-x parameter out of range. Max: %lu",MAXCYCLES_LIMIT); } } diff --git a/test/asm/val/Makefile b/test/asm/val/Makefile index 49b6d5290..09a6b91bc 100644 --- a/test/asm/val/Makefile +++ b/test/asm/val/Makefile @@ -23,7 +23,7 @@ ifdef QUIET endif # sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. -SIM65FLAGS = -x 4294967295 +SIM65FLAGS = -x 4000000000 CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) diff --git a/test/standard/Makefile b/test/standard/Makefile index 9993ba699..40299c1bf 100644 --- a/test/standard/Makefile +++ b/test/standard/Makefile @@ -23,7 +23,7 @@ ifdef QUIET endif # sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. -SIM65FLAGS = -x 4294967295 -c +SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) diff --git a/test/val/Makefile b/test/val/Makefile index 8820e535a..158967f9e 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -25,7 +25,7 @@ ifdef QUIET endif # sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. -SIM65FLAGS = -x 4294967295 -c +SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) From 11cc5b6f06ca9bf1341eb6cc6e5055e5d01c2bc8 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 6 May 2023 14:24:53 -0400 Subject: [PATCH 225/520] remove .feature requirment for addrsize function, silently ignore '.feature addrsize' --- doc/ca65.sgml | 10 ---------- src/ca65/feature.c | 3 ++- src/ca65/global.c | 1 - src/ca65/global.h | 1 - src/ca65/scanner.c | 17 ----------------- 5 files changed, 2 insertions(+), 30 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 258808998..b4ef3e188 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -1409,10 +1409,6 @@ either a string or an expression value. .endmacro </verb></tscreen> - This command is new and must be enabled with the <tt/.FEATURE addrsize/ command. - - See: <tt><ref id=".FEATURE" name=".FEATURE"></tt> - <sect1><tt>.BANK</tt><label id=".BANK"><p> @@ -2795,12 +2791,6 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH <descrip> - <tag><tt>addrsize</tt><label id="addrsize"></tag> - - Enables the .ADDRSIZE pseudo function. This function is experimental and not enabled by default. - - See also: <tt><ref id=".ADDRSIZE" name=".ADDRSIZE"></tt> - <tag><tt>at_in_identifiers</tt><label id="at_in_identifiers"></tag> Accept the at character ('@') as a valid character in identifiers. The diff --git a/src/ca65/feature.c b/src/ca65/feature.c index 41177d66b..8b915cfda 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -118,10 +118,11 @@ void SetFeature (feature_t Feature, unsigned char On) case FEAT_C_COMMENTS: CComments = On; break; case FEAT_FORCE_RANGE: ForceRange = On; break; case FEAT_UNDERLINE_IN_NUMBERS: UnderlineInNumbers= On; break; - case FEAT_ADDRSIZE: AddrSize = On; break; case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = On; break; case FEAT_STRING_ESCAPES: StringEscapes = On; break; case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = On; break; + /* Accept, but ignore addrsize */ + case FEAT_ADDRSIZE: break; default: break; } } diff --git a/src/ca65/global.c b/src/ca65/global.c index 337677e31..050d19e09 100644 --- a/src/ca65/global.c +++ b/src/ca65/global.c @@ -85,5 +85,4 @@ unsigned char OrgPerSeg = 0; /* Make .org local to current seg */ unsigned char CComments = 0; /* Allow C like comments */ unsigned char ForceRange = 0; /* Force values into expected range */ unsigned char UnderlineInNumbers = 0; /* Allow underlines in numbers */ -unsigned char AddrSize = 0; /* Allow .ADDRSIZE function */ unsigned char BracketAsIndirect = 0; /* Use '[]' not '()' for indirection */ diff --git a/src/ca65/global.h b/src/ca65/global.h index 46fb6c763..b3de99df5 100644 --- a/src/ca65/global.h +++ b/src/ca65/global.h @@ -87,7 +87,6 @@ extern unsigned char OrgPerSeg; /* Make .org local to current seg */ extern unsigned char CComments; /* Allow C like comments */ extern unsigned char ForceRange; /* Force values into expected range */ extern unsigned char UnderlineInNumbers; /* Allow underlines in numbers */ -extern unsigned char AddrSize; /* Allow .ADDRSIZE function */ extern unsigned char BracketAsIndirect; /* Use '[]' not '()' for indirection */ diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index add365e84..185100025 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -748,24 +748,7 @@ static token_t FindDotKeyword (void) R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]), sizeof (DotKeywords [0]), CmpDotKeyword); if (R != 0) { - - /* By default, disable any somewhat experiemental DotKeyword. */ - - switch (R->Tok) { - - case TOK_ADDRSIZE: - /* Disallow .ADDRSIZE function by default */ - if (AddrSize == 0) { - return TOK_NONE; - } - break; - - default: - break; - } - return R->Tok; - } else { return TOK_NONE; } From 560085cb1763c331ab5e0e47272b66026e59a36b Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 6 May 2023 16:22:04 -0400 Subject: [PATCH 226/520] modify and add tests --- test/asm/val/addrsize.s | 32 ++++++++++++++++++++++++++++++++ test/asm/val/feature.s | 13 ------------- 2 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 test/asm/val/addrsize.s diff --git a/test/asm/val/addrsize.s b/test/asm/val/addrsize.s new file mode 100644 index 000000000..932090df0 --- /dev/null +++ b/test/asm/val/addrsize.s @@ -0,0 +1,32 @@ +; test .addrsize and ensure .feature addrsize is allowed, but inactive + +.export _main + +.segment "ZEROPAGE" +zplabel: + +.segment "CODE" +abslabel: + +; exit with 0 + +_main: + lda #0 + tax + rts + + +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" + +.feature addrsize +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" + +.feature addrsize + +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" + +.feature addrsize - +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" diff --git a/test/asm/val/feature.s b/test/asm/val/feature.s index 4428cf4c2..39e3a7862 100644 --- a/test/asm/val/feature.s +++ b/test/asm/val/feature.s @@ -2,12 +2,6 @@ .export _main -.segment "ZEROPAGE" -zplabel: - -.segment "CODE" -abslabel: - ; exit with 0 _main: @@ -17,13 +11,6 @@ _main: tax rts - -.feature addrsize + -.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" -.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" -.feature addrsize - - - .feature at_in_identifiers on ident@with@at: rts From bee29dedd17841d77c861fb737874e8905ebe7d3 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sat, 6 May 2023 17:11:57 -0400 Subject: [PATCH 227/520] fix feature.s test --- test/asm/val/feature.s | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/asm/val/feature.s b/test/asm/val/feature.s index 39e3a7862..0def9d92c 100644 --- a/test/asm/val/feature.s +++ b/test/asm/val/feature.s @@ -2,6 +2,11 @@ .export _main +.segment "ZEROPAGE" +zplabel: + +.segment "CODE" + ; exit with 0 _main: From 56df849101318a9a2aa61b407d5c0f9fb7fd2eb6 Mon Sep 17 00:00:00 2001 From: mvax <big.jt@protonmail.com> Date: Sun, 7 May 2023 14:53:44 -0400 Subject: [PATCH 228/520] add warning for .feature addrsize, clean up switch in SetFeature --- src/ca65/feature.c | 2 -- src/ca65/pseudo.c | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ca65/feature.c b/src/ca65/feature.c index 8b915cfda..9f5ca5876 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -121,8 +121,6 @@ void SetFeature (feature_t Feature, unsigned char On) case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = On; break; case FEAT_STRING_ESCAPES: StringEscapes = On; break; case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = On; break; - /* Accept, but ignore addrsize */ - case FEAT_ADDRSIZE: break; default: break; } } diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index 1877512d5..cf4d1f64b 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -1043,6 +1043,12 @@ static void DoFeature (void) ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal); return; } + + if (Feature == FEAT_ADDRSIZE) { + /* Warn for depreciated .feature addrsize */ + Warning (1, "Depreciated feature: '.feature addrsize'. Pseudo function .addrsize is always available."); + } + NextTok (); /* Optional +/- or ON/OFF */ From 0081fe548ce3908dee2624a322d89af8d06ed4bb Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 7 May 2023 16:26:42 -0400 Subject: [PATCH 229/520] sim64 universal 64-bit cycle count support: MaxCycleCount is accounted by countdown, eliminating the 1-instruction-overhead issue, and removing the need to compare against a growing TotalCycles. Makes main.c responsible for counting total cycles, instead of 6502.c, so the size of MaxCycleCount etc. is fully determined in one location. Makes error.c responsible for PrintCycles instead of paravirt.c, so that it can be treated globally instead of Return value of main() should be SIM65_ERROR because it is unreachable by design. --- src/sim65/6502.c | 18 ------------------ src/sim65/6502.h | 6 ------ src/sim65/error.c | 25 +++++++++++++++++++++++++ src/sim65/error.h | 6 ++++++ src/sim65/main.c | 40 +++++++++++++++++++++------------------- src/sim65/paravirt.c | 6 +----- 6 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/sim65/6502.c b/src/sim65/6502.c index 6c23b0dfc..9d2c93da8 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -64,18 +64,12 @@ static CPURegs Regs; /* Cycles for the current insn */ static unsigned Cycles; -/* Total number of CPU cycles exec'd */ -static unsigned long TotalCycles; - /* NMI request active */ static unsigned HaveNMIRequest; /* IRQ request active */ static unsigned HaveIRQRequest; -/* flag to print cycles at program termination */ -int PrintCycles; - /*****************************************************************************/ /* Helper functions and macros */ @@ -3277,18 +3271,6 @@ unsigned ExecuteInsn (void) Handlers[CPU][OPC] (); } - /* Count cycles */ - TotalCycles += Cycles; - /* Return the number of clock cycles needed by this insn */ return Cycles; } - - - -unsigned long GetCycles (void) -/* Return the total number of cycles executed */ -{ - /* Return the total number of cycles */ - return TotalCycles; -} diff --git a/src/sim65/6502.h b/src/sim65/6502.h index f8e894567..39b995793 100644 --- a/src/sim65/6502.h +++ b/src/sim65/6502.h @@ -96,12 +96,6 @@ unsigned ExecuteInsn (void); ** executed instruction. */ -unsigned long GetCycles (void); -/* Return the total number of clock cycles executed */ - -extern int PrintCycles; -/* flag to print cycles at program termination */ - /* End of 6502.h */ diff --git a/src/sim65/error.c b/src/sim65/error.c index 441b07d2a..fc24ca006 100644 --- a/src/sim65/error.c +++ b/src/sim65/error.c @@ -41,6 +41,20 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* flag to print cycles at program termination */ +int PrintCycles = 0; + +/* cycles are counted by main.c */ +extern unsigned long long TotalCycles; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -99,3 +113,14 @@ void Internal (const char* Format, ...) va_end (ap); exit (SIM65_ERROR); } + + + +void SimExit (int Code) +/* Exit the simulation with an exit code */ +{ + if (PrintCycles) { + fprintf (stdout, "%llu cycles\n", TotalCycles); + } + exit (Code); +} diff --git a/src/sim65/error.h b/src/sim65/error.h index ea54fa048..a016881c6 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -55,6 +55,9 @@ #define SIM65_ERROR_TIMEOUT 0x7E /* An error result for max CPU instructions exceeded. */ +extern int PrintCycles; +/* flag to print cycles at program termination */ + /*****************************************************************************/ @@ -75,6 +78,9 @@ void ErrorCode (int Code, const char* Format, ...) attribute((noreturn, format(p void Internal (const char* Format, ...) attribute((noreturn, format(printf,1,2))); /* Print an internal error message and die */ +void SimExit (int Code); +/* Exit the simulation with an exit code */ + /* End of error.h */ diff --git a/src/sim65/main.c b/src/sim65/main.c index d92d52ef6..f5ace1909 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -36,7 +36,6 @@ #include <string.h> #include <stdlib.h> #include <errno.h> -#include <limits.h> /* common */ #include "abend.h" @@ -61,14 +60,14 @@ /* Name of program file */ const char* ProgramFile; -/* exit simulator after MaxCycles Cycles */ -unsigned long MaxCycles; +/* count of total cycles executed */ +unsigned long long TotalCycles = 0; -/* maximum number of cycles that can be tested, -** requires overhead for longest possible instruction, -** which should be 7, using 16 for safety. -*/ -#define MAXCYCLES_LIMIT (ULONG_MAX-16) +/* exit simulator after MaxCycles Cccles */ +unsigned long long MaxCycles = 0; + +/* countdown from MaxCycles */ +unsigned long long RemainCycles; /* Header signature 'sim65' */ static const unsigned char HeaderSignature[] = { @@ -145,11 +144,7 @@ static void OptQuitXIns (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* quit after MaxCycles cycles */ { - MaxCycles = strtoul(Arg, NULL, 0); - /* Guard against overflow. */ - if (MaxCycles >= MAXCYCLES_LIMIT) { - Error("'-x parameter out of range. Max: %lu",MAXCYCLES_LIMIT); - } + MaxCycles = strtoull(Arg, NULL, 0); } static unsigned char ReadProgramFile (void) @@ -247,6 +242,7 @@ int main (int argc, char* argv[]) unsigned I; unsigned char SPAddr; + unsigned int Cycles; /* Initialize the cmdline module */ InitCmdLine (&argc, &argv, "sim65"); @@ -309,18 +305,24 @@ int main (int argc, char* argv[]) MemInit (); SPAddr = ReadProgramFile (); - ParaVirtInit (I, SPAddr); Reset (); + RemainCycles = MaxCycles; while (1) { - ExecuteInsn (); - if (MaxCycles && (GetCycles () >= MaxCycles)) { - ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); + Cycles = ExecuteInsn (); + TotalCycles += Cycles; + if (MaxCycles) { + if (Cycles > RemainCycles) { + ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles (%llu) reached.", MaxCycles); + } + RemainCycles -= Cycles; } } - /* Return an apropriate exit code */ - return EXIT_SUCCESS; + /* Unreachable. sim65 program must exit through paravirtual PVExit + ** or timeout from MaxCycles producing an error. + */ + return SIM65_ERROR; } diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 0b16f89e9..af162acfa 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -124,11 +124,7 @@ static unsigned PopParam (unsigned char Incr) static void PVExit (CPURegs* Regs) { Print (stderr, 1, "PVExit ($%02X)\n", Regs->AC); - if (PrintCycles) { - Print (stdout, 0, "%lu cycles\n", GetCycles ()); - } - - exit (Regs->AC); + SimExit (Regs->AC); } From aad64063c97d2b379955f90d006c244c744c559e Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 7 May 2023 16:33:07 -0400 Subject: [PATCH 230/520] makefiles no longer need comment about sim65 64-bit support --- test/asm/val/Makefile | 1 - test/standard/Makefile | 1 - test/val/Makefile | 1 - 3 files changed, 3 deletions(-) diff --git a/test/asm/val/Makefile b/test/asm/val/Makefile index 09a6b91bc..54b1100ec 100644 --- a/test/asm/val/Makefile +++ b/test/asm/val/Makefile @@ -22,7 +22,6 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. SIM65FLAGS = -x 4000000000 CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) diff --git a/test/standard/Makefile b/test/standard/Makefile index 40299c1bf..bf513c84e 100644 --- a/test/standard/Makefile +++ b/test/standard/Makefile @@ -22,7 +22,6 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) diff --git a/test/val/Makefile b/test/val/Makefile index 158967f9e..56d8e5ff9 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -24,7 +24,6 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) From 3419cbd3484427ad732289c5ff7cab41639cf1ca Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 7 May 2023 16:33:47 -0400 Subject: [PATCH 231/520] sim65 64-bit cycle count tests These take ~10 seconds to run locally --- test/asm/Makefile | 2 +- test/asm/misc/Makefile | 70 +++++++++++++++++++++++++++++++ test/asm/misc/sim65-time-wait.inc | 55 ++++++++++++++++++++++++ test/asm/misc/sim65-timein.s | 17 ++++++++ test/asm/misc/sim65-timeout.s | 17 ++++++++ test/asm/readme.txt | 6 +++ 6 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 test/asm/misc/Makefile create mode 100644 test/asm/misc/sim65-time-wait.inc create mode 100644 test/asm/misc/sim65-timein.s create mode 100644 test/asm/misc/sim65-timeout.s diff --git a/test/asm/Makefile b/test/asm/Makefile index 3481dae78..dea53f6b2 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -12,7 +12,7 @@ endif WORKDIR = ../testwrk/asm -SUBDIRS = cpudetect opcodes listing val err +SUBDIRS = cpudetect opcodes listing val err misc .PHONY: all continue mostlyclean clean diff --git a/test/asm/misc/Makefile b/test/asm/misc/Makefile new file mode 100644 index 000000000..5a9d4f3ef --- /dev/null +++ b/test/asm/misc/Makefile @@ -0,0 +1,70 @@ +# Makefile for the remaining asm tests that need special care in one way or another + +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + S = $(subst /,\,/) + NOT = - # Hack + EXE = .exe + NULLDEV = nul: + MKDIR = mkdir $(subst /,\,$1) + RMDIR = -rmdir /s /q $(subst /,\,$1) +else + S = / + NOT = ! + EXE = + NULLDEV = /dev/null + MKDIR = mkdir -p $1 + RMDIR = $(RM) -r $1 +endif + +ifdef QUIET + .SILENT: + NULLOUT = >$(NULLDEV) + NULLERR = 2>$(NULLDEV) +endif + +SIM65FLAGS = -x 200000000 + +CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) +SIM65 := $(if $(wildcard ../../../bin/sim65*),..$S..$S..$Sbin$Ssim65,sim65) + +WORKDIR = ..$S..$S..$Stestwrk$Sasm$Smisc + +.PHONY: all clean + +SOURCES := $(wildcard *.s) +TESTS = $(SOURCES:%.s=$(WORKDIR)/%.6502.prg) +TESTS += $(SOURCES:%.s=$(WORKDIR)/%.65c02.prg) + +all: $(TESTS) + +$(WORKDIR): + $(call MKDIR,$(WORKDIR)) + +define PRG_template + +# sim65 ensure 64-bit wait time does not timeout +$(WORKDIR)/sim65-timein.$1.prg: sim65-timein.s | $(WORKDIR) + $(if $(QUIET),echo misc/sim65-timein.$1.prg) + $(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR) + $(LD65) -t sim$1 -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR) + $(SIM65) -x 4400000000 -c $$@ $(NULLOUT) $(NULLERR) + +# sim65 ensure 64-bit wait time does timeout +$(WORKDIR)/sim65-timeout.$1.prg: sim65-timeout.s | $(WORKDIR) + $(if $(QUIET),echo misc/sim65-timeout.$1.prg) + $(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR) + $(LD65) -t sim$1 -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR) + $(NOT) $(SIM65) -x 4400000000 -c $$@ $(NULLOUT) $(NULLERR) + +endef # PRG_template + +$(eval $(call PRG_template,6502)) +$(eval $(call PRG_template,65c02)) + +clean: + @$(call RMDIR,$(WORKDIR)) diff --git a/test/asm/misc/sim65-time-wait.inc b/test/asm/misc/sim65-time-wait.inc new file mode 100644 index 000000000..bc761ac16 --- /dev/null +++ b/test/asm/misc/sim65-time-wait.inc @@ -0,0 +1,55 @@ +; Shared timer for: +; sim65-timein.s +; sim65-timeout.s + +; wait A * 100,000,000 cycles, plus small amount of overhead +wait100m: + tay + bne :+ + rts ; return quickly if A=0 +: + jsr wait50331648 ; 50331648 + jsr wait25165824 ; 75497472 + jsr wait12582912 ; 88080384 + jsr wait6291456 ; 94371840 + jsr wait3145728 ; 97517568 + jsr wait1572864 ; 99090432 + jsr wait786432 ; 99876864 + jsr wait98304 ; 99975168 + jsr wait24576 ; 99999744 + jsr wait192 ; 99999936 + jsr wait48 ; 99999984 + nop ; 99999986 + nop ; 99999988 + php ; 99999991 + plp ; 99999995 + dey ; 99999997 + bne :- ; 100000000 + rts +; Note that this branch could cross a page if poorly aligned, +; adding an additional 1 cycle per loop. +; This precision is not important for the tests used. + +wait50331648: jsr wait25165824 +wait25165824: jsr wait12582912 +wait12582912: jsr wait6291456 +wait6291456: jsr wait3145728 +wait3145728: jsr wait1572864 +wait1572864: jsr wait786432 +wait786432: jsr wait393216 +wait393216: jsr wait196608 +wait196608: jsr wait98304 +wait98304: jsr wait49152 +wait49152: jsr wait24576 +wait24576: jsr wait12288 +wait12288: jsr wait6144 +wait6144: jsr wait3072 +wait3072: jsr wait1536 +wait1536: jsr wait768 +wait768: jsr wait384 +wait384: jsr wait192 +wait192: jsr wait96 +wait96: jsr wait48 +wait48: jsr wait24 +wait24: jsr wait12 +wait12: rts diff --git a/test/asm/misc/sim65-timein.s b/test/asm/misc/sim65-timein.s new file mode 100644 index 000000000..13365f0a8 --- /dev/null +++ b/test/asm/misc/sim65-timein.s @@ -0,0 +1,17 @@ +; Verifies that sim65 can handle 64-bit timeout counter. +; sim65 sim65-timein.prg -x 4400000000 + +.export _main +.import exit + +_main: + ; wait ~4,300,000,000 cycles + lda #43 + jsr wait100m + ; This is a positive test. + ; If the timeout did not occur, returning 0 reports success. + lda #0 + rts + +; wait100m +.include "sim65-time-wait.inc" diff --git a/test/asm/misc/sim65-timeout.s b/test/asm/misc/sim65-timeout.s new file mode 100644 index 000000000..6f1778dcd --- /dev/null +++ b/test/asm/misc/sim65-timeout.s @@ -0,0 +1,17 @@ +; Verifies that sim65 can handle 64-bit timeout counter. +; sim65 sim65-timeout.prg -x 4400000000 + +.export _main +.import exit + +_main: + ; wait ~4,500,000,000 cycles + lda #45 + jsr wait100m + ; This is a negative test. + ; If the timeout did not occur, returning 0 reports failure. + lda #0 + rts + +; wait100m +.include "sim65-time-wait.inc" diff --git a/test/asm/readme.txt b/test/asm/readme.txt index 49b530d1c..9b716e60c 100644 --- a/test/asm/readme.txt +++ b/test/asm/readme.txt @@ -36,3 +36,9 @@ val: Runtime assembly tests using sim65 that should end with an exit code of 0 if they pass. If they fail the exit code should be either -1, or a number indicating what part of the test failed. + + +misc: +----- + +This is for tests that require special make steps or conditions. From 7f0baff792bd4f5b4937dc1d19811f310ba7b29f Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 7 May 2023 16:35:05 -0400 Subject: [PATCH 232/520] document how to return from assembly sim65 test --- doc/sim65.sgml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/sim65.sgml b/doc/sim65.sgml index 310de4667..c838cd3b0 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -126,9 +126,17 @@ a set of built-in paravirtualization functions (<ref id="paravirt-internal" name <sect>Creating a Test in Assembly<p> Assembly tests may similarly be assembled and linked with -<tt/--target sim6502/ or <tt/--target sim65c02/, -and the sim65 library provides an <tt/exit/ symbol that the program may <tt/JMP/ -to terminate with the current A register value as an exit code. +<tt/--target sim6502/ or <tt/--target sim65c02/. +Define and export <tt/_main/ as an entry point, +and the sim65 library provides two ways to return an 8-bit exit code: + +<itemize> + +<item>Return from <tt/_main/ with the exit code in <tt/A/. + +<item><tt/jmp exit/ with the code in <tt/A/. + +</itemize> The binary file has a 12 byte header: From 2cb457b85f8019e6437907ddd19e3d2c5f2546c6 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 7 May 2023 16:51:12 -0400 Subject: [PATCH 233/520] sim65 use error codes outside the simulated program's range for non-sim errors --- doc/sim65.sgml | 7 +++++++ src/sim65/error.h | 8 +++++--- src/sim65/paravirt.c | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/sim65.sgml b/doc/sim65.sgml index c838cd3b0..b1e5afbdc 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -44,6 +44,13 @@ The simulator is called as follows: --version Print the simulator version number </verb></tscreen> +sim65 will exit with the error code of the simulated program, +which is limited to an 8-bit result 0-255. + +An error in sim65, like bad arguments or an internal problem will exit with <tt/1/. + +A timeout from <tt/-x/ will exist with <tt/2/. + <sect1>Command line options in detail<p> diff --git a/src/sim65/error.h b/src/sim65/error.h index a016881c6..6dbee974c 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -49,10 +49,12 @@ -#define SIM65_ERROR 0x7F -/* Does not use EXIT_FAILURE because it may overlap with test results. */ +#define SIM65_ERROR -1 +/* An error result for errors that are not part of the simulated test. +** Note that set simulated test can only return 8-bit errors 0-255. +*/ -#define SIM65_ERROR_TIMEOUT 0x7E +#define SIM65_ERROR_TIMEOUT -2 /* An error result for max CPU instructions exceeded. */ extern int PrintCycles; diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index af162acfa..2e52d6e7e 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -124,7 +124,7 @@ static unsigned PopParam (unsigned char Incr) static void PVExit (CPURegs* Regs) { Print (stderr, 1, "PVExit ($%02X)\n", Regs->AC); - SimExit (Regs->AC); + SimExit (Regs->AC); /* Error code in range 0-255. */ } From f15e9c41593cec0aed3eb17b01a91985e778f1e7 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 7 May 2023 17:41:54 -0400 Subject: [PATCH 234/520] Linux build rejects %llu in ErrorCode --- src/sim65/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sim65/main.c b/src/sim65/main.c index f5ace1909..3c7cdc157 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -315,7 +315,7 @@ int main (int argc, char* argv[]) TotalCycles += Cycles; if (MaxCycles) { if (Cycles > RemainCycles) { - ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles (%llu) reached.", MaxCycles); + ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); } RemainCycles -= Cycles; } From 02d38ae17eb9705f9afeb8a50661b3ab8860971b Mon Sep 17 00:00:00 2001 From: Irgendwer <C.Krueger.B@web.de> Date: Mon, 8 May 2023 15:28:22 +0200 Subject: [PATCH 235/520] Fixed comments for Atari OS memory location --- include/_atarios.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/_atarios.h b/include/_atarios.h index ec33b98c9..cbf33bda6 100644 --- a/include/_atarios.h +++ b/include/_atarios.h @@ -334,17 +334,17 @@ struct __os { void (*vserin)(void); // = $020A/$020B POKEY SERIAL INPUT READY IRQ void (*vseror)(void); // = $020C/$020D POKEY SERIAL OUTPUT READY IRQ void (*vseroc)(void); // = $020E/$020F POKEY SERIAL OUTPUT COMPLETE IRQ - void (*vtimr1)(void); // = $0210/$0201 POKEY TIMER 1 IRQ - void (*vtimr2)(void); // = $0212/$0203 POKEY TIMER 2 IRQ - void (*vtimr4)(void); // = $0214/$0205 POKEY TIMER 4 IRQ - void (*vimirq)(void); // = $0216/$0207 IMMEDIATE IRQ VECTOR - unsigned int cdtmv1; // = $0218/$0210 COUNT DOWN TIMER 1 + void (*vtimr1)(void); // = $0210/$0211 POKEY TIMER 1 IRQ + void (*vtimr2)(void); // = $0212/$0213 POKEY TIMER 2 IRQ + void (*vtimr4)(void); // = $0214/$0215 POKEY TIMER 4 IRQ + void (*vimirq)(void); // = $0216/$0217 IMMEDIATE IRQ VECTOR + unsigned int cdtmv1; // = $0218/$0219 COUNT DOWN TIMER 1 unsigned int cdtmv2; // = $021A/$021B COUNT DOWN TIMER 2 unsigned int cdtmv3; // = $021C/$021D COUNT DOWN TIMER 3 unsigned int cdtmv4; // = $021E/$021F COUNT DOWN TIMER 4 unsigned int cdtmv5; // = $0220/$0221 COUNT DOWN TIMER 5 void (*vvblki)(void); // = $0222/$0223 IMMEDIATE VERTICAL BLANK NMI VECTOR - void (*vvblkd)(void); // = $0224/$0224 DEFERRED VERTICAL BLANK NMI VECTOR + void (*vvblkd)(void); // = $0224/$0225 DEFERRED VERTICAL BLANK NMI VECTOR void (*cdtma1)(void); // = $0226/$0227 COUNT DOWN TIMER 1 JSR ADDRESS void (*cdtma2)(void); // = $0228/$0229 COUNT DOWN TIMER 2 JSR ADDRESS unsigned char cdtmf3; // = $022A COUNT DOWN TIMER 3 FLAG From a058d4a2f3bd0361a0b03ffecb39b22d8cc0eb93 Mon Sep 17 00:00:00 2001 From: Movax12 <jeremiah.turner@gmail.com> Date: Mon, 8 May 2023 16:55:54 -0400 Subject: [PATCH 236/520] Fix warning message, remove comment --- src/ca65/pseudo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index cf4d1f64b..2ce1ae087 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -1045,8 +1045,7 @@ static void DoFeature (void) } if (Feature == FEAT_ADDRSIZE) { - /* Warn for depreciated .feature addrsize */ - Warning (1, "Depreciated feature: '.feature addrsize'. Pseudo function .addrsize is always available."); + Warning (1, "Deprecated feature: '.feature addrsize'. Pseudo function .addrsize is always available."); } NextTok (); From ce6097ea7ebf4d37abc6865c1afc806876e5e2f5 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 8 May 2023 18:12:57 -0400 Subject: [PATCH 237/520] rename windows-test-manual to windows-test-scheduled --- .../{windows-test-manual.yml => windows-test-scheduled.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{windows-test-manual.yml => windows-test-scheduled.yml} (100%) diff --git a/.github/workflows/windows-test-manual.yml b/.github/workflows/windows-test-scheduled.yml similarity index 100% rename from .github/workflows/windows-test-manual.yml rename to .github/workflows/windows-test-scheduled.yml From 07963abd52ff5ccf3c5ef7790451454e1c0f3d0b Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 8 May 2023 18:21:21 -0400 Subject: [PATCH 238/520] replace manual-only tests with scheduled test and manual dispatch cache is used to prevent unnecessary rebuild if the previous build was successful make steps now use SHELL=cmd to provide cmd.exe subshell --- .github/workflows/windows-test-scheduled.yml | 48 +++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 854327726..c3516772b 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -1,8 +1,16 @@ -name: Windows Test Manual -# Manually dispatched because it's much slower than the Linux test. +name: Windows Test Scheduled +# Scheduled or manually dispatched because it's slower than the Linux test. on: + schedule: + - cron: '0 0 */1 * *' + # every 1 days workflow_dispatch: + # allow manual dispatch +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + # don't run more than once at a time jobs: build_windows: @@ -10,34 +18,62 @@ jobs: runs-on: windows-latest steps: + + # This cache is used to remember the last build. + # If there are no changes and the last build was successful, + # the build and test steps will be omitted. + # If the last build failed, the full attempt will be repeated. + # Github Actions will retain the last build cache for up to 7 days. + + - name: Create Cache + shell: bash + run: mkdir ~/.cache-sha + + - name: Cache SHA + uses: actions/cache@v3 + id: check-sha + with: + path: ~/.cache-sha + key: cache-sha-wintest-${{ github.sha }} + - name: Git Setup + if: steps.check-sha.outputs.cache-hit != 'true' shell: bash run: git config --global core.autocrlf input - name: Checkout source + if: steps.check-sha.outputs.cache-hit != 'true' uses: actions/checkout@v3 - name: Add msbuild to PATH + if: steps.check-sha.outputs.cache-hit != 'true' uses: microsoft/setup-msbuild@v1.1 - name: Build app (MSVC debug) + if: steps.check-sha.outputs.cache-hit != 'true' run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug - name: Build app (MSVC release) + if: steps.check-sha.outputs.cache-hit != 'true' run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release - name: Build utils (MinGW) + if: steps.check-sha.outputs.cache-hit != 'true' shell: cmd - run: make -j2 util + run: make -j2 util SHELL=cmd - name: Build the platform libraries (make lib) + if: steps.check-sha.outputs.cache-hit != 'true' shell: cmd - run: make -j2 lib QUIET=1 + run: make lib QUIET=1 SHELL=cmd + # make -j2 lib fails with SHELL=cmd (not sure why) - name: Run the regression tests (make test) + if: steps.check-sha.outputs.cache-hit != 'true' shell: cmd - run: make test QUIET=1 + run: make test QUIET=1 SHELL=cmd - name: Test that the samples can be built (make samples) + if: steps.check-sha.outputs.cache-hit != 'true' shell: cmd - run: make -j2 samples + run: make -j2 samples SHELL=cmd From 76328da6824493b75a0d9863c1730e0dcf418ad5 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 8 May 2023 20:57:50 -0400 Subject: [PATCH 239/520] librsc/Makefile: ../lib directory must be created globally before make lib targets in parallel prevents conflict of individual targets each trying to create ../lib enable -j2 in make lib windows action --- .github/workflows/windows-test-scheduled.yml | 3 +-- libsrc/Makefile | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index c3516772b..451b37f79 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -65,8 +65,7 @@ jobs: - name: Build the platform libraries (make lib) if: steps.check-sha.outputs.cache-hit != 'true' shell: cmd - run: make lib QUIET=1 SHELL=cmd - # make -j2 lib fails with SHELL=cmd (not sure why) + run: make -j2 lib QUIET=1 SHELL=cmd - name: Run the regression tests (make test) if: steps.check-sha.outputs.cache-hit != 'true' diff --git a/libsrc/Makefile b/libsrc/Makefile index 732fa1d0e..0873d019f 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -122,9 +122,13 @@ endef # ZIP_recipe zip: $(foreach dir,$(OUTPUTDIRS),$(ZIP_recipe)) -$(TARGETS): +$(TARGETS): | ../lib @$(MAKE) --no-print-directory $@ +# ../lib must be created globally before doing lib targets in parallel +../lib: + @$(call MKDIR,$@) + else # TARGET CA65FLAGS = @@ -293,10 +297,12 @@ $(EXTRA_OBJPAT): $(EXTRA_SRCPAT) | ../libwrk/$(TARGET) ../lib @echo $(TARGET) - $(<F) @$(CA65) -t $(TARGET) $(CA65FLAGS) --create-dep $(@:../lib/%.o=../libwrk/$(TARGET)/%.d) -o $@ $< +$(EXTRA_OBJS): | ../lib + ../lib/$(TARGET).lib: $(OBJS) | ../lib $(AR65) a $@ $? -../libwrk/$(TARGET) ../lib ../target/$(TARGET)/util: +../libwrk/$(TARGET) ../target/$(TARGET)/util: @$(call MKDIR,$@) $(TARGET): $(EXTRA_OBJS) ../lib/$(TARGET).lib From eb2b013d6246ea6e112b769ea0e281ea9d926ff1 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Mon, 8 May 2023 21:22:18 -0400 Subject: [PATCH 240/520] remove workflow comments this obsoletes --- .github/workflows/build-on-pull-request.yml | 4 ---- .github/workflows/snapshot-on-push-master.yml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 1ad810382..55be5db1e 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -72,7 +72,3 @@ jobs: - name: Build app (release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release - - # The regression tests are currently too slow to run for this Windows build, - # but the "Windows Test Manual" workflow (windows-test-manual.yml) can by - # manually dispatched from the Actions menu to test as needed. diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 6ba6b63ed..2aedb0e25 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -29,10 +29,6 @@ jobs: - name: Build app (release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release - # The regression tests are currently too slow to run for this Windows build, - # but the "Windows Test Manual" workflow (windows-test-manual.yml) can by - # manually dispatched from the Actions menu to test as needed. - build_linux: name: Build, Test, and Snapshot (Linux) if: github.repository == 'cc65/cc65' From 3b7be09a7f7a70bff6ee463b24d5514ec52ca1d4 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Thu, 11 May 2023 19:50:58 -0400 Subject: [PATCH 241/520] extern redeclared as static = error (C spec: undefined) static redeclared as extern = warning (C spec: ignore extern) See: #2111 --- src/cc65/symtab.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index d9270f604..a2bbf13dd 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1340,15 +1340,14 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Name); Entry = 0; } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { - /* If a static declaration follows a non-static declaration, then - ** diagnose the conflict. It will warn and compile an extern - ** declaration if both declarations are global, otherwise give an - ** error. + /* If a static declaration follows a non-static declaration, then the result is undefined. + ** Most compilers choose to either give an error at compile time, + ** or remove the extern property for a link time error if used. */ if (SymTab == SymTab0 && (Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { - Warning ("Static declaration of '%s' follows non-static declaration", Name); + Error ("Static declaration of '%s' follows non-static declaration", Name); } else if ((Flags & SC_EXTERN) != 0 && (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && (Entry->Flags & SC_EXTERN) == 0) { @@ -1360,8 +1359,12 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) */ if (Entry->Owner == SymTab0) { if ((Flags & SC_STORAGE) == 0) { - /* Linkage must be unchanged */ + /* Linkage must be unchanged. + ** The C standard specifies that a later extern declaration will be ignored, + ** and will use the previous linkage instead. Giving a warning for this case. + */ Flags &= ~SC_EXTERN; + Warning ("Extern declaration of '%s' follows static declaration, extern ignored", Name); } else { Error ("Non-static declaration of '%s' follows static declaration", Name); } From 5a30d746b404d00463efe909a9d69b3d23f3a30b Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Thu, 11 May 2023 20:15:27 -0400 Subject: [PATCH 242/520] extern/static conflict test: remove warning as errors to match the new expected cases --- test/val/decl-static-extern.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/val/decl-static-extern.c b/test/val/decl-static-extern.c index 6918e0033..2be4d336f 100644 --- a/test/val/decl-static-extern.c +++ b/test/val/decl-static-extern.c @@ -7,13 +7,12 @@ /* see: https://github.com/cc65/cc65/issues/191 + https://github.com/cc65/cc65/issues/2111 */ -#pragma warn(error, on) - static int n = 0; -extern int n; /* should not give an error */ -static int n; /* should not give an error */ +extern int n; /* extern is ignored, gives a warning but keeps previous static definiton */ +static int n; /* no error or warning, the previous static is still in effect */ int main(void) { From 5d9306fed35babfe0061ae31e7f90d5228367203 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Fri, 12 May 2023 02:32:14 +0200 Subject: [PATCH 243/520] Fixed header #2110 --- include/stdlib.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/stdlib.h b/include/stdlib.h index 4e7ffbd6a..05148d7da 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -44,6 +44,12 @@ typedef unsigned size_t; #define _HAVE_size_t #endif +/* NULL pointer */ +#ifndef _HAVE_NULL +#define NULL ((void *) 0) +#define _HAVE_NULL +#endif + /* Standard exit codes */ #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 @@ -167,6 +173,3 @@ int __fastcall__ putenv (char* s); /* End of stdlib.h */ #endif - - - From 6579df4e91981e0516da5216e7cb9a8b82a288b9 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 14 May 2023 02:43:35 +0200 Subject: [PATCH 244/520] Update stdlib.h --- include/stdlib.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/stdlib.h b/include/stdlib.h index 05148d7da..e789f732b 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -45,9 +45,8 @@ typedef unsigned size_t; #endif /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL ((void *) 0) -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* Standard exit codes */ From 680ddaf37d034deededf7a9c814d7a9a6d3ee7c5 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 14 May 2023 17:27:04 +0200 Subject: [PATCH 245/520] Update locale.h --- include/locale.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/locale.h b/include/locale.h index 3f23e01d2..f408e1ef3 100644 --- a/include/locale.h +++ b/include/locale.h @@ -39,9 +39,8 @@ /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* Locale information constants */ @@ -82,6 +81,3 @@ char* __fastcall__ setlocale (int category, const char* locale); /* End of locale.h */ #endif - - - From fd74e6b005bb2d5482310cb5286081065e750310 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 14 May 2023 17:28:09 +0200 Subject: [PATCH 246/520] Update stdio.h --- include/stdio.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/stdio.h b/include/stdio.h index 858dd5059..012b8e2ba 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -38,9 +38,8 @@ /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* size_t is needed */ From c55459b287ec0c5fddbb6c695ea14e7222db1052 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 14 May 2023 17:28:48 +0200 Subject: [PATCH 247/520] Update string.h --- include/string.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/string.h b/include/string.h index 854359dad..abaf80e7d 100644 --- a/include/string.h +++ b/include/string.h @@ -37,9 +37,8 @@ #define _STRING_H /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* size_t is needed */ From ba6747f5da0dd0c3c0cfab96098ad8cc4bfec11a Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 14 May 2023 17:29:42 +0200 Subject: [PATCH 248/520] Update time.h --- include/time.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/time.h b/include/time.h index 642d68c4e..bfc2ac435 100644 --- a/include/time.h +++ b/include/time.h @@ -39,9 +39,8 @@ /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* size_t is needed */ From dd58c7ff5002ad1da1522c2764d60acac27a5546 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 14 May 2023 17:32:30 +0200 Subject: [PATCH 249/520] Update stddef.h --- include/stddef.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/stddef.h b/include/stddef.h index ca93edf62..d2bfd6138 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -53,9 +53,8 @@ typedef unsigned size_t; #endif /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL ((void *) 0) -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* offsetof macro */ @@ -65,6 +64,3 @@ typedef unsigned size_t; /* End of stddef.h */ #endif - - - From 19436f515938e4b16afc214073ff40f5e1e9a45e Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Wed, 17 May 2023 20:24:36 +0200 Subject: [PATCH 250/520] Update gconst.h --- include/geos/gconst.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/geos/gconst.h b/include/geos/gconst.h index e70eb9304..55a99a21f 100644 --- a/include/geos/gconst.h +++ b/include/geos/gconst.h @@ -4,14 +4,14 @@ reassembled by Maciej 'YTM/Elysium' Witkowiak */ -/* Here are constants which didn't fit into any other cathegory... */ +/* Here are constants which didn't fit into any other category... */ #ifndef _GCONST_H #define _GCONST_H -#define NULL 0 -#define FALSE NULL +#define NULL ((void *) 0) #define TRUE 0xff +#define FALSE ~TRUE #define MOUSE_SPRNUM 0 #define DISK_DRV_LGH 0x0d80 From 767875b5a73f5630e75fe60a70473a17dda6a10f Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Thu, 18 May 2023 00:08:26 +0200 Subject: [PATCH 251/520] Added guard --- include/geos/gconst.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/geos/gconst.h b/include/geos/gconst.h index 55a99a21f..a1b6a1d86 100644 --- a/include/geos/gconst.h +++ b/include/geos/gconst.h @@ -9,7 +9,9 @@ #ifndef _GCONST_H #define _GCONST_H +#ifndef NULL #define NULL ((void *) 0) +#endif #define TRUE 0xff #define FALSE ~TRUE #define MOUSE_SPRNUM 0 From 8ff008722b97790fa65aa464b01107b8ba58967d Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Fri, 19 May 2023 02:49:49 +0200 Subject: [PATCH 252/520] Fixed test Don't define NULL yourself. QED --- test/val/add4.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/val/add4.c b/test/val/add4.c index f02a7fb9c..2371aea69 100644 --- a/test/val/add4.c +++ b/test/val/add4.c @@ -22,7 +22,6 @@ long long0 = 0; long long1 = 0; unsigned long ulong0 = 0; unsigned long ulong1 = 0; -#define NULL 0 char *cP0=NULL; char *cP1=NULL; int *iP0=NULL; From accd57460b5b7961b579368a49969f0888ccbbc1 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Fri, 19 May 2023 15:48:31 +0200 Subject: [PATCH 253/520] Reverted gconst.h --- include/geos/gconst.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geos/gconst.h b/include/geos/gconst.h index a1b6a1d86..3e42feed7 100644 --- a/include/geos/gconst.h +++ b/include/geos/gconst.h @@ -12,8 +12,8 @@ #ifndef NULL #define NULL ((void *) 0) #endif +#define FALSE 0 #define TRUE 0xff -#define FALSE ~TRUE #define MOUSE_SPRNUM 0 #define DISK_DRV_LGH 0x0d80 From 3831feba758c16386dd2c61404e6a09aefcf3806 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Fri, 19 May 2023 16:32:09 +0200 Subject: [PATCH 254/520] Fixed typo --- doc/grc65.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/grc65.sgml b/doc/grc65.sgml index c03815ade..d256c31e6 100644 --- a/doc/grc65.sgml +++ b/doc/grc65.sgml @@ -270,7 +270,7 @@ required for the correct process of GEOS sequential application building. <p>Large GEOS applications typically don't fit in one piece in their designated memory area. They are therefore split into overlays which are loaded into memory on demand. The individual overlays are stored as records of a VLIR (Variable -Length Index Record) file. When GEOS starts a VLIR overlay appliation it loads +Length Index Record) file. When GEOS starts a VLIR overlay application it loads record number 0 which is supposed to contain the main program. The record numbers starting with 1 are to be used for the actual overlays. From 40ff9281c6ea020726cd8deb1ffb7b0a2b8bf6e9 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Fri, 19 May 2023 16:36:19 +0200 Subject: [PATCH 255/520] Fixed typo --- doc/funcref.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 6418723b5..2a6d77adc 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -976,7 +976,7 @@ previously been allocated by <tt/<ref id="malloc" name="malloc">/, <tt/<ref id="calloc" name="calloc">/ or <tt/<ref id="realloc" name="realloc">/. <tag/Notes/<itemize> <item>Passing a pointer to a block that was is not the result of one of the -allocation functions, or that has been free'd will give unpredicable results. +allocation functions, or that has been free'd will give unpredictable results. <item>The function is available only as a fastcall function; so, it may be used only in the presence of a prototype. </itemize> From 1377ba0d360aaa0cb505c7bd78129c2c7d4c4cb4 Mon Sep 17 00:00:00 2001 From: jede <jede@oric.org> Date: Tue, 23 May 2023 23:08:56 +0200 Subject: [PATCH 256/520] fix compute length for read and write for telestrat target --- libsrc/telestrat/read.s | 15 ++++++++------- libsrc/telestrat/write.s | 16 ++++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/libsrc/telestrat/read.s b/libsrc/telestrat/read.s index f31909f45..b89f246cc 100644 --- a/libsrc/telestrat/read.s +++ b/libsrc/telestrat/read.s @@ -30,13 +30,14 @@ ldy ptr1+1 BRK_TELEMON XFREAD ; compute nb of bytes read - lda PTR_READ_DEST+1 sec - sbc ptr2+1 - tax - lda PTR_READ_DEST - sec - sbc ptr2 - ; here A and X contains number of bytes read + lda PTR_READ_DEST + sbc ptr2 + sta tmp1 + lda PTR_READ_DEST+1 + sbc ptr2+1 + tax + lda tmp1 + rts .endproc diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 2ce2657ac..06524f749 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -42,16 +42,16 @@ next: ldy ptr3+1 ldx tmp1 ; send fd in X BRK_TELEMON XFWRITE + ; compute nb of bytes written - - - lda PTR_READ_DEST+1 sec - sbc ptr1+1 - tax - lda PTR_READ_DEST - sec - sbc ptr1 + lda PTR_READ_DEST + sbc ptr1 + sta tmp1 + lda PTR_READ_DEST+1 + sbc ptr1+1 + tax + lda tmp1 rts From 1ca9d7e9e707658e2777784fb607d3cabb48b25c Mon Sep 17 00:00:00 2001 From: jede <jede@oric.org> Date: Tue, 23 May 2023 23:15:23 +0200 Subject: [PATCH 257/520] fix tab --- libsrc/telestrat/read.s | 12 ++++++------ libsrc/telestrat/write.s | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libsrc/telestrat/read.s b/libsrc/telestrat/read.s index b89f246cc..1d2315dce 100644 --- a/libsrc/telestrat/read.s +++ b/libsrc/telestrat/read.s @@ -31,12 +31,12 @@ BRK_TELEMON XFREAD ; compute nb of bytes read sec - lda PTR_READ_DEST - sbc ptr2 - sta tmp1 - lda PTR_READ_DEST+1 - sbc ptr2+1 - tax + lda PTR_READ_DEST + sbc ptr2 + sta tmp1 + lda PTR_READ_DEST+1 + sbc ptr2+1 + tax lda tmp1 rts diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 06524f749..16bc64d69 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -45,12 +45,12 @@ next: ; compute nb of bytes written sec - lda PTR_READ_DEST - sbc ptr1 - sta tmp1 - lda PTR_READ_DEST+1 - sbc ptr1+1 - tax + lda PTR_READ_DEST + sbc ptr1 + sta tmp1 + lda PTR_READ_DEST+1 + sbc ptr1+1 + tax lda tmp1 rts From 480600093cef2d67d5e39253364c258d6266300e Mon Sep 17 00:00:00 2001 From: jede <jede@oric.org> Date: Thu, 25 May 2023 18:09:03 +0200 Subject: [PATCH 258/520] fix pla/pha instead of tmp1 --- libsrc/telestrat/read.s | 4 ++-- libsrc/telestrat/write.s | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libsrc/telestrat/read.s b/libsrc/telestrat/read.s index 1d2315dce..736546363 100644 --- a/libsrc/telestrat/read.s +++ b/libsrc/telestrat/read.s @@ -33,11 +33,11 @@ sec lda PTR_READ_DEST sbc ptr2 - sta tmp1 + pha lda PTR_READ_DEST+1 sbc ptr2+1 tax - lda tmp1 + pla rts .endproc diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 16bc64d69..37a896696 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -47,11 +47,11 @@ next: sec lda PTR_READ_DEST sbc ptr1 - sta tmp1 + pha lda PTR_READ_DEST+1 sbc ptr1+1 tax - lda tmp1 + pla rts From c8aa9cc70372accedf5c506ab48090d0890caf4f Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Mon, 29 May 2023 17:20:09 +0200 Subject: [PATCH 259/520] add struct assign check related to #2079 --- test/val/bug2079-struct-assign.c | 66 ++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 test/val/bug2079-struct-assign.c diff --git a/test/val/bug2079-struct-assign.c b/test/val/bug2079-struct-assign.c new file mode 100644 index 000000000..b8a41fe8b --- /dev/null +++ b/test/val/bug2079-struct-assign.c @@ -0,0 +1,66 @@ + +/* test struct assignment, of structs with a length of 3, which happen to be + a special case eg when passing/returning structs + related to bugs #2022, #2079 */ + +#include <stdio.h> +#include <stdlib.h> + +int failures = 0; + +struct foo { char a; char b; char c; }; +struct foo foo, bar; +void f3(void) +{ + foo.a = 6; + foo.b = 6; + foo.c = 6; + bar.a = 1; + bar.b = 2; + bar.c = 3; + foo = bar; + printf("%d %d %d, %d %d %d (1,2,3 1,2,3)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 1) || (foo.b != 2) || (foo.c != 3) || + (bar.a != 1) || (bar.b != 2) || (bar.c != 3)) { + failures++; + } + foo.a = 3; + foo.b = 2; + foo.c = 1; + printf("%d %d %d, %d %d %d (3,2,1 1,2,3)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 3) || (foo.b != 2) || (foo.c != 1) || + (bar.a != 1) || (bar.b != 2) || (bar.c != 3)) { + failures++; + } + bar.a = 5; + bar.b = 6; + bar.c = 7; + printf("%d %d %d, %d %d %d (3,2,1 5,6,7)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 3) || (foo.b != 2) || (foo.c != 1) || + (bar.a != 5) || (bar.b != 6) || (bar.c != 7)) { + failures++; + } + bar = foo; + foo.a = 6; + foo.b = 6; + foo.c = 6; + printf("%d %d %d, %d %d %d (6,6,6 3,2,1)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 6) || (foo.b != 6) || (foo.c != 6) || + (bar.a != 3) || (bar.b != 2) || (bar.c != 1)) { + failures++; + } +} + +int main(void) +{ + f3(); + return failures; +} From 59941d94642a6fc25c6392486fca2b7e4aae46bf Mon Sep 17 00:00:00 2001 From: MooingLemur <mooinglemur@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:30:26 -0400 Subject: [PATCH 260/520] cx16: use KERNAL memsiz for MEMSIZE in asminc/cx16.inc --- asminc/cx16.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asminc/cx16.inc b/asminc/cx16.inc index be63780c8..d264add38 100644 --- a/asminc/cx16.inc +++ b/asminc/cx16.inc @@ -259,7 +259,7 @@ NLINES := $0387 ; Number of screen lines ; BASIC VARTAB := $03E1 ; Pointer to start of BASIC variables -MEMSIZE := $03E9 ; Pointer to highest BASIC RAM location (+1) +MEMSIZE := $0259 ; Pointer to highest BASIC RAM location (+1) ; --------------------------------------------------------------------------- ; Vector and other locations From 3e166c760dcd7dd071aa86f464ce480bf07504e9 Mon Sep 17 00:00:00 2001 From: baktragh <zylon@post.cz> Date: Mon, 26 Jun 2023 16:38:41 +0200 Subject: [PATCH 261/520] Update _atari5200os.h with POT shadows Update the OS struct with POT shadow registers, according to the https://web.archive.org/web/20120830055323/http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html document --- include/_atari5200os.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/_atari5200os.h b/include/_atari5200os.h index 5bba43016..88636dc7f 100644 --- a/include/_atari5200os.h +++ b/include/_atari5200os.h @@ -54,7 +54,15 @@ struct __os { unsigned char color2; // = $0E PF color 2 unsigned char color3; // = $0F PF color 3 unsigned char color4; // = $10 PF color 4 - unsigned char _free_1[0xEF]; // = $11-$FF User space + unsigned char pot0; // = $11 POT0 shadow + unsigned char pot1; // = $12 POT1 shadow + unsigned char pot2; // = $13 POT2 shadow + unsigned char pot3; // = $14 POT3 shadow + unsigned char pot4; // = $15 POT4 shadow + unsigned char pot5; // = $16 POT5 shadow + unsigned char pot6; // = $17 POT6 shadow + unsigned char pot7; // = $18 POT7 shadow + unsigned char _free_1[0xE7]; // = $19-$FF User space /*Stack*/ unsigned char stack[0x100]; // = $100-$1FF Stack From aaec2a627092f4bad90aa408258559ec341a209a Mon Sep 17 00:00:00 2001 From: baktragh <zylon@post.cz> Date: Mon, 26 Jun 2023 16:46:42 +0200 Subject: [PATCH 262/520] Update _atari5200os.h - Remove dangling spaces --- include/_atari5200os.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/_atari5200os.h b/include/_atari5200os.h index 88636dc7f..2e861e797 100644 --- a/include/_atari5200os.h +++ b/include/_atari5200os.h @@ -54,14 +54,14 @@ struct __os { unsigned char color2; // = $0E PF color 2 unsigned char color3; // = $0F PF color 3 unsigned char color4; // = $10 PF color 4 - unsigned char pot0; // = $11 POT0 shadow - unsigned char pot1; // = $12 POT1 shadow - unsigned char pot2; // = $13 POT2 shadow - unsigned char pot3; // = $14 POT3 shadow - unsigned char pot4; // = $15 POT4 shadow - unsigned char pot5; // = $16 POT5 shadow - unsigned char pot6; // = $17 POT6 shadow - unsigned char pot7; // = $18 POT7 shadow + unsigned char pot0; // = $11 POT0 shadow + unsigned char pot1; // = $12 POT1 shadow + unsigned char pot2; // = $13 POT2 shadow + unsigned char pot3; // = $14 POT3 shadow + unsigned char pot4; // = $15 POT4 shadow + unsigned char pot5; // = $16 POT5 shadow + unsigned char pot6; // = $17 POT6 shadow + unsigned char pot7; // = $18 POT7 shadow unsigned char _free_1[0xE7]; // = $19-$FF User space /*Stack*/ From 6c127d6a836ee409f9839224cf74f35eeafb96d7 Mon Sep 17 00:00:00 2001 From: baktragh <zylon@post.cz> Date: Sun, 2 Jul 2023 14:59:00 +0200 Subject: [PATCH 263/520] Update _atari5200os.h Update the page 0 symbols, synchronize the names with the _atarios.h, add locations used by Atari 5200 conio --- include/_atari5200os.h | 44 ++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/include/_atari5200os.h b/include/_atari5200os.h index 2e861e797..d6f7d3875 100644 --- a/include/_atari5200os.h +++ b/include/_atari5200os.h @@ -44,25 +44,31 @@ struct __os { void* sdlst; // = $05,$06 Display list shadow }; - unsigned char sdmctl; // = $07 DMACTL shadow - unsigned char pcolr0; // = $08 PM color 0 - unsigned char pcolr1; // = $09 PM color 1 - unsigned char pcolr2; // = $0A PM color 2 - unsigned char pcolr3; // = $0B PM color 3 - unsigned char color0; // = $0C PF color 0 - unsigned char color1; // = $0D PF color 1 - unsigned char color2; // = $0E PF color 2 - unsigned char color3; // = $0F PF color 3 - unsigned char color4; // = $10 PF color 4 - unsigned char pot0; // = $11 POT0 shadow - unsigned char pot1; // = $12 POT1 shadow - unsigned char pot2; // = $13 POT2 shadow - unsigned char pot3; // = $14 POT3 shadow - unsigned char pot4; // = $15 POT4 shadow - unsigned char pot5; // = $16 POT5 shadow - unsigned char pot6; // = $17 POT6 shadow - unsigned char pot7; // = $18 POT7 shadow - unsigned char _free_1[0xE7]; // = $19-$FF User space + unsigned char sdmctl; // = $07 DMACTL shadow + unsigned char pcolr0; // = $08 PM color 0 + unsigned char pcolr1; // = $09 PM color 1 + unsigned char pcolr2; // = $0A PM color 2 + unsigned char pcolr3; // = $0B PM color 3 + unsigned char color0; // = $0C PF color 0 + unsigned char color1; // = $0D PF color 1 + unsigned char color2; // = $0E PF color 2 + unsigned char color3; // = $0F PF color 3 + unsigned char color4; // = $10 PF color 4 + unsigned char paddl0; // = $11 POT0 Shadow + unsigned char paddl1; // = $12 POT1 Shadow + unsigned char paddl2; // = $13 POT2 Shadow + unsigned char paddl3; // = $14 POT3 Shadow + unsigned char paddl4; // = $15 POT4 Shadow + unsigned char paddl5; // = $16 POT5 Shadow + unsigned char paddl6; // = $17 POT6 Shadow + unsigned char paddl7; // = $18 POT7 Shadow + + /*cc65 runtime zero page variables*/ + unsigned char rowcrs_5200; // = $19 Cursor row (conio) + unsigned char colcrs_5200; // = $1A Cursor column (conio) + unsigned char* savmsc; // = $1B/$1C Pointer to screen memory (conio) + + unsigned char _filler_1[0xE3]; // = $1D-$FF Filler /*Stack*/ unsigned char stack[0x100]; // = $100-$1FF Stack From 7a85473cb0dfcc180048c1c8892cb9bf039ca406 Mon Sep 17 00:00:00 2001 From: baktragh <zylon@post.cz> Date: Sun, 2 Jul 2023 15:16:33 +0200 Subject: [PATCH 264/520] Update _atari5200os.h Remove dangling spaces --- include/_atari5200os.h | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/include/_atari5200os.h b/include/_atari5200os.h index d6f7d3875..196b69e56 100644 --- a/include/_atari5200os.h +++ b/include/_atari5200os.h @@ -44,31 +44,31 @@ struct __os { void* sdlst; // = $05,$06 Display list shadow }; - unsigned char sdmctl; // = $07 DMACTL shadow - unsigned char pcolr0; // = $08 PM color 0 - unsigned char pcolr1; // = $09 PM color 1 - unsigned char pcolr2; // = $0A PM color 2 - unsigned char pcolr3; // = $0B PM color 3 - unsigned char color0; // = $0C PF color 0 - unsigned char color1; // = $0D PF color 1 - unsigned char color2; // = $0E PF color 2 - unsigned char color3; // = $0F PF color 3 - unsigned char color4; // = $10 PF color 4 - unsigned char paddl0; // = $11 POT0 Shadow - unsigned char paddl1; // = $12 POT1 Shadow - unsigned char paddl2; // = $13 POT2 Shadow - unsigned char paddl3; // = $14 POT3 Shadow - unsigned char paddl4; // = $15 POT4 Shadow - unsigned char paddl5; // = $16 POT5 Shadow - unsigned char paddl6; // = $17 POT6 Shadow - unsigned char paddl7; // = $18 POT7 Shadow - - /*cc65 runtime zero page variables*/ - unsigned char rowcrs_5200; // = $19 Cursor row (conio) - unsigned char colcrs_5200; // = $1A Cursor column (conio) - unsigned char* savmsc; // = $1B/$1C Pointer to screen memory (conio) - - unsigned char _filler_1[0xE3]; // = $1D-$FF Filler + unsigned char sdmctl; // = $07 DMACTL shadow + unsigned char pcolr0; // = $08 PM color 0 + unsigned char pcolr1; // = $09 PM color 1 + unsigned char pcolr2; // = $0A PM color 2 + unsigned char pcolr3; // = $0B PM color 3 + unsigned char color0; // = $0C PF color 0 + unsigned char color1; // = $0D PF color 1 + unsigned char color2; // = $0E PF color 2 + unsigned char color3; // = $0F PF color 3 + unsigned char color4; // = $10 PF color 4 + unsigned char paddl0; // = $11 POT0 Shadow + unsigned char paddl1; // = $12 POT1 Shadow + unsigned char paddl2; // = $13 POT2 Shadow + unsigned char paddl3; // = $14 POT3 Shadow + unsigned char paddl4; // = $15 POT4 Shadow + unsigned char paddl5; // = $16 POT5 Shadow + unsigned char paddl6; // = $17 POT6 Shadow + unsigned char paddl7; // = $18 POT7 Shadow + + /*cc65 runtime zero page variables*/ + unsigned char rowcrs_5200; // = $19 Cursor row (conio) + unsigned char colcrs_5200; // = $1A Cursor column (conio) + unsigned char* savmsc; // = $1B/$1C Pointer to screen memory (conio) + + unsigned char _filler_1[0xE3]; // = $1D-$FF Filler /*Stack*/ unsigned char stack[0x100]; // = $100-$1FF Stack From 1f68846116fd1b1bb6b7ca1ae8e9c45974bce6b9 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin.leroy-mira@sigfox.com> Date: Fri, 18 Aug 2023 11:12:16 +0200 Subject: [PATCH 265/520] Avoid using mli.s to initcwd --- libsrc/apple2/initcwd.s | 43 ++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/libsrc/apple2/initcwd.s b/libsrc/apple2/initcwd.s index 7a12bc52a..75224d019 100644 --- a/libsrc/apple2/initcwd.s +++ b/libsrc/apple2/initcwd.s @@ -3,22 +3,39 @@ ; .export initcwd - .import __cwd + .import __cwd, __dos_type .include "zeropage.inc" + .include "apple2.inc" .include "mli.inc" -initcwd: - ; Set static prefix buffer - lda #<__cwd - ldx #>__cwd - sta mliparam + MLI::PREFIX::PATHNAME - stx mliparam + MLI::PREFIX::PATHNAME+1 +mli_parameters: + .byte $01 ; number of parameters + .addr __cwd ; address of parameter - ; Get current working directory - lda #GET_PREFIX_CALL - ldx #PREFIX_COUNT - jsr callmli +initcwd: + ; Check for ProDOS 8 + lda __dos_type + beq oserr + + ; Save random counter + lda RNDL + pha + lda RNDH + pha + + ; Call MLI + jsr $BF00 ; MLI call entry point + .byte GET_PREFIX_CALL ; MLI command + .addr mli_parameters ; MLI parameter + + ; Restore random counter + tax + pla + sta RNDH + pla + sta RNDL + txa ; Check for null prefix ldx __cwd @@ -39,3 +56,7 @@ initcwd: sta __cwd,x done: rts + +oserr: lda #$01 ; "Bad system call number" + sec + rts From 148be69f9705ce78900b2f9bbfb560da8c027b39 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin.leroy-mira@sigfox.com> Date: Fri, 18 Aug 2023 14:36:52 +0200 Subject: [PATCH 266/520] Optimize and fix comments (thanks to Oliver Schmidt) --- libsrc/apple2/initcwd.s | 20 ++++++++++---------- libsrc/apple2/syschdir.s | 3 ++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/libsrc/apple2/initcwd.s b/libsrc/apple2/initcwd.s index 75224d019..03e13bcfb 100644 --- a/libsrc/apple2/initcwd.s +++ b/libsrc/apple2/initcwd.s @@ -9,14 +9,10 @@ .include "apple2.inc" .include "mli.inc" -mli_parameters: - .byte $01 ; number of parameters - .addr __cwd ; address of parameter - initcwd: ; Check for ProDOS 8 lda __dos_type - beq oserr + beq done ; Save random counter lda RNDL @@ -25,17 +21,19 @@ initcwd: pha ; Call MLI + ; We're not using mli.s' callmli because its + ; mliparam is in BSS and this will be called + ; before LC code is moved to the Language Card. + jsr $BF00 ; MLI call entry point .byte GET_PREFIX_CALL ; MLI command .addr mli_parameters ; MLI parameter ; Restore random counter - tax pla sta RNDH pla sta RNDL - txa ; Check for null prefix ldx __cwd @@ -57,6 +55,8 @@ initcwd: done: rts -oserr: lda #$01 ; "Bad system call number" - sec - rts + .rodata + +mli_parameters: + .byte $01 ; Number of parameters + .addr __cwd ; Address of parameter diff --git a/libsrc/apple2/syschdir.s b/libsrc/apple2/syschdir.s index 8afc3af0b..b3f81e2a5 100644 --- a/libsrc/apple2/syschdir.s +++ b/libsrc/apple2/syschdir.s @@ -29,7 +29,8 @@ __syschdir: bcs cleanup ; Update current working directory - jsr initcwd ; Returns with A = 0 + jsr initcwd + lda #$00 ; Cleanup name cleanup:jsr popname ; Preserves A From 39619b629d3614f6b284865f5c254ebda5272f2b Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 19 Aug 2023 15:39:16 +0200 Subject: [PATCH 267/520] fix docs on .version (forgot to update it when __CC65__ was fixed) --- doc/ca65.sgml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b4ef3e188..982c68949 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -1367,17 +1367,22 @@ writable. Reading this pseudo variable will give the assembler version according to the following formula: - VER_MAJOR*$100 + VER_MINOR*$10 + <tt>(VER_MAJOR * 0x100) + VER_MINOR</tt> - It may be used to encode the assembler version or check the assembler for - special features not available with older versions. + The upper 8 bits are the major-, the lower 8 bits are the minor version. Example: - Version 2.14 of the assembler will return $2E0 as numerical constant when - reading the pseudo variable <tt/.VERSION/. - + For example, version 47.11 of the assembler would have this macro defined as + <tt/0x2f0b/. + Note: until 2.19 this pseudo variable was defined as <tt>(VER_MAJOR * 0x100) + VER_MINOR * 0x10</tt> - + which resulted in broken values starting at version 2.16 of the assembler. For + this reason the value of this pseudo variable is considered purely informal - you should + not use it to check for a specific assembler version and use different code + according to the detected version - please update your code to work with the + recent version of the assembler instead (There is very little reason to not use + the most recent version - and even less to support older versions in your code). <sect>Pseudo functions<label id="pseudo-functions"><p> From 2431fb7d85b8aa74153e6e8343bb577e28cc8832 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 19 Aug 2023 13:08:46 -0400 Subject: [PATCH 268/520] correct INIT segment semantic is bss, make this consistent across all default cfg --- cfg/apple2-hgr.cfg | 2 +- cfg/apple2-overlay.cfg | 2 +- cfg/apple2-system.cfg | 2 +- cfg/apple2.cfg | 2 +- cfg/apple2enh-hgr.cfg | 2 +- cfg/apple2enh-overlay.cfg | 2 +- cfg/apple2enh-system.cfg | 2 +- cfg/apple2enh.cfg | 2 +- cfg/atari-overlay.cfg | 2 +- cfg/atari-xex.cfg | 2 +- cfg/atari.cfg | 2 +- cfg/atarixl-largehimem.cfg | 2 +- cfg/atarixl-overlay.cfg | 2 +- cfg/atarixl-xex.cfg | 2 +- cfg/atarixl.cfg | 2 +- cfg/atmos.cfg | 2 +- cfg/c64-overlay.cfg | 2 +- cfg/c64.cfg | 2 +- cfg/creativision.cfg | 2 +- cfg/cx16-bank.cfg | 2 +- cfg/cx16.cfg | 2 +- cfg/telestrat.cfg | 2 +- libsrc/creativision/cputc.s | 6 ------ 23 files changed, 22 insertions(+), 28 deletions(-) diff --git a/cfg/apple2-hgr.cfg b/cfg/apple2-hgr.cfg index cfe577e00..55072ad89 100644 --- a/cfg/apple2-hgr.cfg +++ b/cfg/apple2-hgr.cfg @@ -27,7 +27,7 @@ SEGMENTS { CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2-overlay.cfg b/cfg/apple2-overlay.cfg index a0b7678c1..2ec798bd9 100644 --- a/cfg/apple2-overlay.cfg +++ b/cfg/apple2-overlay.cfg @@ -43,7 +43,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2-system.cfg b/cfg/apple2-system.cfg index 0170feb93..1c11b6949 100644 --- a/cfg/apple2-system.cfg +++ b/cfg/apple2-system.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2.cfg b/cfg/apple2.cfg index a6809cf89..14e3a8f55 100644 --- a/cfg/apple2.cfg +++ b/cfg/apple2.cfg @@ -26,7 +26,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh-hgr.cfg b/cfg/apple2enh-hgr.cfg index cfe577e00..55072ad89 100644 --- a/cfg/apple2enh-hgr.cfg +++ b/cfg/apple2enh-hgr.cfg @@ -27,7 +27,7 @@ SEGMENTS { CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh-overlay.cfg b/cfg/apple2enh-overlay.cfg index a0b7678c1..2ec798bd9 100644 --- a/cfg/apple2enh-overlay.cfg +++ b/cfg/apple2enh-overlay.cfg @@ -43,7 +43,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh-system.cfg b/cfg/apple2enh-system.cfg index 0170feb93..1c11b6949 100644 --- a/cfg/apple2enh-system.cfg +++ b/cfg/apple2enh-system.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh.cfg b/cfg/apple2enh.cfg index a6809cf89..14e3a8f55 100644 --- a/cfg/apple2enh.cfg +++ b/cfg/apple2enh.cfg @@ -26,7 +26,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/atari-overlay.cfg b/cfg/atari-overlay.cfg index 9311a1b22..60f98e453 100644 --- a/cfg/atari-overlay.cfg +++ b/cfg/atari-overlay.cfg @@ -52,7 +52,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; OVERLAY1: load = OVL1, type = ro, define = yes, optional = yes; diff --git a/cfg/atari-xex.cfg b/cfg/atari-xex.cfg index cabde3708..deab5c7a5 100644 --- a/cfg/atari-xex.cfg +++ b/cfg/atari-xex.cfg @@ -36,7 +36,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; } FEATURES { diff --git a/cfg/atari.cfg b/cfg/atari.cfg index 106c75e63..37337ea53 100644 --- a/cfg/atari.cfg +++ b/cfg/atari.cfg @@ -40,7 +40,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } diff --git a/cfg/atarixl-largehimem.cfg b/cfg/atarixl-largehimem.cfg index 38fb68db9..8f70597aa 100644 --- a/cfg/atarixl-largehimem.cfg +++ b/cfg/atarixl-largehimem.cfg @@ -67,7 +67,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } diff --git a/cfg/atarixl-overlay.cfg b/cfg/atarixl-overlay.cfg index 339228ea0..3b8da4256 100644 --- a/cfg/atarixl-overlay.cfg +++ b/cfg/atarixl-overlay.cfg @@ -78,7 +78,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; diff --git a/cfg/atarixl-xex.cfg b/cfg/atarixl-xex.cfg index 1b76855d0..853096c6f 100644 --- a/cfg/atarixl-xex.cfg +++ b/cfg/atarixl-xex.cfg @@ -58,7 +58,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; SRPREPHDR: load = UNUSED, type = ro; SRPREPTRL: load = UNUSED, type = ro; diff --git a/cfg/atarixl.cfg b/cfg/atarixl.cfg index cece23555..1517afb5a 100644 --- a/cfg/atarixl.cfg +++ b/cfg/atarixl.cfg @@ -65,7 +65,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } diff --git a/cfg/atmos.cfg b/cfg/atmos.cfg index 35f184f4f..b06366fee 100644 --- a/cfg/atmos.cfg +++ b/cfg/atmos.cfg @@ -23,7 +23,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; BASTAIL: load = MAIN, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/c64-overlay.cfg b/cfg/c64-overlay.cfg index 0f42434ad..39d8f68e8 100644 --- a/cfg/c64-overlay.cfg +++ b/cfg/c64-overlay.cfg @@ -44,7 +44,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; OVL1ADDR: load = OVL1ADDR, type = ro; diff --git a/cfg/c64.cfg b/cfg/c64.cfg index 5bd8d8240..00a050524 100644 --- a/cfg/c64.cfg +++ b/cfg/c64.cfg @@ -23,7 +23,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; } diff --git a/cfg/creativision.cfg b/cfg/creativision.cfg index 2eb9ac427..f3b7ac0f4 100644 --- a/cfg/creativision.cfg +++ b/cfg/creativision.cfg @@ -11,10 +11,10 @@ SEGMENTS { ZP: load = ZP, type = zp, optional = yes; VECTORS: load = ROM, run = RAM, type = rw, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes, start = $0204; + INIT: load = RAM, type = bss, optional = yes; BSS: load = RAM, type = bss, define = yes; ONCE: load = ROM, type = ro, optional = yes; CODE: load = ROM, type = ro; - INIT: load = ROM, type = ro; RODATA: load = ROM, type = ro; AUDIO: load = ROM, type = ro, optional = yes, start = $BF00; SETUP: load = ROM, type = ro, start = $BFE8; diff --git a/cfg/cx16-bank.cfg b/cfg/cx16-bank.cfg index d3c2c02ae..0aa6fc555 100644 --- a/cfg/cx16-bank.cfg +++ b/cfg/cx16-bank.cfg @@ -57,7 +57,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss,optional = yes; ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; BRAM01ADDR: load = BRAM01ADDR, type = ro, optional = yes; diff --git a/cfg/cx16.cfg b/cfg/cx16.cfg index 4b6025fb6..0f83d4c20 100644 --- a/cfg/cx16.cfg +++ b/cfg/cx16.cfg @@ -24,7 +24,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss,optional = yes; ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; } diff --git a/cfg/telestrat.cfg b/cfg/telestrat.cfg index bd720fb8d..593197866 100644 --- a/cfg/telestrat.cfg +++ b/cfg/telestrat.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; ONCE: load = MAIN, type = ro, define = yes; BASTAIL: load = MAIN, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/libsrc/creativision/cputc.s b/libsrc/creativision/cputc.s index 437b738b2..4389234c1 100644 --- a/libsrc/creativision/cputc.s +++ b/libsrc/creativision/cputc.s @@ -95,12 +95,6 @@ IS_UPPER: BAD_CHAR: jmp plot -;----------------------------------------------------------------------------- -; Initialize the conio subsystem. "INIT" segment is nothing special on the -; Creativision, it is part of the "ROM" memory. - -.segment "INIT" - initconio: lda #$0 sta SCREEN_PTR From 07f08fc547b7e0f5b37aef23dd482a3d5f483352 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 19 Aug 2023 14:36:30 -0400 Subject: [PATCH 269/520] tests verifying jmp (indirect) page crossing error on 6502, and the lack of error for other CPU types --- test/asm/err/jmp-indirect-6502-error.s | 4 ++++ test/asm/val/jmp-indirect-success.s | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 test/asm/err/jmp-indirect-6502-error.s create mode 100644 test/asm/val/jmp-indirect-success.s diff --git a/test/asm/err/jmp-indirect-6502-error.s b/test/asm/err/jmp-indirect-6502-error.s new file mode 100644 index 000000000..aadc37b30 --- /dev/null +++ b/test/asm/err/jmp-indirect-6502-error.s @@ -0,0 +1,4 @@ +; test that jmp (indirect) on a page boundary will give an error for 6502 CPU + +.p02 +jmp ($10FF) diff --git a/test/asm/val/jmp-indirect-success.s b/test/asm/val/jmp-indirect-success.s new file mode 100644 index 000000000..0cdc8c32c --- /dev/null +++ b/test/asm/val/jmp-indirect-success.s @@ -0,0 +1,18 @@ +; test that jmp (indirect) on a page boundary will not give an error for non-6502 CPUs + +.pc02 +jmp ($10FF) + +.psc02 +jmp ($10FF) + +.p816 +jmp ($10FF) + +; main always returns success (the tested issue is only whether the assembly errors) +.import _exit +.export _main +_main: + lda #0 + tax + jmp _exit From f31d8efc1e8a921b098142ec4d501753e59cc4d2 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 19 Aug 2023 15:07:32 -0400 Subject: [PATCH 270/520] tabs are forbidden --- test/asm/val/jmp-indirect-success.s | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/asm/val/jmp-indirect-success.s b/test/asm/val/jmp-indirect-success.s index 0cdc8c32c..592666576 100644 --- a/test/asm/val/jmp-indirect-success.s +++ b/test/asm/val/jmp-indirect-success.s @@ -13,6 +13,6 @@ jmp ($10FF) .import _exit .export _main _main: - lda #0 - tax - jmp _exit + lda #0 + tax + jmp _exit From 28ffe2f59bfc988ea7f3b7f0ed8adf3d3ec8b43e Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 19 Aug 2023 15:39:51 -0400 Subject: [PATCH 271/520] add jmp page crossing to --relax-checks, document it, fix --relax-checks documentation (segment branch error is not suppressed) --- doc/ca65.sgml | 13 ++++++++----- src/ca65/instr.c | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b4ef3e188..29e04a29e 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -120,7 +120,7 @@ Long options: --list-bytes n Maximum number of bytes per listing line --memory-model model Set the memory model --pagelength n Set the page length for the listing - --relax-checks Relax some checks (see docs) + --relax-checks Disables some error checks (see <ref id="option--relax-checks" name="below">) --smart Enable smart mode --target sys Set the target system --verbose Increase verbosity @@ -265,14 +265,17 @@ Here is a description of all the command line options: <label id="option--relax-checks"> <tag><tt>--relax-checks</tt></tag> - Relax some checks done by the assembler. This will allow code that is an + Disables some error checks done by the assembler. This will allow code that is an error in most cases and flagged as such by the assembler, but can be valid in special situations. - Examples are: + Disabled checks are: <itemize> -<item>Short branches between two different segments. -<item>Byte sized address loads where the address is not a zeropage address. +<item>Address vs. fragment size: a byte sized load from an non-zeropage + address is truncated instead of producing an error. +<item>Indirect jump on page boundary: <tt>jmp (label)</tt> on a label that + resides on a page boundary (<tt>$xxFF</tt>) fetches the second byte from the + wrong address on 6502 CPUs, now allowed instead of producing an error. </itemize> diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 0afa281b4..da6bd6e44 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1618,7 +1618,7 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02)) { + if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02) && (RelaxChecks == 0)) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise From 97e59b0756eead4faa5a75185928c3c9e6cdd0fe Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 19 Aug 2023 15:50:22 -0400 Subject: [PATCH 272/520] ref link doesn't work within the usage verb --- doc/ca65.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 29e04a29e..dc727eb49 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -120,7 +120,7 @@ Long options: --list-bytes n Maximum number of bytes per listing line --memory-model model Set the memory model --pagelength n Set the page length for the listing - --relax-checks Disables some error checks (see <ref id="option--relax-checks" name="below">) + --relax-checks Disables some error checks --smart Enable smart mode --target sys Set the target system --verbose Increase verbosity From a2c115fc3effb3b7290478c5e01bab7e156eba23 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin.leroy-mira@sigfox.com> Date: Sat, 26 Aug 2023 15:35:28 +0200 Subject: [PATCH 273/520] Build libs with debug symbols --- libsrc/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/Makefile b/libsrc/Makefile index 0873d019f..cf7389d51 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -131,8 +131,8 @@ $(TARGETS): | ../lib else # TARGET -CA65FLAGS = -CC65FLAGS = -Or -W error +CA65FLAGS = -g +CC65FLAGS = -g -Or -W error EXTZP = cbm510 \ cbm610 \ From 8a140e6503a1972214fdeb1f28f47b966f786794 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin.leroy-mira@sigfox.com> Date: Sat, 2 Sep 2023 11:08:15 +0200 Subject: [PATCH 274/520] Conio: 65c02 optimisations cputc: -2 cycles per char, -2 cycles per carriage return cputs: -5 cycles per char, -3 cycles on cputsxy vcprintf: -6 cycles per char --- libsrc/apple2/cputc.s | 18 +++++++++++++----- libsrc/conio/cputs.s | 38 ++++++++++++++++++++++++++++---------- libsrc/conio/vcprintf.s | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/libsrc/apple2/cputc.s b/libsrc/apple2/cputc.s index 30383fcfe..035b1c047 100644 --- a/libsrc/apple2/cputc.s +++ b/libsrc/apple2/cputc.s @@ -14,6 +14,8 @@ .include "apple2.inc" + .macpack cpu + .segment "ONCE" .ifdef __APPLE2ENH__ @@ -51,8 +53,13 @@ cputdirect: cmp WNDWDTH bcc :+ jsr newline -left: lda #$00 ; Goto left edge of screen +left: + .if (.cpu .bitand CPU_ISET_65SC02) + stz CH ; Goto left edge of screen + .else + lda #$00 ; Goto left edge of screen sta CH + .endif : rts newline: @@ -78,17 +85,18 @@ mask: and INVFLG ; Apply normal, inverse, flash putchardirect: pha - ldy CH .ifdef __APPLE2ENH__ + lda CH bit RD80VID ; In 80 column mode? bpl put ; No, just go ahead - tya lsr ; Div by 2 - tay bcs put ; Odd cols go in main memory bit HISCR ; Assume SET80COL +put: tay + .else + ldy CH .endif -put: lda (BASL),Y ; Get current character + lda (BASL),Y ; Get current character tax ; Return old character for _cgetc pla sta (BASL),Y diff --git a/libsrc/conio/cputs.s b/libsrc/conio/cputs.s index 41191a0b0..62e757b84 100644 --- a/libsrc/conio/cputs.s +++ b/libsrc/conio/cputs.s @@ -8,28 +8,46 @@ .export _cputsxy, _cputs .import gotoxy, _cputc .importzp ptr1, tmp1 + .macpack cpu _cputsxy: sta ptr1 ; Save s for later stx ptr1+1 jsr gotoxy ; Set cursor, pop x and y +.if (.cpu .bitand CPU_ISET_65SC02) + bra L0 ; Same as cputs... +.else jmp L0 ; Same as cputs... +.endif _cputs: sta ptr1 ; Save s stx ptr1+1 -L0: ldy #0 -L1: lda (ptr1),y - beq L9 ; Jump if done + +.if (.cpu .bitand CPU_ISET_65SC02) + +L0: lda (ptr1) ; (5) + beq L9 ; (7) Jump if done + jsr _cputc ; (13) Output char, advance cursor + inc ptr1 ; (18) Bump low byte + bne L0 ; (20) Next char + inc ptr1+1 ; (25) Bump high byte + bne L0 + +.else + +L0: ldy #0 ; (2) +L1: lda (ptr1),y ; (7) + beq L9 ; (9) Jump if done iny - sty tmp1 ; Save offset - jsr _cputc ; Output char, advance cursor - ldy tmp1 ; Get offset - bne L1 ; Next char - inc ptr1+1 ; Bump high byte + sty tmp1 ; (14) Save offset + jsr _cputc ; (20) Output char, advance cursor + ldy tmp1 ; (23) Get offset + bne L1 ; (25) Next char + inc ptr1+1 ; (30) Bump high byte bne L1 +.endif + ; Done L9: rts - - diff --git a/libsrc/conio/vcprintf.s b/libsrc/conio/vcprintf.s index 084efe089..595a2d2c5 100644 --- a/libsrc/conio/vcprintf.s +++ b/libsrc/conio/vcprintf.s @@ -10,7 +10,7 @@ .importzp sp, ptr1, ptr2, ptr3, tmp1 .macpack generic - + .macpack cpu .data @@ -74,21 +74,40 @@ out: jsr popax ; count ; Loop outputting characters +.if (.cpu .bitand CPU_ISET_65SC02) + @L1: dec outdesc+6 beq @L4 -@L2: ldy tmp1 - lda (ptr1),y - iny - bne @L3 - inc ptr1+1 -@L3: sty tmp1 - jsr _cputc - jmp @L1 +@L2: lda (ptr1) ; (5) + inc ptr1 ; (10) + bne @L3 ; (12) + inc ptr1+1 ; (17) +@L3: jsr _cputc ; (23) + bra @L1 ; (26) @L4: dec outdesc+7 bne @L2 rts +.else + +@L1: dec outdesc+6 + beq @L4 +@L2: ldy tmp1 ; (3) + lda (ptr1),y ; (8) + iny ; (10) + bne @L3 ; (12) + inc ptr1+1 ; (17) +@L3: sty tmp1 ; (20) + jsr _cputc ; (26) + jmp @L1 ; (32) + +@L4: dec outdesc+7 + bne @L2 + rts + +.endif + ; ---------------------------------------------------------------------------- ; vcprintf - formatted console i/o ; From 4ebc3b8b044ec1e48ea98a96b4355c42c0d7fa14 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sun, 3 Sep 2023 20:47:48 +0200 Subject: [PATCH 275/520] Don't mix apple2 and apple2enh --- doc/apple2.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 63b40c6f8..0d3ec34e5 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -603,7 +603,7 @@ url="ca65.html" name="assembler manual">. The header file <tt/apple2_filetype.h/ also defines many values that can be used to set these variables. It is included in - <tt/apple2.h/, which is in turn included in <tt/apple2enh.h/. + <tt/apple2.h/. <tag>Example</tag> From e28e32129ae5deef0c8ae18886d403f72b4eea68 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:38:05 +0200 Subject: [PATCH 276/520] Added missing detail --- doc/apple2enh.sgml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 15ceed04f..932aae52d 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -21,7 +21,8 @@ as it comes with the cc65 C compiler. It describes the memory layout, enhanced Apple //e specific header files, available drivers, and any pitfalls specific to that platform. -Please note that enhanced Apple //e specific functions are just mentioned +Please note that this target requires a 65C02 or 65816 CPU, +enhanced Apple //e specific functions are just mentioned here, they are described in detail in the separate <url url="funcref.html" name="function reference">. Even functions marked as "platform dependent" may be available on more than one platform. Please see the function reference for From d4c4786ff045e38876c9b9bd399524b44f115505 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Tue, 5 Sep 2023 00:24:18 -0400 Subject: [PATCH 277/520] undo change of INIT to bss for configurations that were also using rw to reserve space, add a comment documenting the reason --- cfg/apple2-hgr.cfg | 2 +- cfg/apple2-overlay.cfg | 2 +- cfg/apple2-system.cfg | 2 +- cfg/apple2.cfg | 2 +- cfg/apple2enh-hgr.cfg | 2 +- cfg/apple2enh-overlay.cfg | 2 +- cfg/apple2enh-system.cfg | 2 +- cfg/apple2enh.cfg | 2 +- cfg/atmos.cfg | 2 +- cfg/c64-overlay.cfg | 2 +- cfg/c64.cfg | 2 +- cfg/cx16-bank.cfg | 2 +- cfg/cx16.cfg | 2 +- cfg/telestrat.cfg | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cfg/apple2-hgr.cfg b/cfg/apple2-hgr.cfg index 55072ad89..109fbe4f6 100644 --- a/cfg/apple2-hgr.cfg +++ b/cfg/apple2-hgr.cfg @@ -27,7 +27,7 @@ SEGMENTS { CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2-overlay.cfg b/cfg/apple2-overlay.cfg index 2ec798bd9..754ece90f 100644 --- a/cfg/apple2-overlay.cfg +++ b/cfg/apple2-overlay.cfg @@ -43,7 +43,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2-system.cfg b/cfg/apple2-system.cfg index 1c11b6949..3dd94d793 100644 --- a/cfg/apple2-system.cfg +++ b/cfg/apple2-system.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2.cfg b/cfg/apple2.cfg index 14e3a8f55..19932b1f9 100644 --- a/cfg/apple2.cfg +++ b/cfg/apple2.cfg @@ -26,7 +26,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh-hgr.cfg b/cfg/apple2enh-hgr.cfg index 55072ad89..109fbe4f6 100644 --- a/cfg/apple2enh-hgr.cfg +++ b/cfg/apple2enh-hgr.cfg @@ -27,7 +27,7 @@ SEGMENTS { CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh-overlay.cfg b/cfg/apple2enh-overlay.cfg index 2ec798bd9..754ece90f 100644 --- a/cfg/apple2enh-overlay.cfg +++ b/cfg/apple2enh-overlay.cfg @@ -43,7 +43,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh-system.cfg b/cfg/apple2enh-system.cfg index 1c11b6949..3dd94d793 100644 --- a/cfg/apple2enh-system.cfg +++ b/cfg/apple2enh-system.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/apple2enh.cfg b/cfg/apple2enh.cfg index 14e3a8f55..19932b1f9 100644 --- a/cfg/apple2enh.cfg +++ b/cfg/apple2enh.cfg @@ -26,7 +26,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/atmos.cfg b/cfg/atmos.cfg index b06366fee..4c370903a 100644 --- a/cfg/atmos.cfg +++ b/cfg/atmos.cfg @@ -23,7 +23,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BASTAIL: load = MAIN, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/c64-overlay.cfg b/cfg/c64-overlay.cfg index 39d8f68e8..ae760b30c 100644 --- a/cfg/c64-overlay.cfg +++ b/cfg/c64-overlay.cfg @@ -44,7 +44,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; OVL1ADDR: load = OVL1ADDR, type = ro; diff --git a/cfg/c64.cfg b/cfg/c64.cfg index 00a050524..59cadd46b 100644 --- a/cfg/c64.cfg +++ b/cfg/c64.cfg @@ -23,7 +23,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; } diff --git a/cfg/cx16-bank.cfg b/cfg/cx16-bank.cfg index 0aa6fc555..264b6fcba 100644 --- a/cfg/cx16-bank.cfg +++ b/cfg/cx16-bank.cfg @@ -57,7 +57,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss,optional = yes; + INIT: load = MAIN, type = rw, optional = yes; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; BRAM01ADDR: load = BRAM01ADDR, type = ro, optional = yes; diff --git a/cfg/cx16.cfg b/cfg/cx16.cfg index 0f83d4c20..a160a6718 100644 --- a/cfg/cx16.cfg +++ b/cfg/cx16.cfg @@ -24,7 +24,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss,optional = yes; + INIT: load = MAIN, type = rw, optional = yes; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; } diff --git a/cfg/telestrat.cfg b/cfg/telestrat.cfg index 593197866..52b982eef 100644 --- a/cfg/telestrat.cfg +++ b/cfg/telestrat.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = bss; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BASTAIL: load = MAIN, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; From 30b80548d4871cb0dea952a70b608d24dcb5fe91 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:16:14 +0200 Subject: [PATCH 278/520] Update README.md For the sake of completeness --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e0e2c24e7..16e246342 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ For details look at the [Website](https://cc65.github.io). ## People +Project founder: + +* Ulrich von Bassewitz + Core team members: * [Christian Groessler](https://github.com/groessler): Atari, Atari5200, and CreatiVision library Maintainer From 1a6e20ca6e8a95238e0a9ef848e52698bfeedf46 Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Wed, 6 Sep 2023 14:47:14 +0200 Subject: [PATCH 279/520] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16e246342..3e04158e1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For details look at the [Website](https://cc65.github.io). Project founder: -* Ulrich von Bassewitz +* Ullrich von Bassewitz Core team members: From dfe7562f76185b141795f1db1bad905a5f4b1acc Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 6 Sep 2023 08:09:00 +0200 Subject: [PATCH 280/520] Add ntohs, htons, ntohl, htons. --- doc/funcref.sgml | 87 +++++++++++++++++++++++++++++++++++++ include/arpa/inet.h | 10 +++++ libsrc/common/ntohl.s | 34 +++++++++++++++ libsrc/common/ntohs.s | 18 ++++++++ test/val/lib_common_htonl.c | 34 +++++++++++++++ test/val/lib_common_htons.c | 34 +++++++++++++++ 6 files changed, 217 insertions(+) create mode 100644 include/arpa/inet.h create mode 100644 libsrc/common/ntohl.s create mode 100644 libsrc/common/ntohs.s create mode 100644 test/val/lib_common_htonl.c create mode 100644 test/val/lib_common_htons.c diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 2a6d77adc..ae25851e8 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -435,6 +435,16 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. (incomplete) +<sect1><tt/inet.h/<label id="inet.h"><p> + +<itemize> +<item><ref id="htonl" name="htonl"> +<item><ref id="htons" name="htons"> +<item><ref id="ntohl" name="ntohl"> +<item><ref id="ntohs" name="ntohs"> +</itemize> + + <sect1><tt/geos.h/<label id="geos.h"><p> <url url="geos.html" name="GEOS API">. @@ -4388,6 +4398,45 @@ to undefined behaviour. </descrip> </quote> +<sect1>htonl<label id="htonl"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 32 bit word. +<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Declaration/<tt/int htonl(val)/ +<tag/Description/Converts a 32 bit word from from network byte order +(big endian) to little endian (or vice-versa). +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="ntohl" name="ntohl"> +<tag/Availability/cc65 +</descrip> +</quote> + + +<sect1>htons<label id="htons"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 16 bit word. +<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Declaration/<tt/int htons(val)/ +<tag/Description/Converts a 16 bit word from from network byte order +(big endian) to little endian (or vice-versa) by swapping both its bytes. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="ntohs" name="ntohs"> +<tag/Availability/cc65 +</descrip> +</quote> + <sect1>isalnum<label id="isalnum"><p> @@ -5757,6 +5806,44 @@ memory allocated for the driver. </descrip> </quote> +<sect1>ntohl<label id="ntohl"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 32 bit word. +<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Declaration/<tt/int __fastcall__ ntohl (int val);/ +<tag/Description/Converts a 32 bit word from from host byte order (little endian) +to big endian (or vice-versa). +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="htonl" name="htonl"> +<tag/Availability/cc65 +</descrip> +</quote> + +<sect1>ntohs<label id="ntohs"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 16 bit word. +<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Declaration/<tt/int __fastcall__ ntohs (int val);/ +<tag/Description/Converts a 16 bit word from from host byte order (little endian) +to big endian (or vice-versa) by swapping both its bytes. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="htons" name="htons"> +<tag/Availability/cc65 +</descrip> +</quote> + <sect1>offsetof<label id="offsetof"><p> <quote> diff --git a/include/arpa/inet.h b/include/arpa/inet.h new file mode 100644 index 000000000..ddcea2446 --- /dev/null +++ b/include/arpa/inet.h @@ -0,0 +1,10 @@ +#ifndef _ARPA_INET_H +#define _ARPA_INET_H + +int __fastcall__ ntohs (int val); +int __fastcall__ htons (int val); + +long __fastcall__ ntohl (long val); +long __fastcall__ htonl (long val); + +#endif diff --git a/libsrc/common/ntohl.s b/libsrc/common/ntohl.s new file mode 100644 index 000000000..6bf959b95 --- /dev/null +++ b/libsrc/common/ntohl.s @@ -0,0 +1,34 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2023-09-06 +; +; int __fastcall__ ntohl (long val); +; + +.export _ntohl, _htonl +.import popa +.importzp tmp1, tmp2, sreg + +_htonl := _ntohl +.code + +_ntohl: + ; The parts of our 32 bit word + ; are in sreg+1, sreg, X, A. + + + ; Save A and X + stx tmp1 + sta tmp2 + + ; Invert high word + lda sreg+1 + ldx sreg + + ; Invert low word + ldy tmp1 + sty sreg + + ldy tmp2 + sty sreg+1 + + rts diff --git a/libsrc/common/ntohs.s b/libsrc/common/ntohs.s new file mode 100644 index 000000000..e1914770e --- /dev/null +++ b/libsrc/common/ntohs.s @@ -0,0 +1,18 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2023-09-06 +; +; int __fastcall__ ntohs (int val); +; + +.export _ntohs, _htons +.importzp tmp1 + +_htons := _ntohs + +.code + +_ntohs: + sta tmp1 + txa + ldx tmp1 + rts diff --git a/test/val/lib_common_htonl.c b/test/val/lib_common_htonl.c new file mode 100644 index 000000000..53a210a84 --- /dev/null +++ b/test/val/lib_common_htonl.c @@ -0,0 +1,34 @@ +/* + !!DESCRIPTION!! A small test for htons. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> + +static unsigned int Failures = 0; + +static void CheckHtonl (long input, long expected) +{ + long result = htonl(input); + if (result != expected) { + printf ("htonl error:\n" + " result = %ld for %ld, should be %ld\n", result, input, expected); + ++Failures; + } +} + +int main (void) +{ + CheckHtonl(0x00000000, 0x00000000); + CheckHtonl(0x12345678, 0x78563412); + CheckHtonl(0xAABBCCDD, 0xDDCCBBAA); + CheckHtonl(0xFFFFFFFF, 0xFFFFFFFF); + + printf ("Failures: %u\n", Failures); + + return Failures; +} diff --git a/test/val/lib_common_htons.c b/test/val/lib_common_htons.c new file mode 100644 index 000000000..42bbb3d6b --- /dev/null +++ b/test/val/lib_common_htons.c @@ -0,0 +1,34 @@ +/* + !!DESCRIPTION!! A small test for htons. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> + +static unsigned int Failures = 0; + +static void CheckHtons (int input, int expected) +{ + int result = htons(input); + if (result != expected) { + printf ("htons error:\n" + " result = %d for %d, should be %d\n", result, input, expected); + ++Failures; + } +} + +int main (void) +{ + CheckHtons(0x0000, 0x0000); + CheckHtons(0x1234, 0x3412); + CheckHtons(0xA0F2, 0xF2A0); + CheckHtons(0xFFFF, 0xFFFF); + + printf ("Failures: %u\n", Failures); + + return Failures; +} From 3f3b6123b9b5242f581085d3932ed5fb63ddbd04 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 30 Aug 2023 21:28:09 +0200 Subject: [PATCH 281/520] Serial: Optimize TryToSend We don't need to reload SendFreeCnt each time we loop to wait for the ACIA to be ready. --- libsrc/apple2/ser/a2.ssc.s | 7 ++++--- libsrc/atmos/ser/atmos-acia.s | 7 ++++--- libsrc/c128/ser/c128-swlink.s | 14 +++++++------- libsrc/c64/ser/c64-swlink.s | 14 +++++++------- libsrc/cbm510/ser/cbm510-std.s | 14 +++++++------- libsrc/cbm610/ser/cbm610-std.s | 14 +++++++------- libsrc/plus4/ser/plus4-stdser.s | 14 +++++++------- 7 files changed, 43 insertions(+), 41 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 6ad9c3825..c2b0bf321 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -427,12 +427,13 @@ Done: rts TryToSend: sta tmp1 ; Remember tryHard flag -Again: lda SendFreeCnt +NextByte: + lda SendFreeCnt cmp #$FF beq Quit ; Bail out ; Check for flow stopped - lda Stopped +Again: lda Stopped bne Quit ; Bail out ; Check that ACIA is ready to send @@ -449,4 +450,4 @@ Send: ldy SendHead sta ACIA_DATA,x inc SendHead inc SendFreeCnt - jmp Again + jmp NextByte diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index f679125d1..ed5386471 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -352,12 +352,13 @@ Done: rts TryToSend: sta tmp1 ; Remember tryHard flag -Again: lda SendFreeCnt +NextByte: + lda SendFreeCnt cmp #$FF beq Quit ; Bail out ; Check for flow stopped - lda Stopped +Again: lda Stopped bne Quit ; Bail out ; Check that ACIA is ready to send @@ -374,4 +375,4 @@ Send: ldy SendHead sta ACIA::DATA inc SendHead inc SendFreeCnt - jmp Again + jmp NextByte diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s index 7d36eb5bc..57b5ea3b2 100644 --- a/libsrc/c128/ser/c128-swlink.s +++ b/libsrc/c128/ser/c128-swlink.s @@ -466,25 +466,25 @@ NmiHandler: sta tmp1 ; Remember tryHard flag @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: lda ACIA_STATUS + lda ACIA_STATUS and #$10 - bne @L4 + bne @L3 bit tmp1 ;keep trying if must try hard - bmi @L0 -@L3: rts + bmi @L1 +@L2: rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x sta ACIA_DATA inc SendHead diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s index 81c9916a6..8c8b8bf39 100644 --- a/libsrc/c64/ser/c64-swlink.s +++ b/libsrc/c64/ser/c64-swlink.s @@ -443,25 +443,25 @@ NmiHandler: sta tmp1 ; Remember tryHard flag @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: lda ACIA_STATUS + lda ACIA_STATUS and #$10 - bne @L4 + bne @L3 bit tmp1 ;keep trying if must try hard - bmi @L0 -@L3: rts + bmi @L1 +@L2: rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x sta ACIA_DATA inc SendHead diff --git a/libsrc/cbm510/ser/cbm510-std.s b/libsrc/cbm510/ser/cbm510-std.s index cc58c1233..34fe02d0f 100644 --- a/libsrc/cbm510/ser/cbm510-std.s +++ b/libsrc/cbm510/ser/cbm510-std.s @@ -395,31 +395,31 @@ SER_IRQ: sta IndReg ; Switch to the system bank @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: ldy #ACIA::STATUS + ldy #ACIA::STATUS lda (acia),y and #$10 - bne @L4 + bne @L3 bit tmp1 ; Keep trying if must try hard - bmi @L0 + bmi @L1 ; Switch back the bank and return -@L3: lda ExecReg +@L2: lda ExecReg sta IndReg rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x ldy #ACIA::DATA sta (acia),y diff --git a/libsrc/cbm610/ser/cbm610-std.s b/libsrc/cbm610/ser/cbm610-std.s index f7ddde935..c241d5908 100644 --- a/libsrc/cbm610/ser/cbm610-std.s +++ b/libsrc/cbm610/ser/cbm610-std.s @@ -395,31 +395,31 @@ SER_IRQ: sta IndReg ; Switch to the system bank @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: ldy #ACIA::STATUS + ldy #ACIA::STATUS lda (acia),y and #$10 - bne @L4 + bne @L3 bit tmp1 ; Keep trying if must try hard - bmi @L0 + bmi @L1 ; Switch back the bank and return -@L3: lda ExecReg +@L2: lda ExecReg sta IndReg rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x ldy #ACIA::DATA sta (acia),y diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index fbdc61b2e..5c4c550e6 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -387,25 +387,25 @@ SER_IRQ: sta tmp1 ; Remember tryHard flag @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: lda ACIA_STATUS + lda ACIA_STATUS and #$10 - bne @L4 + bne @L3 bit tmp1 ;keep trying if must try hard - bmi @L0 -@L3: rts + bmi @L1 +@L2: rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x sta ACIA_DATA inc SendHead From 89af42e5cbcb98f43fc2269fc5ca5bb0e43f6bf1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 30 Aug 2023 21:14:07 +0200 Subject: [PATCH 282/520] Serial: Optimize SER_IRQ Apple2 and Atmos have Index in X, but can still use it for the best-case path as long as we reload it in the worst-case part (when we assert flow control). Also, standardize the free space to trigger flow control to 32 characters left (compare with RecvFreeCnt before decrement) --- libsrc/apple2/ser/a2.ssc.s | 8 ++++---- libsrc/atmos/ser/atmos-acia.s | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index c2b0bf321..1dbf5f3a4 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -404,19 +404,19 @@ SER_IRQ: and #$08 beq Done ; Jump if no ACIA interrupt lda ACIA_DATA,x ; Get byte from ACIA - ldy RecvFreeCnt ; Check if we have free space left + ldx RecvFreeCnt ; Check if we have free space left beq Flow ; Jump if no space in receive buffer ldy RecvTail ; Load buffer pointer sta RecvBuf,y ; Store received byte in buffer inc RecvTail ; Increment buffer pointer dec RecvFreeCnt ; Decrement free space counter - ldy RecvFreeCnt ; Check for buffer space low - cpy #33 + cpx #33 ; Check for buffer space low bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) ; Assert flow control if buffer space too low -Flow: lda RtsOff +Flow: ldx Index +lda RtsOff sta ACIA_CMD,x sta Stopped sec ; Interrupt handled diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index ed5386471..774a337bd 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -329,19 +329,19 @@ SER_IRQ: and #$08 beq Done ; Jump if no ACIA interrupt lda ACIA::DATA,x ; Get byte from ACIA - ldy RecvFreeCnt ; Check if we have free space left + ldx RecvFreeCnt ; Check if we have free space left beq Flow ; Jump if no space in receive buffer ldy RecvTail ; Load buffer pointer sta RecvBuf,y ; Store received byte in buffer inc RecvTail ; Increment buffer pointer dec RecvFreeCnt ; Decrement free space counter - ldy RecvFreeCnt ; Check for buffer space low - cpy #33 + cpx #33 bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) ; Assert flow control if buffer space too low -Flow: lda RtsOff +Flow: ldx Index ; Reload port + lda RtsOff sta ACIA::CMD,x sta Stopped sec ; Interrupt handled From e8f5ad24712f9ebad8b308ab57b673aebacd9c0a Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 30 Aug 2023 21:02:47 +0200 Subject: [PATCH 283/520] Serial: Optimize SER_PUT We don't need to reload SendFreeCnt if we don't jsr TryToSend. --- libsrc/apple2/ser/a2.ssc.s | 7 ++++--- libsrc/atmos/ser/atmos-acia.s | 7 ++++--- libsrc/c128/ser/c128-swlink.s | 12 +++++++----- libsrc/c64/ser/c64-swlink.s | 12 +++++++----- libsrc/cbm510/ser/cbm510-std.s | 12 +++++++----- libsrc/cbm610/ser/cbm610-std.s | 12 +++++++----- libsrc/plus4/ser/plus4-stdser.s | 12 +++++++----- 7 files changed, 43 insertions(+), 31 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 1dbf5f3a4..7705f0933 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -328,20 +328,21 @@ SER_PUT: ; Try to send ldy SendFreeCnt - iny ; Y = $FF? + cpy #$FF ; Nothing to flush beq :+ pha lda #$00 ; TryHard = false jsr TryToSend pla - ; Put byte into send buffer & send -: ldy SendFreeCnt + ; Reload SendFreeCnt after TryToSend + ldy SendFreeCnt bne :+ lda #SER_ERR_OVERFLOW ldx #0 ; return value is char rts + ; Put byte into send buffer & send : ldy SendTail sta SendBuf,y inc SendTail diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index 774a337bd..c918fe995 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -269,20 +269,21 @@ SER_GET: SER_PUT: ; Try to send ldy SendFreeCnt - iny ; Y = $FF? + cpy #$FF ; Nothing to flush beq :+ pha lda #$00 ; TryHard = false jsr TryToSend pla - ; Put byte into send buffer & send -: ldy SendFreeCnt + ; Reload SendFreeCnt after TryToSend + ldy SendFreeCnt bne :+ lda #SER_ERR_OVERFLOW ldx #0 ; return value is char rts + ; Put byte into send buffer & send : ldy SendTail sta SendBuf,y inc SendTail diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s index 57b5ea3b2..a0fc43050 100644 --- a/libsrc/c128/ser/c128-swlink.s +++ b/libsrc/c128/ser/c128-swlink.s @@ -362,21 +362,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$FF ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s index 8c8b8bf39..c2934e669 100644 --- a/libsrc/c64/ser/c64-swlink.s +++ b/libsrc/c64/ser/c64-swlink.s @@ -336,21 +336,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$FF ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt diff --git a/libsrc/cbm510/ser/cbm510-std.s b/libsrc/cbm510/ser/cbm510-std.s index 34fe02d0f..633a392c7 100644 --- a/libsrc/cbm510/ser/cbm510-std.s +++ b/libsrc/cbm510/ser/cbm510-std.s @@ -292,21 +292,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$FF ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt diff --git a/libsrc/cbm610/ser/cbm610-std.s b/libsrc/cbm610/ser/cbm610-std.s index c241d5908..1216e3794 100644 --- a/libsrc/cbm610/ser/cbm610-std.s +++ b/libsrc/cbm610/ser/cbm610-std.s @@ -293,21 +293,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$ff ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index 5c4c550e6..d1d84267f 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -300,21 +300,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$ff ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt From a89a311abfda385b1c1a9a0bb654180681c23d74 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 30 Aug 2023 20:51:48 +0200 Subject: [PATCH 284/520] Serial: Micro-optimisation for apple2enh On 65C02 we can spare one cycle on sta (abs); we also not have to care about the 6502 false reads --- libsrc/apple2/ser/a2.ssc.s | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 7705f0933..12e975965 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -26,6 +26,7 @@ .include "ser-error.inc" .macpack module + .macpack cpu ; ------------------------------------------------------------------------ ; Header. Includes jump table @@ -57,9 +58,13 @@ ;---------------------------------------------------------------------------- ; I/O definitions +.if (.cpu .bitand CPU_ISET_65C02) +ACIA = $C088 +.else Offset = $8F ; Move 6502 false read out of I/O to page $BF - ACIA = $C088-Offset +.endif + ACIA_DATA = ACIA+0 ; Data register ACIA_STATUS = ACIA+1 ; Status register ACIA_CMD = ACIA+2 ; Command register @@ -200,7 +205,9 @@ SER_OPEN: asl asl asl +.if .not (.cpu .bitand CPU_ISET_65C02) adc #Offset ; Assume carry to be clear +.endif tax ; Check if the handshake setting is valid @@ -315,7 +322,11 @@ SER_GET: inc RecvHead inc RecvFreeCnt ldx #$00 ; (59) +.if (.cpu .bitand CPU_ISET_65C02) + sta (ptr1) +.else sta (ptr1,x) +.endif txa ; Return code = 0 rts From 734541ee05000bad9e9dd2f903a2db754e8c36f3 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 30 Aug 2023 20:49:39 +0200 Subject: [PATCH 285/520] Serial: Optimize SER_GET There is no need to TryToSend before getting the character. We send bytes during SER_PUT, and if interrupted during sending, we still try to do it at the beginning of the next SER_PUT. --- libsrc/apple2/ser/a2.ssc.s | 7 +------ libsrc/atmos/ser/atmos-acia.s | 8 +------- libsrc/c128/ser/c128-swlink.s | 7 +------ libsrc/c64/ser/c64-swlink.s | 7 +------ libsrc/cbm510/ser/cbm510-std.s | 7 +------ libsrc/cbm610/ser/cbm610-std.s | 7 +------ libsrc/plus4/ser/plus4-stdser.s | 7 +------ 7 files changed, 7 insertions(+), 43 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 12e975965..5942d43e9 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -291,14 +291,9 @@ InvBaud:lda #SER_ERR_BAUD_UNAVAIL SER_GET: ldx Index - ldy SendFreeCnt ; Send data if necessary - iny ; Y == $FF? - beq :+ - lda #$00 ; TryHard = false - jsr TryToSend ; Check for buffer empty -: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$FF bne :+ lda #SER_ERR_NO_DATA diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index c918fe995..1509803d2 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -227,14 +227,8 @@ InvBaud:lda #<SER_ERR_BAUD_UNAVAIL ; returned. SER_GET: - ldy SendFreeCnt ; Send data if necessary - iny ; Y == $FF? - beq :+ - lda #$00 ; TryHard = false - jsr TryToSend - ; Check for buffer empty -: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$FF bne :+ lda #SER_ERR_NO_DATA diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s index a0fc43050..b8f08159b 100644 --- a/libsrc/c128/ser/c128-swlink.s +++ b/libsrc/c128/ser/c128-swlink.s @@ -314,15 +314,10 @@ SER_CLOSE: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$ff bne @L2 lda #SER_ERR_NO_DATA diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s index c2934e669..067a7ca92 100644 --- a/libsrc/c64/ser/c64-swlink.s +++ b/libsrc/c64/ser/c64-swlink.s @@ -288,15 +288,10 @@ SER_CLOSE: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$ff bne @L2 lda #SER_ERR_NO_DATA diff --git a/libsrc/cbm510/ser/cbm510-std.s b/libsrc/cbm510/ser/cbm510-std.s index 633a392c7..800007492 100644 --- a/libsrc/cbm510/ser/cbm510-std.s +++ b/libsrc/cbm510/ser/cbm510-std.s @@ -244,15 +244,10 @@ InvBaud: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt + lda RecvFreeCnt cmp #$ff bne @L2 lda #SER_ERR_NO_DATA diff --git a/libsrc/cbm610/ser/cbm610-std.s b/libsrc/cbm610/ser/cbm610-std.s index 1216e3794..45b18eadf 100644 --- a/libsrc/cbm610/ser/cbm610-std.s +++ b/libsrc/cbm610/ser/cbm610-std.s @@ -245,15 +245,10 @@ InvBaud: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt + lda RecvFreeCnt cmp #$ff bne @L2 lda #SER_ERR_NO_DATA diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index d1d84267f..77445c7a2 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -252,15 +252,10 @@ InvBaud: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$ff bne @L2 lda #SER_ERR_NO_DATA From a90aa575105b5a1f33c99e7feab8d1921aee99d4 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 7 Sep 2023 16:48:56 +0200 Subject: [PATCH 286/520] Address Oliver's comments --- doc/funcref.sgml | 8 ++++---- include/arpa/inet.h | 30 ++++++++++++++++++++++++++++++ libsrc/common/ntohl.s | 2 -- libsrc/common/ntohs.s | 2 -- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index ae25851e8..5fa1720e4 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -4403,7 +4403,7 @@ to undefined behaviour. <quote> <descrip> <tag/Function/Swaps byte order in a 32 bit word. -<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ <tag/Declaration/<tt/int htonl(val)/ <tag/Description/Converts a 32 bit word from from network byte order (big endian) to little endian (or vice-versa). @@ -4423,7 +4423,7 @@ be used in presence of a prototype. <quote> <descrip> <tag/Function/Swaps byte order in a 16 bit word. -<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ <tag/Declaration/<tt/int htons(val)/ <tag/Description/Converts a 16 bit word from from network byte order (big endian) to little endian (or vice-versa) by swapping both its bytes. @@ -5811,7 +5811,7 @@ memory allocated for the driver. <quote> <descrip> <tag/Function/Swaps byte order in a 32 bit word. -<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ <tag/Declaration/<tt/int __fastcall__ ntohl (int val);/ <tag/Description/Converts a 32 bit word from from host byte order (little endian) to big endian (or vice-versa). @@ -5830,7 +5830,7 @@ be used in presence of a prototype. <quote> <descrip> <tag/Function/Swaps byte order in a 16 bit word. -<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ <tag/Declaration/<tt/int __fastcall__ ntohs (int val);/ <tag/Description/Converts a 16 bit word from from host byte order (little endian) to big endian (or vice-versa) by swapping both its bytes. diff --git a/include/arpa/inet.h b/include/arpa/inet.h index ddcea2446..e1e970e07 100644 --- a/include/arpa/inet.h +++ b/include/arpa/inet.h @@ -1,3 +1,33 @@ +/*****************************************************************************/ +/* */ +/* arpa/inet.h */ +/* */ +/* Endianness utilities for cc65 */ +/* */ +/* */ +/* */ +/* (C) 2023 Colin Leroy-Mira, <colin@colino.net> */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + #ifndef _ARPA_INET_H #define _ARPA_INET_H diff --git a/libsrc/common/ntohl.s b/libsrc/common/ntohl.s index 6bf959b95..77adba253 100644 --- a/libsrc/common/ntohl.s +++ b/libsrc/common/ntohl.s @@ -9,13 +9,11 @@ .importzp tmp1, tmp2, sreg _htonl := _ntohl -.code _ntohl: ; The parts of our 32 bit word ; are in sreg+1, sreg, X, A. - ; Save A and X stx tmp1 sta tmp2 diff --git a/libsrc/common/ntohs.s b/libsrc/common/ntohs.s index e1914770e..042ddb005 100644 --- a/libsrc/common/ntohs.s +++ b/libsrc/common/ntohs.s @@ -9,8 +9,6 @@ _htons := _ntohs -.code - _ntohs: sta tmp1 txa From cbc5603d631cff1f1ba8aaa78909c56bbd1387f9 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 7 Sep 2023 21:00:30 +0200 Subject: [PATCH 287/520] Inline ntohs/htons as a macro if -i is passed --- include/arpa/inet.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/arpa/inet.h b/include/arpa/inet.h index e1e970e07..2fb47388d 100644 --- a/include/arpa/inet.h +++ b/include/arpa/inet.h @@ -31,8 +31,22 @@ #ifndef _ARPA_INET_H #define _ARPA_INET_H +#ifndef __OPT_i__ int __fastcall__ ntohs (int val); int __fastcall__ htons (int val); +#else + +#define ntohs(x) \ + ( \ + __AX__=(x), \ + asm("sta tmp1"), \ + asm("txa"), \ + asm("ldx tmp1"), \ + __AX__ \ + ) +#define htons(x) ntohs(x) + +#endif long __fastcall__ ntohl (long val); long __fastcall__ htonl (long val); From 9669710cc3bd047b8a3a4a8d0e5490c31d4fd5bf Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 7 Sep 2023 21:36:39 +0200 Subject: [PATCH 288/520] Fix usage of __OPT_i__ --- include/arpa/inet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/arpa/inet.h b/include/arpa/inet.h index 2fb47388d..5f4b994e8 100644 --- a/include/arpa/inet.h +++ b/include/arpa/inet.h @@ -31,7 +31,7 @@ #ifndef _ARPA_INET_H #define _ARPA_INET_H -#ifndef __OPT_i__ +#if (__OPT_i__ < 200) int __fastcall__ ntohs (int val); int __fastcall__ htons (int val); #else From 9e5620f127c636afe0e2c3901ca5cbfdf39fa96e Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 7 Sep 2023 22:37:30 +0200 Subject: [PATCH 289/520] Fix coding-style on header --- include/arpa/inet.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/arpa/inet.h b/include/arpa/inet.h index 5f4b994e8..cd353a2bb 100644 --- a/include/arpa/inet.h +++ b/include/arpa/inet.h @@ -28,9 +28,19 @@ /* */ /*****************************************************************************/ + + #ifndef _ARPA_INET_H #define _ARPA_INET_H + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + #if (__OPT_i__ < 200) int __fastcall__ ntohs (int val); int __fastcall__ htons (int val); @@ -51,4 +61,7 @@ int __fastcall__ htons (int val); long __fastcall__ ntohl (long val); long __fastcall__ htonl (long val); + + +/* End of arpa/inet.h */ #endif From 38f7daf40e13696cf5db56874a8fee38ce76db8a Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 10 Sep 2023 21:13:27 +0200 Subject: [PATCH 290/520] Make sure not to load ACIA driver on IIgs' Z8530 The Pascal Firmware Protocol Bytes ID are not enough to differentiate an SSC card from a IIgs serial firmware: http://www.1000bit.it/support/manuali/apple/technotes/misc/tn.misc.08.html Loading a2(e).ssc.ser on a IIgs succeeds, then goes to limbo when one tries to use the serial port. Check first byte on the slot's firmware in addition to the four existing ones, as it's supposed to be $2C (BIT instruction) on an SSC card, and $EF (65C816 SEP instruction) on the IIgs' serial firmware (ROM revisions 0, 1, 3). --- libsrc/apple2/ser/a2.ssc.s | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 5942d43e9..b58afb7d0 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -132,16 +132,33 @@ ParityTable: .byte $60 ; SER_PAR_EVEN .byte $A0 ; SER_PAR_MARK .byte $E0 ; SER_PAR_SPACE + + ; (*): The first byte we'll check at offset 0 is + ; expected to be a BIT instruction on any Apple2 + ; machine with an ACIA 6551, including the //c + ; and //c+. + ; The IIgs, on the other hand, has a + ; Zilog Z8530 chip and its firmware starts with + ; a SEP instruction. We don't want to load this + ; driver on the IIgs' serial port. + ; + ; The next four bytes we check are the Pascal + ; Firmware Protocol Bytes that identify a + ; serial card. + IdOfsTable: + .byte $00 ; First instruction .byte $05 ; Pascal 1.0 ID byte .byte $07 ; Pascal 1.0 ID byte .byte $0B ; Pascal 1.1 generic signature byte .byte $0C ; Device signature byte IdValTable: - .byte $38 ; Fixed - .byte $18 ; Fixed - .byte $01 ; Fixed - .byte $31 ; Serial or parallel I/O card type 1 + .byte $2C ; BIT + .byte $38 ; ID Byte 0 (from Pascal 1.0), fixed + .byte $18 ; ID Byte 1 (from Pascal 1.0), fixed + .byte $01 ; Generic signature for Pascal 1.1, fixed + .byte $31 ; Device signature byte (serial or + ; parallel I/O card type 1) IdTableLen = * - IdValTable From aa5ee8423a0fd741eed4b598211c25cf3c8d829d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 10 Sep 2023 22:34:03 +0200 Subject: [PATCH 291/520] Clearer wording for the firmware check --- libsrc/apple2/ser/a2.ssc.s | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index b58afb7d0..13f903597 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -133,18 +133,26 @@ ParityTable: .byte $A0 ; SER_PAR_MARK .byte $E0 ; SER_PAR_SPACE - ; (*): The first byte we'll check at offset 0 is - ; expected to be a BIT instruction on any Apple2 - ; machine with an ACIA 6551, including the //c - ; and //c+. + ; Check five bytes at known positions on the + ; slot's firmware to make sure this is an SSC + ; (or Apple //c comm port) firmware that drives + ; an ACIA 6551 chip. + ; + ; The SSC firmware and the Apple //c(+) comm + ; port firmware all begin with a BIT instruction. ; The IIgs, on the other hand, has a ; Zilog Z8530 chip and its firmware starts with ; a SEP instruction. We don't want to load this - ; driver on the IIgs' serial port. + ; driver on the IIgs' serial port. We'll + ; differentiate the firmware on this byte. ; ; The next four bytes we check are the Pascal ; Firmware Protocol Bytes that identify a - ; serial card. + ; serial card. Those are the same bytes for + ; SSC firmwares, Apple //c firmwares and IIgs + ; Zilog Z8530 firmwares - which is the reason + ; we have to check for the firmware's first + ; instruction too. IdOfsTable: .byte $00 ; First instruction From 3fc074014ef780b7e0ed9d48817fd3c1473afa0f Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 12 Sep 2023 20:54:32 +0200 Subject: [PATCH 292/520] Serial kernel: Fix driver lifecycle - Make sure we don't install a driver on top of another one - Make sure we don't uninstall a driver twice - Make sure we uninstall a driver if needed at exit --- doc/funcref.sgml | 3 ++- libsrc/serial/ser-kernel.s | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 5fa1720e4..12b45db2b 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -6693,7 +6693,8 @@ be used in presence of a prototype. <tag/Function/Uninstall the currently loaded driver but do not unload it. <tag/Header/<tt/<ref id="serial.h" name="serial.h">/ <tag/Declaration/<tt/unsigned char ser_uninstall (void);/ -<tag/Description/Uninstall the currently loaded driver but do not unload it. +<tag/Description/Uninstall the currently loaded driver but do not unload it. This +function returns SER_ERR_NO_DRIVER if no driver was installed, 0 otherwise. <tag/Availability/cc65 <tag/See also/Other serial functions. <tag/Example/None. diff --git a/libsrc/serial/ser-kernel.s b/libsrc/serial/ser-kernel.s index b6c57a3b5..1514bcf77 100644 --- a/libsrc/serial/ser-kernel.s +++ b/libsrc/serial/ser-kernel.s @@ -7,6 +7,7 @@ .import return0, ser_libref .importzp ptr1 .interruptor ser_irq, 29 ; Export as high priority IRQ handler + .destructor _ser_uninstall .include "ser-kernel.inc" .include "ser-error.inc" @@ -44,7 +45,16 @@ ser_sig: .byte $73, $65, $72, SER_API_VERSION ; "ser", version _ser_install: - sta _ser_drv + ldy _ser_drv ; Check no driver is installed + bne ErrInstalled + ldy _ser_drv+1 + beq :+ +ErrInstalled: + ldx #$00 + lda #SER_ERR_INSTALLED + rts + +: sta _ser_drv sta ptr1 stx _ser_drv+1 stx ptr1+1 @@ -107,7 +117,14 @@ copy: lda (ptr1),y ; */ _ser_uninstall: - jsr ser_uninstall ; Call driver routine + ldx _ser_drv ; Check a driver is installed + bne :+ + ldx _ser_drv+1 + bne :+ + lda #SER_ERR_NO_DRIVER + rts + +: jsr ser_uninstall ; Call driver routine lda #$60 ; RTS opcode sta ser_irq ; Disable IRQ entry point @@ -117,5 +134,6 @@ _ser_clear_ptr: ; External entry point sta _ser_drv sta _ser_drv+1 ; Clear the driver pointer +done: tax rts ; Return zero From 8e62cbf0925f323cd15cef7cf58e3fe7437499d7 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 13 Sep 2023 22:26:41 +0800 Subject: [PATCH 293/520] Improved checks on function return types. --- src/cc65/function.c | 8 ++++++-- src/cc65/stmt.c | 33 ++++++++++----------------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/cc65/function.c b/src/cc65/function.c index 1ad89b111..06800b133 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -466,11 +466,15 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Check return type */ ReturnType = F_GetReturnType (CurrentFunc); - if (IsIncompleteESUType (ReturnType)) { + if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { /* There are already diagnostics on returning arrays or functions */ - if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { + if (IsIncompleteESUType (ReturnType)) { Error ("Function has incomplete return type '%s'", GetFullTypeName (ReturnType)); + } else if (IsPassByRefType (ReturnType)) { + /* Handle struct/union specially */ + Error ("Function return type '%s' of size %u is unsupported", + GetFullTypeName (ReturnType), SizeOf (ReturnType)); } } diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index e1add990e..c86f25f8a 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -311,7 +311,6 @@ static void ReturnStatement (void) /* Handle the 'return' statement */ { ExprDesc Expr; - const Type* ReturnType; ED_Init (&Expr); NextToken (); @@ -327,31 +326,19 @@ static void ReturnStatement (void) if (F_HasVoidReturn (CurrentFunc)) { Error ("Returning a value in function with return type 'void'"); } else { - /* Check the return type first */ - ReturnType = F_GetReturnType (CurrentFunc); - if (IsIncompleteESUType (ReturnType)) { - /* Avoid excess errors */ - if (ErrorCount == 0) { - Error ("Returning a value in function with incomplete return type"); - } + const Type* ReturnType = F_GetReturnType (CurrentFunc); + + /* Convert the return value to the type of the function result */ + TypeConversion (&Expr, ReturnType); + + /* Load the value into the primary */ + if (IsClassStruct (Expr.Type)) { + /* Handle struct/union specially */ + LoadExpr (CG_TypeOf (GetStructReplacementType (ReturnType)), &Expr); } else { - /* Convert the return value to the type of the function result */ - TypeConversion (&Expr, ReturnType); - /* Load the value into the primary */ - if (IsClassStruct (Expr.Type)) { - /* Handle struct/union specially */ - ReturnType = GetStructReplacementType (Expr.Type); - if (ReturnType == Expr.Type) { - Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); - } - LoadExpr (CG_TypeOf (ReturnType), &Expr); - - } else { - /* Load the value into the primary */ - LoadExpr (CF_NONE, &Expr); - } + LoadExpr (CF_NONE, &Expr); } /* Append deferred inc/dec at sequence point */ From 878264d948e6eafec068f13616b3c9b3b7454d1f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 13 Sep 2023 22:26:41 +0800 Subject: [PATCH 294/520] Minor consistency improvement for AddEnumSym() usage. No impact. --- src/cc65/declare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index cd174c92d..644291869 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -756,7 +756,7 @@ static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) Flags |= SC_FICTITIOUS; } - return AddEnumSym (Name, Flags, MemberType, FieldTab, DSFlags); + return AddEnumSym (Name, SC_DEF | Flags, MemberType, FieldTab, DSFlags); } From 2e9843b32d43a84f4fc5826fd811370d8e2a6a5b Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:35:36 +0200 Subject: [PATCH 295/520] optimisation scandisplay --- libsrc/kim1/scandisplay.s | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsrc/kim1/scandisplay.s b/libsrc/kim1/scandisplay.s index 0f46a5de4..768adb2b9 100644 --- a/libsrc/kim1/scandisplay.s +++ b/libsrc/kim1/scandisplay.s @@ -15,7 +15,6 @@ sta $FA ; Middle display data jsr popa sta $FB ; Leftmost display data - jsr SCANDS - rts + jmp SCANDS .endproc From e145145fbb9faada2c7879962b54ded7a4f79d3f Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Sat, 16 Sep 2023 21:01:37 +0200 Subject: [PATCH 296/520] Added KIM-1 refs some clean-up --- doc/funcref.sgml | 50 ++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 5fa1720e4..a3f847867 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -435,6 +435,11 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. (incomplete) +<sect1><tt/geos.h/<label id="geos.h"><p> + +<url url="geos.html" name="GEOS API">. + + <sect1><tt/inet.h/<label id="inet.h"><p> <itemize> @@ -445,11 +450,6 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. </itemize> -<sect1><tt/geos.h/<label id="geos.h"><p> - -<url url="geos.html" name="GEOS API">. - - <sect1><tt/joystick.h/<label id="joystick.h"><p> <itemize> @@ -462,6 +462,18 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. </itemize> +<sect1><tt/kim1.h/<label id="kim1.h"><p> + +<itemize> +<!-- <item><ref id="getkey" name="getkey"> --> +<!-- <item><ref id="scandisplay" name="scandisplay"> --> +<item><ref id="loadt" name="loadt"> +<item><ref id="dumpt" name="dumpt"> +</itemize> + +(incomplete) + + <sect1><tt/locale.h/<label id="locale.h"><p> <itemize> @@ -1745,7 +1757,7 @@ used in presence of a prototype. <tag/See also/ <ref id="fdisp" name="fdisp">, <ref id="loadt" name="loadt">, -<ref id="dumpt" name="dumpt">, +<ref id="dumpt" name="dumpt"> <tag/Example/None. </descrip> </quote> @@ -1890,7 +1902,7 @@ be used in presence of a prototype. <ref id="_swap" name="_swap">, <ref id="memcpy" name="memcpy">, <ref id="memmove" name="memmove">, -<ref id="memset" name="memset">, +<ref id="memset" name="memset"> <tag/Example/None. </descrip> </quote> @@ -1963,7 +1975,7 @@ sent a command to TALK and a secondary address if it needs one. </itemize> <tag/Availability/cc65 <tag/See also/ -<ref id="cbm_k_talk" name="cbm_k_talk">, +<ref id="cbm_k_talk" name="cbm_k_talk"> <tag/Example/None. </descrip> </quote> @@ -2365,8 +2377,8 @@ function, in order to provide input from the keyboard. <tag/See also/ <ref id="cbm_k_getin" name="cbm_k_getin">, <ref id="cbm_k_udtim" name="cbm_k_udtim">, -<ref id="cgetc" name="cgetc">, -<!-- <ref id="getc" name="getc"> --> +<ref id="cgetc" name="cgetc"> +<!-- <ref id="getc" name="getc">, --> <!-- <ref id="getchar" name="getchar"> --> <tag/Example/None. </descrip> @@ -3284,7 +3296,7 @@ used in presence of a prototype. <tag/Availability/cc65 (not all platforms) <tag/See also/ <ref id="get_c128_speed" name="get_c128_speed">, -<ref id="set_c128_speed" name="set_c128_speed">, +<ref id="set_c128_speed" name="set_c128_speed"> <tag/Example/None. </descrip> </quote> @@ -3324,7 +3336,7 @@ used in presence of a prototype. <tag/Availability/cc65 (not all platforms) <tag/See also/ <ref id="get_c65_speed" name="get_c65_speed">, -<ref id="set_c65_speed" name="set_c65_speed">, +<ref id="set_c65_speed" name="set_c65_speed"> <tag/Example/None. </descrip> </quote> @@ -3450,11 +3462,11 @@ int main(void) <quote> <descrip> <tag/Function/Dump memory to tape. -<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/ +<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/, <tt/<ref id="kim1.h" name="kim1.h">/ <tag/Declaration/<tt/int __fastcall__ dumpt (unsigned char id, const void* start, const void* end);/ <tag/Description/<tt/dumpt/ saves memory onto data tape. <tag/Notes/<itemize> -<item>The function is specific to the Sym-1. +<item>The function is specific to the Sym-1 and KIM-1. <item>The return value is status. Non-zero status indicates an error. <item>The function is only available as fastcall function, so it may only be used in presence of a prototype. @@ -3463,7 +3475,7 @@ be used in presence of a prototype. <tag/See also/ <ref id="beep" name="beep">, <ref id="fdisp" name="fdisp">, -<ref id="loadt" name="loadt">, +<ref id="loadt" name="loadt"> <tag/Example/None. </descrip> </quote> @@ -3843,7 +3855,7 @@ switching the CPU into double clock mode. <tag/See also/ <ref id="beep" name="beep">, <ref id="loadt" name="loadt">, -<ref id="dumpt" name="dumpt">, +<ref id="dumpt" name="dumpt"> <tag/Example/None. </descrip> </quote> @@ -5133,11 +5145,11 @@ used in presence of a prototype. <quote> <descrip> <tag/Function/Load memory from tape. -<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/ +<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/, <tt/<ref id="kim1.h" name="kim1.h">/ <tag/Declaration/<tt/int __fastcall__ loadt (unsigned char id);/ <tag/Description/<tt/loadt/ loads memory from data tape. <tag/Notes/<itemize> -<item>The function is specific to the Sym-1. +<item>The function is specific to the Sym-1 and KIM-1. <item>The return value is status. Non-zero status indicates an error. <item>The function is only available as fastcall function, so it may only be used in presence of a prototype. @@ -5146,7 +5158,7 @@ be used in presence of a prototype. <tag/See also/ <ref id="beep" name="beep">, <ref id="fdisp" name="fdisp">, -<ref id="dumpt" name="dumpt">, +<ref id="dumpt" name="dumpt"> <tag/Example/None. </descrip> </quote> From 39abd233fe6124c1549e377b150eb51d974e7ef6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 17 Sep 2023 23:47:22 +0800 Subject: [PATCH 297/520] Fixed check for conflicting extern vs no-linkage/static declarations in functions. --- src/cc65/symtab.c | 43 ++++++++++++++----------- test/err/bug2162-none-extern-auto.c | 8 +++++ test/err/bug2162-none-static-extern.c | 8 +++++ test/err/bug2162-static-extern-auto.c | 10 ++++++ test/err/bug2162-static-static-extern.c | 10 ++++++ 5 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 test/err/bug2162-none-extern-auto.c create mode 100644 test/err/bug2162-none-static-extern.c create mode 100644 test/err/bug2162-static-extern-auto.c create mode 100644 test/err/bug2162-static-static-extern.c diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a2bbf13dd..951ed9e5e 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1225,6 +1225,15 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { + int CheckExtern = 0; + if ((Flags & SC_STRUCTFIELD) == 0) { + while (Entry && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { + /* Get the aliased entry */ + Entry = Entry->V.A.Field; + /* Check for conflict with local storage class */ + CheckExtern = 1; + } + } /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { @@ -1234,19 +1243,14 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { Error ("Multiple definition of '%s'", Entry->Name); Entry = 0; - } else if ((Flags & (SC_AUTO | SC_REGISTER)) != 0 && - (Entry->Flags & SC_EXTERN) != 0) { - /* Check for local storage class conflict */ - Error ("Declaration of '%s' with no linkage follows extern declaration", - Name); - Entry = 0; - } else { - /* If a static declaration follows a non-static declaration, - ** then it is an error. - */ - if ((Flags & SC_DEF) && - (Flags & SC_EXTERN) == 0 && - (Entry->Flags & SC_EXTERN) != 0) { + } else if (CheckExtern) { + if ((Flags & (SC_AUTO | SC_REGISTER)) != 0) { + Error ("Declaration of '%s' with no linkage follows extern declaration", Name); + Entry = 0; + } else if ((Flags & SC_DEF) != 0 && (Flags & SC_EXTERN) == 0) { + /* If a static declaration follows a non-static declaration, + ** then it is an error. + */ Error ("Static declaration of '%s' follows extern declaration", Name); Entry = 0; } @@ -1340,7 +1344,8 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Name); Entry = 0; } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { - /* If a static declaration follows a non-static declaration, then the result is undefined. + /* If a static declaration follows a non-static declaration, then + ** the result is undefined. ** Most compilers choose to either give an error at compile time, ** or remove the extern property for a link time error if used. */ @@ -1348,6 +1353,7 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) (Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { Error ("Static declaration of '%s' follows non-static declaration", Name); + Entry = 0; } else if ((Flags & SC_EXTERN) != 0 && (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && (Entry->Flags & SC_EXTERN) == 0) { @@ -1359,14 +1365,15 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) */ if (Entry->Owner == SymTab0) { if ((Flags & SC_STORAGE) == 0) { - /* Linkage must be unchanged. - ** The C standard specifies that a later extern declaration will be ignored, - ** and will use the previous linkage instead. Giving a warning for this case. + /* The C standard specifies that a later extern declaration will keep + ** the previously declared internal or external linkage unchanged. + ** Though not required by the standard, we are warning on this case. */ Flags &= ~SC_EXTERN; - Warning ("Extern declaration of '%s' follows static declaration, extern ignored", Name); + Warning ("Extern declaration of '%s' follows static declaration, linkage unchanged", Name); } else { Error ("Non-static declaration of '%s' follows static declaration", Name); + Entry = 0; } } else { Error ("Extern declaration of '%s' follows static declaration", Name); diff --git a/test/err/bug2162-none-extern-auto.c b/test/err/bug2162-none-extern-auto.c new file mode 100644 index 000000000..777fc48e7 --- /dev/null +++ b/test/err/bug2162-none-extern-auto.c @@ -0,0 +1,8 @@ +/* Bug #2162 - conflicting declarations in functions */ + +int main(void) +{ + extern int i; + int i = 42; /* Error */ + return i; +} diff --git a/test/err/bug2162-none-static-extern.c b/test/err/bug2162-none-static-extern.c new file mode 100644 index 000000000..cffb6a3bf --- /dev/null +++ b/test/err/bug2162-none-static-extern.c @@ -0,0 +1,8 @@ +/* Bug #2162 - conflicting declarations in functions */ + +int main(void) +{ + static int i = 42; + extern int i; /* Error */ + return i; +} diff --git a/test/err/bug2162-static-extern-auto.c b/test/err/bug2162-static-extern-auto.c new file mode 100644 index 000000000..08d91e0e1 --- /dev/null +++ b/test/err/bug2162-static-extern-auto.c @@ -0,0 +1,10 @@ +/* Bug #2162 - conflicting declarations in functions */ + +static int i; + +int main(void) +{ + extern int i; /* cc65 allows this */ + int i = 42; /* Error - if this were accepted, it would be confusing which object i refers to */ + return i; +} diff --git a/test/err/bug2162-static-static-extern.c b/test/err/bug2162-static-static-extern.c new file mode 100644 index 000000000..e0a536d6c --- /dev/null +++ b/test/err/bug2162-static-static-extern.c @@ -0,0 +1,10 @@ +/* Bug #2162 - conflicting declarations in functions */ + +static int i; + +int main(void) +{ + static int i = 42; /* OK - this shadows the i in file scope */ + extern int i; /* Error - if this were accepted, it would be confusing which object i refers to */ + return i; +} From 3626c94c975a5145d874ae32666f41450c896897 Mon Sep 17 00:00:00 2001 From: ToboterXP <55210408+ToboterXP@users.noreply.github.com> Date: Sun, 17 Sep 2023 22:48:47 +0200 Subject: [PATCH 298/520] Added FX registers to cx16.h --- include/cx16.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/include/cx16.h b/include/cx16.h index 66f21843e..620b9801d 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -256,6 +256,42 @@ struct __vera { unsigned char vstart; /* Vertical start position */ unsigned char vstop; /* Vertical stop position */ }; + struct { /* Visible when DCSEL flag = 2 */ + unsigned char fxctrl; + unsigned char fxtilebase; + unsigned char fxmapbase; + unsigned char fxmult; + }; + struct { /* Visible when DCSEL flag = 3 */ + unsigned char fxxincrl; + unsigned char fxxincrh; + unsigned char fxyincrl; + unsigned char fxyincrh; + }; + struct { /* Visible when DCSEL flag = 4 */ + unsigned char fxxposl; + unsigned char fxxposh; + unsigned char fxyposl; + unsigned char fxyposh; + }; + struct { /* Visible when DCSEL flag = 5 */ + unsigned char fxxposs; + unsigned char fxyposs; + unsigned char fxpolyfilll; + unsigned char fxpolyfillh; + }; + struct { /* Visible when DCSEL flag = 6 */ + unsigned char fxcachel; + unsigned char fxcachem; + unsigned char fxcacheh; + unsigned char fxcacheu; + }; + struct { /* Visible when DCSEL flag = 63 */ + unsigned char dcver0; + unsigned char dcver1; + unsigned char dcver2; + unsigned char dcver3; + } } display; struct { unsigned char config; /* Layer map geometry */ From d251bd46c817710c268432b895a73e26c66b51b9 Mon Sep 17 00:00:00 2001 From: ToboterXP <55210408+ToboterXP@users.noreply.github.com> Date: Sun, 17 Sep 2023 22:54:48 +0200 Subject: [PATCH 299/520] Removed dangling spaces --- include/cx16.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cx16.h b/include/cx16.h index 620b9801d..5971cc2f6 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -290,7 +290,7 @@ struct __vera { unsigned char dcver0; unsigned char dcver1; unsigned char dcver2; - unsigned char dcver3; + unsigned char dcver3; } } display; struct { From 0815c8890b5d4d472dfd563706efc5b295489908 Mon Sep 17 00:00:00 2001 From: ToboterXP <55210408+ToboterXP@users.noreply.github.com> Date: Sun, 17 Sep 2023 22:57:42 +0200 Subject: [PATCH 300/520] Added semicolon --- include/cx16.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cx16.h b/include/cx16.h index 5971cc2f6..5bbd21247 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -291,7 +291,7 @@ struct __vera { unsigned char dcver1; unsigned char dcver2; unsigned char dcver3; - } + }; } display; struct { unsigned char config; /* Layer map geometry */ From fc603129da73bd5b3daed358479b99f140b521f0 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 18 Sep 2023 15:44:58 +0800 Subject: [PATCH 301/520] A structure with a flexible array member shall not be a member of a structure or an element of an array according to the ISO C Standard. --- src/cc65/declare.c | 10 ++++++++++ test/err/bug2016-fam-member.c | 11 +++++++++++ test/err/bug2017-fam-element.c | 9 +++++++++ 3 files changed, 30 insertions(+) create mode 100644 test/err/bug2016-fam-member.c create mode 100644 test/err/bug2017-fam-element.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 59eb555c4..80be9ceb7 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -515,6 +515,13 @@ static void CheckArrayElementType (Type* DataType) if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { Error ("Array of 0-size element type '%s'", GetFullTypeName (T)); } + } else { + if (IsTypeStruct (T)) { + SymEntry* TagEntry = GetESUTagSym (T); + if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { + Error ("Invalid use of struct with flexible array member"); + } + } } } else { ++T; @@ -1193,6 +1200,9 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { Field->Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM; + if (IsTypeStruct (Decl.Type)) { + Error ("Invalid use of struct with flexible array member"); + } } } diff --git a/test/err/bug2016-fam-member.c b/test/err/bug2016-fam-member.c new file mode 100644 index 000000000..02c9ec275 --- /dev/null +++ b/test/err/bug2016-fam-member.c @@ -0,0 +1,11 @@ +/* Bug #2016 - cc65 erroneously allows struct fields that are structs with flexible array members */ + +typedef struct x { + int a; + int b[]; /* Ok: Flexible array member can be last */ +} x; + +struct y { + x x; /* Not ok: Contains flexible array member */ + int a; +}; diff --git a/test/err/bug2017-fam-element.c b/test/err/bug2017-fam-element.c new file mode 100644 index 000000000..195ca6597 --- /dev/null +++ b/test/err/bug2017-fam-element.c @@ -0,0 +1,9 @@ +/* Bug #2017 - cc65 erroneously allows arrays of structs with flexible array members */ + +struct z { + int a; + int c; + int b[]; +}; + +struct z y[3]; /* Should be an error */ From 51e304f10f6a184f39f1f69d2f0d52e7cd9e5347 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 18 Sep 2023 16:44:04 +0800 Subject: [PATCH 302/520] Added check for total arguments size for variadic functions. --- src/cc65/expr.c | 4 ++++ test/err/bug2144.c | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 test/err/bug2144.c diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 47a05eca0..37af494b5 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1035,6 +1035,10 @@ static void FunctionCall (ExprDesc* Expr) /* Parse the argument list and pass them to the called function */ ArgSize = FunctionArgList (Func, IsFastcall, Expr); + if (ArgSize > 0xFF && (Func->Flags & FD_VARIADIC) != 0) { + Error ("Total size of all arguments passed to a variadic function cannot exceed 255 bytes"); + } + /* We need the closing paren here */ ConsumeRParen (); diff --git a/test/err/bug2144.c b/test/err/bug2144.c new file mode 100644 index 000000000..eb27d672b --- /dev/null +++ b/test/err/bug2144.c @@ -0,0 +1,13 @@ +/* Bug #2144 - Maximum parameter size is not checked for variadic functions */ + +void a(...) {} + +void b() +{ + /* Argument size > 255 */ + a(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L); +} From 13e1ed3e7bb3540fcb17141fbbd3496d29e3f7bc Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 22 Sep 2023 10:29:52 +0800 Subject: [PATCH 303/520] Fixed compound initialization with omitted enclosing curly braces when an array/struct/union to initialize is nested. --- src/cc65/initdata.c | 26 +++++++++++++++++-------- test/val/bug2135.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 test/val/bug2135.c diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 619fe4897..f63e9e95c 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -343,8 +343,8 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Char array initialized by string constant */ int NeedParen; - /* If we initializer is enclosed in brackets, remember this fact and - ** skip the opening bracket. + /* If the initializer is enclosed in curly braces, remember this fact + ** and skip the opening one. */ NeedParen = (CurTok.Tok == TOK_LCURLY); if (NeedParen) { @@ -377,7 +377,9 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) } else { - /* Arrays can be initialized without a pair of curly braces */ + /* An array can be initialized without a pair of enclosing curly braces + ** if it is itself a member of a struct/union or an element of an array. + */ if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) { /* Consume the opening curly brace */ HasCurly = ConsumeLCurly (); @@ -387,14 +389,22 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Initialize the array members */ Count = 0; while (CurTok.Tok != TOK_RCURLY) { - /* Flexible array members may not be initialized within - ** an array (because the size of each element may differ - ** otherwise). + /* Flexible array members cannot be initialized within an array. + ** (Otherwise the size of each element may differ.) */ ParseInitInternal (ElementType, Braces, 0); ++Count; - if (CurTok.Tok != TOK_COMMA) + if (CurTok.Tok != TOK_COMMA) { break; + } + + if (!HasCurly && ElementCount > 0 && Count >= ElementCount) { + /* If the array is initialized without enclosing curly braces, + ** it only accepts how many elements initializers up to its + ** count of elements, leaving any following initializers out. + */ + break; + } NextToken (); } @@ -491,7 +501,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) Error ("Excess elements in %s initializer", GetBasicTypeName (T)); SkipInitializer (HasCurly); } - return SI.Offs; + break; } /* Check for special members that don't consume the initializer */ diff --git a/test/val/bug2135.c b/test/val/bug2135.c new file mode 100644 index 000000000..1da0d2316 --- /dev/null +++ b/test/val/bug2135.c @@ -0,0 +1,47 @@ +/* Bug #2135 - Compound initialization consumes wrong amount of initializers with omitted +** enclosing curly braces when an array/struct/union to initialize is itself +** a member/element of a struct/union/array. +*/ + +#include <stdint.h> +#include <stdio.h> + +struct s { + union { + int8_t a[2][2]; + char c[sizeof (int8_t) * 2 * 2 + sizeof (int16_t) * 4]; + }; + int16_t b[4]; +}; +struct s x = { 1, 2, 3, 4, 5, 6 }; +struct s y = { {{{1, 2}, {3, 4}}}, {5, 6} }; + +unsigned failures; + +int main(void) +{ + unsigned i, j; + + for (i = 0; i < 2; ++i) + { + for (j = 0; j < 2; ++j) + { + if (x.a[i][j] != y.a[i][j]) + { + ++failures; + printf("x.a[%u][%u] = %d\n, expected %d\n", i, j, x.a[i][j], y.a[i][j]); + } + } + } + + for (i = 0; i < 4; ++i) + { + if (x.b[i] != y.b[i]) + { + ++failures; + printf("x.b[%u] = %d\n, expected %d\n", i, x.b[i], y.b[i]); + } + } + + return failures; +} From e3bc143f27caef2cdd761f43a2f2b2d9e4a87637 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 13 Sep 2023 08:18:31 +0200 Subject: [PATCH 304/520] Another two bytes saved --- libsrc/apple2/ser/a2.ssc.s | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 13f903597..5b81128f9 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -330,8 +330,12 @@ SER_GET: beq :+ cmp #63 bcc :+ +.if (.cpu .bitand CPU_ISET_65C02) + stz Stopped +.else lda #$00 sta Stopped +.endif lda RtsOff ora #%00001000 sta ACIA_CMD,x From b81e5d00f205efc8d19a11841b5be16da109349d Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Fri, 22 Sep 2023 16:43:03 +0200 Subject: [PATCH 305/520] Update funcref cx16 --- doc/funcref.sgml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 067736e7c..524818b19 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -334,9 +334,11 @@ function. <itemize> <!-- <item><ref id="get_numbanks" name="get_numbanks"> --> -<!-- <item><ref id="get_ostype" name="get_ostype"> --> +<item><ref id="get_ostype" name="get_ostype"> <!-- <item><ref id="get_tv" name="get_tv"> --> <!-- <item><ref id="set_tv" name="set_tv"> --> +<!-- <item><ref id="vera_layer_enable" name="vera_layer_enable"> --> +<!-- <item><ref id="vera_sprites_enable" name="vera_sprites_enable"> --> <item><ref id="videomode" name="videomode"> <!-- <item><ref id="vpeek" name="vpeek"> --> <!-- <item><ref id="vpoke" name="vpoke"> --> @@ -3971,7 +3973,8 @@ be used in presence of a prototype. <descrip> <tag/Function/The function returns the operating system, the program runs on. <tag/Header/<tt/<ref id="apple2.h" name="apple2.h">, -<ref id="atari.h" name="atari.h">, <ref id="c64.h" name="c64.h">/ +<ref id="atari.h" name="atari.h">, <ref id="c64.h" name="c64.h">, +<ref id="cx16.h" name="cx16.h">/ <tag/Declaration/<tt/unsigned char get_ostype (void);/ <tag/Description/<tt/get_ostype/ is machine dependent and does not exist for all supported targets. If it exists, it returns a number that identifies the From d83354a8dd4887690535b649189e9bb1d98093ae Mon Sep 17 00:00:00 2001 From: Evgeny Vrublevsky <me@veg.by> Date: Sat, 23 Sep 2023 14:16:21 +0300 Subject: [PATCH 306/520] Fix default VS project settings. --- src/cc65.props | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc65.props b/src/cc65.props index ef5a37fea..19fb0b646 100644 --- a/src/cc65.props +++ b/src/cc65.props @@ -21,6 +21,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;WINVER=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="$(MSBuildProjectName) != 'common'">common</AdditionalIncludeDirectories> + <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <AdditionalDependencies Condition="$(MSBuildProjectName) != 'common'">$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> @@ -42,13 +43,13 @@ <!-- Release settings. --> <PropertyGroup Condition="'$(Configuration)'=='Release'"> <LinkIncremental>false</LinkIncremental> - <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> <ClCompile> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <Optimization>MaxSpeed</Optimization> + <WholeProgramOptimization>true</WholeProgramOptimization> <BufferSecurityCheck>false</BufferSecurityCheck> <ControlFlowGuard>false</ControlFlowGuard> </ClCompile> @@ -57,6 +58,7 @@ <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + <AdditionalOptions>/EMITTOOLVERSIONINFO:NO /NOVCFEATURE /NOCOFFGRPINFO %(AdditionalOptions)</AdditionalOptions> </Link> </ItemDefinitionGroup> </Project> From 9b9e955eb11e0e86b4dfc73a81005d61905b773a Mon Sep 17 00:00:00 2001 From: Evgeny Vrublevsky <me@veg.by> Date: Sat, 23 Sep 2023 14:16:50 +0300 Subject: [PATCH 307/520] Add basic .editorconfig. --- .editorconfig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..3920829e6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true +guidelines = 80, 120 + +[*.{c,h}] +cpp_new_line_before_open_brace_block=same_line +cpp_new_line_before_open_brace_function=new_line +cpp_space_before_function_open_parenthesis=insert +cpp_new_line_before_else=false From 148942ff1dce082928f66e3a510b33def4817b44 Mon Sep 17 00:00:00 2001 From: Evgeny Vrublevsky <me@veg.by> Date: Sat, 23 Sep 2023 14:25:22 +0300 Subject: [PATCH 308/520] Don't use hardcoded Visual Studio installation path in msbuild.cmd. --- src/msbuild.cmd | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/msbuild.cmd b/src/msbuild.cmd index 2e1821f0a..092bfb578 100644 --- a/src/msbuild.cmd +++ b/src/msbuild.cmd @@ -1,18 +1,23 @@ @echo off +setlocal -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 +where msbuild.exe 1>nul 2>&1 && goto :ready -echo Error: VsDevCmd.bat not found! -goto:eof +set VSWHERE_PATH=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe +if not exist "%VSWHERE_PATH%" set VSWHERE_PATH=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe +if not exist "%VSWHERE_PATH%" goto :error +for /f "usebackq delims=#" %%a in (`"%VSWHERE_PATH%" -latest -property installationPath`) do set VSDEVCMD_PATH=%%a\Common7\Tools\VsDevCmd.bat +if not exist "%VSDEVCMD_PATH%" goto :error +set VSCMD_SKIP_SENDTELEMETRY=1 +call "%VSDEVCMD_PATH%" -no_logo -startdir=none -:vs2017 -call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" -goto run +where msbuild.exe 1>nul 2>&1 && goto :ready -:vs2019 -call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" -goto run +:error + +echo Error: Can't find MSBuild. +exit /b 1 + +:ready -:run msbuild.exe %* From 4ff917816e5b0895b2525d18d35f9f7a9a57eed3 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 26 Sep 2023 22:27:17 +0200 Subject: [PATCH 309/520] Apple2: Always copy cmdline Otherwise, the last exec() cmdline lingers around for the next exec("file", NULL). --- libsrc/apple2/exec.s | 2 -- 1 file changed, 2 deletions(-) diff --git a/libsrc/apple2/exec.s b/libsrc/apple2/exec.s index d5cbf8788..27a6487bd 100644 --- a/libsrc/apple2/exec.s +++ b/libsrc/apple2/exec.s @@ -213,8 +213,6 @@ source: jsr $BF00 bcs error ; Check for cmdline handling - lda $0100 ; Valid cmdline? - beq jump ; No, jump to program right away ldx file_type ; SYS file? bne system ; Yes, check for startup filename From df4902157aebc48a86bac266df01efe8143179a0 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sat, 30 Sep 2023 11:09:27 +0200 Subject: [PATCH 310/520] Cleanup coding style a bit --- libsrc/apple2/ser/a2.ssc.s | 235 ++++++++++++++++++------------------- 1 file changed, 114 insertions(+), 121 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 5b81128f9..f81155543 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -38,8 +38,8 @@ .endif ; Driver signature - .byte $73, $65, $72 ; "ser" - .byte SER_API_VERSION ; Serial API version number + .byte $73, $65, $72 ; "ser" + .byte SER_API_VERSION ; Serial API version number ; Library reference .addr $0000 @@ -59,16 +59,16 @@ ; I/O definitions .if (.cpu .bitand CPU_ISET_65C02) -ACIA = $C088 +ACIA := $C088 .else Offset = $8F ; Move 6502 false read out of I/O to page $BF -ACIA = $C088-Offset +ACIA := $C088-Offset .endif -ACIA_DATA = ACIA+0 ; Data register -ACIA_STATUS = ACIA+1 ; Status register -ACIA_CMD = ACIA+2 ; Command register -ACIA_CTRL = ACIA+3 ; Control register +ACIA_DATA := ACIA+0 ; Data register +ACIA_STATUS := ACIA+1 ; Status register +ACIA_CMD := ACIA+2 ; Command register +ACIA_CTRL := ACIA+3 ; Control register ;---------------------------------------------------------------------------- ; Global variables @@ -77,16 +77,17 @@ ACIA_CTRL = ACIA+3 ; Control register RecvHead: .res 1 ; Head of receive buffer RecvTail: .res 1 ; Tail of receive buffer -RecvFreeCnt: .res 1 ; Number of bytes in receive buffer +RecvFreeCnt: .res 1 ; Number of free bytes in receive buffer SendHead: .res 1 ; Head of send buffer SendTail: .res 1 ; Tail of send buffer -SendFreeCnt: .res 1 ; Number of bytes in send buffer +SendFreeCnt: .res 1 ; Number of free bytes in send buffer Stopped: .res 1 ; Flow-stopped flag -RtsOff: .res 1 ; +RtsOff: .res 1 ; Cached value of command register with + ; flow stopped -RecvBuf: .res 256 ; Receive buffers: 256 bytes -SendBuf: .res 256 ; Send buffers: 256 bytes +RecvBuf: .res 256 ; Receive buffer: 256 bytes +SendBuf: .res 256 ; Send buffer: 256 bytes Index: .res 1 ; I/O register index @@ -96,8 +97,9 @@ Slot: .byte $02 ; Default to SSC in slot 2 .rodata - ; Tables used to translate RS232 params into register values -BaudTable: ; bit7 = 1 means setting is invalid +BaudTable: ; Table used to translate RS232 baudrate param + ; into control register value + ; bit7 = 1 means setting is invalid .byte $FF ; SER_BAUD_45_5 .byte $01 ; SER_BAUD_50 .byte $02 ; SER_BAUD_75 @@ -118,49 +120,55 @@ BaudTable: ; bit7 = 1 means setting is invalid .byte $FF ; SER_BAUD_57600 .byte $FF ; SER_BAUD_115200 .byte $FF ; SER_BAUD_230400 -BitTable: + +BitTable: ; Table used to translate RS232 databits param + ; into control register value .byte $60 ; SER_BITS_5 .byte $40 ; SER_BITS_6 .byte $20 ; SER_BITS_7 .byte $00 ; SER_BITS_8 -StopTable: + +StopTable: ; Table used to translate RS232 stopbits param + ; into control register value .byte $00 ; SER_STOP_1 .byte $80 ; SER_STOP_2 -ParityTable: + +ParityTable: ; Table used to translate RS232 parity param + ; into command register value .byte $00 ; SER_PAR_NONE .byte $20 ; SER_PAR_ODD .byte $60 ; SER_PAR_EVEN .byte $A0 ; SER_PAR_MARK .byte $E0 ; SER_PAR_SPACE - ; Check five bytes at known positions on the - ; slot's firmware to make sure this is an SSC - ; (or Apple //c comm port) firmware that drives - ; an ACIA 6551 chip. - ; - ; The SSC firmware and the Apple //c(+) comm - ; port firmware all begin with a BIT instruction. - ; The IIgs, on the other hand, has a - ; Zilog Z8530 chip and its firmware starts with - ; a SEP instruction. We don't want to load this - ; driver on the IIgs' serial port. We'll - ; differentiate the firmware on this byte. - ; - ; The next four bytes we check are the Pascal - ; Firmware Protocol Bytes that identify a - ; serial card. Those are the same bytes for - ; SSC firmwares, Apple //c firmwares and IIgs - ; Zilog Z8530 firmwares - which is the reason - ; we have to check for the firmware's first - ; instruction too. - -IdOfsTable: +IdOfsTable: ; Table of bytes positions, used to check five + ; specific bytes on the slot's firmware to make + ; sure this is an SSC (or Apple //c comm port) + ; firmware that drives an ACIA 6551 chip. + ; + ; The SSC firmware and the Apple //c(+) comm + ; port firmware all begin with a BIT instruction. + ; The IIgs, on the other hand, has a + ; Zilog Z8530 chip and its firmware starts with + ; a SEP instruction. We don't want to load this + ; driver on the IIgs' serial port. We'll + ; differentiate the firmware on this byte. + ; + ; The next four bytes we check are the Pascal + ; Firmware Protocol Bytes that identify a + ; serial card. Those are the same bytes for + ; SSC firmwares, Apple //c firmwares and IIgs + ; Zilog Z8530 firmwares - which is the reason + ; we have to check for the firmware's first + ; instruction too. .byte $00 ; First instruction .byte $05 ; Pascal 1.0 ID byte .byte $07 ; Pascal 1.0 ID byte .byte $0B ; Pascal 1.1 generic signature byte .byte $0C ; Device signature byte -IdValTable: + +IdValTable: ; Table of expected values for the five checked + ; bytes .byte $2C ; BIT .byte $38 ; ID Byte 0 (from Pascal 1.0), fixed .byte $18 ; ID Byte 1 (from Pascal 1.0), fixed @@ -193,12 +201,10 @@ SER_CLOSE: ldx Index ; Check for open port beq :+ - ; Deactivate DTR and disable 6551 interrupts - lda #%00001010 + lda #%00001010 ; Deactivate DTR and disable 6551 interrupts sta ACIA_CMD,x - ; Done, return an error code -: lda #SER_ERR_OK +: lda #SER_ERR_OK ; Done, return an error code .assert SER_ERR_OK = 0, error tax stx Index ; Mark port as closed @@ -215,98 +221,96 @@ SER_OPEN: ora Slot sta ptr2+1 - ; Check Pascal 1.1 Firmware Protocol ID bytes -: ldy IdOfsTable,x +: ldy IdOfsTable,x ; Check Pascal 1.1 Firmware Protocol ID bytes lda IdValTable,x cmp (ptr2),y - bne NoDevice + bne NoDev inx cpx #IdTableLen bcc :- - ; Convert slot to I/O register index - lda Slot + lda Slot ; Convert slot to I/O register index asl asl asl asl .if .not (.cpu .bitand CPU_ISET_65C02) - adc #Offset ; Assume carry to be clear + adc #Offset ; Assume carry to be clear .endif tax ; Check if the handshake setting is valid - ldy #SER_PARAMS::HANDSHAKE ; Handshake + ldy #SER_PARAMS::HANDSHAKE lda (ptr1),y - cmp #SER_HS_HW ; This is all we support - bne InvParam + cmp #SER_HS_HW ; This is all we support + bne InvParm - ; Initialize buffers - ldy #$00 + ldy #$00 ; Initialize buffers sty Stopped sty RecvHead sty RecvTail sty SendHead sty SendTail - dey ; Y = 255 + dey ; Y = 255 sty RecvFreeCnt sty SendFreeCnt ; Set the value for the control register, which contains stop bits, ; word length and the baud rate. ldy #SER_PARAMS::BAUDRATE - lda (ptr1),y ; Baudrate index + lda (ptr1),y ; Baudrate index tay - lda BaudTable,y ; Get 6551 value - bmi InvBaud ; Branch if rate not supported + lda BaudTable,y ; Get 6551 value + bmi InvBaud ; Branch if rate not supported sta tmp1 - ldy #SER_PARAMS::DATABITS ; Databits - lda (ptr1),y + ldy #SER_PARAMS::DATABITS + lda (ptr1),y ; Databits index tay - lda BitTable,y + lda BitTable,y ; Get 6551 value ora tmp1 sta tmp1 - ldy #SER_PARAMS::STOPBITS ; Stopbits - lda (ptr1),y + ldy #SER_PARAMS::STOPBITS + lda (ptr1),y ; Stopbits index tay - lda StopTable,y + lda StopTable,y ; Get 6551 value ora tmp1 - ora #%00010000 ; Receiver clock source = baudrate + ora #%00010000 ; Set receiver clock source = baudrate sta ACIA_CTRL,x ; Set the value for the command register. We remember the base value ; in RtsOff, since we will have to manipulate ACIA_CMD often. - ldy #SER_PARAMS::PARITY ; Parity - lda (ptr1),y + ldy #SER_PARAMS::PARITY + lda (ptr1),y ; Parity index tay - lda ParityTable,y - ora #%00000001 ; DTR active - sta RtsOff - ora #%00001000 ; Enable receive interrupts + lda ParityTable,y ; Get 6551 value + + ora #%00000001 ; Set DTR active + sta RtsOff ; Store value to easily handle flow control later + ora #%00001000 ; Enable receive interrupts (RTS low) sta ACIA_CMD,x ; Done - stx Index ; Mark port as open + stx Index ; Mark port as open lda #SER_ERR_OK .assert SER_ERR_OK = 0, error tax rts ; Device (hardware) not found -NoDevice:lda #SER_ERR_NO_DEVICE - ldx #0 ; return value is char +NoDev: lda #SER_ERR_NO_DEVICE + ldx #$00 ; return value is char rts ; Invalid parameter -InvParam:lda #SER_ERR_INIT_FAILED - ldx #0 ; return value is char +InvParm:lda #SER_ERR_INIT_FAILED + ldx #$00 ; return value is char rts ; Baud rate not available InvBaud:lda #SER_ERR_BAUD_UNAVAIL - ldx #0 ; return value is char + ldx #$00 ; return value is char rts ;---------------------------------------------------------------------------- @@ -317,21 +321,19 @@ InvBaud:lda #SER_ERR_BAUD_UNAVAIL SER_GET: ldx Index - ; Check for buffer empty - lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; Check for buffer empty cmp #$FF bne :+ lda #SER_ERR_NO_DATA - ldx #0 ; return value is char + ldx #$00 ; return value is char rts - ; Check for flow stopped & enough free: release flow control -: ldy Stopped ; (34) +: ldy Stopped ; Check for flow stopped beq :+ - cmp #63 + cmp #63 ; Enough free? bcc :+ .if (.cpu .bitand CPU_ISET_65C02) - stz Stopped + stz Stopped ; Release flow control .else lda #$00 sta Stopped @@ -340,14 +342,13 @@ SER_GET: ora #%00001000 sta ACIA_CMD,x - ; Get byte from buffer -: ldy RecvHead ; (41) +: ldy RecvHead ; Get byte from buffer lda RecvBuf,y inc RecvHead inc RecvFreeCnt - ldx #$00 ; (59) + ldx #$00 .if (.cpu .bitand CPU_ISET_65C02) - sta (ptr1) + sta (ptr1) ; Store it for caller .else sta (ptr1,x) .endif @@ -361,29 +362,26 @@ SER_GET: SER_PUT: ldx Index - ; Try to send - ldy SendFreeCnt - cpy #$FF ; Nothing to flush + ldy SendFreeCnt ; Anything to send first? + cpy #$FF ; No beq :+ pha lda #$00 ; TryHard = false - jsr TryToSend + jsr TryToSend ; Try to flush send buffer pla - ; Reload SendFreeCnt after TryToSend - ldy SendFreeCnt + ldy SendFreeCnt ; Reload SendFreeCnt after TryToSend bne :+ lda #SER_ERR_OVERFLOW - ldx #0 ; return value is char + ldx #$00 ; return value is char rts - ; Put byte into send buffer & send -: ldy SendTail +: ldy SendTail ; Put byte into send buffer sta SendBuf,y inc SendTail dec SendFreeCnt lda #$FF ; TryHard = true - jsr TryToSend + jsr TryToSend ; Flush send buffer lda #SER_ERR_OK .assert SER_ERR_OK = 0, error tax @@ -405,26 +403,25 @@ SER_STATUS: ;---------------------------------------------------------------------------- ; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl ; specific data in ptr1, and the ioctl code in A. +; The ioctl data is the slot number to open. ; Must return an SER_ERR_xx code in a/x. SER_IOCTL: - ; Check data msb and code to be 0 - ora ptr1+1 + ora ptr1+1 ; Check data msb and code to be 0 bne :+ - ; Check data lsb to be [1..7] - ldx ptr1 + ldx ptr1 ; Check data lsb to be [1..7] beq :+ cpx #7+1 bcs :+ - stx Slot + stx Slot ; Store slot .assert SER_ERR_OK = 0, error tax rts : lda #SER_ERR_INV_IOCTL - ldx #0 ; return value is char + ldx #$00 ; return value is char rts ;---------------------------------------------------------------------------- @@ -450,8 +447,7 @@ SER_IRQ: bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) - ; Assert flow control if buffer space too low -Flow: ldx Index +Flow: ldx Index ; Assert flow control if buffer space too low lda RtsOff sta ACIA_CMD,x sta Stopped @@ -464,26 +460,23 @@ Done: rts TryToSend: sta tmp1 ; Remember tryHard flag NextByte: - lda SendFreeCnt - cmp #$FF - beq Quit ; Bail out + lda SendFreeCnt ; Is there anything to send? This can happen if + cmp #$FF ; we got interrupted by RX while sending, and + beq Quit ; flow control was asserted. - ; Check for flow stopped -Again: lda Stopped - bne Quit ; Bail out +Again: lda Stopped ; Is flow stopped? + bne Quit ; Yes, Bail out - ; Check that ACIA is ready to send - lda ACIA_STATUS,x + lda ACIA_STATUS,x ; Check that ACIA is ready to send and #$10 - bne Send + bne Send ; It is! bit tmp1 ; Keep trying if must try hard bmi Again Quit: rts - ; Send byte and try again -Send: ldy SendHead +Send: ldy SendHead ; Get first byte to send lda SendBuf,y - sta ACIA_DATA,x + sta ACIA_DATA,x ; Send it inc SendHead inc SendFreeCnt - jmp NextByte + jmp NextByte ; And try next one From 330b1ab3f90af82b79c8c2a709f638304338cfd2 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 3 Oct 2023 13:36:04 +0200 Subject: [PATCH 311/520] Remove useless comments --- libsrc/apple2/ser/a2.ssc.s | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index f81155543..942adad82 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -300,17 +300,17 @@ SER_OPEN: ; Device (hardware) not found NoDev: lda #SER_ERR_NO_DEVICE - ldx #$00 ; return value is char + ldx #$00 rts ; Invalid parameter InvParm:lda #SER_ERR_INIT_FAILED - ldx #$00 ; return value is char + ldx #$00 rts ; Baud rate not available InvBaud:lda #SER_ERR_BAUD_UNAVAIL - ldx #$00 ; return value is char + ldx #$00 rts ;---------------------------------------------------------------------------- @@ -325,7 +325,7 @@ SER_GET: cmp #$FF bne :+ lda #SER_ERR_NO_DATA - ldx #$00 ; return value is char + ldx #$00 rts : ldy Stopped ; Check for flow stopped @@ -373,7 +373,7 @@ SER_PUT: ldy SendFreeCnt ; Reload SendFreeCnt after TryToSend bne :+ lda #SER_ERR_OVERFLOW - ldx #$00 ; return value is char + ldx #$00 rts : ldy SendTail ; Put byte into send buffer @@ -421,7 +421,7 @@ SER_IOCTL: rts : lda #SER_ERR_INV_IOCTL - ldx #$00 ; return value is char + ldx #$00 rts ;---------------------------------------------------------------------------- From e16a5e0dbefb90325ed043f0d2edd795e2d97056 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 7 Sep 2023 21:30:01 +0200 Subject: [PATCH 312/520] Add Apple IIgs serial driver --- doc/apple2.sgml | 27 +- doc/apple2enh.sgml | 27 +- include/apple2.h | 1 + include/apple2enh.h | 1 + libsrc/apple2/ser/a2.gs.s | 692 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 744 insertions(+), 4 deletions(-) create mode 100644 libsrc/apple2/ser/a2.gs.s diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 0d3ec34e5..a3ddb1d39 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -427,8 +427,12 @@ The names in the parentheses denote the symbols to be used for static linking of <descrip> <tag><tt/a2.ssc.ser (a2_ssc_ser)/</tag> - Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Driver for the Apple II Super Serial Card. + They are extension cards for the II, II+, IIe, and the Apple //c and //c+ have + the same hardware and firmware integrated. + It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and + does interrupt driven receives. Speeds faster than 9600 baud aren't reachable + because the ROM and ProDOS IRQ handlers are too slow. 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. @@ -438,6 +442,25 @@ The names in the parentheses denote the symbols to be used for static linking of succeeds for all Apple II slots, but <tt/ser_open()/ fails with <tt/SER_ERR_NO_DEVICE/ if there's no SSC firmware found in the selected slot. + In the Apple //c and //c+, slot 1 is the printer port, and slot 2 is the modem + port. + + Never call <tt/ser_apple2_slot()/ after <tt/ser_open()/. + + <tag><tt/a2.gs.ser (a2_gs_ser)/</tag> + Driver for the Apple IIgs serial ports (printer and modem). + It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and + does interrupt driven receives. Speeds faster than 9600 baud aren't reachable + because the ROM and ProDOS IRQ handlers are too slow. + Note that transmits are not interrupt driven, and the transceiver blocks if + the receiver asserts flow control because of a full buffer. + + The driver defaults to opening the modem port. Calling <tt/ser_apple2_slot()/ + prior to <tt/ser_open()/ allows to select the printer port (1) or the modem + port (0). + + Never call <tt/ser_apple2_slot()/ after <tt/ser_open()/. + </descrip><p> diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 932aae52d..6bc6a3adf 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -428,8 +428,12 @@ The names in the parentheses denote the symbols to be used for static linking of <descrip> <tag><tt/a2e.ssc.ser (a2e_ssc_ser)/</tag> - Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Driver for the Apple II Super Serial Card. + They are extension cards for the II, II+, IIe, and the Apple //c and //c+ have + the same hardware and firmware integrated. + It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and + does interrupt driven receives. Speeds faster than 9600 baud aren't reachable + because the ROM and ProDOS IRQ handlers are too slow. 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. @@ -439,6 +443,25 @@ The names in the parentheses denote the symbols to be used for static linking of succeeds for all Apple II slots, but <tt/ser_open()/ fails with <tt/SER_ERR_NO_DEVICE/ if there's no SSC firmware found in the selected slot. + In the Apple //c and //c+, slot 1 is the printer port, and slot 2 is the modem + port. + + Never call <tt/ser_apple2_slot()/ after <tt/ser_open()/. + + <tag><tt/a2e.gs.ser (a2e_gs_ser)/</tag> + Driver for the Apple IIgs serial ports (printer and modem). + It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and + does interrupt driven receives. Speeds faster than 9600 baud aren't reachable + because the ROM and ProDOS IRQ handlers are too slow. + Note that transmits are not interrupt driven, and the transceiver blocks if + the receiver asserts flow control because of a full buffer. + + The driver defaults to opening the modem port. Calling <tt/ser_apple2_slot()/ + prior to <tt/ser_open()/ allows to select the printer port (1) or the modem + port (0). + + Never call <tt/ser_apple2_slot()/ after <tt/ser_open()/. + </descrip><p> diff --git a/include/apple2.h b/include/apple2.h index 9f644bc97..8b9a3e0ea 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -172,6 +172,7 @@ extern void a2_auxmem_emd[]; extern void a2_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void a2_stdmou_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void a2_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ +extern void a2_gs_ser[]; /* IIgs serial driver */ extern void a2_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2_lo_tgi[]; #endif diff --git a/include/apple2enh.h b/include/apple2enh.h index bfe5cdb18..3989d0b8d 100644 --- a/include/apple2enh.h +++ b/include/apple2enh.h @@ -100,6 +100,7 @@ extern void a2e_auxmem_emd[]; extern void a2e_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void a2e_stdmou_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void a2e_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ +extern void a2e_gs_ser[]; /* IIgs serial driver */ extern void a2e_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2e_lo_tgi[]; diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s new file mode 100644 index 000000000..5cb3ea322 --- /dev/null +++ b/libsrc/apple2/ser/a2.gs.s @@ -0,0 +1,692 @@ +; +; Serial driver for the Apple IIgs Zilog Z8530. +; +; Colin Leroy-Mira <colin@colino.net>, 2023 +; +; This software is licensed under the same license as cc65, +; the zlib license (see LICENSE file). +; +; Documentation from http://www.applelogic.org/files/Z8530UM.pdf (pages +; referred to where applicable) +; and https://gswv.apple2.org.za/a2zine/Utils/Z8530_SCCsamples_info.txt + + + + .setcpu "65816" + + .include "zeropage.inc" + .include "ser-kernel.inc" + .include "ser-error.inc" + + .macpack module + +; ------------------------------------------------------------------------ +; Header. Includes jump table + + .ifdef __APPLE2ENH__ + module_header _a2e_gs_ser + .else + module_header _a2_gs_ser + .endif + + ; Driver signature + .byte $73, $65, $72 ; "ser" + .byte SER_API_VERSION ; Serial API version number + + ; Library reference + .addr $0000 + + ; Jump table + .addr SER_INSTALL + .addr SER_UNINSTALL + .addr SER_OPEN + .addr SER_CLOSE + .addr SER_GET + .addr SER_PUT + .addr SER_STATUS + .addr SER_IOCTL + .addr SER_IRQ + +;---------------------------------------------------------------------------- +; Global variables + + .bss + +RecvHead: .res 1 ; Head of receive buffer +RecvTail: .res 1 ; Tail of receive buffer +RecvFreeCnt: .res 1 ; Number of bytes in receive buffer +SendHead: .res 1 ; Head of send buffer +SendTail: .res 1 ; Tail of send buffer +SendFreeCnt: .res 1 ; Number of bytes in send buffer + +Stopped: .res 1 ; Flow-stopped flag +RtsOff: .res 1 + +RecvBuf: .res 256 ; Receive buffers: 256 bytes +SendBuf: .res 256 ; Send buffers: 256 bytes + + .data + +Slot: .byte $00 ; 2 when opened +Channel: .byte $00 ; Channel B by default +CurChanIrqFlags:.byte INTR_PENDING_RX_EXT_B + +SerFlagOrig: .byte $00 + +; Tables used to translate RS232 params into register values +; (Ref page 5-18 and 5-19) +BaudLowTable: + .byte $7E ; SER_BAUD_300 + .byte $5E ; SER_BAUD_1200 + .byte $2E ; SER_BAUD_2400 + .byte $16 ; SER_BAUD_4800 + .byte $0A ; SER_BAUD_9600 + .byte $04 ; SER_BAUD_19200 + .byte $01 ; SER_BAUD_38400 + .byte $00 ; SER_BAUD_57600 + +BaudHighTable: + .byte $01 ; SER_BAUD_300 + .byte $00 ; SER_BAUD_1200 + .byte $00 ; SER_BAUD_2400 + .byte $00 ; SER_BAUD_4800 + .byte $00 ; SER_BAUD_9600 + .byte $00 ; SER_BAUD_19200 + .byte $00 ; SER_BAUD_38400 + .byte $00 ; SER_BAUD_57600 + +RxBitTable: + .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) + .byte %10000000 ; SER_BITS_6 (Ref page 5-7) + .byte %01000000 ; SER_BITS_7 + .byte %11000000 ; SER_BITS_8 +TxBitTable: + .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) + .byte %01000000 ; SER_BITS_6 (Ref page 5-9) + .byte %00100000 ; SER_BITS_7 + .byte %01100000 ; SER_BITS_8 + + .rodata + +BaudTable: ; bit7 = 1 means setting is invalid + ; Otherwise refers to the index in + ; Baud(Low/High)Table + .byte $FF ; SER_BAUD_45_5 + .byte $FF ; SER_BAUD_50 + .byte $FF ; SER_BAUD_75 + .byte $FF ; SER_BAUD_110 + .byte $FF ; SER_BAUD_134_5 + .byte $FF ; SER_BAUD_150 + .byte $00 ; SER_BAUD_300 + .byte $FF ; SER_BAUD_600 + .byte $01 ; SER_BAUD_1200 + .byte $FF ; SER_BAUD_1800 + .byte $02 ; SER_BAUD_2400 + .byte $FF ; SER_BAUD_3600 + .byte $03 ; SER_BAUD_4800 + .byte $FF ; SER_BAUD_7200 + .byte $04 ; SER_BAUD_9600 + .byte $05 ; SER_BAUD_19200 + .byte $06 ; SER_BAUD_38400 + .byte $07 ; SER_BAUD_57600 + .byte $FF ; SER_BAUD_115200 + .byte $FF ; SER_BAUD_230400 + +StopTable: + .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) + .byte %00001100 ; SER_STOP_2 (Ref page 5-8) +ParityTable: + .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) + .byte %00000001 ; SER_PAR_ODD (Ref page 5-8) + .byte %00000011 ; SER_PAR_EVEN + .byte $FF ; SER_PAR_MARK + .byte $FF ; SER_PAR_SPACE +IdOfsTable: + .byte $00 ; First firmware instruction + .byte $05 ; Pascal 1.0 ID byte + .byte $07 ; Pascal 1.0 ID byte + .byte $0B ; Pascal 1.1 generic signature byte + .byte $0C ; Device signature byte +IdValTable: + .byte $E2 ; SEP instruction + .byte $38 ; Fixed + .byte $18 ; Fixed + .byte $01 ; Fixed + .byte $31 ; Serial or parallel I/O card type 1 + +IdTableLen = * - IdValTable + +; ------------------------------------------------------------------------ +; Addresses + +ZILOG_BASE := $C200 + +SCCAREG := $C039 +SCCBREG := $C038 +SCCADATA := $C03B +SCCBDATA := $C03A + +; We're supposed to get SerFlag's address using GetAddr on ROMs 1 and 3. +; (https://archive.org/details/IIgs_2523018_SCC_Access, page 9) +; But, it's the same value as on ROM0. As we don't expect a ROM 4 anytime +; soon with a different value, let's keep it simple. + +SER_FLAG := $E10104 + +; ------------------------------------------------------------------------ +; Write registers, read registers, and values that interest us + +WR_INIT_CTRL = 0 +RR_INIT_STATUS = 0 +INIT_CTRL_CLEAR_EIRQ = %00010000 +INIT_CTRL_CLEAR_ERR = %00110000 +INIT_STATUS_READY = %00000100 +INIT_STATUS_RTS = %00100000 + +WR_TX_RX_MODE_CTRL = 1 +TX_RX_MODE_OFF = %00000000 +TX_RX_MODE_RXIRQ = %00010001 + +WR_RX_CTRL = 3 ; (Ref page 5-7) +RR_RX_STATUS = 9 ; Corresponding status register +RX_CTRL_ON = %00000001 ; ORed, Rx enabled +RX_CTRL_OFF = %11111110 ; ANDed,Rx disabled + +WR_TX_RX_CTRL = 4 +RR_TX_RX_STATUS = 4 +TX_RX_CLOCK_MUL = %01000000 ; Clock x16 (Ref page 5-8) + +WR_TX_CTRL = 5 ; (Ref page 5-9) +RR_TX_STATUS = 5 ; Corresponding status register +TX_CTRL_ON = %00001000 ; ORed, Tx enabled +TX_CTRL_OFF = %11110111 ; ANDed,Tx disabled +TX_DTR_ON = %01111111 ; ANDed,DTR ON (high) +TX_DTR_OFF = %10000000 ; ORed, DTR OFF +TX_RTS_ON = %00000010 ; ORed, RTS ON (low) +TX_RTS_OFF = %11111101 ; ANDed, RTS OFF + +WR_MASTER_IRQ_RST = 9 ; (Ref page 5-14) +MASTER_IRQ_SHUTDOWN = %00000010 ; STA'd +MASTER_IRQ_MIE_RST = %00001010 ; STA'd +MASTER_IRQ_SET = %00011001 ; STA'd + +WR_CLOCK_CTRL = 11 ; (Ref page 5-17) +CLOCK_CTRL_CH_A = %11010000 +CLOCK_CTRL_CH_B = %01010000 + +WR_BAUDL_CTRL = 12 ; (Ref page 5-18) +WR_BAUDH_CTRL = 13 ; (Ref page 5-19) + +WR_MISC_CTRL = 14 ; (Ref page 5-19) +MISC_CTRL_RATE_GEN_ON = %00000001 ; ORed +MISC_CTRL_RATE_GEN_OFF = %11111110 ; ANDed + +WR_IRQ_CTRL = 15 ; (Ref page 5-20) +IRQ_CLEANUP_EIRQ = %00001000 + +RR_SPEC_COND_STATUS = 1 ; (Ref page 5-23) +SPEC_COND_FRAMING_ERR = %01000000 +SPEC_COND_OVERRUN_ERR = %00100000 + +RR_IRQ_STATUS = 2 ; (Ref page 5-24) +IRQ_MASQ = %01110000 ; ANDed +IRQ_RX = %00100000 +IRQ_SPECIAL = %01100000 + +RR_INTR_PENDING_STATUS = 3 ; (Ref page 5-25) +INTR_PENDING_RX_EXT_A = %00101000 ; ANDed (RX or special IRQ) +INTR_PENDING_RX_EXT_B = %00000101 ; ANDed (RX or special IRQ) +INTR_IS_RX = %00100100 ; ANDed (RX IRQ, channel A or B) + +SER_FLAG_CH_A = %00111000 +SER_FLAG_CH_B = %00000111 + + .code + +; Read a register +; Input: X as channel +; Output result in A +.macro rra In,Reg + lda Reg + sta In,x + lda In,x +.endmacro + +; Write value of A to a register. +; Input: X as channel +.macro wra Out,Reg + pha + lda Reg + sta Out,x + pla + sta Out,x +.endmacro + +; Write value passed as parameter to a register. +; Input: X as channel +.macro wrr Out,Reg,Val + lda Reg + sta Out,x + lda Val + sta Out,x +.endmacro + +;---------------------------------------------------------------------------- +; SER_INSTALL: Is called after the driver is loaded into memory. If possible, +; check if the hardware is present. Must return an SER_ERR_xx code in a/x. +; +; Since we don't have to manage the IRQ vector on the Apple II, this is +; actually the same as: +; +; SER_UNINSTALL: Is called before the driver is removed from memory. +; No return code required (the driver is removed from memory on return). +; +; and: +; +; SER_CLOSE: Close the port and disable interrupts. Called without parameters. +; Must return an SER_ERR_xx code in a/x. + +SER_INSTALL: +SER_UNINSTALL: +SER_CLOSE: + ldx Slot ; Check for open port + beq :+ + ldx Channel + + ; Deactivate interrupts + sei + wrr SCCBREG, #WR_MASTER_IRQ_RST, #MASTER_IRQ_SHUTDOWN + wrr SCCBREG, #WR_TX_RX_MODE_CTRL, #TX_RX_MODE_OFF + + ; Reset SerFlag to what it was + lda SerFlagOrig + sta SER_FLAG + + lda SCCBDATA + + ; Clear external interrupts (twice) + ldy #WR_INIT_CTRL + lda #INIT_CTRL_CLEAR_EIRQ + + sty SCCBREG + sta SCCBREG + sty SCCBREG + sta SCCBREG + + ; Reset MIE for firmware use + wrr SCCBREG, #WR_MASTER_IRQ_RST, #MASTER_IRQ_MIE_RST + + ldx #$00 + stx Slot ; Mark port as closed + + cli +: txa + rts + +;---------------------------------------------------------------------------- +; SER_OPEN: A pointer to a ser_params structure is passed in ptr1. +; Must return an SER_ERR_xx code in a/x. + +SER_OPEN: + ; Check Pascal 1.1 Firmware Protocol ID bytes + ldx #$00 +Check: ldy IdOfsTable,x + lda IdValTable,x + cmp ZILOG_BASE,y + bne NoDevice + inx + cpx #IdTableLen + bcc Check + + beq HardwareFound + + ; Device (hardware) not found +NoDevice: + lda #SER_ERR_NO_DEVICE +SetupErrOut: + cli + ldx #$00 ; Return value is char + stx Slot ; Mark port closed + rts + +HardwareFound: + ; Check if the handshake setting is valid + ldy #SER_PARAMS::HANDSHAKE ; Handshake + lda (ptr1),y + cmp #SER_HS_HW ; This is all we support + beq SetupBufs + +InvParam: + lda #SER_ERR_INIT_FAILED + jmp SetupErrOut + +SetupBufs: + ; Initialize buffers + ldy #$00 + sty Stopped + sty RecvHead + sty RecvTail + sty SendHead + sty SendTail + dey ; Y = 255 + sty RecvFreeCnt + sty SendFreeCnt + + ldx Channel + + rra SCCBREG,#$00 ; Hit rr0 once to sync up + + ldy #SER_PARAMS::STOPBITS + lda (ptr1),y ; Stop bits + tay + lda StopTable,y ; Get value + + pha + ldy #SER_PARAMS::PARITY + lda (ptr1),y ; Parity bits + tay + cmp #$FF + beq InvParam + pla + ora ParityTable,y ; Get value + + ora #TX_RX_CLOCK_MUL + + wra SCCBREG,#WR_TX_RX_CTRL + + cpx #$00 + bne ClockA +ClockB: + wrr SCCBREG,#WR_CLOCK_CTRL,#CLOCK_CTRL_CH_B + + lda #INTR_PENDING_RX_EXT_B ; Store which IRQ bits we'll check + sta CurChanIrqFlags + + bra SetBaud +ClockA: + wrr SCCBREG,#WR_CLOCK_CTRL,#CLOCK_CTRL_CH_A + + lda #INTR_PENDING_RX_EXT_A ; Store which IRQ bits we'll check + sta CurChanIrqFlags + +SetBaud: + ldy #SER_PARAMS::BAUDRATE + lda (ptr1),y ; Baudrate index - cc65 value + tay + + lda BaudTable,y ; Get chip value from Low/High tables + tay + + lda BaudLowTable,y ; Get low byte + bmi InvParam ; Branch if rate not supported + + wra SCCBREG,#WR_BAUDL_CTRL + + lda BaudHighTable,y ; Get high byte + wra SCCBREG,#WR_BAUDH_CTRL + + lda #$00 + wra SCCBREG,#WR_MISC_CTRL + + ora #MISC_CTRL_RATE_GEN_ON ; Time to turn this thing on + wra SCCBREG,#WR_MISC_CTRL + + ; Final write to RX_CTRL + ldy #SER_PARAMS::DATABITS + lda (ptr1),y ; Data bits + tay + lda RxBitTable,y ; Data bits for RX + ora #RX_CTRL_ON ; Plus turn on + wra SCCBREG,#WR_RX_CTRL + + lda TxBitTable,y ; Data bits for TX + ora #TX_CTRL_ON ; Plus turn on + and #TX_DTR_ON + + sta RtsOff ; Save value for flow control + + ora #TX_RTS_ON + wra SCCBREG,#WR_TX_CTRL + + wrr SCCBREG,#WR_IRQ_CTRL,#IRQ_CLEANUP_EIRQ + + lda #WR_INIT_CTRL ; Clear ext status (write twice) + sta SCCBREG,x + lda #INIT_CTRL_CLEAR_EIRQ + sta SCCBREG,x + + lda #WR_INIT_CTRL + sta SCCBREG,x + lda #INIT_CTRL_CLEAR_EIRQ + sta SCCBREG,x + + ; Activate RX IRQ + wrr SCCBREG,#WR_TX_RX_MODE_CTRL,#TX_RX_MODE_RXIRQ + + wrr SCCBREG,#WR_MASTER_IRQ_RST,#MASTER_IRQ_SET + + lda SER_FLAG ; Get SerFlag's current value + sta SerFlagOrig ; and save it + + cpx #$00 + bne IntA +IntB: + ora #SER_FLAG_CH_B ; Inform firmware we want channel B IRQs + bra StoreFlag +IntA: + ora #SER_FLAG_CH_A ; Inform firmware we want channel A IRQs +StoreFlag: + sta SER_FLAG + + ldy #$02 ; Mark port opened + sty Slot + cli + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax + rts + +;---------------------------------------------------------------------------- +; SER_GET: Will fetch a character from the receive buffer and store it into the +; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is +; returned. + +SER_GET: + ldx Channel + + lda RecvFreeCnt ; Check for buffer empty + cmp #$FF + beq NoData + + ldy Stopped ; Check for flow stopped + beq :+ + cmp #63 ; Enough free? + bcc :+ + stz Stopped ; Release flow control + + lda RtsOff + ora #TX_RTS_ON + wra SCCBREG,#WR_TX_CTRL + +: ldy RecvHead ; Get byte from buffer + lda RecvBuf,y + inc RecvHead + inc RecvFreeCnt + sta (ptr1) + ldx #$00 + txa ; Return code = 0 + rts +NoData: + lda #SER_ERR_NO_DATA + ldx #$00 + rts + +;---------------------------------------------------------------------------- +; SER_PUT: Output character in A. +; Must return an SER_ERR_xx code in a/x. + +SER_PUT: + ldx Channel + + ldy SendFreeCnt ; Anything to send first? + iny ; Y = $FF? + beq :+ + pha + lda #$00 ; TryHard = false + jsr TryToSend + pla + +: ldy SendFreeCnt ; Do we have room to store byte? + bne :+ + lda #SER_ERR_OVERFLOW + ldx #$00 ; Return value is char + rts + +: ldy SendTail ; Put byte into send buffer & send + sta SendBuf,y + inc SendTail + dec SendFreeCnt + lda #$FF ; TryHard = true + jsr TryToSend + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax + rts + +;---------------------------------------------------------------------------- +; SER_STATUS: Return the status in the variable pointed to by ptr1. +; Must return an SER_ERR_xx code in a/x. +; We provide the read register 0, containing interesting info like +; INIT_STATUS_READY (hardware handshake status) or INIT_STATUS_RTS +; (ready to send). + +SER_STATUS: + ldx Channel + lda SCCBREG,x + ldx #$00 + sta (ptr1) + .assert SER_ERR_OK = 0, error + txa + rts + +;---------------------------------------------------------------------------- +; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl +; specific data in ptr1, and the ioctl code in A. +; Sets communication channel A or B (A = 1, B = 0) +; Must return an SER_ERR_xx code in a/x. + +SER_IOCTL: + ora ptr1+1 ; Check data msb and code to be 0 + bne :+ + + ldx ptr1 ; Check data lsb to be 0 or 1 + bmi :+ + cpx #$02 + bcs :+ + + stx Channel + .assert SER_ERR_OK = 0, error + tax + rts + +: lda #SER_ERR_INV_IOCTL + ldx #$00 ; Return value is char + rts + +;---------------------------------------------------------------------------- +; SER_IRQ: Called from the builtin runtime IRQ handler as a subroutine. All +; registers are already saved, no parameters are passed, but the carry flag +; is clear on entry. The routine must return with carry set if the interrupt +; was handled, otherwise with carry clear. + +SER_IRQ: + ldx #$00 ; IRQ status is always in A reg + rra SCCAREG,#RR_INTR_PENDING_STATUS + and CurChanIrqFlags ; Is this ours? + beq Done + + and #INTR_IS_RX ; Is this an RX irq? + beq CheckSpecial + + ldx Channel + lda SCCBDATA,x ; Get byte + ldx RecvFreeCnt ; Check if we have free space left + beq Flow ; Jump if no space in receive buffer + ldy RecvTail ; Load buffer pointer + sta RecvBuf,y ; Store received byte in buffer + inc RecvTail ; Increment buffer pointer + dec RecvFreeCnt ; Decrement free space counter + cpx #33 + bcc Flow ; Assert flow control if buffer space low + rts ; Interrupt handled (carry already set) + +CheckSpecial: + ; Always check IRQ special flags from Channel B (Ref page 5-24) + ; X is still 0 there. + rra SCCBREG,#RR_IRQ_STATUS + + and #IRQ_MASQ + cmp #IRQ_SPECIAL + beq Special + + ; Clear exint + ldx Channel + wrr SCCBREG,#WR_INIT_CTRL,#INIT_CTRL_CLEAR_EIRQ + sec + rts + +Flow: ldx Channel ; Assert flow control if buffer space too low + lda RtsOff + wra SCCBREG,#WR_TX_CTRL + sta Stopped + sec ; Interrupt handled +Done: rts + +Special: + rra SCCBREG,#RR_SPEC_COND_STATUS + tax + and #SPEC_COND_FRAMING_ERR + bne BadChar + txa + and #SPEC_COND_OVERRUN_ERR + beq BadChar + + wrr SCCBREG,#WR_INIT_CTRL,#INIT_CTRL_CLEAR_ERR + sec + rts + +BadChar: + lda SCCBDATA,x ; Remove char in error + sec + rts + +;---------------------------------------------------------------------------- +; Try to send a byte. Internal routine. A = TryHard, X = Channel + +TryToSend: + sta tmp1 ; Remember tryHard flag +Again: lda SendFreeCnt ; Anything to send? + cmp #$FF + beq Quit ; No + + lda Stopped ; Check for flow stopped + bne Quit ; Bail out if it is + + lda SCCBREG,x ; Check that we're ready to send + tay + and #INIT_STATUS_READY + bne Send + tya + and #INIT_STATUS_RTS ; Ready to send + bit tmp1 ; Keep trying if must try hard + bmi Again +Quit: rts + +Send: ldy SendHead ; Send byte + lda SendBuf,y + + sta SCCBDATA,x + + inc SendHead + inc SendFreeCnt + jmp Again ; Continue flushing TX buffer From 996a2659d51b8807af297cd246c42002c93fb472 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 3 Oct 2023 13:34:02 +0200 Subject: [PATCH 313/520] Address code review comments --- doc/apple2.sgml | 2 +- doc/apple2enh.sgml | 2 +- libsrc/apple2/ser/a2.gs.s | 139 ++++++++++++++++++++------------------ 3 files changed, 74 insertions(+), 69 deletions(-) diff --git a/doc/apple2.sgml b/doc/apple2.sgml index a3ddb1d39..063cfc71f 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -428,7 +428,7 @@ The names in the parentheses denote the symbols to be used for static linking of <tag><tt/a2.ssc.ser (a2_ssc_ser)/</tag> Driver for the Apple II Super Serial Card. - They are extension cards for the II, II+, IIe, and the Apple //c and //c+ have + The SSC is an extension card for the II, II+, IIe; the Apple //c and //c+ have the same hardware and firmware integrated. It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud aren't reachable diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 6bc6a3adf..30088521a 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -429,7 +429,7 @@ The names in the parentheses denote the symbols to be used for static linking of <tag><tt/a2e.ssc.ser (a2e_ssc_ser)/</tag> Driver for the Apple II Super Serial Card. - They are extension cards for the II, II+, IIe, and the Apple //c and //c+ have + The SSC is an extension card for the II, II+, IIe; the Apple //c and //c+ have the same hardware and firmware integrated. It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud aren't reachable diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 5cb3ea322..946778f92 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -73,86 +73,90 @@ CurChanIrqFlags:.byte INTR_PENDING_RX_EXT_B SerFlagOrig: .byte $00 -; Tables used to translate RS232 params into register values +; Tables used to translate cc65 RS232 params into register values ; (Ref page 5-18 and 5-19) BaudLowTable: - .byte $7E ; SER_BAUD_300 - .byte $5E ; SER_BAUD_1200 - .byte $2E ; SER_BAUD_2400 - .byte $16 ; SER_BAUD_4800 - .byte $0A ; SER_BAUD_9600 - .byte $04 ; SER_BAUD_19200 - .byte $01 ; SER_BAUD_38400 - .byte $00 ; SER_BAUD_57600 + .byte $7E ; SER_BAUD_300 + .byte $5E ; SER_BAUD_1200 + .byte $2E ; SER_BAUD_2400 + .byte $16 ; SER_BAUD_4800 + .byte $0A ; SER_BAUD_9600 + .byte $04 ; SER_BAUD_19200 + .byte $01 ; SER_BAUD_38400 + .byte $00 ; SER_BAUD_57600 BaudHighTable: - .byte $01 ; SER_BAUD_300 - .byte $00 ; SER_BAUD_1200 - .byte $00 ; SER_BAUD_2400 - .byte $00 ; SER_BAUD_4800 - .byte $00 ; SER_BAUD_9600 - .byte $00 ; SER_BAUD_19200 - .byte $00 ; SER_BAUD_38400 - .byte $00 ; SER_BAUD_57600 + .byte $01 ; SER_BAUD_300 + .byte $00 ; SER_BAUD_1200 + .byte $00 ; SER_BAUD_2400 + .byte $00 ; SER_BAUD_4800 + .byte $00 ; SER_BAUD_9600 + .byte $00 ; SER_BAUD_19200 + .byte $00 ; SER_BAUD_38400 + .byte $00 ; SER_BAUD_57600 RxBitTable: - .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) - .byte %10000000 ; SER_BITS_6 (Ref page 5-7) - .byte %01000000 ; SER_BITS_7 - .byte %11000000 ; SER_BITS_8 + .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) + .byte %10000000 ; SER_BITS_6 (Ref page 5-7) + .byte %01000000 ; SER_BITS_7 + .byte %11000000 ; SER_BITS_8 + TxBitTable: - .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) - .byte %01000000 ; SER_BITS_6 (Ref page 5-9) - .byte %00100000 ; SER_BITS_7 - .byte %01100000 ; SER_BITS_8 + .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) + .byte %01000000 ; SER_BITS_6 (Ref page 5-9) + .byte %00100000 ; SER_BITS_7 + .byte %01100000 ; SER_BITS_8 .rodata BaudTable: ; bit7 = 1 means setting is invalid ; Otherwise refers to the index in ; Baud(Low/High)Table - .byte $FF ; SER_BAUD_45_5 - .byte $FF ; SER_BAUD_50 - .byte $FF ; SER_BAUD_75 - .byte $FF ; SER_BAUD_110 - .byte $FF ; SER_BAUD_134_5 - .byte $FF ; SER_BAUD_150 - .byte $00 ; SER_BAUD_300 - .byte $FF ; SER_BAUD_600 - .byte $01 ; SER_BAUD_1200 - .byte $FF ; SER_BAUD_1800 - .byte $02 ; SER_BAUD_2400 - .byte $FF ; SER_BAUD_3600 - .byte $03 ; SER_BAUD_4800 - .byte $FF ; SER_BAUD_7200 - .byte $04 ; SER_BAUD_9600 - .byte $05 ; SER_BAUD_19200 - .byte $06 ; SER_BAUD_38400 - .byte $07 ; SER_BAUD_57600 - .byte $FF ; SER_BAUD_115200 - .byte $FF ; SER_BAUD_230400 + .byte $FF ; SER_BAUD_45_5 + .byte $FF ; SER_BAUD_50 + .byte $FF ; SER_BAUD_75 + .byte $FF ; SER_BAUD_110 + .byte $FF ; SER_BAUD_134_5 + .byte $FF ; SER_BAUD_150 + .byte $00 ; SER_BAUD_300 + .byte $FF ; SER_BAUD_600 + .byte $01 ; SER_BAUD_1200 + .byte $FF ; SER_BAUD_1800 + .byte $02 ; SER_BAUD_2400 + .byte $FF ; SER_BAUD_3600 + .byte $03 ; SER_BAUD_4800 + .byte $FF ; SER_BAUD_7200 + .byte $04 ; SER_BAUD_9600 + .byte $05 ; SER_BAUD_19200 + .byte $06 ; SER_BAUD_38400 + .byte $07 ; SER_BAUD_57600 + .byte $FF ; SER_BAUD_115200 + .byte $FF ; SER_BAUD_230400 StopTable: - .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) - .byte %00001100 ; SER_STOP_2 (Ref page 5-8) + .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) + .byte %00001100 ; SER_STOP_2 (Ref page 5-8) + ParityTable: - .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) - .byte %00000001 ; SER_PAR_ODD (Ref page 5-8) - .byte %00000011 ; SER_PAR_EVEN - .byte $FF ; SER_PAR_MARK - .byte $FF ; SER_PAR_SPACE + .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) + .byte %00000001 ; SER_PAR_ODD (Ref page 5-8) + .byte %00000011 ; SER_PAR_EVEN + .byte $FF ; SER_PAR_MARK + .byte $FF ; SER_PAR_SPACE + IdOfsTable: - .byte $00 ; First firmware instruction - .byte $05 ; Pascal 1.0 ID byte - .byte $07 ; Pascal 1.0 ID byte - .byte $0B ; Pascal 1.1 generic signature byte - .byte $0C ; Device signature byte + .byte $00 ; First firmware instruction + .byte $05 ; Pascal 1.0 ID byte + .byte $07 ; Pascal 1.0 ID byte + .byte $0B ; Pascal 1.1 generic signature byte + .byte $0C ; Device signature byte + IdValTable: - .byte $E2 ; SEP instruction - .byte $38 ; Fixed - .byte $18 ; Fixed - .byte $01 ; Fixed - .byte $31 ; Serial or parallel I/O card type 1 + .byte $E2 ; SEP instruction + .byte $38 ; Fixed + .byte $18 ; Fixed + .byte $01 ; Fixed + .byte $31 ; Serial or parallel I/O card type 1 IdTableLen = * - IdValTable @@ -345,7 +349,7 @@ NoDevice: lda #SER_ERR_NO_DEVICE SetupErrOut: cli - ldx #$00 ; Return value is char + ldx #$00 stx Slot ; Mark port closed rts @@ -513,8 +517,9 @@ SER_GET: inc RecvHead inc RecvFreeCnt sta (ptr1) - ldx #$00 - txa ; Return code = 0 + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts NoData: lda #SER_ERR_NO_DATA @@ -539,7 +544,7 @@ SER_PUT: : ldy SendFreeCnt ; Do we have room to store byte? bne :+ lda #SER_ERR_OVERFLOW - ldx #$00 ; Return value is char + ldx #$00 rts : ldy SendTail ; Put byte into send buffer & send @@ -590,7 +595,7 @@ SER_IOCTL: rts : lda #SER_ERR_INV_IOCTL - ldx #$00 ; Return value is char + ldx #$00 rts ;---------------------------------------------------------------------------- From 3c17c133575b894fd93d26e3d76db3374548da84 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 3 Oct 2023 17:56:20 +0200 Subject: [PATCH 314/520] Address latest comments --- libsrc/apple2/ser/a2.gs.s | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 946778f92..5d5ca5e09 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -75,8 +75,7 @@ SerFlagOrig: .byte $00 ; Tables used to translate cc65 RS232 params into register values ; (Ref page 5-18 and 5-19) -BaudLowTable: - .byte $7E ; SER_BAUD_300 +BaudLowTable: .byte $7E ; SER_BAUD_300 .byte $5E ; SER_BAUD_1200 .byte $2E ; SER_BAUD_2400 .byte $16 ; SER_BAUD_4800 @@ -85,8 +84,7 @@ BaudLowTable: .byte $01 ; SER_BAUD_38400 .byte $00 ; SER_BAUD_57600 -BaudHighTable: - .byte $01 ; SER_BAUD_300 +BaudHighTable: .byte $01 ; SER_BAUD_300 .byte $00 ; SER_BAUD_1200 .byte $00 ; SER_BAUD_2400 .byte $00 ; SER_BAUD_4800 @@ -95,14 +93,12 @@ BaudHighTable: .byte $00 ; SER_BAUD_38400 .byte $00 ; SER_BAUD_57600 -RxBitTable: - .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) +RxBitTable: .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) .byte %10000000 ; SER_BITS_6 (Ref page 5-7) .byte %01000000 ; SER_BITS_7 .byte %11000000 ; SER_BITS_8 -TxBitTable: - .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) +TxBitTable: .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) .byte %01000000 ; SER_BITS_6 (Ref page 5-9) .byte %00100000 ; SER_BITS_7 .byte %01100000 ; SER_BITS_8 @@ -133,26 +129,22 @@ BaudTable: ; bit7 = 1 means setting is invalid .byte $FF ; SER_BAUD_115200 .byte $FF ; SER_BAUD_230400 -StopTable: - .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) +StopTable: .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) .byte %00001100 ; SER_STOP_2 (Ref page 5-8) -ParityTable: - .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) +ParityTable: .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) .byte %00000001 ; SER_PAR_ODD (Ref page 5-8) .byte %00000011 ; SER_PAR_EVEN .byte $FF ; SER_PAR_MARK .byte $FF ; SER_PAR_SPACE -IdOfsTable: - .byte $00 ; First firmware instruction +IdOfsTable: .byte $00 ; First firmware instruction .byte $05 ; Pascal 1.0 ID byte .byte $07 ; Pascal 1.0 ID byte .byte $0B ; Pascal 1.1 generic signature byte .byte $0C ; Device signature byte -IdValTable: - .byte $E2 ; SEP instruction +IdValTable: .byte $E2 ; SEP instruction .byte $38 ; Fixed .byte $18 ; Fixed .byte $01 ; Fixed @@ -349,7 +341,7 @@ NoDevice: lda #SER_ERR_NO_DEVICE SetupErrOut: cli - ldx #$00 + ldx #$00 ; Promote char return value stx Slot ; Mark port closed rts @@ -523,7 +515,7 @@ SER_GET: rts NoData: lda #SER_ERR_NO_DATA - ldx #$00 + ldx #$00 ; Promote char return value rts ;---------------------------------------------------------------------------- @@ -595,7 +587,7 @@ SER_IOCTL: rts : lda #SER_ERR_INV_IOCTL - ldx #$00 + ldx #$00 ; Promote char return value rts ;---------------------------------------------------------------------------- From 9667a5f9914d1f6bb8bb5be6ea889aa9be620092 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 3 Oct 2023 17:48:54 +0200 Subject: [PATCH 315/520] Re-introduce necessary comments, wit a clearer wording --- Contributing.md | 2 +- libsrc/apple2/ser/a2.ssc.s | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Contributing.md b/Contributing.md index 3b355373c..25c6217aa 100644 --- a/Contributing.md +++ b/Contributing.md @@ -139,7 +139,7 @@ You can refer to Annex B of the ISO C99 standard ([here](https://www.open-std.or * If a function is declared to return a char-sized value, it actually must return an integer-sized value. (When cc65 promotes a returned value, it sometimes assumes that the value already is an integer.) This must be done in one of the following ways: <pre> lda #RETURN_VALUE - ldx #0 ; return value is char + ldx #0 ; Promote char return value </pre> or, if the value is 0, you can use: <pre> diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 942adad82..022ef2fd5 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -300,17 +300,17 @@ SER_OPEN: ; Device (hardware) not found NoDev: lda #SER_ERR_NO_DEVICE - ldx #$00 + ldx #$00 ; Promote char return value rts ; Invalid parameter InvParm:lda #SER_ERR_INIT_FAILED - ldx #$00 + ldx #$00 ; Promote char return value rts ; Baud rate not available InvBaud:lda #SER_ERR_BAUD_UNAVAIL - ldx #$00 + ldx #$00 ; Promote char return value rts ;---------------------------------------------------------------------------- @@ -325,7 +325,7 @@ SER_GET: cmp #$FF bne :+ lda #SER_ERR_NO_DATA - ldx #$00 + ldx #$00 ; Promote char return value rts : ldy Stopped ; Check for flow stopped @@ -373,7 +373,7 @@ SER_PUT: ldy SendFreeCnt ; Reload SendFreeCnt after TryToSend bne :+ lda #SER_ERR_OVERFLOW - ldx #$00 + ldx #$00 ; Promote char return value rts : ldy SendTail ; Put byte into send buffer @@ -421,7 +421,7 @@ SER_IOCTL: rts : lda #SER_ERR_INV_IOCTL - ldx #$00 + ldx #$00 ; Promote char return value rts ;---------------------------------------------------------------------------- From 20c3e994c6126a6d81dbe5854e9444779afe4a6f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 4 Oct 2023 21:22:04 +0800 Subject: [PATCH 316/520] Fixed compiling with pragmas in the middle of declarations or statements. --- src/cc65/compile.c | 6 - src/cc65/pragma.c | 290 +++++++++++++++++++++++++++++++-------------- src/cc65/pragma.h | 6 +- src/cc65/scanner.c | 48 +++++--- src/cc65/scanner.h | 4 +- src/cc65/stmt.c | 4 - test/val/bug2151.c | 29 +++++ 7 files changed, 271 insertions(+), 116 deletions(-) create mode 100644 test/val/bug2151.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 7bf3cd8ab..9d7fbe20a 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -111,12 +111,6 @@ static void Parse (void) continue; } - /* Check for a #pragma */ - if (CurTok.Tok == TOK_PRAGMA) { - DoPragma (); - continue; - } - /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { ParseStaticAssert (); diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 83ed362c8..21d426a26 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -41,12 +41,11 @@ #include "chartype.h" #include "segnames.h" #include "tgttrans.h" +#include "xmalloc.h" /* cc65 */ #include "codegen.h" #include "error.h" -#include "expr.h" -#include "funcdesc.h" #include "global.h" #include "litpool.h" #include "scanner.h" @@ -58,7 +57,7 @@ /*****************************************************************************/ -/* data */ +/* Data */ /*****************************************************************************/ @@ -142,6 +141,21 @@ typedef enum { PP_ERROR, } PushPopResult; +/* Effective scope of the pragma. +** This talks about how far the pragma has effects on whenever it shows up, +** even in the middle of an expression, statement or something. +*/ +typedef enum { + PES_NONE, + PES_IMM, /* No way back */ + PES_EXPR, /* Current expression/declarator */ + PES_STMT, /* Current statement/declaration */ + PES_SCOPE, /* Current scope */ + PES_FUNC, /* Current function */ + PES_FILE, /* Current file */ + PES_ALL, /* All */ +} pragma_scope_t; + /*****************************************************************************/ @@ -339,7 +353,7 @@ static void PushInt (IntStack* S, long Val) -static int BoolKeyword (StrBuf* Ident) +static int IsBoolKeyword (StrBuf* Ident) /* Check if the identifier in Ident is a keyword for a boolean value. Currently ** accepted are true/false/on/off. */ @@ -364,17 +378,92 @@ static int BoolKeyword (StrBuf* Ident) +static void ApplyPragma (int PushPop, IntStack* Stack, long Val) +/* Apply a pragma immediately */ +{ + if (PushPop > 0) { + /* Push the new value */ + PushInt (Stack, Val); + } else if (PushPop < 0) { + /* Pop the old value */ + PopInt (Stack); + } else { + /* Set the new value */ + IS_Set (Stack, Val); + } +} + + + +static void ApplySegNamePragma (pragma_t Token, int PushPop, const char* Name, unsigned char AddrSize) +/* Process a segname pragma */ +{ + segment_t Seg = SEG_CODE; + + switch (Token) { + case PRAGMA_CODE_NAME: + case PRAGMA_CODESEG: + Seg = SEG_CODE; + break; + + case PRAGMA_RODATA_NAME: + case PRAGMA_RODATASEG: + Seg = SEG_RODATA; + break; + + case PRAGMA_DATA_NAME: + case PRAGMA_DATASEG: + Seg = SEG_DATA; + break; + + case PRAGMA_BSS_NAME: + case PRAGMA_BSSSEG: + Seg = SEG_BSS; + break; + + default: + Internal ("Unknown segment name pragma: %02X", Token); + break; + } + + /* Set the new name */ + if (PushPop > 0) { + PushSegName (Seg, Name); + } else if (PushPop < 0) { + PopSegName (Seg); + } else { + SetSegName (Seg, Name); + } + + /* Set the optional address size for the segment if valid */ + if (PushPop >= 0 && AddrSize != ADDR_SIZE_INVALID) { + SetSegAddrSize (Name, AddrSize); + } + + /* BSS variables are output at the end of the compilation. Don't + ** bother to change their segment, now. + */ + if (Seg != SEG_BSS) { + g_segname (Seg); + } +} + + + /*****************************************************************************/ /* Pragma handling functions */ /*****************************************************************************/ -static void StringPragma (StrBuf* B, void (*Func) (const char*)) +static void StringPragma (pragma_scope_t Scope, StrBuf* B, void (*Func) (const char*)) /* Handle a pragma that expects a string parameter */ { StrBuf S = AUTO_STRBUF_INITIALIZER; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* We expect a string here */ if (GetString (B, &S)) { /* Call the given function with the string argument */ @@ -387,14 +476,17 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*)) -static void SegNamePragma (StrBuf* B, segment_t Seg) +static void SegNamePragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B) /* Handle a pragma that expects a segment name parameter */ { const char* Name; unsigned char AddrSize = ADDR_SIZE_INVALID; StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf A = AUTO_STRBUF_INITIALIZER; - int Push = 0; + int PushPop = 0; + + /* Unused at the moment */ + (void)Scope; /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -403,19 +495,12 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) break; case PP_PUSH: - Push = 1; + PushPop = 1; break; case PP_POP: /* Pop the old value and output it */ - PopSegName (Seg); - - /* BSS variables are output at the end of the compilation. Don't - ** bother to change their segment, now. - */ - if (Seg != SEG_BSS) { - g_segname (Seg); - } + ApplySegNamePragma (Token, -1, 0, 0); /* Done */ goto ExitPoint; @@ -454,27 +539,14 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) /* Get the address size for the segment */ AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A)); - /* Set the address size for the segment if valid */ - if (AddrSize != ADDR_SIZE_INVALID) { - SetSegAddrSize (Name, AddrSize); - } else { - Warning ("Invalid address size for segment!"); + /* Check the address size for the segment */ + if (AddrSize == ADDR_SIZE_INVALID) { + Warning ("Invalid address size for segment"); } } /* Set the new name and optionally address size */ - if (Push) { - PushSegName (Seg, Name); - } else { - SetSegName (Seg, Name); - } - - /* BSS variables are output at the end of the compilation. Don't - ** bother to change their segment, now. - */ - if (Seg != SEG_BSS) { - g_segname (Seg); - } + ApplySegNamePragma (Token, PushPop, Name, AddrSize); } else { @@ -484,13 +556,15 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) } ExitPoint: + /* Call the string buf destructor */ SB_Done (&S); SB_Done (&A); } -static void WrappedCallPragma (StrBuf* B) + +static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B) /* Handle the wrapped-call pragma */ { StrBuf S = AUTO_STRBUF_INITIALIZER; @@ -498,6 +572,9 @@ static void WrappedCallPragma (StrBuf* B) long Val; SymEntry *Entry; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -573,11 +650,14 @@ ExitPoint: -static void CharMapPragma (StrBuf* B) +static void CharMapPragma (pragma_scope_t Scope, StrBuf* B) /* Change the character map */ { long Index, C; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* Read the character index */ if (!GetNumber (B, &Index)) { return; @@ -619,7 +699,7 @@ static void CharMapPragma (StrBuf* B) -static void WarnPragma (StrBuf* B) +static void WarnPragma (pragma_scope_t Scope, StrBuf* B) /* Enable/disable warnings */ { long Val; @@ -627,6 +707,10 @@ static void WarnPragma (StrBuf* B) /* A warning name must follow */ IntStack* S = GetWarning (B); + + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + if (S == 0) { return; } @@ -680,48 +764,47 @@ static void WarnPragma (StrBuf* B) -static void FlagPragma (StrBuf* B, IntStack* Stack) +static void FlagPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack) /* Handle a pragma that expects a boolean parameter */ { StrBuf Ident = AUTO_STRBUF_INITIALIZER; long Val; - int Push; + int PushPop = 0; + /* Unused at the moment */ + (void)Scope; + (void)Token; /* Try to read an identifier */ int IsIdent = SB_GetSym (B, &Ident, 0); /* Check if we have a first argument named "pop" */ if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) { - PopInt (Stack); + /* Pop the old value and bail out */ + ApplyPragma (-1, Stack, 0); + /* No other arguments allowed */ return; } /* Check if we have a first argument named "push" */ if (IsIdent && SB_CompareStr (&Ident, "push") == 0) { - Push = 1; + PushPop = 1; if (!GetComma (B)) { goto ExitPoint; } IsIdent = SB_GetSym (B, &Ident, 0); - } else { - Push = 0; } /* Boolean argument follows */ if (IsIdent) { - Val = BoolKeyword (&Ident); + Val = IsBoolKeyword (&Ident); } else if (!GetNumber (B, &Val)) { goto ExitPoint; } - /* Set/push the new value */ - if (Push) { - PushInt (Stack, Val); - } else { - IS_Set (Stack, Val); - } + /* Add this pragma and apply it whenever appropriately */ + ApplyPragma (PushPop, Stack, Val); ExitPoint: /* Free the identifier */ @@ -730,12 +813,16 @@ ExitPoint: -static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) +static void IntPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack, long Low, long High) /* Handle a pragma that expects an int parameter */ { long Val; int Push; + /* Unused at the moment */ + (void)Scope; + (void)Token; + /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -749,7 +836,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) case PP_POP: /* Pop the old value and bail out */ - PopInt (Stack); + ApplyPragma (-1, Stack, 0); return; case PP_ERROR: @@ -772,31 +859,32 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) return; } - /* Set/push the new value */ - if (Push) { - PushInt (Stack, Val); - } else { - IS_Set (Stack, Val); - } + /* Add this pragma and apply it whenever appropriately */ + ApplyPragma (Push, Stack, Val); } -static void MakeMessage (const char* Message) +static void NoteMessagePragma (const char* Message) +/* Wrapper for printf-like Note() function protected from user-provided format +** specifiers. +*/ { Note ("%s", Message); } -static void ParsePragma (void) -/* Parse the contents of the _Pragma statement */ +static void ParsePragmaString (void) +/* Parse the contents of _Pragma */ { pragma_t Pragma; StrBuf Ident = AUTO_STRBUF_INITIALIZER; /* Create a string buffer from the string literal */ StrBuf B = AUTO_STRBUF_INITIALIZER; + + SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); /* Skip the string token */ @@ -837,111 +925,130 @@ static void ParsePragma (void) switch (Pragma) { case PRAGMA_ALIGN: - IntPragma (&B, &DataAlignment, 1, 4096); + /* TODO: PES_EXPR (PES_DECL) */ + IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096); break; case PRAGMA_ALLOW_EAGER_INLINE: - FlagPragma (&B, &EagerlyInlineFuncs); + FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs); break; case PRAGMA_BSSSEG: Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead"); /* FALLTHROUGH */ case PRAGMA_BSS_NAME: - SegNamePragma (&B, SEG_BSS); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B); break; case PRAGMA_CHARMAP: - CharMapPragma (&B); + CharMapPragma (PES_IMM, &B); break; case PRAGMA_CHECKSTACK: Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead"); /* FALLTHROUGH */ case PRAGMA_CHECK_STACK: - FlagPragma (&B, &CheckStack); + /* TODO: PES_SCOPE maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &CheckStack); break; case PRAGMA_CODESEG: Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead"); /* FALLTHROUGH */ case PRAGMA_CODE_NAME: - SegNamePragma (&B, SEG_CODE); + /* PES_FUNC is the only sensible option so far */ + SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B); break; case PRAGMA_CODESIZE: - IntPragma (&B, &CodeSizeFactor, 10, 1000); + /* PES_EXPR would be optimization nightmare */ + IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000); break; case PRAGMA_DATASEG: Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead"); /* FALLTHROUGH */ case PRAGMA_DATA_NAME: - SegNamePragma (&B, SEG_DATA); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B); break; case PRAGMA_INLINE_STDFUNCS: - FlagPragma (&B, &InlineStdFuncs); + /* TODO: PES_EXPR maybe? */ + FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs); break; case PRAGMA_LOCAL_STRINGS: - FlagPragma (&B, &LocalStrings); + /* TODO: PES_STMT or even PES_EXPR */ + FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings); break; case PRAGMA_MESSAGE: - StringPragma (&B, MakeMessage); + /* PES_IMM is the only sensible option */ + StringPragma (PES_IMM, &B, NoteMessagePragma); break; case PRAGMA_OPTIMIZE: - FlagPragma (&B, &Optimize); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_STMT, Pragma, &B, &Optimize); break; case PRAGMA_REGVARADDR: - FlagPragma (&B, &AllowRegVarAddr); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr); break; case PRAGMA_REGVARS: Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead"); /* FALLTHROUGH */ case PRAGMA_REGISTER_VARS: - FlagPragma (&B, &EnableRegVars); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars); break; case PRAGMA_RODATASEG: Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead"); /* FALLTHROUGH */ case PRAGMA_RODATA_NAME: - SegNamePragma (&B, SEG_RODATA); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B); break; case PRAGMA_SIGNEDCHARS: Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead"); /* FALLTHROUGH */ case PRAGMA_SIGNED_CHARS: - FlagPragma (&B, &SignedChars); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &SignedChars); break; case PRAGMA_STATICLOCALS: Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead"); /* FALLTHROUGH */ case PRAGMA_STATIC_LOCALS: - FlagPragma (&B, &StaticLocals); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals); break; case PRAGMA_WRAPPED_CALL: - WrappedCallPragma(&B); + /* PES_IMM is the only sensible option */ + WrappedCallPragma (PES_IMM, &B); break; case PRAGMA_WARN: - WarnPragma (&B); + /* PES_IMM is the only sensible option */ + WarnPragma (PES_IMM, &B); break; case PRAGMA_WRITABLE_STRINGS: - FlagPragma (&B, &WritableStrings); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings); break; case PRAGMA_ZPSYM: - StringPragma (&B, MakeZPSym); + /* PES_IMM is the only sensible option */ + StringPragma (PES_IMM, &B, MakeZPSym); break; default: @@ -975,10 +1082,18 @@ ExitPoint: -void DoPragma (void) -/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ConsumePragma (void) +/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma() +** operator. +*/ { - /* Skip the token itself */ + /* Skip the _Pragma token */ NextToken (); /* We expect an opening paren */ @@ -988,7 +1103,6 @@ void DoPragma (void) /* String literal */ if (CurTok.Tok != TOK_SCONST) { - /* Print a diagnostic */ Error ("String literal expected"); @@ -996,11 +1110,9 @@ void DoPragma (void) ** enclosing paren, or a semicolon. */ PragmaErrorSkip (); - } else { - - /* Parse the _Pragma statement */ - ParsePragma (); + /* Parse the pragma */ + ParsePragmaString (); } /* Closing paren needed */ diff --git a/src/cc65/pragma.h b/src/cc65/pragma.h index d1b94fa23..55e907453 100644 --- a/src/cc65/pragma.h +++ b/src/cc65/pragma.h @@ -44,8 +44,10 @@ -void DoPragma (void); -/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ +void ConsumePragma (void); +/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma() +** operator. +*/ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index bf3d9365d..4591f86a2 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -56,6 +56,7 @@ #include "ident.h" #include "input.h" #include "litpool.h" +#include "pragma.h" #include "preproc.h" #include "scanner.h" #include "standard.h" @@ -800,36 +801,30 @@ static void NumericConst (void) -void NextToken (void) +static void GetNextInputToken (void) /* Get next token from input stream */ { ident token; /* We have to skip white space here before shifting tokens, since the ** tokens and the current line info is invalid at startup and will get - ** initialized by reading the first time from the file. Remember if - ** we were at end of input and handle that later. + ** initialized by reading the first time from the file. Remember if we + ** were at end of input and handle that later. */ - int GotEOF = (SkipWhite() == 0); + int GotEOF = (SkipWhite () == 0); /* Current token is the lookahead token */ if (CurTok.LI) { ReleaseLineInfo (CurTok.LI); } - CurTok = NextTok; - /* When reading the first time from the file, the line info in NextTok, - ** which was copied to CurTok is invalid. Since the information from - ** the token is used for error messages, we must make it valid. - */ - if (CurTok.LI == 0) { - CurTok.LI = UseLineInfo (GetCurLineInfo ()); - } + /* Get the current token */ + CurTok = NextTok; /* Remember the starting position of the next token */ NextTok.LI = UseLineInfo (GetCurLineInfo ()); - /* Now handle end of input. */ + /* Now handle end of input */ if (GotEOF) { /* End of file reached */ NextTok.Tok = TOK_CEOF; @@ -859,7 +854,8 @@ void NextToken (void) if (!PPParserRunning) { /* Check for a keyword */ - if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) { + NextTok.Tok = FindKey (token); + if (NextTok.Tok != TOK_IDENT) { /* Reserved word found */ return; } @@ -1117,7 +1113,31 @@ void NextToken (void) UnknownChar (CurC); } +} + + +void NextToken (void) +/* Get next non-pragma token from input stream consuming any pragmas +** encountered. Adjacent string literal tokens will be concatenated. +*/ +{ + /* When reading the first time from the file, the line info in NextTok, + ** which will be copied to CurTok is invalid. Since the information from + ** the token is used for error messages, we must make it valid. + */ + if (NextTok.LI == 0) { + NextTok.LI = UseLineInfo (GetCurLineInfo ()); + } + + /* Read the next token from the file */ + GetNextInputToken (); + + /* Consume all pragmas at hand, including those nested in a _Pragma() */ + if (CurTok.Tok == TOK_PRAGMA) { + /* Repeated and/or nested _Pragma()'s will be handled recursively */ + ConsumePragma (); + } } diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 338ad6a65..cff2b0347 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -299,7 +299,9 @@ void CopyPPNumber (StrBuf* Target); /* Copy a pp-number from the input to Target */ void NextToken (void); -/* Get next token from input stream */ +/* Get next non-pragma token from input stream consuming any pragmas +** encountered. Adjacent string literal tokens will be concatenated. +*/ void SkipTokens (const token_t* TokenList, unsigned TokenCount); /* Skip tokens until we reach TOK_CEOF or a token in the given token list. diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 613129e1b..18df2b2b1 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -735,10 +735,6 @@ int AnyStatement (int* PendingToken) GotBreak = 1; break; - case TOK_PRAGMA: - DoPragma (); - break; - case TOK_SEMI: /* Empty statement. Ignore it */ CheckSemi (PendingToken); diff --git a/test/val/bug2151.c b/test/val/bug2151.c new file mode 100644 index 000000000..905eeac39 --- /dev/null +++ b/test/val/bug2151.c @@ -0,0 +1,29 @@ +/* Bug #2151 - #pragma causes errors when used within functions */ + +#pragma bss-name("BSS1") +int +#pragma code-name("CODE_WUT") +main _Pragma("message(\"_Pragma note\")") +( +void +_Pragma _Pragma ( +#pragma message("nested message 1") +"message(\"nested message 2\")" +) +( +"message(\"_Pragma in function parentheses\")") +#pragma code-name("CODE") +) +#pragma bss-name("BSS") +{ + extern int y; +#pragma bss-name("BSS2") + static +#pragma zpsym ("y") + int x; // TODO: currently in "BSS", but supposed to be in "BSS2"? + x = 0; + return x + y; +#pragma bss-name("BSS") +} + +int y; From c8df241337756d88526ea3589b0ceaa53c4a10d3 Mon Sep 17 00:00:00 2001 From: Evgeny Vrublevsky <me@veg.by> Date: Sat, 23 Sep 2023 14:19:11 +0300 Subject: [PATCH 317/520] Add line_continuations feature that works as .LINECONT but in a consistent way with other features. --- src/ca65/feature.c | 2 ++ src/ca65/feature.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ca65/feature.c b/src/ca65/feature.c index 9f5ca5876..6a38900b5 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -67,6 +67,7 @@ static const char* const FeatureKeys[FEAT_COUNT] = { "bracket_as_indirect", "string_escapes", "long_jsr_jmp_rts", + "line_continuations", }; @@ -121,6 +122,7 @@ void SetFeature (feature_t Feature, unsigned char On) case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = On; break; case FEAT_STRING_ESCAPES: StringEscapes = On; break; case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = On; break; + case FEAT_LINE_CONTINUATIONS: LineCont = On; break; default: break; } } diff --git a/src/ca65/feature.h b/src/ca65/feature.h index 8eeb62e6f..4d307f74d 100644 --- a/src/ca65/feature.h +++ b/src/ca65/feature.h @@ -69,6 +69,7 @@ typedef enum { FEAT_BRACKET_AS_INDIRECT, FEAT_STRING_ESCAPES, FEAT_LONG_JSR_JMP_RTS, + FEAT_LINE_CONTINUATIONS, /* Special value: Number of features available */ FEAT_COUNT From 850007cb441f89646ee42be047a87e80b30568ca Mon Sep 17 00:00:00 2001 From: Evgeny Vrublevsky <me@veg.by> Date: Sat, 23 Sep 2023 18:45:27 +0300 Subject: [PATCH 318/520] Document line_continuations feature. --- doc/ca65.sgml | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b0ccf6f5e..104c59bbd 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -2867,6 +2867,26 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH overridden. When using this feature, you may also get into trouble if later versions of the assembler define new keywords starting with a dot. + <tag><tt>line_continuations</tt><label id="line_continuations"></tag> + + Switch on or off line continuations using the backslash character + before a newline. The option is off by default. + Note: Line continuations do not work in a comment. A backslash at the + end of a comment is treated as part of the comment and does not trigger + line continuation. + + Example: + + <tscreen><verb> + .feature line_continuations + ; Allow line continuations + + lda \ + #$20 ; This is legal now + </verb></tscreen> + + For backward compatibility reasons, the <tt>.LINECONT +</tt> control command + is also supported and enables the same feature. + <tag><tt>long_jsr_jmp_rts</tt><label id="long_jsr_jmp_rts"></tag> Affects 65816 mode only. @@ -3371,26 +3391,6 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH the feature in more detail. -<sect1><tt>.LINECONT</tt><label id=".LINECONT"><p> - - Switch on or off line continuations using the backslash character - before a newline. The option is off by default. - Note: Line continuations do not work in a comment. A backslash at the - end of a comment is treated as part of the comment and does not trigger - line continuation. - The command can be followed by a '+' or '-' character to switch the - option on or off respectively. - - Example: - - <tscreen><verb> - .linecont + ; Allow line continuations - - lda \ - #$20 ; This is legal now - </verb></tscreen> - - <sect1><tt>.LIST</tt><label id=".LIST"><p> Enable output to the listing. The command can be followed by a boolean @@ -4489,9 +4489,9 @@ different: <item> Macros defined with <tt><ref id=".DEFINE" name=".DEFINE"></tt> may not span more than a line. You may use line continuation (see <tt><ref - id=".LINECONT" name=".LINECONT"></tt>) to spread the definition over - more than one line for increased readability, but the macro itself - may not contain an end-of-line token. + id="line_continuations" name="line_continuations"></tt>) to spread the + definition over more than one line for increased readability, but the + macro itself may not contain an end-of-line token. <item> Macros defined with <tt><ref id=".DEFINE" name=".DEFINE"></tt> share the name space with classic macros, but they are detected and replaced From 0028b14071bd102e79be22abcbe1f555e9ffa3e2 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 5 Oct 2023 17:48:16 +0800 Subject: [PATCH 319/520] The type category in a function definition cannot be inherited from a typedef. --- src/cc65/compile.c | 28 ++++++++++++++++++---------- test/err/bug2020-definition.c | 6 ++++++ test/val/bug2020-ok.c | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 test/err/bug2020-definition.c create mode 100644 test/val/bug2020-ok.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 1cb109bbe..7bf3cd8ab 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -65,6 +65,7 @@ #include "preproc.h" #include "standard.h" #include "staticassert.h" +#include "typecmp.h" #include "symtab.h" @@ -325,17 +326,24 @@ static void Parse (void) if (Sym && IsTypeFunc (Sym->Type)) { /* Function */ - if (!comma) { - if (CurTok.Tok == TOK_SEMI) { - /* Prototype only */ - NextToken (); - } else if (CurTok.Tok == TOK_LCURLY) { - /* Parse the function body */ - NewFunc (Sym, FuncDef); - - /* Make sure we aren't omitting any work */ - CheckDeferredOpAllDone (); + if (CurTok.Tok == TOK_SEMI) { + /* Prototype only */ + NextToken (); + } else if (CurTok.Tok == TOK_LCURLY) { + /* ISO C: The type category in a function definition cannot be + ** inherited from a typedef. + */ + if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) { + Error ("Function cannot be defined with a typedef"); + } else if (comma) { + Error ("';' expected after top level declarator"); } + + /* Parse the function body anyways */ + NewFunc (Sym, FuncDef); + + /* Make sure we aren't omitting any work */ + CheckDeferredOpAllDone (); } } else { diff --git a/test/err/bug2020-definition.c b/test/err/bug2020-definition.c new file mode 100644 index 000000000..7443e3a9a --- /dev/null +++ b/test/err/bug2020-definition.c @@ -0,0 +1,6 @@ +/* Bug #2020 - ISO/IEC 9899:1999 (E), 6.9.1 footnote 137: +** "The intent is that the type category in a function definition cannot be inherited from a typedef" + */ + +typedef void F(void); +F c { } /* Should fail */ diff --git a/test/val/bug2020-ok.c b/test/val/bug2020-ok.c new file mode 100644 index 000000000..6c52a9498 --- /dev/null +++ b/test/val/bug2020-ok.c @@ -0,0 +1,16 @@ +/* Bug #2020 - Right cases */ + +typedef int F(void); // type F is "function with no parameters returning int" + +F f, g; // f and g both have type compatible with F +int f(void) { return 0; } // RIGHT: f has type compatible with F +int g() { return 0; } // RIGHT: g has type compatible with F +F *e(void) { return 0; } // e returns a pointer to a function +F *((h))(void) { return 0; } // similar: parentheses irrelevant +int (*fp)(void); // fp points to a function that has type F +F *Fp; // Fp points to a function that has type + +int main(void) +{ + return 0; +} From 32a20cf5caf51594f7d6e6ea1e3fb36a51ac945b Mon Sep 17 00:00:00 2001 From: Brian Peek <2321675+BrianPeek@users.noreply.github.com> Date: Fri, 6 Oct 2023 19:51:36 -0700 Subject: [PATCH 320/520] remove abc ptrs --- libsrc/lynx/extzp.inc | 7 ------- libsrc/lynx/extzp.s | 8 -------- 2 files changed, 15 deletions(-) diff --git a/libsrc/lynx/extzp.inc b/libsrc/lynx/extzp.inc index 2b0f68701..7103ff5b6 100644 --- a/libsrc/lynx/extzp.inc +++ b/libsrc/lynx/extzp.inc @@ -12,10 +12,6 @@ .global __iodir: zp .global __viddma: zp .global __sprsys: zp - .global _abc_score_ptr0: zp - .global _abc_score_ptr1: zp - .global _abc_score_ptr2: zp - .global _abc_score_ptr3: zp .global _FileEntry: zp .global _FileStartBlock: zp .global _FileBlockOffset: zp @@ -25,6 +21,3 @@ .global _FileCurrBlock: zp .global _FileBlockByte: zp .global _FileDestPtr: zp - - - diff --git a/libsrc/lynx/extzp.s b/libsrc/lynx/extzp.s index f22cfaefb..df53c3d9a 100644 --- a/libsrc/lynx/extzp.s +++ b/libsrc/lynx/extzp.s @@ -16,13 +16,6 @@ __iodir: .res 1 __viddma: .res 1 __sprsys: .res 1 -; ------------------------------------------------------------------------ -; sound effect pointers for multitimbral Lynx music hardware -_abc_score_ptr0: .res 2 -_abc_score_ptr1: .res 2 -_abc_score_ptr2: .res 2 -_abc_score_ptr3: .res 2 - ; ------------------------------------------------------------------------ ; Filesystem variables needed for reading stuff from the Lynx cart _FileEntry: ; The file directory entry is 8 bytes @@ -35,4 +28,3 @@ _FileFileLen: .res 2 _FileCurrBlock: .res 1 _FileBlockByte: .res 2 _FileDestPtr: .res 2 - From 7b6f8249a0280b9f9ec82912eed3fffd790170c5 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 11 Oct 2023 22:29:15 +0800 Subject: [PATCH 321/520] General fixes for prerequisites for optimization on certain std functions. Added utility functions for extracting expression info. --- src/cc65/exprdesc.c | 28 ++++++++++++++++ src/cc65/exprdesc.h | 9 +++++ src/cc65/stdfunc.c | 82 ++++++++++++++++++++------------------------- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 5924ab6cf..a1af0bb8b 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -167,6 +167,17 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr) +int ED_IsLocZP (const ExprDesc* Expr) +/* Return true if the expression is in a location on a zeropage */ +{ + return ED_IsLocRegister (Expr) || + (ED_IsLocConst (Expr) && + Expr->Sym != 0 && + (Expr->Sym->Flags & SC_ZEROPAGE) != 0); +} + + + #if !defined(HAVE_INLINE) int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) /* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ @@ -302,6 +313,23 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr) + +int ED_IsStackAddr (const ExprDesc* Expr) +/* Return true if the expression denotes a fixed address on stack */ +{ + return ED_IsAddrExpr (Expr) && ED_IsLocStack (Expr); +} + + + +int ED_IsZPInd (const ExprDesc* Expr) +/* Return true if the expression is located on the zeropage */ +{ + return ED_IsIndExpr (Expr) && ED_IsLocZP (Expr); +} + + + int ED_IsNullPtr (const ExprDesc* Expr) /* Return true if the given expression is a NULL pointer constant */ { diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 2ef8b617f..93a8604c9 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -369,6 +369,9 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr); */ #endif +int ED_IsLocZP (const ExprDesc* Expr); +/* Return true if the expression is in a location on a zeropage */ + #if defined(HAVE_INLINE) INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) /* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ @@ -572,6 +575,12 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr); ** This can be a constant address or a stack variable address. */ +int ED_IsStackAddr (const ExprDesc* Expr); +/* Return true if the expression denotes a fixed address on stack */ + +int ED_IsZPInd (const ExprDesc* Expr); +/* Return true if the expression is located on the zeropage */ + int ED_IsNullPtr (const ExprDesc* Expr); /* Return true if the given expression is a NULL pointer constant */ diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index e968aaf1f..246bce192 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -280,13 +280,11 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** generated, and emit better code. */ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) { + (ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr))) { - int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr); - int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr); + int Reg1 = ED_IsZPInd (&Arg1.Expr); + int Reg2 = ED_IsZPInd (&Arg2.Expr); /* Drop the generated code */ RemoveCode (&Arg1.Expr.Start); @@ -342,9 +340,9 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) && - ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && - (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) { + ED_IsConstAddr (&Arg2.Expr) && + ED_IsStackAddr (&Arg1.Expr) && + ED_GetStackOffs (&Arg1.Expr, Arg3.Expr.IVal) < 256) { /* It is possible to just use one index register even if the stack ** offset is not zero, by adjusting the offset to the constant @@ -353,7 +351,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg2.Expr) && !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -420,9 +418,9 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && - (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 && - ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) { + ED_IsStackAddr (&Arg2.Expr) && + ED_GetStackOffs (&Arg2.Expr, Arg3.Expr.IVal) < 256 && + ED_IsConstAddr (&Arg1.Expr)) { /* It is possible to just use one index register even if the stack ** offset is not zero, by adjusting the offset to the constant @@ -431,7 +429,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg1.Expr) && !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -497,8 +495,8 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) goto ExitPoint; } - if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && + if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && + ED_IsStackAddr (&Arg2.Expr) && (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) { /* Drop the generated code but leave the load of the first argument*/ @@ -637,10 +635,9 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt (&Arg2.Expr) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) { + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr))) { - int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr); + int Reg = ED_IsZPInd (&Arg1.Expr); /* Drop the generated code */ RemoveCode (&Arg1.Expr.Start); @@ -689,8 +686,8 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt (&Arg2.Expr) && - ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && - (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) { + ED_IsStackAddr (&Arg1.Expr) && + ED_GetStackOffs (&Arg1.Expr, Arg3.Expr.IVal) < 256) { /* Calculate the real stack offset */ int Offs = ED_GetStackOffs (&Arg1.Expr, 0); @@ -850,7 +847,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* We do now have Arg1 in the primary. Load the first character from ** this string and cast to int. This is the function result. */ - IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr); + IsArray = IsTypeArray (Arg1.Type) && ED_IsAddrExpr (&Arg1.Expr); if (IsArray && ED_IsLocStack (&Arg1.Expr) && (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) { /* Drop the generated code */ @@ -878,22 +875,20 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } } else if ((IS_Get (&CodeSizeFactor) >= 165) && - ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) && + (ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr)) && (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) { unsigned Entry, Loop, Fin; /* Labels */ const char* Load; const char* Compare; - if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) { + if (ED_IsZPInd (&Arg1.Expr)) { Load = "lda (%s),y"; } else { Load = "lda %s,y"; } - if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { + if (ED_IsZPInd (&Arg2.Expr)) { Compare = "cmp (%s),y"; } else { Compare = "cmp %s,y"; @@ -924,14 +919,13 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) g_defcodelabel (Fin); } else if ((IS_Get (&CodeSizeFactor) > 190) && - ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && + (ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) { unsigned Entry, Loop, Fin; /* Labels */ const char* Compare; - if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { + if (ED_IsZPInd (&Arg2.Expr)) { Compare = "cmp (%s),y"; } else { Compare = "cmp %s,y"; @@ -1028,21 +1022,19 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** be generated. If such a situation is detected, throw away the ** generated, and emit better code. */ - if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) && + if ((ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr)) && (IS_Get (&EagerlyInlineFuncs) || (ECount != UNSPECIFIED && ECount < 256))) { const char* Load; const char* Store; - if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { + if (ED_IsZPInd (&Arg2.Expr)) { Load = "lda (%s),y"; } else { Load = "lda %s,y"; } - if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) { + if (ED_IsZPInd (&Arg1.Expr)) { Store = "sta (%s),y"; } else { Store = "sta %s,y"; @@ -1069,9 +1061,9 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) goto ExitPoint; } - if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && + if (ED_IsStackAddr (&Arg2.Expr) && StackPtr >= -255 && - ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) { + ED_IsConstAddr (&Arg1.Expr)) { /* It is possible to just use one index register even if the stack ** offset is not zero, by adjusting the offset to the constant @@ -1080,7 +1072,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg1.Expr) && !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -1116,8 +1108,8 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) goto ExitPoint; } - if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) && - ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && + if (ED_IsConstAddr (&Arg2.Expr) && + ED_IsStackAddr (&Arg1.Expr) && StackPtr >= -255) { /* It is possible to just use one index register even if the stack @@ -1127,7 +1119,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg2.Expr) && !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -1276,7 +1268,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** completely within the reach of a byte sized index register. */ if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex && - (Arg.IVal - StackPtr) + ECount < 256) { + ED_GetStackOffs (&Arg, ECount) < 256) { /* Calculate the true stack offset */ int Offs = ED_GetStackOffs (&Arg, 0); @@ -1305,7 +1297,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** get inlined if requested on the command line, since we cannot know how ** big the buffer actually is, so inlining is not always safe. */ - if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr && + if (ED_IsZPInd (&Arg) && IsPtr && IS_Get (&EagerlyInlineFuncs)) { /* Generate the strlen code */ From b14efbb57836c142129e0a647aed471e56472b89 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 11 Oct 2023 23:57:45 +0800 Subject: [PATCH 322/520] Fixed EOL settings (LF vs CRLF) for Visual Studio. --- .editorconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 3920829e6..1a6397284 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,6 @@ root = true [*] charset = utf-8 -end_of_line = crlf indent_style = space indent_size = 4 trim_trailing_whitespace = true From 74922afa7cb662dcaffd9896d671b865fbf0d7cf Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 12 Oct 2023 17:31:49 +0800 Subject: [PATCH 323/520] Made the intension of functions in src/cc65/declare.c less confusing. --- src/cc65/declare.c | 181 ++++++++++++++++++++++----------------------- src/cc65/declare.h | 7 +- src/cc65/expr.c | 6 +- 3 files changed, 95 insertions(+), 99 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index eab0ff0d6..cd174c92d 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -72,7 +72,7 @@ -static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified); +static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified); /* Parse a type specifier */ @@ -125,7 +125,7 @@ static unsigned ParseOneStorageClass (void) -static int ParseStorageClass (DeclSpec* D) +static int ParseStorageClass (DeclSpec* Spec) /* Parse storage class specifiers. Return true if a specifier is read even if ** it was duplicated or disallowed. */ { @@ -137,9 +137,9 @@ static int ParseStorageClass (DeclSpec* D) } while (StorageClass != 0) { - if (D->StorageClass == 0) { - D->StorageClass = StorageClass; - } else if (D->StorageClass == StorageClass) { + if (Spec->StorageClass == 0) { + Spec->StorageClass = StorageClass; + } else if (Spec->StorageClass == StorageClass) { Warning ("Duplicate storage class specifier"); } else { Error ("Conflicting storage class specifier"); @@ -346,12 +346,12 @@ static void OptionalSigned (int* SignednessSpecified) -void InitDeclSpec (DeclSpec* D) +static void InitDeclSpec (DeclSpec* Spec) /* Initialize the DeclSpec struct for use */ { - D->StorageClass = 0; - D->Type[0].C = T_END; - D->Flags = 0; + Spec->StorageClass = 0; + Spec->Type[0].C = T_END; + Spec->Flags = 0; } @@ -1250,7 +1250,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { -static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified) +static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified) /* Parse a type specifier. Store whether one of "signed" or "unsigned" was ** specified, so bit-fields of unspecified signedness can be treated as ** unsigned; without special handling, it would be treated as signed. @@ -1265,25 +1265,25 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci } /* Assume we have an explicit type */ - D->Flags &= ~DS_DEF_TYPE; + Spec->Flags &= ~DS_DEF_TYPE; /* Read storage specifiers and/or type qualifiers if we have any */ - OptionalSpecifiers (D, &Qualifiers, TSFlags); + OptionalSpecifiers (Spec, &Qualifiers, TSFlags); /* Look at the data type */ switch (CurTok.Tok) { case TOK_VOID: NextToken (); - D->Type[0].C = T_VOID; - D->Type[0].A.U = 0; - D->Type[1].C = T_END; + Spec->Type[0].C = T_VOID; + Spec->Type[0].A.U = 0; + Spec->Type[1].C = T_END; break; case TOK_CHAR: NextToken (); - D->Type[0].C = T_CHAR; - D->Type[1].C = T_END; + Spec->Type[0].C = T_CHAR; + Spec->Type[1].C = T_END; break; case TOK_LONG: @@ -1294,13 +1294,13 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci } NextToken (); OptionalInt (); - D->Type[0].C = T_ULONG; - D->Type[1].C = T_END; + Spec->Type[0].C = T_ULONG; + Spec->Type[1].C = T_END; } else { OptionalSigned (SignednessSpecified); OptionalInt (); - D->Type[0].C = T_LONG; - D->Type[1].C = T_END; + Spec->Type[0].C = T_LONG; + Spec->Type[1].C = T_END; } break; @@ -1312,20 +1312,20 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci } NextToken (); OptionalInt (); - D->Type[0].C = T_USHORT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_USHORT; + Spec->Type[1].C = T_END; } else { OptionalSigned (SignednessSpecified); OptionalInt (); - D->Type[0].C = T_SHORT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_SHORT; + Spec->Type[1].C = T_END; } break; case TOK_INT: NextToken (); - D->Type[0].C = T_INT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; break; case TOK_SIGNED: @@ -1337,22 +1337,22 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci case TOK_CHAR: NextToken (); - D->Type[0].C = T_SCHAR; - D->Type[1].C = T_END; + Spec->Type[0].C = T_SCHAR; + Spec->Type[1].C = T_END; break; case TOK_SHORT: NextToken (); OptionalInt (); - D->Type[0].C = T_SHORT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_SHORT; + Spec->Type[1].C = T_END; break; case TOK_LONG: NextToken (); OptionalInt (); - D->Type[0].C = T_LONG; - D->Type[1].C = T_END; + Spec->Type[0].C = T_LONG; + Spec->Type[1].C = T_END; break; case TOK_INT: @@ -1360,8 +1360,8 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci /* FALL THROUGH */ default: - D->Type[0].C = T_INT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; break; } break; @@ -1375,22 +1375,22 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci case TOK_CHAR: NextToken (); - D->Type[0].C = T_UCHAR; - D->Type[1].C = T_END; + Spec->Type[0].C = T_UCHAR; + Spec->Type[1].C = T_END; break; case TOK_SHORT: NextToken (); OptionalInt (); - D->Type[0].C = T_USHORT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_USHORT; + Spec->Type[1].C = T_END; break; case TOK_LONG: NextToken (); OptionalInt (); - D->Type[0].C = T_ULONG; - D->Type[1].C = T_END; + Spec->Type[0].C = T_ULONG; + Spec->Type[1].C = T_END; break; case TOK_INT: @@ -1398,22 +1398,22 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci /* FALL THROUGH */ default: - D->Type[0].C = T_UINT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_UINT; + Spec->Type[1].C = T_END; break; } break; case TOK_FLOAT: NextToken (); - D->Type[0].C = T_FLOAT; - D->Type[1].C = T_END; + Spec->Type[0].C = T_FLOAT; + Spec->Type[1].C = T_END; break; case TOK_DOUBLE: NextToken (); - D->Type[0].C = T_DOUBLE; - D->Type[1].C = T_END; + Spec->Type[0].C = T_DOUBLE; + Spec->Type[1].C = T_END; break; case TOK_UNION: @@ -1426,13 +1426,13 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci AnonName (Ident, "union"); } /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; + Spec->Flags |= DS_EXTRA_TYPE; /* Declare the union in the current scope */ - TagEntry = ParseUnionSpec (Ident, &D->Flags); + TagEntry = ParseUnionSpec (Ident, &Spec->Flags); /* Encode the union entry into the type */ - D->Type[0].C = T_UNION; - SetESUTagSym (D->Type, TagEntry); - D->Type[1].C = T_END; + Spec->Type[0].C = T_UNION; + SetESUTagSym (Spec->Type, TagEntry); + Spec->Type[1].C = T_END; break; case TOK_STRUCT: @@ -1445,13 +1445,13 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci AnonName (Ident, "struct"); } /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; + Spec->Flags |= DS_EXTRA_TYPE; /* Declare the struct in the current scope */ - TagEntry = ParseStructSpec (Ident, &D->Flags); + TagEntry = ParseStructSpec (Ident, &Spec->Flags); /* Encode the struct entry into the type */ - D->Type[0].C = T_STRUCT; - SetESUTagSym (D->Type, TagEntry); - D->Type[1].C = T_END; + Spec->Type[0].C = T_STRUCT; + SetESUTagSym (Spec->Type, TagEntry); + Spec->Type[1].C = T_END; break; case TOK_ENUM: @@ -1467,13 +1467,13 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci AnonName (Ident, "enum"); } /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; + Spec->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ - TagEntry = ParseEnumSpec (Ident, &D->Flags); + TagEntry = ParseEnumSpec (Ident, &Spec->Flags); /* Encode the enum entry into the type */ - D->Type[0].C |= T_ENUM; - SetESUTagSym (D->Type, TagEntry); - D->Type[1].C = T_END; + Spec->Type[0].C |= T_ENUM; + SetESUTagSym (Spec->Type, TagEntry); + Spec->Type[1].C = T_END; /* The signedness of enums is determined by the type, so say this is specified to avoid ** the int -> unsigned int handling for plain int bit-fields in AddBitField. */ @@ -1489,7 +1489,7 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci if (TagEntry && SymIsTypeDef (TagEntry)) { /* It's a typedef */ NextToken (); - TypeCopy (D->Type, TagEntry->Type); + TypeCopy (Spec->Type, TagEntry->Type); /* If it's a typedef, we should actually use whether the signedness was ** specified on the typedef, but that information has been lost. Treat the ** signedness as being specified to work around the ICE in #1267. @@ -1506,29 +1506,29 @@ static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpeci ** in DeclareLocals. The type code used here doesn't matter as ** long as it has no qualifiers. */ - D->Flags |= DS_DEF_TYPE; - D->Type[0].C = T_INT; - D->Type[1].C = T_END; + Spec->Flags |= DS_DEF_TYPE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; break; } /* FALL THROUGH */ default: if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { - D->Flags |= DS_NO_TYPE; - D->Type[0].C = T_INT; - D->Type[1].C = T_END; + Spec->Flags |= DS_NO_TYPE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; } else { - D->Flags |= DS_DEF_TYPE; - D->Type[0].C = T_INT; - D->Type[1].C = T_END; + Spec->Flags |= DS_DEF_TYPE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; } break; } /* There may also be specifiers/qualifiers *after* the initial type */ - OptionalSpecifiers (D, &Qualifiers, TSFlags); - D->Type[0].C |= Qualifiers; + OptionalSpecifiers (Spec, &Qualifiers, TSFlags); + Spec->Type[0].C |= Qualifiers; } @@ -1629,7 +1629,7 @@ static void ParseOldStyleParamList (FuncDesc* F) /* Parse a comma separated variable list */ while (1) { - Declarator Decl; + Declarator Decl; /* Read the parameter */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); @@ -1646,7 +1646,7 @@ static void ParseOldStyleParamList (FuncDesc* F) SymEntry* Param = FindLocalSym (Decl.Ident); if (Param) { /* Check if we already changed the type for this - ** parameter + ** parameter. */ if (Param->Flags & SC_DEFTYPE) { /* Found it, change the default type to the one given */ @@ -1691,9 +1691,9 @@ static void ParseAnsiParamList (FuncDesc* F) /* Parse params */ while (CurTok.Tok != TOK_RPAREN) { - DeclSpec Spec; - Declarator Decl; - SymEntry* Param; + DeclSpec Spec; + Declarator Decl; + SymEntry* Param; /* Allow an ellipsis as last parameter */ if (CurTok.Tok == TOK_ELLIPSIS) { @@ -1780,7 +1780,6 @@ static void ParseAnsiParamList (FuncDesc* F) static FuncDesc* ParseFuncDecl (void) /* Parse the argument list of a function with the enclosing parentheses */ { - SymEntry* Sym; SymEntry* WrappedCall; unsigned int WrappedCallData; @@ -1806,7 +1805,7 @@ static FuncDesc* ParseFuncDecl (void) /* If the identifier is a typedef, we have a new-style parameter list; ** if it's some other identifier, it's an old-style parameter list. */ - Sym = FindSym (CurTok.Ident); + SymEntry* Sym = FindSym (CurTok.Ident); if (Sym == 0 || !SymIsTypeDef (Sym)) { /* Old-style (K&R) function. */ F->Flags |= FD_OLDSTYLE; @@ -2012,7 +2011,7 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /*****************************************************************************/ -/* code */ +/* Code */ /*****************************************************************************/ @@ -2150,34 +2149,34 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) -void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage) +void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage) /* Parse a declaration specification */ { /* Initialize the DeclSpec struct */ - InitDeclSpec (D); + InitDeclSpec (Spec); /* Assume we're using an explicit storage class */ - D->Flags &= ~DS_DEF_STORAGE; + Spec->Flags &= ~DS_DEF_STORAGE; /* Parse the type specifiers */ - ParseTypeSpec (D, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL); + ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL); /* If no explicit storage class is given, use the default */ - if (D->StorageClass == 0) { - D->Flags |= DS_DEF_STORAGE; - D->StorageClass = DefStorage; + if (Spec->StorageClass == 0) { + Spec->Flags |= DS_DEF_STORAGE; + Spec->StorageClass = DefStorage; } } -void CheckEmptyDecl (const DeclSpec* D) +void CheckEmptyDecl (const DeclSpec* Spec) /* Called after an empty type declaration (that is, a type declaration without ** a variable). Checks if the declaration does really make sense and issues a ** warning if not. */ { - if ((D->Flags & DS_EXTRA_TYPE) == 0) { + if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { Warning ("Useless declaration"); } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 0facccba3..89d7be7ea 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -113,19 +113,16 @@ typedef enum { -void InitDeclSpec (DeclSpec* D); -/* Initialize the DeclSpec struct for use */ - Type* ParseType (Type* Type); /* Parse a complete type specification */ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); /* Parse a variable, type or function declarator */ -void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage); +void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage); /* Parse a declaration specification */ -void CheckEmptyDecl (const DeclSpec* D); +void CheckEmptyDecl (const DeclSpec* Spec); /* Called after an empty type declaration (that is, a type declaration without ** a variable). Checks if the declaration does really make sense and issues a ** warning if not. diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 37af494b5..5e8c48c1a 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1397,13 +1397,13 @@ static void Primary (ExprDesc* E) break; } else { /* Let's see if this is a C99-style declaration */ - DeclSpec Spec; - InitDeclSpec (&Spec); + DeclSpec Spec; ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); if (Spec.Type->C != T_END) { - + /* Recognized but not supported */ Error ("Mixed declarations and code are not supported in cc65"); + while (CurTok.Tok != TOK_SEMI) { Declarator Decl; From c6ead99b002fc2e6e0c61f8ee54f11353fa696fb Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 13 Oct 2023 16:32:05 +0800 Subject: [PATCH 324/520] Fixed string literal concatenation with pragmas in between. --- src/cc65/litpool.c | 13 +++- src/cc65/litpool.h | 5 +- src/cc65/scanner.c | 163 ++++++++++++++++++++++++++++++--------------- 3 files changed, 124 insertions(+), 57 deletions(-) diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index d741f87d0..5433f6d95 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -160,13 +160,24 @@ void ReleaseLiteral (Literal* L) void TranslateLiteral (Literal* L) -/* Translate a literal into the target charset. */ +/* Translate a literal into the target charset */ { TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data)); } +void ConcatLiteral (Literal* L, const Literal* Appended) +/* Concatenate string literals */ +{ + if (SB_GetLen (&L->Data) > 0 && SB_LookAtLast (&L->Data) == '\0') { + SB_Drop (&L->Data, 1); + } + SB_Append (&L->Data, &Appended->Data); +} + + + unsigned GetLiteralLabel (const Literal* L) /* Return the asm label for a literal */ { diff --git a/src/cc65/litpool.h b/src/cc65/litpool.h index 78f432138..5f444bfb8 100644 --- a/src/cc65/litpool.h +++ b/src/cc65/litpool.h @@ -75,7 +75,10 @@ void ReleaseLiteral (Literal* L); /* Decrement the reference counter for the literal */ void TranslateLiteral (Literal* L); -/* Translate a literal into the target charset. */ +/* Translate a literal into the target charset */ + +void ConcatLiteral (Literal* L, const Literal* Appended); +/* Concatenate string literals */ unsigned GetLiteralLabel (const Literal* L); /* Return the asm label for a literal */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 4591f86a2..e9ef34173 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -70,6 +70,7 @@ +static Token SavedTok; /* Saved token */ Token CurTok; /* The current token */ Token NextTok; /* The next token */ int PPParserRunning; /* Is tokenizer used by the preprocessor */ @@ -324,7 +325,7 @@ static void SetTok (int tok) static int ParseChar (void) -/* Parse a character. Converts escape chars into character codes. */ +/* Parse a character token. Converts escape chars into character codes. */ { int C; int HadError; @@ -426,7 +427,7 @@ static int ParseChar (void) static void CharConst (void) -/* Parse a character constant. */ +/* Parse a character constant token */ { int C; @@ -463,7 +464,7 @@ static void CharConst (void) static void StringConst (void) -/* Parse a quoted string */ +/* Parse a quoted string token */ { /* String buffer */ StrBuf S = AUTO_STRBUF_INITIALIZER; @@ -471,43 +472,34 @@ static void StringConst (void) /* Assume next token is a string constant */ NextTok.Tok = TOK_SCONST; - /* Concatenate strings. If at least one of the concenated strings is a wide - ** character literal, the whole string is a wide char literal, otherwise - ** it's a normal string literal. - */ - while (1) { + /* Check if this is a normal or a wide char string */ + if (CurC == 'L' && NextC == '\"') { + /* Wide character literal */ + NextTok.Tok = TOK_WCSCONST; + NextChar (); + NextChar (); + } else if (CurC == '\"') { + /* Skip the quote char */ + NextChar (); + } else { + /* No string */ + goto ExitPoint; + } - /* Check if this is a normal or a wide char string */ - if (CurC == 'L' && NextC == '\"') { - /* Wide character literal */ - NextTok.Tok = TOK_WCSCONST; - NextChar (); - NextChar (); - } else if (CurC == '\"') { - /* Skip the quote char */ - NextChar (); - } else { - /* No string */ + /* Read until end of string */ + while (CurC != '\"') { + if (CurC == '\0') { + Error ("Unexpected newline"); break; } - - /* Read until end of string */ - while (CurC != '\"') { - if (CurC == '\0') { - Error ("Unexpected newline"); - break; - } - SB_AppendChar (&S, ParseChar ()); - } - - /* Skip closing quote char if there was one */ - NextChar (); - - /* Skip white space, read new input */ - SkipWhite (); - + SB_AppendChar (&S, ParseChar ()); } + /* Skip closing quote char if there was one */ + NextChar (); + +ExitPoint: + /* Terminate the string */ SB_AppendChar (&S, '\0'); @@ -521,7 +513,7 @@ static void StringConst (void) static void NumericConst (void) -/* Parse a numeric constant */ +/* Parse a numeric constant token */ { unsigned Base; /* Temporary number base according to prefix */ unsigned Index; @@ -806,13 +798,6 @@ static void GetNextInputToken (void) { ident token; - /* We have to skip white space here before shifting tokens, since the - ** tokens and the current line info is invalid at startup and will get - ** initialized by reading the first time from the file. Remember if we - ** were at end of input and handle that later. - */ - int GotEOF = (SkipWhite () == 0); - /* Current token is the lookahead token */ if (CurTok.LI) { ReleaseLineInfo (CurTok.LI); @@ -821,13 +806,27 @@ static void GetNextInputToken (void) /* Get the current token */ CurTok = NextTok; - /* Remember the starting position of the next token */ - NextTok.LI = UseLineInfo (GetCurLineInfo ()); + if (SavedTok.Tok == TOK_INVALID) { + /* We have to skip white space here before shifting tokens, since the + ** tokens and the current line info is invalid at startup and will get + ** initialized by reading the first time from the file. Remember if we + ** were at end of input and handle that later. + */ + int GotEOF = (SkipWhite () == 0); - /* Now handle end of input */ - if (GotEOF) { - /* End of file reached */ - NextTok.Tok = TOK_CEOF; + /* Remember the starting position of the next token */ + NextTok.LI = UseLineInfo (GetCurLineInfo ()); + + /* Now handle end of input */ + if (GotEOF) { + /* End of file reached */ + NextTok.Tok = TOK_CEOF; + return; + } + } else { + /* Just use the saved token */ + NextTok = SavedTok; + SavedTok.Tok = TOK_INVALID; return; } @@ -1122,6 +1121,9 @@ void NextToken (void) ** encountered. Adjacent string literal tokens will be concatenated. */ { + /* Used for string literal concatenation */ + Token PrevTok; + /* When reading the first time from the file, the line info in NextTok, ** which will be copied to CurTok is invalid. Since the information from ** the token is used for error messages, we must make it valid. @@ -1130,13 +1132,64 @@ void NextToken (void) NextTok.LI = UseLineInfo (GetCurLineInfo ()); } - /* Read the next token from the file */ - GetNextInputToken (); + PrevTok.Tok = TOK_INVALID; + while (1) { + /* Read the next token from the file */ + GetNextInputToken (); - /* Consume all pragmas at hand, including those nested in a _Pragma() */ - if (CurTok.Tok == TOK_PRAGMA) { - /* Repeated and/or nested _Pragma()'s will be handled recursively */ - ConsumePragma (); + /* Consume all pragmas at hand, including those nested in a _Pragma() */ + if (CurTok.Tok == TOK_PRAGMA) { + /* Repeated and/or nested _Pragma()'s will be handled recursively */ + ConsumePragma (); + } + + /* Check for string concatenation */ + if (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST) { + if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) { + /* Concatenate strings */ + ConcatLiteral (PrevTok.SVal, CurTok.SVal); + + /* If at least one of the concatenated strings is a wide + ** character literal, the whole string is a wide char + ** literal, otherwise it is a normal string literal. + */ + if (CurTok.Tok == TOK_WCSCONST) { + PrevTok.Tok = TOK_WCSCONST; + PrevTok.Type = CurTok.Type; + } + } + + if (NextTok.Tok == TOK_SCONST || + NextTok.Tok == TOK_WCSCONST || + NextTok.Tok == TOK_PRAGMA) { + /* Remember current string literal token */ + if (PrevTok.Tok == TOK_INVALID) { + PrevTok = CurTok; + PrevTok.LI = UseLineInfo (PrevTok.LI); + } + + /* Keep looping */ + continue; + } + } + + break; + } + + /* Use the concatenated string literal token if there is one */ + if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) { + if (CurTok.Tok != TOK_SCONST && CurTok.Tok != TOK_WCSCONST) { + /* Push back the incoming tokens */ + SavedTok = NextTok; + NextTok = CurTok; + } else { + /* The last string literal token can be just replaced */ + if (CurTok.LI) { + ReleaseLineInfo (CurTok.LI); + } + } + /* Replace the current token with the concatenated string literal */ + CurTok = PrevTok; } } From 25832ef5fcd5509a8b91accbaa8dabdefce595a6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 13 Oct 2023 16:32:06 +0800 Subject: [PATCH 325/520] Fixed timing of #pragma charmap. Now it is immediately applied and affects almost all characters and string literals after it. Exceptions: - String literals as the message of a static assertion or inline assembler code (only the required one, not any optional formatted arguments) in an asm() expression are not translated with either #pragma charmap or target presets. - String literals used for preprocessor directives or as the result of stringized macro arguments are never translated. --- src/cc65/asmstmt.c | 9 +++++++ src/cc65/expr.c | 2 -- src/cc65/initdata.c | 3 --- src/cc65/pragma.c | 6 +++++ src/cc65/scanner.c | 21 ++++++++++++---- src/cc65/scanner.h | 8 +++--- src/cc65/staticassert.c | 8 +++++- test/val/bug2151.c | 54 ++++++++++++++++++++++++++++++++++++++--- 8 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index d182140fd..6cb6f2ef7 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -417,6 +417,9 @@ void AsmStatement (void) ** a string literal in parenthesis. */ { + /* Prevent from translating the inline code string literal in asm */ + NoCharMap = 1; + /* Skip the ASM */ NextToken (); @@ -431,9 +434,15 @@ void AsmStatement (void) /* Need left parenthesis */ if (!ConsumeLParen ()) { + NoCharMap = 0; return; } + /* We have got the inline code string untranslated, now reenable string + ** literal translation for string arguments (if any). + */ + NoCharMap = 0; + /* String literal */ if (CurTok.Tok != TOK_SCONST) { diff --git a/src/cc65/expr.c b/src/cc65/expr.c index f6b069f3f..8b0824c31 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1336,8 +1336,6 @@ static void Primary (ExprDesc* E) /* String literal */ if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { E->V.LVal = UseLiteral (CurTok.SVal); - /* Translate into target charset */ - TranslateLiteral (E->V.LVal); } else { E->V.LVal = CurTok.SVal; } diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index f576c0255..5702d57c1 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -245,9 +245,6 @@ static void DefineBitFieldData (StructInitData* SI) static void DefineStrData (Literal* Lit, unsigned Count) { - /* Translate into target charset */ - TranslateLiteral (Lit); - /* Output the data */ g_defbytes (GetLiteralStr (Lit), Count); } diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 21d426a26..db306040d 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -1096,8 +1096,12 @@ void ConsumePragma (void) /* Skip the _Pragma token */ NextToken (); + /* Prevent from translating string literals in _Pragma */ + ++InPragmaParser; + /* We expect an opening paren */ if (!ConsumeLParen ()) { + --InPragmaParser; return; } @@ -1115,6 +1119,8 @@ void ConsumePragma (void) ParsePragmaString (); } + --InPragmaParser; + /* Closing paren needed */ ConsumeRParen (); } diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index e9ef34173..409e778ab 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -70,10 +70,12 @@ -static Token SavedTok; /* Saved token */ -Token CurTok; /* The current token */ -Token NextTok; /* The next token */ -int PPParserRunning; /* Is tokenizer used by the preprocessor */ +static Token SavedTok; /* Saved token */ +Token CurTok; /* The current token */ +Token NextTok; /* The next token */ +int PPParserRunning; /* Is tokenizer used by the preprocessor */ +int NoCharMap; /* Disable literal translation */ +unsigned InPragmaParser; /* Depth of pragma parser calling */ @@ -455,7 +457,7 @@ static void CharConst (void) } /* Translate into target charset */ - NextTok.IVal = SignExtendChar (TgtTranslateChar (C)); + NextTok.IVal = SignExtendChar (C); /* Character constants have type int */ NextTok.Type = type_int; @@ -798,6 +800,15 @@ static void GetNextInputToken (void) { ident token; + if (!NoCharMap && !InPragmaParser) { + /* Translate string and character literals into target charset */ + if (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST) { + TranslateLiteral (NextTok.SVal); + } else if (NextTok.Tok == TOK_CCONST || NextTok.Tok == TOK_WCCONST) { + NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal)); + } + } + /* Current token is the lookahead token */ if (CurTok.LI) { ReleaseLineInfo (CurTok.LI); diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index cff2b0347..e6b788660 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -219,9 +219,11 @@ struct Token { const Type* Type; /* Type if integer or float constant */ }; -extern Token CurTok; /* The current token */ -extern Token NextTok; /* The next token */ -extern int PPParserRunning; /* Is tokenizer used by the preprocessor */ +extern Token CurTok; /* The current token */ +extern Token NextTok; /* The next token */ +extern int PPParserRunning; /* Is tokenizer used by the preprocessor */ +extern int NoCharMap; /* Disable literal translation */ +extern unsigned InPragmaParser; /* Depth of pragma parser calling */ diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 1bf8dd4c5..abb2c57ca 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -72,9 +72,15 @@ void ParseStaticAssert () ** support the C2X syntax with only an expression. */ if (CurTok.Tok == TOK_COMMA) { - /* Skip the comma. */ + /* Prevent from translating the message string literal */ + NoCharMap = 1; + + /* Skip the comma and get the next token */ NextToken (); + /* Reenable string literal translation */ + NoCharMap = 0; + /* String literal */ if (CurTok.Tok != TOK_SCONST) { Error ("String literal expected for static_assert message"); diff --git a/test/val/bug2151.c b/test/val/bug2151.c index 905eeac39..25f145506 100644 --- a/test/val/bug2151.c +++ b/test/val/bug2151.c @@ -1,9 +1,39 @@ /* Bug #2151 - #pragma causes errors when used within functions */ +#include <stdio.h> +#include <string.h> + +#pragma charmap(0x61, 0x61) +_Static_assert('A'== +#pragma charmap(0x61, 0x41) +'a' +#pragma charmap(0x61, 0x42) +, +#pragma charmap(0x61, 0x61) +"charmap failed"); + +char str[] = +"a" +#pragma charmap(0x61, 0x42) +"a" +#pragma charmap(0x61, 0x43) +"a" +#pragma charmap(0x61, 0x61) +; + +unsigned failures; + #pragma bss-name("BSS1") int #pragma code-name("CODE_WUT") -main _Pragma("message(\"_Pragma note\")") +main _Pragma +#pragma charmap(0x61, 0x32) +( +"message(\"_Pragma string" +/* Concatenated string literals in _Pragma is a cc65 extension */ +" unaffected by charmap\")" +) +#pragma charmap(0x61, 0x61) ( void _Pragma _Pragma ( @@ -20,9 +50,27 @@ _Pragma _Pragma ( #pragma bss-name("BSS2") static #pragma zpsym ("y") - int x; // TODO: currently in "BSS", but supposed to be in "BSS2"? + int x; // TODO: currently in "BSS", but supposed to be in "BSS2"? x = 0; - return x + y; + + if (memcmp(str, "aBC", 3)) + { + ++failures; + printf("%3s\n", str); + } + + if (x + y != 0) + { + ++failures; + printf("%d\n", x + y); + } + + if (failures != 0) + { + printf("faiures: %d\n", failures); + } + + return failures; #pragma bss-name("BSS") } From 7b0d1d9679d22235bf91c096bf061c02fc3ef1a6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 13 Oct 2023 14:54:54 +0800 Subject: [PATCH 326/520] Added warning on concatenated string literals in _Pragma operations. --- src/cc65/scanner.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 409e778ab..00dde9e83 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -1157,6 +1157,11 @@ void NextToken (void) /* Check for string concatenation */ if (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST) { if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) { + /* Warn on non-ISO behavior */ + if (InPragmaParser) { + Warning ("Concatenated string literals in _Pragma operation"); + } + /* Concatenate strings */ ConcatLiteral (PrevTok.SVal, CurTok.SVal); From 9e87e558d28ee166f8c373f31cfe691b1b2e83c8 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 9 Oct 2023 22:03:08 +0200 Subject: [PATCH 327/520] Fix register r/w timing --- libsrc/apple2/ser/a2.gs.s | 189 ++++++++++++++++++++++++-------------- 1 file changed, 118 insertions(+), 71 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 5d5ca5e09..18ee1c307 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -239,33 +239,34 @@ SER_FLAG_CH_B = %00000111 .code -; Read a register -; Input: X as channel -; Output result in A -.macro rra In,Reg - lda Reg - sta In,x - lda In,x -.endmacro +; Read register value to A. +; Input: X as channel +; Y as register +; Output: A +readSSCReg: + cpx #0 + bne ReadAreg + sty SCCBREG + lda SCCBREG + rts +ReadAreg: + sty SCCAREG + lda SCCAREG + rts ; Write value of A to a register. ; Input: X as channel -.macro wra Out,Reg - pha - lda Reg - sta Out,x - pla - sta Out,x -.endmacro - -; Write value passed as parameter to a register. -; Input: X as channel -.macro wrr Out,Reg,Val - lda Reg - sta Out,x - lda Val - sta Out,x -.endmacro +; Y as register +writeSCCReg: + cpx #0 + bne WriteAreg + sty SCCBREG + sta SCCBREG + rts +WriteAreg: + sty SCCAREG + sta SCCAREG + rts ;---------------------------------------------------------------------------- ; SER_INSTALL: Is called after the driver is loaded into memory. If possible, @@ -291,8 +292,13 @@ SER_CLOSE: ; Deactivate interrupts sei - wrr SCCBREG, #WR_MASTER_IRQ_RST, #MASTER_IRQ_SHUTDOWN - wrr SCCBREG, #WR_TX_RX_MODE_CTRL, #TX_RX_MODE_OFF + ldy #WR_MASTER_IRQ_RST + lda #MASTER_IRQ_SHUTDOWN + jsr writeSCCReg + + ldy #WR_TX_RX_MODE_CTRL + lda #TX_RX_MODE_OFF + jsr writeSCCReg ; Reset SerFlag to what it was lda SerFlagOrig @@ -303,14 +309,13 @@ SER_CLOSE: ; Clear external interrupts (twice) ldy #WR_INIT_CTRL lda #INIT_CTRL_CLEAR_EIRQ - - sty SCCBREG - sta SCCBREG - sty SCCBREG - sta SCCBREG + jsr writeSCCReg + jsr writeSCCReg ; Reset MIE for firmware use - wrr SCCBREG, #WR_MASTER_IRQ_RST, #MASTER_IRQ_MIE_RST + ldy #WR_MASTER_IRQ_RST + lda #MASTER_IRQ_MIE_RST + jsr writeSCCReg ldx #$00 stx Slot ; Mark port as closed @@ -346,6 +351,8 @@ SetupErrOut: rts HardwareFound: + sei ; Disable interrupts + ; Check if the handshake setting is valid ldy #SER_PARAMS::HANDSHAKE ; Handshake lda (ptr1),y @@ -370,7 +377,12 @@ SetupBufs: ldx Channel - rra SCCBREG,#$00 ; Hit rr0 once to sync up + ldy #RR_INIT_STATUS ; Hit rr0 once to sync up + jsr readSSCReg + + ldy #WR_MISC_CTRL ; Turn everything off + lda #$00 + jsr writeSCCReg ldy #SER_PARAMS::STOPBITS lda (ptr1),y ; Stop bits @@ -388,19 +400,24 @@ SetupBufs: ora #TX_RX_CLOCK_MUL - wra SCCBREG,#WR_TX_RX_CTRL + ldy #WR_TX_RX_CTRL ; Setup stop & parity bits + jsr writeSCCReg cpx #$00 bne ClockA ClockB: - wrr SCCBREG,#WR_CLOCK_CTRL,#CLOCK_CTRL_CH_B + ldy #WR_CLOCK_CTRL + lda #CLOCK_CTRL_CH_B + jsr writeSCCReg lda #INTR_PENDING_RX_EXT_B ; Store which IRQ bits we'll check sta CurChanIrqFlags bra SetBaud ClockA: - wrr SCCBREG,#WR_CLOCK_CTRL,#CLOCK_CTRL_CH_A + ldy #WR_CLOCK_CTRL + lda #CLOCK_CTRL_CH_A + jsr writeSCCReg lda #INTR_PENDING_RX_EXT_A ; Store which IRQ bits we'll check sta CurChanIrqFlags @@ -416,50 +433,58 @@ SetBaud: lda BaudLowTable,y ; Get low byte bmi InvParam ; Branch if rate not supported - wra SCCBREG,#WR_BAUDL_CTRL + phy + ldy #WR_BAUDL_CTRL + jsr writeSCCReg + ply lda BaudHighTable,y ; Get high byte - wra SCCBREG,#WR_BAUDH_CTRL + ldy #WR_BAUDH_CTRL + jsr writeSCCReg - lda #$00 - wra SCCBREG,#WR_MISC_CTRL + ldy #WR_MISC_CTRL ; Time to turn this thing on + lda #MISC_CTRL_RATE_GEN_ON + jsr writeSCCReg - ora #MISC_CTRL_RATE_GEN_ON ; Time to turn this thing on - wra SCCBREG,#WR_MISC_CTRL - - ; Final write to RX_CTRL ldy #SER_PARAMS::DATABITS lda (ptr1),y ; Data bits tay lda RxBitTable,y ; Data bits for RX - ora #RX_CTRL_ON ; Plus turn on - wra SCCBREG,#WR_RX_CTRL + ora #RX_CTRL_ON ; and turn RX on + + phy + ldy #WR_RX_CTRL + jsr writeSCCReg + ply lda TxBitTable,y ; Data bits for TX - ora #TX_CTRL_ON ; Plus turn on + ora #TX_CTRL_ON ; and turn TX on and #TX_DTR_ON sta RtsOff ; Save value for flow control ora #TX_RTS_ON - wra SCCBREG,#WR_TX_CTRL - wrr SCCBREG,#WR_IRQ_CTRL,#IRQ_CLEANUP_EIRQ + ldy #WR_TX_CTRL + jsr writeSCCReg - lda #WR_INIT_CTRL ; Clear ext status (write twice) - sta SCCBREG,x + ldy #WR_IRQ_CTRL + lda #IRQ_CLEANUP_EIRQ + jsr writeSCCReg + + ldy #WR_INIT_CTRL ; Clear ext status (write twice) lda #INIT_CTRL_CLEAR_EIRQ - sta SCCBREG,x + jsr writeSCCReg + jsr writeSCCReg - lda #WR_INIT_CTRL - sta SCCBREG,x - lda #INIT_CTRL_CLEAR_EIRQ - sta SCCBREG,x + ldy #WR_TX_RX_MODE_CTRL ; Activate RX IRQ + lda #TX_RX_MODE_RXIRQ + jsr writeSCCReg - ; Activate RX IRQ - wrr SCCBREG,#WR_TX_RX_MODE_CTRL,#TX_RX_MODE_RXIRQ - - wrr SCCBREG,#WR_MASTER_IRQ_RST,#MASTER_IRQ_SET + lda SCCBREG ; Activate master IRQ + ldy #WR_MASTER_IRQ_RST + lda #MASTER_IRQ_SET + jsr writeSCCReg lda SER_FLAG ; Get SerFlag's current value sta SerFlagOrig ; and save it @@ -502,7 +527,9 @@ SER_GET: lda RtsOff ora #TX_RTS_ON - wra SCCBREG,#WR_TX_CTRL + + ldy #WR_TX_CTRL + jsr writeSCCReg : ldy RecvHead ; Get byte from buffer lda RecvBuf,y @@ -597,8 +624,10 @@ SER_IOCTL: ; was handled, otherwise with carry clear. SER_IRQ: - ldx #$00 ; IRQ status is always in A reg - rra SCCAREG,#RR_INTR_PENDING_STATUS + ldx #$01 ; IRQ status is always in A reg + ldy #RR_INTR_PENDING_STATUS + jsr readSSCReg + and CurChanIrqFlags ; Is this ours? beq Done @@ -620,7 +649,8 @@ SER_IRQ: CheckSpecial: ; Always check IRQ special flags from Channel B (Ref page 5-24) ; X is still 0 there. - rra SCCBREG,#RR_IRQ_STATUS + ldy #RR_IRQ_STATUS + jsr readSSCReg and #IRQ_MASQ cmp #IRQ_SPECIAL @@ -628,19 +658,28 @@ CheckSpecial: ; Clear exint ldx Channel - wrr SCCBREG,#WR_INIT_CTRL,#INIT_CTRL_CLEAR_EIRQ + + ldy #WR_INIT_CTRL + lda #INIT_CTRL_CLEAR_EIRQ + jsr writeSCCReg + sec rts Flow: ldx Channel ; Assert flow control if buffer space too low + + ldy #WR_TX_CTRL lda RtsOff - wra SCCBREG,#WR_TX_CTRL + jsr writeSCCReg + sta Stopped sec ; Interrupt handled Done: rts -Special: - rra SCCBREG,#RR_SPEC_COND_STATUS +Special:ldx Channel + ldy #RR_SPEC_COND_STATUS + jsr readSSCReg + tax and #SPEC_COND_FRAMING_ERR bne BadChar @@ -648,7 +687,10 @@ Special: and #SPEC_COND_OVERRUN_ERR beq BadChar - wrr SCCBREG,#WR_INIT_CTRL,#INIT_CTRL_CLEAR_ERR + ldy #WR_INIT_CTRL + lda #INIT_CTRL_CLEAR_ERR + jsr writeSCCReg + sec rts @@ -669,14 +711,19 @@ Again: lda SendFreeCnt ; Anything to send? lda Stopped ; Check for flow stopped bne Quit ; Bail out if it is +Wait: lda SCCBREG,x ; Check that we're ready to send tay and #INIT_STATUS_READY - bne Send + beq NotReady + tya and #INIT_STATUS_RTS ; Ready to send + bne Send + +NotReady: bit tmp1 ; Keep trying if must try hard - bmi Again + bmi Wait Quit: rts Send: ldy SendHead ; Send byte From 65ce3a6792dd73da62046a056e9793e3ff2f9a41 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 10 Oct 2023 21:17:44 +0200 Subject: [PATCH 328/520] Don't check firmware at $C200, as we don't use it Just check that we're on a IIgs instead. --- libsrc/apple2/ser/a2.gs.s | 46 ++++++++++----------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 18ee1c307..6b64252b3 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -67,7 +67,7 @@ SendBuf: .res 256 ; Send buffers: 256 bytes .data -Slot: .byte $00 ; 2 when opened +Opened: .byte $00 ; 1 when opened Channel: .byte $00 ; Channel B by default CurChanIrqFlags:.byte INTR_PENDING_RX_EXT_B @@ -138,25 +138,9 @@ ParityTable: .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) .byte $FF ; SER_PAR_MARK .byte $FF ; SER_PAR_SPACE -IdOfsTable: .byte $00 ; First firmware instruction - .byte $05 ; Pascal 1.0 ID byte - .byte $07 ; Pascal 1.0 ID byte - .byte $0B ; Pascal 1.1 generic signature byte - .byte $0C ; Device signature byte - -IdValTable: .byte $E2 ; SEP instruction - .byte $38 ; Fixed - .byte $18 ; Fixed - .byte $01 ; Fixed - .byte $31 ; Serial or parallel I/O card type 1 - -IdTableLen = * - IdValTable - ; ------------------------------------------------------------------------ ; Addresses -ZILOG_BASE := $C200 - SCCAREG := $C039 SCCBREG := $C038 SCCADATA := $C03B @@ -286,7 +270,7 @@ WriteAreg: SER_INSTALL: SER_UNINSTALL: SER_CLOSE: - ldx Slot ; Check for open port + ldx Opened ; Check for open port beq :+ ldx Channel @@ -318,7 +302,7 @@ SER_CLOSE: jsr writeSCCReg ldx #$00 - stx Slot ; Mark port as closed + stx Opened ; Mark port as closed cli : txa @@ -329,28 +313,22 @@ SER_CLOSE: ; Must return an SER_ERR_xx code in a/x. SER_OPEN: - ; Check Pascal 1.1 Firmware Protocol ID bytes - ldx #$00 -Check: ldy IdOfsTable,x - lda IdValTable,x - cmp ZILOG_BASE,y - bne NoDevice - inx - cpx #IdTableLen - bcc Check - - beq HardwareFound + bit $C082 ; Check if this is a IIgs + lda $FE1F ; https://prodos8.com/docs/technote/misc/07/ + cmp #$60 ; Everything but the IIgs has an RTS there + bne HardwareFound ; Device (hardware) not found -NoDevice: + bit $C080 lda #SER_ERR_NO_DEVICE SetupErrOut: cli ldx #$00 ; Promote char return value - stx Slot ; Mark port closed + stx Opened ; Mark port closed rts HardwareFound: + bit $C080 sei ; Disable interrupts ; Check if the handshake setting is valid @@ -499,8 +477,8 @@ IntA: StoreFlag: sta SER_FLAG - ldy #$02 ; Mark port opened - sty Slot + ldy #$01 ; Mark port opened + sty Opened cli lda #SER_ERR_OK .assert SER_ERR_OK = 0, error From e3759a3eadf5053217566ef3d4d9256837e4b39b Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 10 Oct 2023 21:33:17 +0200 Subject: [PATCH 329/520] Spare cycles on IRQ path --- libsrc/apple2/ser/a2.gs.s | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 6b64252b3..f2dff98d1 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -602,9 +602,9 @@ SER_IOCTL: ; was handled, otherwise with carry clear. SER_IRQ: - ldx #$01 ; IRQ status is always in A reg - ldy #RR_INTR_PENDING_STATUS - jsr readSSCReg + ldy #RR_INTR_PENDING_STATUS ; IRQ status is always in A reg + sty SCCAREG + lda SCCAREG and CurChanIrqFlags ; Is this ours? beq Done @@ -626,9 +626,9 @@ SER_IRQ: CheckSpecial: ; Always check IRQ special flags from Channel B (Ref page 5-24) - ; X is still 0 there. ldy #RR_IRQ_STATUS - jsr readSSCReg + sty SCCBREG + lda SCCBREG and #IRQ_MASQ cmp #IRQ_SPECIAL @@ -636,7 +636,6 @@ CheckSpecial: ; Clear exint ldx Channel - ldy #WR_INIT_CTRL lda #INIT_CTRL_CLEAR_EIRQ jsr writeSCCReg @@ -645,7 +644,6 @@ CheckSpecial: rts Flow: ldx Channel ; Assert flow control if buffer space too low - ldy #WR_TX_CTRL lda RtsOff jsr writeSCCReg From 39d60809458f59a1198222e4307e3c9008fbe12c Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 11 Oct 2023 08:47:45 +0200 Subject: [PATCH 330/520] Do the IIgs check as documented --- libsrc/apple2/ser/a2.gs.s | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index f2dff98d1..7c4085f3c 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -314,20 +314,19 @@ SER_CLOSE: SER_OPEN: bit $C082 ; Check if this is a IIgs - lda $FE1F ; https://prodos8.com/docs/technote/misc/07/ - cmp #$60 ; Everything but the IIgs has an RTS there - bne HardwareFound + sec + jsr $FE1F ; https://prodos8.com/docs/technote/misc/07/ + bcc IIgs - ; Device (hardware) not found bit $C080 - lda #SER_ERR_NO_DEVICE + lda #SER_ERR_NO_DEVICE ; Not a IIgs SetupErrOut: cli ldx #$00 ; Promote char return value stx Opened ; Mark port closed rts -HardwareFound: +IIgs: bit $C080 sei ; Disable interrupts From e90a8f3123a14736e5f37ea38feef487dac3931b Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 11 Oct 2023 12:54:12 +0200 Subject: [PATCH 331/520] Address code review comments --- libsrc/apple2/ser/a2.gs.s | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 7c4085f3c..a47b115bf 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -305,7 +305,7 @@ SER_CLOSE: stx Opened ; Mark port as closed cli -: txa +: txa ; Promote char return value rts ;---------------------------------------------------------------------------- @@ -313,13 +313,17 @@ SER_CLOSE: ; Must return an SER_ERR_xx code in a/x. SER_OPEN: - bit $C082 ; Check if this is a IIgs + ; Check if this is a IIgs (Apple II Miscellaneous TechNote #7, + ; Apple II Family Identification) sec - jsr $FE1F ; https://prodos8.com/docs/technote/misc/07/ + bit $C082 + jsr $FE1F + bit $C080 + bcc IIgs - bit $C080 lda #SER_ERR_NO_DEVICE ; Not a IIgs + SetupErrOut: cli ldx #$00 ; Promote char return value @@ -327,8 +331,7 @@ SetupErrOut: rts IIgs: - bit $C080 - sei ; Disable interrupts + sei ; Check if the handshake setting is valid ldy #SER_PARAMS::HANDSHAKE ; Handshake From c7db08b4e3c80fc0ff647a3ab83387ff77674097 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 12 Oct 2023 08:39:35 +0200 Subject: [PATCH 332/520] Move hardware check to ser_install --- libsrc/apple2/ser/a2.gs.s | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index a47b115bf..060f42735 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -270,8 +270,23 @@ WriteAreg: SER_INSTALL: SER_UNINSTALL: SER_CLOSE: + ; Check if this is a IIgs (Apple II Miscellaneous TechNote #7, + ; Apple II Family Identification) + sec + bit $C082 + jsr $FE1F + bit $C080 + + bcc IIgs + + lda #SER_ERR_NO_DEVICE ; Not a IIgs + ldx #$00 ; Promote char return value + rts + +IIgs: ldx Opened ; Check for open port beq :+ + ldx Channel ; Deactivate interrupts @@ -313,24 +328,6 @@ SER_CLOSE: ; Must return an SER_ERR_xx code in a/x. SER_OPEN: - ; Check if this is a IIgs (Apple II Miscellaneous TechNote #7, - ; Apple II Family Identification) - sec - bit $C082 - jsr $FE1F - bit $C080 - - bcc IIgs - - lda #SER_ERR_NO_DEVICE ; Not a IIgs - -SetupErrOut: - cli - ldx #$00 ; Promote char return value - stx Opened ; Mark port closed - rts - -IIgs: sei ; Check if the handshake setting is valid @@ -341,7 +338,8 @@ IIgs: InvParam: lda #SER_ERR_INIT_FAILED - jmp SetupErrOut + ldy #$00 ; Mark port closed + jmp SetupOut SetupBufs: ; Initialize buffers @@ -479,12 +477,14 @@ IntA: StoreFlag: sta SER_FLAG - ldy #$01 ; Mark port opened - sty Opened cli + + ldy #$01 ; Mark port opened lda #SER_ERR_OK - .assert SER_ERR_OK = 0, error - tax + +SetupOut: + ldx #$00 ; Promote char return value + sty Opened rts ;---------------------------------------------------------------------------- From 6e0bbb00ee3040f6aac73f15471d440d92b642f4 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 12 Oct 2023 08:53:05 +0200 Subject: [PATCH 333/520] Fix logic error on parameters checks --- libsrc/apple2/ser/a2.gs.s | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 060f42735..540bc2705 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -334,14 +334,8 @@ SER_OPEN: ldy #SER_PARAMS::HANDSHAKE ; Handshake lda (ptr1),y cmp #SER_HS_HW ; This is all we support - beq SetupBufs + bne InvParam -InvParam: - lda #SER_ERR_INIT_FAILED - ldy #$00 ; Mark port closed - jmp SetupOut - -SetupBufs: ; Initialize buffers ldy #$00 sty Stopped @@ -371,10 +365,9 @@ SetupBufs: ldy #SER_PARAMS::PARITY lda (ptr1),y ; Parity bits tay - cmp #$FF - beq InvParam pla ora ParityTable,y ; Get value + bmi InvParam ora #TX_RX_CLOCK_MUL @@ -406,10 +399,17 @@ SetBaud: tay lda BaudTable,y ; Get chip value from Low/High tables + bpl BaudOK ; Verify baudrate is supported + +InvParam: + lda #SER_ERR_INIT_FAILED + ldy #$00 ; Mark port closed + bra SetupOut + +BaudOK: tay lda BaudLowTable,y ; Get low byte - bmi InvParam ; Branch if rate not supported phy ldy #WR_BAUDL_CTRL From e138403727c1e0c80550deb5e9204f136a0e92e1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 12 Oct 2023 08:56:50 +0200 Subject: [PATCH 334/520] Move cli in common out path --- libsrc/apple2/ser/a2.gs.s | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 540bc2705..7f6bc824d 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -477,14 +477,13 @@ IntA: StoreFlag: sta SER_FLAG - cli - ldy #$01 ; Mark port opened lda #SER_ERR_OK SetupOut: ldx #$00 ; Promote char return value sty Opened + cli rts ;---------------------------------------------------------------------------- From bb92144717373670bbbe11b5f0c562e3d7c008a7 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:21:08 +0200 Subject: [PATCH 335/520] changed "inline assembler statement" to "inline assembler expression" as suggested in #2191 --- doc/cc65.sgml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index e1e9214e5..6d036c149 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -837,7 +837,7 @@ This cc65 version has some extensions to the ISO C standard. <itemize> -<item> The compiler allows to insert assembler statements into the output +<item> The compiler allows to insert assembler expressions into the output file. The syntax is <tscreen><verb> @@ -851,7 +851,7 @@ This cc65 version has some extensions to the ISO C standard. The first form is in the user namespace; and, is disabled if the <tt/-A/ switch is given. - There is a whole section covering inline assembler statements, + There is a whole section covering inline assembler expressions, <ref id="inline-asm" name="see there">. <p> @@ -1714,7 +1714,7 @@ bloated code and a slowdown. <sect>Inline assembler<label id="inline-asm"><p> -The compiler allows to insert assembler statements into the output file. The +The compiler allows to insert assembler expressions into the output file. The syntax is <tscreen><verb> @@ -1729,7 +1729,7 @@ or The first form is in the user namespace; and, is disabled by <tt><ref id="option--standard" name="--standard"></tt> if the argument is not <tt/cc65/. -The <tt/asm/ statement can be used only inside a function. Please note that +The <tt/asm/ expression can be used only inside a function. Please note that the result of an inline assembler expression is always of type <tt/void/. The contents of the string literal are preparsed by the compiler; and, inserted @@ -1741,15 +1741,15 @@ even if the ca65 assembler (which is used to translate the generated assembler code) would accept them. The built-in inline assembler is not a replacement for the full-blown macro assembler which comes with the compiler. -Note: Inline assembler statements are subject to all optimizations done by the -compiler. There currently is no way to protect an inline assembler statement +Note: Inline assembler expressions are subject to all optimizations done by the +compiler. There currently is no way to protect an inline assembler expression -- alone -- from being moved or removed completely by the optimizer. If in doubt, check the generated assembler output; or, disable optimizations (for that function). As a shortcut, you can put the <tt/volatile/ qualifier in your <tt/asm/ -statements. It will disable optimization for the functions in which those -<tt/asm volatile/ statements sit. The effect is the same as though you put +expressions. It will disable optimization for the functions in which those +<tt/asm volatile/ expressions sit. The effect is the same as though you put <tt/#pragma optimize(push, off)/ above those functions, and <tt/#pragma optimize(pop)/ below those functions. @@ -1844,7 +1844,7 @@ Arrays also can be accessed: <p> Note: Do not embed the assembler labels that are used as names of global -variables or functions into your <tt/asm/ statements. Code such as this: +variables or functions into your <tt/asm/ expressions. Code such as this: <tscreen><verb> int foo; From 6222cd91156ea037b0b654daeab814be1f900b7a Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:04:12 +0200 Subject: [PATCH 336/520] don't use a label in the bgt macro, as that may cause subtle problems. fixes #2222 --- asminc/generic.mac | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/asminc/generic.mac b/asminc/generic.mac index bc6f5924e..5e5b210f5 100644 --- a/asminc/generic.mac +++ b/asminc/generic.mac @@ -31,10 +31,8 @@ ; bgt - jump if unsigned greater .macro bgt Arg - .local L - beq L + beq *+4 bcs Arg -L: .endmacro ; ble - jump if unsigned less or equal From 8111946731a0e12462328a51b8d751f3ea58a095 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 15 Oct 2023 15:53:03 +0800 Subject: [PATCH 337/520] Fixed array subscript with a bit-field with patch by kugelfuhr. --- src/cc65/expr.c | 5 +++-- test/val/bug2186.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 test/val/bug2186.c diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 8b0824c31..aef0679d4 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3001,8 +3001,9 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) } if (!AddDone) { - if (ED_IsLocQuasiConst (&Expr2) && - rscale == 1 && + if (ED_IsLocQuasiConst (&Expr2) && + !IsTypeBitField (Expr2.Type) && + rscale == 1 && CheckedSizeOf (rhst) == SIZEOF_CHAR) { /* Change the order back */ RemoveCode (&Mark); diff --git a/test/val/bug2186.c b/test/val/bug2186.c new file mode 100644 index 000000000..143111d97 --- /dev/null +++ b/test/val/bug2186.c @@ -0,0 +1,39 @@ +/* Bug #2186 - Wrong array indexing when index comes from bit-field */ + +#include <stdio.h> + +unsigned failures; + +typedef struct { + char flag : 1; + char index : 7; +} weird_type; + +const char array[] = { '6', '5', '0', '2' }; + +weird_type data; + +int main(void) { + data.flag = 1; + + data.index = 0; + if (array[data.index] != array[0]) + { + ++failures; + printf("Got '%c', expected '%c'\n", array[data.index], array[0]); + } + + data.index = 1; + if (array[data.index] != array[1]) + { + ++failures; + printf("Got '%c', expected '%c'\n", array[data.index], array[1]); + } + + if (failures > 0) + { + printf("Failures: %u\n", failures); + } + + return failures; +} \ No newline at end of file From 21b6cbb66e63ff6b7c76cf04f3d1e45463d61183 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 15 Oct 2023 16:43:40 +0800 Subject: [PATCH 338/520] Updated documentation on string literal translation in asm() and _Static_assert(). --- doc/cc65.sgml | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 6d036c149..37e3e493d 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -837,21 +837,21 @@ This cc65 version has some extensions to the ISO C standard. <itemize> -<item> The compiler allows to insert assembler expressions into the output - file. The syntax is +<item> The compiler allows to insert inline assembler code in the form of the + <tt/asm/ expression into the output file. The syntax is <tscreen><verb> - asm [optional volatile] (<string literal>[, optional parameters]) ; + asm [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> or <tscreen><verb> - __asm__ [optional volatile] (<string literal>[, optional parameters]) ; + __asm__ [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> The first form is in the user namespace; and, is disabled if the <tt/-A/ switch is given. - There is a whole section covering inline assembler expressions, + There is a whole section covering the inline assembler, <ref id="inline-asm" name="see there">. <p> @@ -1008,6 +1008,13 @@ This cc65 version has some extensions to the ISO C standard. <tt/_Static_assert/ is also available as the macro <tt/static_assert/ in <tt/assert.h/. + Note: The string literal as the message in the <tt/_Static_assert/ + declaration is not subject to string literal translation (see + <tt/<ref id="pragma-charmap" name="#pragma charmap()">/) and will + always be in the host encoding. On the other hand, any character or + string literals present in the condition expression of the + <tt/_Static_assert/ declaration will be translated as usual. + <item> cc65 supports bit-fields of any integral type that is int-sized or smaller, and enumerated types with those types as their underlying type. (Only <tt/int/, <tt/signed int/, and <tt/unsigned int/ are @@ -1317,7 +1324,9 @@ parameter with the <tt/#pragma/. <sect1><tt>#pragma charmap (<index>, <code>)</tt><label id="pragma-charmap"><p> - Each literal string and each literal character in the source is translated + Each literal string and each literal character in the preprocessed source, + except when used in an <tt/asm/ expression as the inline assembler code or + in a <tt/_Static_assert/ declaration as the failure message, is translated by use of a translation table. That translation table is preset when the compiler is started, depending on the target system; for example, to map ISO-8859-1 characters into PETSCII if the target is a Commodore machine. @@ -1714,23 +1723,23 @@ bloated code and a slowdown. <sect>Inline assembler<label id="inline-asm"><p> -The compiler allows to insert assembler expressions into the output file. The -syntax is +The compiler allows to insert inline assembler code in the form of the <tt/asm/ +expression into the output file. The syntax is <tscreen><verb> - asm [optional volatile] (<string literal>[, optional parameters]) ; + asm [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> or <tscreen><verb> - __asm__ [optional volatile] (<string literal>[, optional parameters]) ; + __asm__ [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> <p> The first form is in the user namespace; and, is disabled by <tt><ref id="option--standard" name="--standard"></tt> if the argument is not <tt/cc65/. -The <tt/asm/ expression can be used only inside a function. Please note that -the result of an inline assembler expression is always of type <tt/void/. +The <tt/asm/ expression can be used only inside a function. The result of an +<tt/asm/ expression is always of type <tt/void/. The contents of the string literal are preparsed by the compiler; and, inserted into the generated assembly output, so that it can be processed further by @@ -1757,6 +1766,13 @@ The string literal may contain format specifiers from the following list. For each format specifier, an argument is expected which is inserted instead of the format specifier, before passing the assembly code line to the backend. +Note: The string literal as the inline assembler code itself in the <tt/asm/ +expression is not subject to string literal translation (see +<tt/<ref id="pragma-charmap" name="#pragma charmap()">/) and will always +be in the host encoding. On the other hand, all character and string literals +as the arguments for replacing the format specifiers will be translated as +usual. + <itemize> <item><tt/%b/ - Numerical 8-bit value <item><tt/%w/ - Numerical 16-bit value From 96e893795b5aa992068d82f33bbc19428c55acc3 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 9 Oct 2023 10:48:01 +0200 Subject: [PATCH 339/520] Better check for 6551 --- libsrc/apple2/ser/a2.ssc.s | 72 ++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 022ef2fd5..82ac6690f 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -25,6 +25,8 @@ .include "ser-kernel.inc" .include "ser-error.inc" + .importzp tmp1, tmp2 + .macpack module .macpack cpu @@ -141,35 +143,16 @@ ParityTable: ; Table used to translate RS232 parity param .byte $A0 ; SER_PAR_MARK .byte $E0 ; SER_PAR_SPACE -IdOfsTable: ; Table of bytes positions, used to check five +IdOfsTable: ; Table of bytes positions, used to check four ; specific bytes on the slot's firmware to make - ; sure this is an SSC (or Apple //c comm port) - ; firmware that drives an ACIA 6551 chip. - ; - ; The SSC firmware and the Apple //c(+) comm - ; port firmware all begin with a BIT instruction. - ; The IIgs, on the other hand, has a - ; Zilog Z8530 chip and its firmware starts with - ; a SEP instruction. We don't want to load this - ; driver on the IIgs' serial port. We'll - ; differentiate the firmware on this byte. - ; - ; The next four bytes we check are the Pascal - ; Firmware Protocol Bytes that identify a - ; serial card. Those are the same bytes for - ; SSC firmwares, Apple //c firmwares and IIgs - ; Zilog Z8530 firmwares - which is the reason - ; we have to check for the firmware's first - ; instruction too. - .byte $00 ; First instruction + ; sure this is an SSC (or Apple //c comm port). .byte $05 ; Pascal 1.0 ID byte .byte $07 ; Pascal 1.0 ID byte .byte $0B ; Pascal 1.1 generic signature byte .byte $0C ; Device signature byte -IdValTable: ; Table of expected values for the five checked +IdValTable: ; Table of expected values for the four checked ; bytes - .byte $2C ; BIT .byte $38 ; ID Byte 0 (from Pascal 1.0), fixed .byte $18 ; ID Byte 1 (from Pascal 1.0), fixed .byte $01 ; Generic signature for Pascal 1.1, fixed @@ -210,6 +193,48 @@ SER_CLOSE: stx Index ; Mark port as closed rts +;---------------------------------------------------------------------------- +; Internal function to test whether we're really talking with a 6551, +; or at least it really looks like it. +; Input: X= Index +; Output: carry set if this is very probably a 6551, clear if not + +Verify6551: + lda ACIA_STATUS,x ; Save current values in what we expect to be + pha ; the ACIA status register + lda ACIA_CMD,x ; and command register. So we can restore them + sta tmp1 ; if this isn't a 6551. + + and #$01 + bne NotAcia ; We expect command register bit 0 to be 0 + + lda tmp1 + ora #$01 ; Enable receiver/transmitter + sta ACIA_CMD,x + sta tmp2 ; Store it for comparison + + lda ACIA_CMD,x ; Is command register what we wrote? + cmp tmp2 + bne NotAcia + + sta ACIA_STATUS,x ; Reset Acia (value written is not important) + + lda ACIA_CMD,x ; Is the value back to the original value? + cmp tmp1 + bne NotAcia + + pla ; Clear stack of leftovers + clc + rts + +NotAcia: + lda tmp1 ; Restore saved bytes, they're probably needed + sta ACIA_CMD,x + pla + sta ACIA_STATUS,x + sec + rts + ;---------------------------------------------------------------------------- ; SER_OPEN: A pointer to a ser_params structure is passed in ptr1. ; Must return an SER_ERR_xx code in a/x. @@ -239,6 +264,9 @@ SER_OPEN: .endif tax + jsr Verify6551 + bcs NoDev + ; Check if the handshake setting is valid ldy #SER_PARAMS::HANDSHAKE lda (ptr1),y From 6e035c864e06560d560ecaca4aeb019e156576e7 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 12 Oct 2023 07:58:40 +0200 Subject: [PATCH 340/520] Apple2 SSC: on IIgs, verify slot mode If the slot is not in "Your Card" mode, there's no way it is an SSC --- libsrc/apple2/ser/a2.ssc.s | 112 ++++++++++++++----------------------- 1 file changed, 43 insertions(+), 69 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 82ac6690f..411c72dcd 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -25,8 +25,6 @@ .include "ser-kernel.inc" .include "ser-error.inc" - .importzp tmp1, tmp2 - .macpack module .macpack cpu @@ -72,6 +70,8 @@ ACIA_STATUS := ACIA+1 ; Status register ACIA_CMD := ACIA+2 ; Command register ACIA_CTRL := ACIA+3 ; Control register +SLTROMSEL := $C02D ; For Apple IIgs slot verification + ;---------------------------------------------------------------------------- ; Global variables @@ -193,54 +193,35 @@ SER_CLOSE: stx Index ; Mark port as closed rts -;---------------------------------------------------------------------------- -; Internal function to test whether we're really talking with a 6551, -; or at least it really looks like it. -; Input: X= Index -; Output: carry set if this is very probably a 6551, clear if not - -Verify6551: - lda ACIA_STATUS,x ; Save current values in what we expect to be - pha ; the ACIA status register - lda ACIA_CMD,x ; and command register. So we can restore them - sta tmp1 ; if this isn't a 6551. - - and #$01 - bne NotAcia ; We expect command register bit 0 to be 0 - - lda tmp1 - ora #$01 ; Enable receiver/transmitter - sta ACIA_CMD,x - sta tmp2 ; Store it for comparison - - lda ACIA_CMD,x ; Is command register what we wrote? - cmp tmp2 - bne NotAcia - - sta ACIA_STATUS,x ; Reset Acia (value written is not important) - - lda ACIA_CMD,x ; Is the value back to the original value? - cmp tmp1 - bne NotAcia - - pla ; Clear stack of leftovers - clc - rts - -NotAcia: - lda tmp1 ; Restore saved bytes, they're probably needed - sta ACIA_CMD,x - pla - sta ACIA_STATUS,x - sec - rts - ;---------------------------------------------------------------------------- ; SER_OPEN: A pointer to a ser_params structure is passed in ptr1. ; Must return an SER_ERR_xx code in a/x. +; Note: Hardware checks are done in SER_OPEN instead of SER_INSTALL, +; because they depend on the selected slot, and we can't select the slot +; before SER_INSTALL. SER_OPEN: - ldx #<$C000 + ; Check if this is a IIgs (Apple II Miscellaneous TechNote #7, + ; Apple II Family Identification) + sec + bit $C082 + jsr $FE1F + bit $C080 + + bcs NotIIgs + + ; We're on a IIgs. For every slot N, either bit N of $C02D is + ; 0 for the internal ROM, or 1 for "Your Card". Let's make sure + ; that slot N's bit is set to 1, otherwise, that can't be an SSC. + + ldy Slot + lda SLTROMSEL +: lsr + dey + bpl :- ; Shift until slot's bit ends in carry + bcc NoDev + +NotIIgs:ldx #<$C000 stx ptr2 lda #>$C000 ora Slot @@ -249,8 +230,12 @@ SER_OPEN: : ldy IdOfsTable,x ; Check Pascal 1.1 Firmware Protocol ID bytes lda IdValTable,x cmp (ptr2),y - bne NoDev - inx + beq ByteOK + +NoDev: lda #SER_ERR_NO_DEVICE + bne Out + +ByteOK: inx cpx #IdTableLen bcc :- @@ -264,15 +249,16 @@ SER_OPEN: .endif tax - jsr Verify6551 - bcs NoDev - ; Check if the handshake setting is valid ldy #SER_PARAMS::HANDSHAKE lda (ptr1),y cmp #SER_HS_HW ; This is all we support - bne InvParm + beq HandshakeOK + lda #SER_ERR_INIT_FAILED + bne Out + +HandshakeOK: ldy #$00 ; Initialize buffers sty Stopped sty RecvHead @@ -289,9 +275,12 @@ SER_OPEN: lda (ptr1),y ; Baudrate index tay lda BaudTable,y ; Get 6551 value - bmi InvBaud ; Branch if rate not supported - sta tmp1 + bpl BaudOK ; Check that baudrate is supported + lda #SER_ERR_BAUD_UNAVAIL + bne Out + +BaudOK: sta tmp1 ldy #SER_PARAMS::DATABITS lda (ptr1),y ; Databits index tay @@ -322,22 +311,7 @@ SER_OPEN: ; Done stx Index ; Mark port as open lda #SER_ERR_OK - .assert SER_ERR_OK = 0, error - tax - rts - - ; Device (hardware) not found -NoDev: lda #SER_ERR_NO_DEVICE - ldx #$00 ; Promote char return value - rts - - ; Invalid parameter -InvParm:lda #SER_ERR_INIT_FAILED - ldx #$00 ; Promote char return value - rts - - ; Baud rate not available -InvBaud:lda #SER_ERR_BAUD_UNAVAIL +Out: ldx #$00 ; Promote char return value rts From 3107f34ec9e9aba16c94be986d9a971166f9dd1b Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sat, 14 Oct 2023 21:51:31 +0200 Subject: [PATCH 341/520] Put back 6551 test as a last verification --- libsrc/apple2/ser/a2.ssc.s | 51 +++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 411c72dcd..c7890c591 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -145,7 +145,7 @@ ParityTable: ; Table used to translate RS232 parity param IdOfsTable: ; Table of bytes positions, used to check four ; specific bytes on the slot's firmware to make - ; sure this is an SSC (or Apple //c comm port). + ; sure this is a serial card. .byte $05 ; Pascal 1.0 ID byte .byte $07 ; Pascal 1.0 ID byte .byte $0B ; Pascal 1.1 generic signature byte @@ -230,12 +230,8 @@ NotIIgs:ldx #<$C000 : ldy IdOfsTable,x ; Check Pascal 1.1 Firmware Protocol ID bytes lda IdValTable,x cmp (ptr2),y - beq ByteOK - -NoDev: lda #SER_ERR_NO_DEVICE - bne Out - -ByteOK: inx + bne NoDev + inx cpx #IdTableLen bcc :- @@ -249,8 +245,45 @@ ByteOK: inx .endif tax + ; Check that this works like an ACIA 6551 is expected to work + + lda ACIA_STATUS,x ; Save current values in what we expect to be + sta tmp1 ; the ACIA status register + lda ACIA_CMD,x ; and command register. So we can restore them + sta tmp2 ; if this isn't a 6551. + + and #%11111110 + sta ACIA_CMD,x ; Disable receiver/transmitter + + lda ACIA_CMD,x ; Reload register + and #%00000001 + bne NotAcia ; We expect command register bit 0 to be 0 + + lda tmp2 + ora #%00000001 ; Enable receiver/transmitter + sta ACIA_CMD,x + sta tmp3 ; Store it for comparison + + lda ACIA_CMD,x ; Is command register what we wrote? + cmp tmp3 + bne NotAcia + + sta ACIA_STATUS,x ; Reset Acia (value written is not important) + + lda ACIA_CMD,x ; Is the Acia disabled now? + and #%00000001 + beq AciaOK + +NotAcia:lda tmp2 ; Restore original values + sta ACIA_CMD,x + lda tmp1 + sta ACIA_STATUS,x + +NoDev: lda #SER_ERR_NO_DEVICE + bne Out + ; Check if the handshake setting is valid - ldy #SER_PARAMS::HANDSHAKE +AciaOK: ldy #SER_PARAMS::HANDSHAKE lda (ptr1),y cmp #SER_HS_HW ; This is all we support beq HandshakeOK @@ -481,4 +514,4 @@ Send: ldy SendHead ; Get first byte to send sta ACIA_DATA,x ; Send it inc SendHead inc SendFreeCnt - jmp NextByte ; And try next one + bne NextByte ; And try next one From 882f6fd10342529e7f6407fe8bdbe77a9663dbaf Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 15 Oct 2023 15:26:58 +0200 Subject: [PATCH 342/520] Fix ACIA check so we don't enable RX/TX with IRQs on --- libsrc/apple2/ser/a2.ssc.s | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index c7890c591..762050a66 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -252,32 +252,23 @@ NotIIgs:ldx #<$C000 lda ACIA_CMD,x ; and command register. So we can restore them sta tmp2 ; if this isn't a 6551. - and #%11111110 - sta ACIA_CMD,x ; Disable receiver/transmitter - - lda ACIA_CMD,x ; Reload register - and #%00000001 - bne NotAcia ; We expect command register bit 0 to be 0 - - lda tmp2 - ora #%00000001 ; Enable receiver/transmitter + ldy #%00000010 ; Disable TX/RX, disable IRQ +: tya sta ACIA_CMD,x - sta tmp3 ; Store it for comparison - - lda ACIA_CMD,x ; Is command register what we wrote? - cmp tmp3 + cmp ACIA_CMD,x ; Verify what we stored is there bne NotAcia + iny ; Enable TX/RX, disable IRQ + cpy #%00000100 + bne :- + sta ACIA_STATUS,x ; Reset ACIA + lda ACIA_CMD,x ; Check that RX/TX is disabled + lsr + bcc AciaOK - sta ACIA_STATUS,x ; Reset Acia (value written is not important) - - lda ACIA_CMD,x ; Is the Acia disabled now? - and #%00000001 - beq AciaOK - -NotAcia:lda tmp2 ; Restore original values - sta ACIA_CMD,x - lda tmp1 - sta ACIA_STATUS,x +NotAcia:lda tmp2 ; Restore original values + sta ACIA_CMD,x + lda tmp1 + sta ACIA_STATUS,x NoDev: lda #SER_ERR_NO_DEVICE bne Out From 3c864fc7d8fc91a11acc8818fcb60b485b0001f4 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt <ol.sc@web.de> Date: Sun, 15 Oct 2023 17:00:15 +0200 Subject: [PATCH 343/520] Minimal style adjustment --- libsrc/apple2/ser/a2.ssc.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 762050a66..f48e948ed 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -265,7 +265,7 @@ NotIIgs:ldx #<$C000 lsr bcc AciaOK -NotAcia:lda tmp2 ; Restore original values +NotAcia:lda tmp2 ; Restore original values sta ACIA_CMD,x lda tmp1 sta ACIA_STATUS,x From 178573a12863d7d50149f01892193eada528b206 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Tue, 17 Oct 2023 17:57:19 +0800 Subject: [PATCH 344/520] Fixed inlined strlen when it takes a string literal with extra characters after the first '\0'. Added testcases. --- src/cc65/stdfunc.c | 8 +- test/val/bug2207.c | 569 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 575 insertions(+), 2 deletions(-) create mode 100644 test/val/bug2207.c diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 246bce192..6d74dd386 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -833,7 +833,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsLocLiteral (&Arg2.Expr) && IS_Get (&WritableStrings) == 0 && - GetLiteralSize (Arg2.Expr.V.LVal) == 1 && + 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 @@ -1226,9 +1226,13 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** at runtime. */ if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { + /* Get the length of the C string within the string literal. + ** Note: Keep in mind that the literal could contain '\0' in it. + */ + size_t Len = strnlen (GetLiteralStr (Arg.V.LVal), GetLiteralSize (Arg.V.LVal) - 1); /* Constant string literal */ - ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t); + ED_MakeConstAbs (Expr, Len, type_size_t); /* We don't need the literal any longer */ ReleaseLiteral (Arg.V.LVal); diff --git a/test/val/bug2207.c b/test/val/bug2207.c new file mode 100644 index 000000000..df4887fe3 --- /dev/null +++ b/test/val/bug2207.c @@ -0,0 +1,569 @@ +#include <assert.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + + + +/********* Macros *********/ + +#define DEBUG_DETAIL 0 + +/* TODO: enable these when they can be compiled */ +#define SKIP_VOID_RETURN_VALUE_TESTS 1 +#define SKIP_INLINED_ARG_SIDE_EFFECT_TESTS 1 + +#define CHECK(C) \ + if (!(C)) { \ + ++failures; \ + print_header(); \ + printf(" failed: expected %s\n", #C); \ + } + +#define CHECK_RANGE(L, R, D, N) \ + index = my_memcmp(L, R, D, N); \ + if (index >= 0) { \ + ++failures; \ + print_header(); \ + printf(" failed: %s vs %s dismatch at [%d]\n", #L, #R, index); \ + } + +#define MEM_SIZE 512 +#define STACK_SIZE 160 +#define ZP_SIZE 8 +#define MAGIC_SIZE 129 + +#define BROKEN_STR "hello\0!" +#define BROKEN_STR_SIZE 6 +#define BROKEN_STR_LEN 5 + + + +/********* Data *********/ + +unsigned failures; +int need_header = 1; +const char* test_header; + +static int x; +static int y; +static int z; +static int index; + +static char mem_dst[MEM_SIZE]; +static char mem_src[MEM_SIZE]; +static char mem_ori[MEM_SIZE]; + +#pragma data-name(push, "ZEROPAGE", "zp") +#pragma bss-name(push, "ZEROPAGE", "zp") +static char zp_src[ZP_SIZE]; +static char zp_dst[ZP_SIZE]; +static char zp_ori[ZP_SIZE]; +static char* p_zp_src; +static char* p_zp_dst; +static char* p_zp_ori; +#pragma bss-name(pop) +#pragma data-name(pop) + + + +/********* Helpers *********/ + +void my_memfill(void *dst, int init, size_t size) +{ + unsigned i; + + for (i = 0; i < size; ++i) + { + ((char*)dst)[i] = init; + init += 3; + } +} + +void my_memset(void *dst, int val, size_t size) +{ + unsigned i; + + for (i = 0; i < size; ++i) + { + ((unsigned char *)dst)[i] = val; + } +} + +void my_memcpy(void *dst, const void *src, size_t size) +{ + unsigned i; + + for (i = 0; i < size; ++i) + { + ((char*)dst)[i] = ((char*)src)[i]; + } +} + +int my_memcmp(const void *dst, const void *src, int diff, size_t size) +{ + unsigned i; + + diff %= 256; + for (i = 0; i < size; ++i) + { + if ((unsigned char)(((unsigned char*)dst)[i] - ((unsigned char*)src)[i]) != diff) + { +#if DEBUG_DETAIL + /* DEBUG */ + printf("%d vs %d\n", (unsigned char)(((unsigned char*)dst)[i] - ((unsigned char*)src)[i]), diff); +#endif + return i; + } + } + + return -1; +} + +void* mul_two(void* p, int* px) +{ + *px *= 2; + return p; +} + +void* add_one(void* p, int* px) +{ + *px += 1; + return p; +} + +void* negate(void* p, int* px) +{ + *px = -*px; + return p; +} + +void set_header(const char* name) +{ + if (need_header == 0) + { + printf("\n"); + } + test_header = name; + need_header = 1; +} + +void print_header(void) +{ + if (need_header) + { + need_header = 0; + printf("%s test\n", test_header); + } +} + + + +/********* Tests *********/ + +/* memcpy */ +void test_memcpy(void) +{ + const char *name = 0; + unsigned size = 0; + void *p; + + /* init */ + my_memfill(mem_ori, 1000, sizeof mem_ori); + my_memfill(zp_ori, 1000, sizeof zp_ori); + p_zp_src = zp_src; + p_zp_dst = zp_dst; + p_zp_ori = zp_ori; + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* arg3 == 0 */ + set_header("p = memcpy(arg1, arg2, 0)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + x = 42; + y = -42; + z = 36; + p = memcpy(mul_two(mem_dst, &x), add_one(mem_src, &y), (negate(0, &z), 0)); + CHECK(p == mem_dst); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); + CHECK_RANGE(mem_src, mem_ori, 1000, size); +#endif + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* Check if the arguments are still generated if the return value is unused. + ** We have this question since the first argument could be directly used as + ** the return value when this function gets inlined. + */ +#if !SKIP_VOID_RETURN_VALUE_TESTS + set_header("(void)memcpy(arg1, arg2, 0)"); +#else + set_header("memcpy(arg1, arg2, 0)"); +#endif + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + x = 42; + y = -42; + z = 36; +#if !SKIP_VOID_RETURN_VALUE_TESTS + (void) +#endif + memcpy(mul_two(mem_dst, &x), add_one(mem_src, &y), (negate(0, &z), 0)); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); + CHECK_RANGE(mem_src, mem_ori, 1000, size); +#endif + + /* The memcpy inliner will give up with further optimizations if any of + ** the arguments have side effects. + */ + + /* arg1: const addr, arg2: const addr, arg3 <= 129 */ + set_header("memcpy(const_addr_1, const_addr_2, 129) w/ side-effects"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + x = 42; + y = -42; + z = 36; + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mul_two(mem_dst, &x), add_one(mem_src, &y), (negate(0, &z), MAGIC_SIZE)); + size = MAGIC_SIZE; + CHECK(p == mem_dst); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, MEM_SIZE - size); + + /* arg1: (void*)ptr, arg2: const_addr_2 */ + set_header("memcpy((void*)ptr, const_addr_2, 129)"); + { + void *ptr = mem_dst; + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy((void*)ptr, mem_src, MAGIC_SIZE); + size = MAGIC_SIZE; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, MEM_SIZE - size); + } + + /* arg1: const addr, arg2: const addr, arg3 <= 129 */ + set_header("memcpy(const_addr_1, const_addr_2, 129)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, mem_src, MAGIC_SIZE); + size = MAGIC_SIZE; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, MEM_SIZE - size); + + /* arg1: const addr, arg2: const addr, arg3 <= 256 */ + set_header("memcpy(const_addr_1, const_addr_2, 256)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, mem_src, 256); + size = 256; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, size - size); + + /* arg1: ptr on zp, arg2: ptr on zp, arg3 <= 256 */ + set_header("memcpy(p_on_zp_1, p_on_zp_2, 4)"); + /* We cannot allocate 256 bytes on the zeropage, unfortunately */ + my_memfill(zp_dst, 3000, ZP_SIZE); + my_memfill(zp_src, 2000, ZP_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(p_zp_dst, p_zp_src, ZP_SIZE / 2); + size = ZP_SIZE / 2; + CHECK(p == zp_dst); + CHECK_RANGE(zp_dst, zp_ori, 1000, size); + CHECK_RANGE(zp_dst + size, zp_ori + size, 2000, ZP_SIZE - size); + + /* arg1: on stack, arg2: const addr, arg3 <= 129 */ + set_header("memcpy(on_stack_1, const_addr_2, 129)"); + { + char sp_dst[STACK_SIZE]; + my_memfill(sp_dst, 3000, STACK_SIZE); + my_memfill(mem_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(sp_dst, mem_src, MAGIC_SIZE); + size = MAGIC_SIZE; + CHECK(p == sp_dst); + CHECK_RANGE(sp_dst, mem_ori, 1000, size); + CHECK_RANGE(sp_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } + + /* arg1: on stack, arg2: const addr, arg3 <= 256 */ + set_header("memcpy(on_stack_1, const_addr_2, 144)"); + { + char sp_dst[STACK_SIZE]; + /* We cannot allocate 256 bytes on the stack, unfortunately */ + my_memfill(sp_dst, 3000, STACK_SIZE); + my_memfill(mem_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(sp_dst, mem_src, 144); + size = 144; + CHECK(p == sp_dst); + CHECK_RANGE(sp_dst, mem_ori, 1000, size); + CHECK_RANGE(sp_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } + + /* arg1: const addr, arg2: on stack, arg3 <= 129 */ + set_header("memcpy(const_addr_1, on_stack_2, 129)"); + { + char sp_src[STACK_SIZE]; + /* We cannot allocate 256 bytes on the stack, unfortunately */ + my_memfill(mem_dst, 3000, STACK_SIZE); + my_memfill(sp_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, sp_src, 129); + size = 129; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } + + /* arg1: const addr, arg2: on stack, arg3 <= 256 */ + set_header("memcpy(const_addr_1, on_stack_2, 144)"); + { + char sp_src[STACK_SIZE]; + /* We cannot allocate 256 bytes on the stack, unfortunately */ + my_memfill(mem_dst, 3000, STACK_SIZE); + my_memfill(sp_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, sp_src, 144); + size = 144; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } +} + +/* memset */ +void test_memset(void) +{ + const char *name = 0; + unsigned size = 0; + void *p; + + /* init */ + my_memfill(mem_ori, 1000, sizeof mem_ori); + my_memfill(zp_ori, 1000, sizeof zp_ori); + p_zp_dst = zp_dst; + p_zp_ori = zp_ori; + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* arg3 == 0 */ + set_header("p = memset(arg1, arg2, 0)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + x = 42; + y = -42; + z = 36; + p = memset(mul_two(mem_dst, &x), (add_one(0, &y), 42), (negate(0, &z), 0)); + CHECK(p == mem_dst); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); +#endif + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* Check if the arguments are still generated if the return value is unused. + ** We have this question since the first argument could be directly used as + ** the return value when this function gets inlined. + */ +#if !SKIP_VOID_RETURN_VALUE_TESTS + set_header("(void)memset(arg1, arg2, 0)"); +#else + set_header("memset(arg1, arg2, 0)"); +#endif + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + x = 42; + y = -42; + z = 36; +#if !SKIP_VOID_RETURN_VALUE_TESTS + (void) +#endif + memset(mul_two(mem_dst, &x), (add_one(0, &y), 42), (negate(0, &z), 0)); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); +#endif +} + +/* strcmp */ +void test_strcmp(void) +{ + const char *name = 0; + unsigned size = 0; + int res = 0; + + /* init */ + p_zp_dst = zp_dst; + + /* Compared to zero-length C string literal */ + set_header("strcmp(arg1, \"\\0Z\")"); + { + char str[] = "AA"; + size = sizeof str; + my_memcpy(zp_dst, str, size); + x = 42; + y = -42; + + res = strcmp(zp_dst, "\0Z"); + CHECK(res > 0); + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + res = strcmp(mul_two(p_zp_dst, &x), (add_one(0, &y), "\0Z")); + CHECK(res > 0); + CHECK(x == 84); + CHECK(y == -41); +#endif + } +} + +/* strcpy */ +void test_strcpy(void) +{ + const char *name = 0; + unsigned size = 0; + void *p; + char stack_dst[ZP_SIZE]; + char stack_src[ZP_SIZE]; + + /* init */ + { + char str[] = BROKEN_STR; + size = BROKEN_STR_LEN + 1; + my_memfill(mem_src, 1000, ZP_SIZE); + my_memfill(stack_src, 1000, ZP_SIZE); + my_memfill(zp_src, 1000, ZP_SIZE); + my_memcpy(mem_src, str, size); + my_memcpy(stack_src, str, size); + my_memcpy(zp_src, str, size); + p_zp_src = zp_src; + p_zp_dst = zp_dst; + p_zp_dst = zp_dst; + } + + /* arg1: const addr, arg2: const addr */ + set_header("strcpy(const_addr_1, const_addr_2)"); + my_memfill(mem_dst, 2000, ZP_SIZE); + size = BROKEN_STR_LEN + 1; + p = strcpy(mem_dst, mem_src); + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_src, 0, size); + CHECK_RANGE(mem_dst + size, mem_src + size, 1000, ZP_SIZE - size); + + /* arg1: ptr on zp, arg2: ptr on zp */ + set_header("strcpy(p_on_zp_1, p_on_zp_2)"); + my_memfill(zp_dst, 2000, ZP_SIZE); + size = BROKEN_STR_LEN + 1; + p = strcpy(zp_dst, zp_src); + CHECK(p == zp_dst); + CHECK_RANGE(zp_dst, zp_src, 0, size); + CHECK_RANGE(zp_dst + size, zp_src + size, 1000, ZP_SIZE - size); + + /* arg1: on stack, arg2: on stack */ + set_header("strcpy(on_stack_1, on_stack_2)"); + my_memfill(stack_dst, 2000, ZP_SIZE); + size = BROKEN_STR_LEN + 1; + p = strcpy(stack_dst, stack_src); + CHECK(p == stack_dst); + CHECK_RANGE(stack_dst, stack_src, 0, size); + CHECK_RANGE(stack_dst + size, stack_src + size, 1000, ZP_SIZE - size); + + /* TODO: args side-effects tests */ +} + +/* strlen */ +void test_strlen(void) +{ + const char *name = 0; + size_t len; + + /* init */ + { + char str[] = BROKEN_STR; + my_memcpy(mem_ori, str, ZP_SIZE); + //my_memcpy(stack_ori, str, ZP_SIZE); + my_memcpy(zp_ori, str, ZP_SIZE); + p_zp_ori = zp_ori; + } + + /* arg1: string_literal */ + set_header("strlen(\"hello\\0!\")"); + x = 42; + y = -42; + len = strlen((mul_two(0, &x), BROKEN_STR)); + (void)strlen((add_one(0, &y), BROKEN_STR)); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); + + /* arg1: array with const addr */ + set_header("strlen(array_const_addr[8])"); + x = 42; + y = -42; + len = strlen((mul_two(0, &x), zp_ori)); + (void)strlen((add_one(0, &y), zp_ori)); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); + + /* arg1: array on stack */ + set_header("strlen(array_on_stack[8])"); + { + char p_on_stack[] = BROKEN_STR; + x = 42; + y = -42; + len = strlen((mul_two(0, &x), p_on_stack)); + strlen((add_one(0, &y), p_on_stack)); + CHECK(sizeof p_on_stack == 8); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); + } + + /* arg1: ptr on zp */ + set_header("strlen(ptr_on_zp)"); + x = 42; + y = -42; + len = strlen((mul_two(0, &x), p_zp_ori)); + (void)strlen((add_one(0, &y), p_zp_ori)); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); +} + +int main(void) +{ + test_memcpy(); + test_memset(); + test_strcmp(); + test_strcpy(); + test_strlen(); + + if (failures > 0) + { + printf("failed items: %u\n", failures); + } + return failures; +} From 3e602682438c6916d6ae0e50d833d026f8201ef7 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:39:15 +0200 Subject: [PATCH 345/520] add newline at the end --- test/val/bug2186.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/val/bug2186.c b/test/val/bug2186.c index 143111d97..24546fb3d 100644 --- a/test/val/bug2186.c +++ b/test/val/bug2186.c @@ -36,4 +36,4 @@ int main(void) { } return failures; -} \ No newline at end of file +} From c52427fc65e565d32d9adc2857b669f36211bd9f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 21 Oct 2023 23:52:14 +0800 Subject: [PATCH 346/520] Minor fix for error recovery from duplicated struct members. --- src/cc65/symtab.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 951ed9e5e..5efed63e8 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -793,8 +793,6 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) */ Error ("Redeclaration of enumerator constant '%s'", Sym->Name); Sym = 0; - } else if (Flags & SC_STRUCTFIELD) { - Error ("Duplicate member '%s'", Sym->Name); } } } @@ -998,6 +996,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, { /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (FieldTab, Name, HashStr (Name)); + if (Entry) { /* We have a symbol with this name already */ @@ -1044,6 +1043,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val { /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + if (Entry) { if ((Entry->Flags & SC_CONST) != SC_CONST) { Error ("Symbol '%s' is already different kind", Name); @@ -1114,6 +1114,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (LabelTab, Name, HashStr (Name)); + if (Entry) { if (SymIsDef (Entry) && (Flags & SC_DEF) != 0) { @@ -1224,6 +1225,7 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + if (Entry) { int CheckExtern = 0; if ((Flags & SC_STRUCTFIELD) == 0) { @@ -1254,6 +1256,9 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Error ("Static declaration of '%s' follows extern declaration", Name); Entry = 0; } + } else if ((Flags & SC_STRUCTFIELD) != 0) { + Error ("Duplicate member '%s'", Entry->Name); + Entry = 0; } } From e5bbdfa995bcef1618d17c485dbb1eb081d5f59b Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 21 Oct 2023 23:56:01 +0800 Subject: [PATCH 347/520] Separated boolean optimizers from bitwise unary operator optimizers. Renamed OptCmp6 to OptBoolCmp. --- src/cc65.vcxproj | 6 +- src/cc65/codeopt.c | 9 +- src/cc65/{coptneg.c => coptbool.c} | 453 ++++++++++++++++------------- src/cc65/{coptneg.h => coptbool.h} | 78 ++--- src/cc65/coptcmp.c | 243 ---------------- src/cc65/coptcmp.h | 20 -- src/cc65/coptunary.c | 236 +++++++++++++++ src/cc65/coptunary.h | 96 ++++++ 8 files changed, 619 insertions(+), 522 deletions(-) rename src/cc65/{coptneg.c => coptbool.c} (66%) rename src/cc65/{coptneg.h => coptbool.h} (80%) create mode 100644 src/cc65/coptunary.c create mode 100644 src/cc65/coptunary.h diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 556c616b0..6f3f8e4e4 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -64,12 +64,12 @@ <ClInclude Include="cc65\codeseg.h" /> <ClInclude Include="cc65\compile.h" /> <ClInclude Include="cc65\coptadd.h" /> + <ClInclude Include="cc65\coptbool.h" /> <ClInclude Include="cc65\coptc02.h" /> <ClInclude Include="cc65\coptcmp.h" /> <ClInclude Include="cc65\coptind.h" /> <ClInclude Include="cc65\coptjmp.h" /> <ClInclude Include="cc65\coptmisc.h" /> - <ClInclude Include="cc65\coptneg.h" /> <ClInclude Include="cc65\coptptrload.h" /> <ClInclude Include="cc65\coptptrstore.h" /> <ClInclude Include="cc65\coptpush.h" /> @@ -79,6 +79,7 @@ <ClInclude Include="cc65\coptstore.h" /> <ClInclude Include="cc65\coptsub.h" /> <ClInclude Include="cc65\copttest.h" /> + <ClInclude Include="cc65\coptunary.h" /> <ClInclude Include="cc65\dataseg.h" /> <ClInclude Include="cc65\datatype.h" /> <ClInclude Include="cc65\declare.h" /> @@ -144,12 +145,12 @@ <ClCompile Include="cc65\codeseg.c" /> <ClCompile Include="cc65\compile.c" /> <ClCompile Include="cc65\coptadd.c" /> + <ClCompile Include="cc65\coptbool.c" /> <ClCompile Include="cc65\coptc02.c" /> <ClCompile Include="cc65\coptcmp.c" /> <ClCompile Include="cc65\coptind.c" /> <ClCompile Include="cc65\coptjmp.c" /> <ClCompile Include="cc65\coptmisc.c" /> - <ClCompile Include="cc65\coptneg.c" /> <ClCompile Include="cc65\coptptrload.c" /> <ClCompile Include="cc65\coptptrstore.c" /> <ClCompile Include="cc65\coptpush.c" /> @@ -159,6 +160,7 @@ <ClCompile Include="cc65\coptstore.c" /> <ClCompile Include="cc65\coptsub.c" /> <ClCompile Include="cc65\copttest.c" /> + <ClCompile Include="cc65\coptunary.c" /> <ClCompile Include="cc65\dataseg.c" /> <ClCompile Include="cc65\datatype.c" /> <ClCompile Include="cc65\declare.c" /> diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 208ada134..6c2a1fddd 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -52,12 +52,12 @@ #include "codeinfo.h" #include "codeopt.h" #include "coptadd.h" +#include "coptbool.h" #include "coptc02.h" #include "coptcmp.h" #include "coptind.h" #include "coptjmp.h" #include "coptmisc.h" -#include "coptneg.h" #include "coptptrload.h" #include "coptptrstore.h" #include "coptpush.h" @@ -67,6 +67,7 @@ #include "coptstore.h" #include "coptsub.h" #include "copttest.h" +#include "coptunary.h" #include "error.h" #include "global.h" #include "output.h" @@ -115,6 +116,7 @@ static OptFunc DOptBNegAX1 = { OptBNegAX1, "OptBNegAX1", 100, 0, static OptFunc DOptBNegAX2 = { OptBNegAX2, "OptBNegAX2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX3 = { OptBNegAX3, "OptBNegAX3", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX4 = { OptBNegAX4, "OptBNegAX4", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolCmp = { OptBoolCmp, "OptBoolCmp", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolTrans = { OptBoolTrans, "OptBoolTrans", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist = { OptBranchDist, "OptBranchDist", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist2 = { OptBranchDist2, "OptBranchDist2", 0, 0, 0, 0, 0, 0 }; @@ -123,7 +125,6 @@ static OptFunc DOptCmp2 = { OptCmp2, "OptCmp2", 85, 0, static OptFunc DOptCmp3 = { OptCmp3, "OptCmp3", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp4 = { OptCmp4, "OptCmp4", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptCmp6 = { OptCmp6, "OptCmp6", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp7 = { OptCmp7, "OptCmp7", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp9 = { OptCmp9, "OptCmp9", 85, 0, 0, 0, 0, 0 }; @@ -221,6 +222,7 @@ static OptFunc* OptFuncs[] = { &DOptBNegAX2, &DOptBNegAX3, &DOptBNegAX4, + &DOptBoolCmp, &DOptBoolTrans, &DOptBranchDist, &DOptBranchDist2, @@ -229,7 +231,6 @@ static OptFunc* OptFuncs[] = { &DOptCmp3, &DOptCmp4, &DOptCmp5, - &DOptCmp6, &DOptCmp7, &DOptCmp8, &DOptCmp9, @@ -684,6 +685,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptDeadJumps, 1); C += RunOptFunc (S, &DOptDeadCode, 1); + C += RunOptFunc (S, &DOptBoolCmp, 1); C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptJumpTarget1, 1); C += RunOptFunc (S, &DOptJumpTarget2, 1); @@ -696,7 +698,6 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptCmp3, 1); C += RunOptFunc (S, &DOptCmp4, 1); C += RunOptFunc (S, &DOptCmp5, 1); - C += RunOptFunc (S, &DOptCmp6, 1); C += RunOptFunc (S, &DOptCmp7, 1); C += RunOptFunc (S, &DOptCmp9, 1); C += RunOptFunc (S, &DOptTest1, 1); diff --git a/src/cc65/coptneg.c b/src/cc65/coptbool.c similarity index 66% rename from src/cc65/coptneg.c rename to src/cc65/coptbool.c index 27171c68d..ee88cac0d 100644 --- a/src/cc65/coptneg.c +++ b/src/cc65/coptbool.c @@ -1,8 +1,8 @@ /*****************************************************************************/ /* */ -/* coptneg.c */ +/* coptbool.c */ /* */ -/* Optimize negation sequences */ +/* Optimize boolean sequences */ /* */ /* */ /* */ @@ -36,7 +36,257 @@ /* cc65 */ #include "codeent.h" #include "codeinfo.h" -#include "coptneg.h" +#include "error.h" +#include "coptbool.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Table used to invert a condition, indexed by condition */ +static const unsigned char CmpInvertTab[] = { + CMP_NE, CMP_EQ, + CMP_LE, CMP_LT, CMP_GE, CMP_GT, + CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT +}; + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static void ReplaceBranchCond (CodeSeg* S, unsigned I, cmp_t Cond) +/* Helper function for the replacement of routines that return a boolean +** followed by a conditional jump. Instead of the boolean value, the condition +** codes are evaluated directly. +** I is the index of the conditional branch, the sequence is already checked +** to be correct. +*/ +{ + CodeEntry* N; + CodeLabel* L; + + /* Get the entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Replace the conditional branch */ + switch (Cond) { + + case CMP_EQ: + CE_ReplaceOPC (E, OP65_JEQ); + break; + + case CMP_NE: + CE_ReplaceOPC (E, OP65_JNE); + break; + + case CMP_GT: + /* Replace by + ** beq @L + ** jpl Target + ** @L: ... + */ + if ((N = CS_GetNextEntry (S, I)) == 0) { + /* No such entry */ + Internal ("Invalid program flow"); + } + L = CS_GenLabel (S, N); + N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I); + CE_ReplaceOPC (E, OP65_JPL); + break; + + case CMP_GE: + CE_ReplaceOPC (E, OP65_JPL); + break; + + case CMP_LT: + CE_ReplaceOPC (E, OP65_JMI); + break; + + case CMP_LE: + /* Replace by + ** jmi Target + ** jeq Target + */ + CE_ReplaceOPC (E, OP65_JMI); + L = E->JumpTo; + N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I+1); + break; + + case CMP_UGT: + /* Replace by + ** beq @L + ** jcs Target + ** @L: ... + */ + if ((N = CS_GetNextEntry (S, I)) == 0) { + /* No such entry */ + Internal ("Invalid program flow"); + } + L = CS_GenLabel (S, N); + N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I); + CE_ReplaceOPC (E, OP65_JCS); + break; + + case CMP_UGE: + CE_ReplaceOPC (E, OP65_JCS); + break; + + case CMP_ULT: + CE_ReplaceOPC (E, OP65_JCC); + break; + + case CMP_ULE: + /* Replace by + ** jcc Target + ** jeq Target + */ + CE_ReplaceOPC (E, OP65_JCC); + L = E->JumpTo; + N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I+1); + break; + + default: + Internal ("Unknown jump condition: %d", Cond); + + } + +} + + + +/*****************************************************************************/ +/* Optimize bool comparison and transformer subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolCmp (CodeSeg* S) +/* Search for calls to compare subroutines followed by a conditional branch +** and replace them by cheaper versions, since the branch means that the +** boolean value returned by these routines is not needed (we may also check +** that explicitly, but for the current code generator it is always true). +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_JSR && + (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + !CE_HasLabel (N)) { + + /* The tos... functions will return a boolean value in a/x and + ** the Z flag says if this value is zero or not. We will call + ** a cheaper subroutine instead, one that does not return a + ** boolean value but only valid flags. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Replace the subroutine call. */ + E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); + CS_InsertEntry (S, E, I+1); + CS_DelEntry (S, I); + + /* Replace the conditional branch */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolTrans (CodeSeg* S) +/* Try to remove the call to boolean transformer routines where the call is +** not really needed and change following branch condition accordingly. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a boolean transformer */ + if (E->OPC == OP65_JSR && + (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0) { + + /* Make the boolean transformer unnecessary by changing the + ** the conditional jump to evaluate the condition flags that + ** are set after the compare directly. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Check if we can replace the code by something better */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remove the call to the bool transformer */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} @@ -408,200 +658,3 @@ unsigned OptBNegAX4 (CodeSeg* S) /* Return the number of changes made */ return Changes; } - - - -/*****************************************************************************/ -/* negax optimizations */ -/*****************************************************************************/ - - - -unsigned OptNegAX1 (CodeSeg* S) -/* Search for a call to negax and replace it by -** -** eor #$FF -** clc -** adc #$01 -** -** if X isn't used later. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X isn't used later */ - if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Add replacement code behind */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+2); - - X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); - CS_InsertEntry (S, X, I+3); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* Skip the generated code */ - I += 2; - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptNegAX2 (CodeSeg* S) -/* Search for a call to negax and replace it by -** -** ldx #$FF -** eor #$FF -** clc -** adc #$01 -** bcc L1 -** inx -** L1: -** -** if X is known and zero on entry. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* P; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X is known and zero */ - if (E->RI->In.RegX == 0 && - CE_IsCallTo (E, "negax") && - (P = CS_GetNextEntry (S, I)) != 0) { - - CodeEntry* X; - CodeLabel* L; - - /* Add replacement code behind */ - - /* ldx #$FF */ - X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* eor #$FF */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+2); - - /* clc */ - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+3); - - /* adc #$01 */ - X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); - CS_InsertEntry (S, X, I+4); - - /* Get the label attached to the insn following the call */ - L = CS_GenLabel (S, P); - - /* bcc L */ - X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, X, I+5); - - /* inx */ - X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+6); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* Skip the generated code */ - I += 5; - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* complax optimizations */ -/*****************************************************************************/ - - - -unsigned OptComplAX1 (CodeSeg* S) -/* Search for a call to complax and replace it by -** -** eor #$FF -** -** if X isn't used later. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X isn't used later */ - if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Add replacement code behind */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} diff --git a/src/cc65/coptneg.h b/src/cc65/coptbool.h similarity index 80% rename from src/cc65/coptneg.h rename to src/cc65/coptbool.h index 844d8b886..195751a02 100644 --- a/src/cc65/coptneg.h +++ b/src/cc65/coptbool.h @@ -1,8 +1,8 @@ /*****************************************************************************/ /* */ -/* coptneg.h */ +/* coptbool.h */ /* */ -/* Optimize negation sequences */ +/* Optimize boolean sequences */ /* */ /* */ /* */ @@ -33,8 +33,8 @@ -#ifndef COPTNEG_H -#define COPTNEG_H +#ifndef COPTBOOL_H +#define COPTBOOL_H @@ -43,6 +43,26 @@ +/*****************************************************************************/ +/* Optimize bool comparison and transformer subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolCmp (CodeSeg* S); +/* Search for calls to compare subroutines followed by a conditional branch +** and replace them by cheaper versions, since the branch means that the +** boolean value returned by these routines is not needed (we may also check +** that explicitly, but for the current code generator it is always true). +*/ + +unsigned OptBoolTrans (CodeSeg* S); +/* Try to remove the call to boolean transformer routines where the call is +** not really needed and change following branch condition accordingly. +*/ + + + /*****************************************************************************/ /* bnega optimizations */ /*****************************************************************************/ @@ -132,54 +152,6 @@ unsigned OptBNegAX4 (CodeSeg* S); -/*****************************************************************************/ -/* negax optimizations */ -/*****************************************************************************/ - - - -unsigned OptNegAX1 (CodeSeg* S); -/* Search for a call to negax and replace it by -** -** eor #$FF -** clc -** adc #$01 -** -** if X isn't used later. -*/ - -unsigned OptNegAX2 (CodeSeg* S); -/* Search for a call to negax and replace it by -** -** ldx #$FF -** eor #$FF -** clc -** adc #$01 -** bcc L1 -** inx -** L1: -** -** if X is known and zero on entry. -*/ - - - -/*****************************************************************************/ -/* complax optimizations */ -/*****************************************************************************/ - - - -unsigned OptComplAX1 (CodeSeg* S); -/* Search for a call to complax and replace it by -** -** eor #$FF -** -** if X isn't used later. -*/ - - - -/* End of coptneg.h */ +/* End of coptbool.h */ #endif diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index 92401a858..2970b363b 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -43,131 +43,12 @@ -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -/* Table used to invert a condition, indexed by condition */ -static const unsigned char CmpInvertTab [] = { - CMP_NE, CMP_EQ, - CMP_LE, CMP_LT, CMP_GE, CMP_GT, - CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT -}; - - - /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ -static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond) -/* Helper function for the replacement of routines that return a boolean -** followed by a conditional jump. Instead of the boolean value, the condition -** codes are evaluated directly. -** I is the index of the conditional branch, the sequence is already checked -** to be correct. -*/ -{ - CodeEntry* N; - CodeLabel* L; - - /* Get the entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Replace the conditional branch */ - switch (Cond) { - - case CMP_EQ: - CE_ReplaceOPC (E, OP65_JEQ); - break; - - case CMP_NE: - CE_ReplaceOPC (E, OP65_JNE); - break; - - case CMP_GT: - /* Replace by - ** beq @L - ** jpl Target - ** @L: ... - */ - if ((N = CS_GetNextEntry (S, I)) == 0) { - /* No such entry */ - Internal ("Invalid program flow"); - } - L = CS_GenLabel (S, N); - N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I); - CE_ReplaceOPC (E, OP65_JPL); - break; - - case CMP_GE: - CE_ReplaceOPC (E, OP65_JPL); - break; - - case CMP_LT: - CE_ReplaceOPC (E, OP65_JMI); - break; - - case CMP_LE: - /* Replace by - ** jmi Target - ** jeq Target - */ - CE_ReplaceOPC (E, OP65_JMI); - L = E->JumpTo; - N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I+1); - break; - - case CMP_UGT: - /* Replace by - ** beq @L - ** jcs Target - ** @L: ... - */ - if ((N = CS_GetNextEntry (S, I)) == 0) { - /* No such entry */ - Internal ("Invalid program flow"); - } - L = CS_GenLabel (S, N); - N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I); - CE_ReplaceOPC (E, OP65_JCS); - break; - - case CMP_UGE: - CE_ReplaceOPC (E, OP65_JCS); - break; - - case CMP_ULT: - CE_ReplaceOPC (E, OP65_JCC); - break; - - case CMP_ULE: - /* Replace by - ** jcc Target - ** jeq Target - */ - CE_ReplaceOPC (E, OP65_JCC); - L = E->JumpTo; - N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I+1); - break; - - default: - Internal ("Unknown jump condition: %d", Cond); - - } - -} - - - static int IsImmCmp16 (CodeEntry** L) /* Check if the instructions at L are an immediate compare of a/x: ** @@ -205,68 +86,6 @@ static int GetCmpRegVal (const CodeEntry* E) -/*****************************************************************************/ -/* Remove calls to the bool transformer subroutines */ -/*****************************************************************************/ - - - -unsigned OptBoolTrans (CodeSeg* S) -/* Try to remove the call to boolean transformer routines where the call is -** not really needed. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - cmp_t Cond; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for a boolean transformer */ - if (E->OPC == OP65_JSR && - (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0) { - - /* Make the boolean transformer unnecessary by changing the - ** the conditional jump to evaluate the condition flags that - ** are set after the compare directly. Note: jeq jumps if - ** the condition is not met, jne jumps if the condition is met. - ** Invert the code if we jump on condition not met. - */ - if (GetBranchCond (N->OPC) == BC_EQ) { - /* Jumps if condition false, invert condition */ - Cond = CmpInvertTab [Cond]; - } - - /* Check if we can replace the code by something better */ - ReplaceCmp (S, I+1, Cond); - - /* Remove the call to the bool transformer */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - /*****************************************************************************/ /* Optimizations for compares */ /*****************************************************************************/ @@ -684,68 +503,6 @@ unsigned OptCmp5 (CodeSeg* S) -unsigned OptCmp6 (CodeSeg* S) -/* Search for calls to compare subroutines followed by a conditional branch -** and replace them by cheaper versions, since the branch means that the -** boolean value returned by these routines is not needed (we may also check -** that explicitly, but for the current code generator it is always true). -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - cmp_t Cond; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_JSR && - (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0 && - !CE_HasLabel (N)) { - - /* The tos... functions will return a boolean value in a/x and - ** the Z flag says if this value is zero or not. We will call - ** a cheaper subroutine instead, one that does not return a - ** boolean value but only valid flags. Note: jeq jumps if - ** the condition is not met, jne jumps if the condition is met. - ** Invert the code if we jump on condition not met. - */ - if (GetBranchCond (N->OPC) == BC_EQ) { - /* Jumps if condition false, invert condition */ - Cond = CmpInvertTab [Cond]; - } - - /* Replace the subroutine call. */ - E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); - CS_InsertEntry (S, E, I+1); - CS_DelEntry (S, I); - - /* Replace the conditional branch */ - ReplaceCmp (S, I+1, Cond); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - unsigned OptCmp7 (CodeSeg* S) /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. diff --git a/src/cc65/coptcmp.h b/src/cc65/coptcmp.h index 0cdcf2d3d..dd188f7fc 100644 --- a/src/cc65/coptcmp.h +++ b/src/cc65/coptcmp.h @@ -43,19 +43,6 @@ -/*****************************************************************************/ -/* Remove calls to the bool transformer subroutines */ -/*****************************************************************************/ - - - -unsigned OptBoolTrans (CodeSeg* S); -/* Try to remove the call to boolean transformer routines where the call is -** not really needed. -*/ - - - /*****************************************************************************/ /* Optimizations for compares */ /*****************************************************************************/ @@ -136,13 +123,6 @@ unsigned OptCmp5 (CodeSeg* S); ** jne/jeq L2 */ -unsigned OptCmp6 (CodeSeg* S); -/* Search for calls to compare subroutines followed by a conditional branch -** and replace them by cheaper versions, since the branch means that the -** boolean value returned by these routines is not needed (we may also check -** that explicitly, but for the current code generator it is always true). -*/ - unsigned OptCmp7 (CodeSeg* S); /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. diff --git a/src/cc65/coptunary.c b/src/cc65/coptunary.c new file mode 100644 index 000000000..4d92f9d4a --- /dev/null +++ b/src/cc65/coptunary.c @@ -0,0 +1,236 @@ +/*****************************************************************************/ +/* */ +/* coptunary.c */ +/* */ +/* Optimize bitwise unary sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "coptbool.h" + + + +/*****************************************************************************/ +/* negax optimizations */ +/*****************************************************************************/ + + + +unsigned OptNegAX1 (CodeSeg* S) +/* Search for a call to negax and replace it by +** +** eor #$FF +** clc +** adc #$01 +** +** if X isn't used later. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X isn't used later */ + if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Add replacement code behind */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+2); + + X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); + CS_InsertEntry (S, X, I+3); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* Skip the generated code */ + I += 2; + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptNegAX2 (CodeSeg* S) +/* Search for a call to negax and replace it by +** +** ldx #$FF +** eor #$FF +** clc +** adc #$01 +** bcc L1 +** inx +** L1: +** +** if X is known and zero on entry. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* P; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X is known and zero */ + if (E->RI->In.RegX == 0 && + CE_IsCallTo (E, "negax") && + (P = CS_GetNextEntry (S, I)) != 0) { + + CodeEntry* X; + CodeLabel* L; + + /* Add replacement code behind */ + + /* ldx #$FF */ + X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* eor #$FF */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+2); + + /* clc */ + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+3); + + /* adc #$01 */ + X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); + CS_InsertEntry (S, X, I+4); + + /* Get the label attached to the insn following the call */ + L = CS_GenLabel (S, P); + + /* bcc L */ + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, X, I+5); + + /* inx */ + X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+6); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* Skip the generated code */ + I += 5; + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* complax optimizations */ +/*****************************************************************************/ + + + +unsigned OptComplAX1 (CodeSeg* S) +/* Search for a call to complax and replace it by +** +** eor #$FF +** +** if X isn't used later. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X isn't used later */ + if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Add replacement code behind */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptunary.h b/src/cc65/coptunary.h new file mode 100644 index 000000000..a7fd6d7b4 --- /dev/null +++ b/src/cc65/coptunary.h @@ -0,0 +1,96 @@ +/*****************************************************************************/ +/* */ +/* coptunary.h */ +/* */ +/* Optimize bitwise unary sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTUNARY_H +#define COPTUNARY_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* negax optimizations */ +/*****************************************************************************/ + + + +unsigned OptNegAX1 (CodeSeg* S); +/* Search for a call to negax and replace it by +** +** eor #$FF +** clc +** adc #$01 +** +** if X isn't used later. +*/ + +unsigned OptNegAX2 (CodeSeg* S); +/* Search for a call to negax and replace it by +** +** ldx #$FF +** eor #$FF +** clc +** adc #$01 +** bcc L1 +** inx +** L1: +** +** if X is known and zero on entry. +*/ + + + +/*****************************************************************************/ +/* complax optimizations */ +/*****************************************************************************/ + + + +unsigned OptComplAX1 (CodeSeg* S); +/* Search for a call to complax and replace it by +** +** eor #$FF +** +** if X isn't used later. +*/ + + + +/* End of coptunary.h */ + +#endif From 79c52e742f32d4e52d6dce9ed85a17f21bb4c102 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 21 Oct 2023 23:56:04 +0800 Subject: [PATCH 348/520] Added new opts OptBoolUnary1/OptBoolUnary2 to remove unnecessary cmp + bcastax/bnegax, as well as OptBoolUnary3 to "strength-reduce" certain bcastax/bnegax to boolne/booleq. --- src/cc65/codeopt.c | 14 ++- src/cc65/coptbool.c | 221 ++++++++++++++++++++++++++++++++++++++++++++ src/cc65/coptbool.h | 30 ++++++ 3 files changed, 263 insertions(+), 2 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 6c2a1fddd..ab3111a9b 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -118,6 +118,9 @@ static OptFunc DOptBNegAX3 = { OptBNegAX3, "OptBNegAX3", 100, 0, static OptFunc DOptBNegAX4 = { OptBNegAX4, "OptBNegAX4", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolCmp = { OptBoolCmp, "OptBoolCmp", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolTrans = { OptBoolTrans, "OptBoolTrans", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary1 = { OptBoolUnary1, "OptBoolUnary1", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary2 = { OptBoolUnary2, "OptBoolUnary2", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary3 = { OptBoolUnary3, "OptBoolUnary3", 40, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist = { OptBranchDist, "OptBranchDist", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist2 = { OptBranchDist2, "OptBranchDist2", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp1 = { OptCmp1, "OptCmp1", 42, 0, 0, 0, 0, 0 }; @@ -224,6 +227,9 @@ static OptFunc* OptFuncs[] = { &DOptBNegAX4, &DOptBoolCmp, &DOptBoolTrans, + &DOptBoolUnary1, + &DOptBoolUnary2, + &DOptBoolUnary3, &DOptBranchDist, &DOptBranchDist2, &DOptCmp1, @@ -613,7 +619,6 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptPtrLoad15, 1); Changes += RunOptFunc (S, &DOptPtrLoad16, 1); Changes += RunOptFunc (S, &DOptPtrLoad17, 1); - Changes += RunOptFunc (S, &DOptBNegAX1, 1); Changes += RunOptFunc (S, &DOptBNegAX2, 1); Changes += RunOptFunc (S, &DOptBNegAX3, 1); Changes += RunOptFunc (S, &DOptBNegAX4, 1); @@ -673,7 +678,12 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptBNegA2, 1); C += RunOptFunc (S, &DOptNegAX1, 1); C += RunOptFunc (S, &DOptNegAX2, 1); - C += RunOptFunc (S, &DOptStackOps, 3); + C += RunOptFunc (S, &DOptStackOps, 3); /* Before OptBoolUnary1 */ + C += RunOptFunc (S, &DOptCmp8, 1); /* Before OptBoolUnary1 */ + C += RunOptFunc (S, &DOptBoolUnary1, 3); + C += RunOptFunc (S, &DOptBoolUnary2, 3); + C += RunOptFunc (S, &DOptBoolUnary3, 1); + C += RunOptFunc (S, &DOptBNegAX1, 1); /* After OptBoolUnary2 */ C += RunOptFunc (S, &DOptShift1, 1); C += RunOptFunc (S, &DOptShift4, 1); C += RunOptFunc (S, &DOptComplAX1, 1); diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c index ee88cac0d..331fa77ab 100644 --- a/src/cc65/coptbool.c +++ b/src/cc65/coptbool.c @@ -290,6 +290,227 @@ unsigned OptBoolTrans (CodeSeg* S) +/*****************************************************************************/ +/* Remove calls to the boolean cast/negation subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolUnary1 (CodeSeg* S) +/* Search for and remove cmp #0/bcastax/boolne following a bcastax/bnegax. +** Or search for and remove cmp #1/bnegax/booleq following a bcastax/bnegax +** and invert the bcastax/bnegax. +*/ +{ + unsigned Changes = 0; + int Neg = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence. + ** We allow the first entry to have labels. + */ + if (L[0]->OPC == OP65_JSR && + (L[1] = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (L[1])) { + if (strcmp (L[0]->Arg, "bnegax") == 0) { + Neg = 1; + } else if (strcmp (L[0]->Arg, "bcastax") == 0) { + Neg = 0; + } else { + /* Next entry */ + ++I; + continue; + } + if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) || + CE_IsCallTo (L[1], "boolne") || + CE_IsCallTo (L[1], "bcastax")) { + /* Delete the entry no longer needed. */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + + } else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) || + CE_IsCallTo (L[1], "booleq") || + CE_IsCallTo (L[1], "bnegax")) { + /* Invert the previous bool conversion */ + CE_SetArg (L[0], Neg ? "bcastax" : "bnegax"); + + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary2 (CodeSeg* S) +/* Search for and remove cmp #0/bcastax/boolne following a boolean transformer. +** Or search for and remove cmp #1/bnegax/booleq following a boolean transformer +** and invert the boolean transformer. +*/ +{ + unsigned Changes = 0; + cmp_t Cond; + char Buf[16]; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence. + ** We allow the first entry to have labels. + */ + if (L[0]->OPC == OP65_JSR && + (L[1] = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (L[1]) && + (Cond = FindBoolCmpCond (L[0]->Arg)) != CMP_INV) { + if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) || + CE_IsCallTo (L[1], "boolne") || + CE_IsCallTo (L[1], "bcastax")) { + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + + } else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) || + CE_IsCallTo (L[1], "booleq") || + CE_IsCallTo (L[1], "bnegax")) { + /* Invert the bool conversion */ + if (GetBoolCmpSuffix (Buf, GetNegatedCond (Cond)) == 0) { + Internal ("No inverted boolean transformer for: %s", L[0]->Arg); + } + CE_SetArg (L[0], Buf); + + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary3 (CodeSeg* S) +/* If A == 0, replace bcastax/bnegax with +** +** cpx #0 +** jsr boolne/booleq +** +** Or if X == 0, replace bcastax/bnegax with +** +** cmp #0 +** jsr boolne/booleq +** +*/ +{ + unsigned Changes = 0; + opc_t Op = OP65_COUNT; + const char* Sub = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* E; + CodeEntry* X; + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (!CE_HasLabel (E)) { + /* Choose the right subroutine */ + if (CE_IsCallTo (E, "bnegax")) { + Sub = "booleq"; + } else if (CE_IsCallTo (E, "bcastax")) { + Sub = "boolne"; + } + /* Choose the right opcode */ + if (RegValIsKnown (E->RI->In.RegA) && E->RI->In.RegA == 0) { + Op = OP65_CPX; + } else if (RegValIsKnown (E->RI->In.RegX) && E->RI->In.RegX == 0) { + Op = OP65_CMP; + } + /* Replace the sequence if all requirements are met*/ + if (Op != OP65_COUNT && Sub != 0) { + /* Replace bcastax/bnegax with boolne/booleq */ + CE_SetArg (E, Sub); + + /* Insert the compare */ + X = NewCodeEntry (Op, AM65_IMM, "$00", 0, E->LI); + CS_InsertEntry (S, X, I); + + /* Remember, we had changes */ + ++Changes; + + /* Correct the index */ + ++I; + } + + /* Reset the choices */ + Op = OP65_COUNT; + Sub = 0; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + /*****************************************************************************/ /* bnega optimizations */ /*****************************************************************************/ diff --git a/src/cc65/coptbool.h b/src/cc65/coptbool.h index 195751a02..19554482e 100644 --- a/src/cc65/coptbool.h +++ b/src/cc65/coptbool.h @@ -63,6 +63,36 @@ unsigned OptBoolTrans (CodeSeg* S); +/*****************************************************************************/ +/* Remove calls to the boolean cast/negation subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolUnary1 (CodeSeg* S); +/* Search for and remove bcastax adjacent to bnegax */ + +unsigned OptBoolUnary2 (CodeSeg* S); +/* Search for and remove bcastax/bnegax following a boolean transformer. +** Invert the boolean transformer if it is bnegax to be removed. +*/ + +unsigned OptBoolUnary3 (CodeSeg* S); +/* Replace bcastax/bnegax with +** +** cpx #0 +** jsr boolne/booleq +** +** if A == 0, or replace bcastax/bnegax with +** +** cmp #0 +** jsr boolne/booleq +** +** if X == 0. +*/ + + + /*****************************************************************************/ /* bnega optimizations */ /*****************************************************************************/ From f321bb16e56f0bfc7c0b1747162f566f46a1b220 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 21 Oct 2023 23:56:07 +0800 Subject: [PATCH 349/520] Fixed potential bugs with boolean branch optimizers when more than one jeq/jne follows. --- src/cc65/coptbool.c | 15 ++-- test/val/booltrans.c | 161 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 test/val/booltrans.c diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c index 331fa77ab..663e4e85e 100644 --- a/src/cc65/coptbool.c +++ b/src/cc65/coptbool.c @@ -255,7 +255,8 @@ unsigned OptBoolTrans (CodeSeg* S) if (E->OPC == OP65_JSR && (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0) { + (N->Info & OF_ZBRA) != 0 && + (GetRegInfo (S, I + 2, PSTATE_Z) & PSTATE_Z) == 0) { /* Make the boolean transformer unnecessary by changing the ** the conditional jump to evaluate the condition flags that @@ -606,7 +607,8 @@ unsigned OptBNegA2 (CodeSeg* S) CE_IsCallTo (L[0], "bnega") && !CE_HasLabel (L[0]) && (L[1]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[1])) { + !CE_HasLabel (L[1]) && + (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) { /* Invert the branch */ CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); @@ -709,7 +711,8 @@ unsigned OptBNegAX2 (CodeSeg* S) CS_GetEntries (S, L+1, I+1, 3) && CE_IsCallTo (L[1], "ldaxysp") && CE_IsCallTo (L[2], "bnegax") && - (L[3]->Info & OF_ZBRA) != 0) { + (L[3]->Info & OF_ZBRA) != 0 && + (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) { CodeEntry* X; @@ -781,7 +784,8 @@ unsigned OptBNegAX3 (CodeSeg* S) CE_IsCallTo (L[1], "bnegax") && !CE_HasLabel (L[1]) && (L[2]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[2])) { + !CE_HasLabel (L[2]) && + (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) { /* ldx --> ora */ CE_ReplaceOPC (L[0], OP65_ORA); @@ -840,7 +844,8 @@ unsigned OptBNegAX4 (CodeSeg* S) strncmp (L[0]->Arg,"bnega",5) == 0 && !CE_HasLabel (L[0]) && (L[1]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[1])) { + !CE_HasLabel (L[1]) && + (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) { CodeEntry* X; diff --git a/test/val/booltrans.c b/test/val/booltrans.c new file mode 100644 index 000000000..51439d45b --- /dev/null +++ b/test/val/booltrans.c @@ -0,0 +1,161 @@ +/* Optimization bugs with multiple inverse Z branches following one boolean transformer */ + +#include <stdint.h> +#include <stdio.h> + +unsigned failures; + +int a; + +/* To reveal the bug, the second Z branch must jump over the destination of the first Z branch */ + +int test_booltrans(int8_t x) +{ + a = x; + __asm__("lda #$00"); + __asm__("cmp %v", a); + __asm__("jsr booleq"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnega2(int8_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("jsr bnega"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnegax2(int16_t x) +{ + int a = x; + __asm__("ldy #%o+1", a); + __asm__("jsr ldaxysp"); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +void __fastcall__ f(void) {} + +int test_bnegax3(int16_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("ldx %v+1", a); + __asm__("jsr %v", f); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnegax4(int16_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("ldx %v+1", a); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int main(void) +{ + a = test_booltrans(0); + if (a != 0) + { + ++failures; + printf("test_booltrans(0): %d, expected: 0\n", a); + } + + a = test_booltrans(1); + if (a != 1) + { + ++failures; + printf("test_booltrans(1): %d, expected: 1\n", a); + } + + a = test_bnega2(0); + if (a != 0) + { + ++failures; + printf("test_bnega2(0): %d, expected: 0\n", a); + } + + a = test_bnega2(1); + if (a != 1) + { + ++failures; + printf("test_bnega2(1): %d, expected: 1\n", a); + } + + a = test_bnegax2(0); + if (a != 0) + { + ++failures; + printf("test_bnegax2(0): %d, expected: 0\n", a); + } + + a = test_bnegax2(1); + if (a != 1) + { + ++failures; + printf("test_bnegax2(1): %d, expected: 1\n", a); + } + + a = test_bnegax3(0); + if (a != 0) + { + ++failures; + printf("test_bnegax3(0): %d, expected: 0\n", a); + } + + a = test_bnegax3(1); + if (a != 1) + { + ++failures; + printf("test_bnegax3(1): %d, expected: 1\n", a); + } + + a = test_bnegax4(0); + if (a != 0) + { + ++failures; + printf("test_bnegax4(0): %d, expected: 0\n", a); + } + + a = test_bnegax4(1); + if (a != 1) + { + ++failures; + printf("test_bnegax4(1): %d, expected: 1\n", a); + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + return failures; +} From 70549e868edba97c3cb3271c2993aeafc41fd232 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 21 Oct 2023 23:56:13 +0800 Subject: [PATCH 350/520] New optimizer steps to restore some possibly lost optimization with boolean due to the previous fix. --- src/cc65/codeopt.c | 36 +++++++++++-------- src/cc65/coptbool.c | 58 ++++++++++++++++++++++++++++++- src/cc65/coptjmp.c | 84 ++++++++++++++++++++++++++++++++++++++++----- src/cc65/coptjmp.h | 24 ++++++++++--- 4 files changed, 173 insertions(+), 29 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index ab3111a9b..baa44f99e 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -132,8 +132,10 @@ static OptFunc DOptCmp7 = { OptCmp7, "OptCmp7", 85, 0, static OptFunc DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp9 = { OptCmp9, "OptCmp9", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptComplAX1 = { OptComplAX1, "OptComplAX1", 65, 0, 0, 0, 0, 0 }; -static OptFunc DOptCondBranches1= { OptCondBranches1,"OptCondBranches1", 80, 0, 0, 0, 0, 0 }; -static OptFunc DOptCondBranches2= { OptCondBranches2,"OptCondBranches2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch1 = { OptCondBranch1, "OptCondBranch1", 80, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch2 = { OptCondBranch2, "OptCondBranch2", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch3 = { OptCondBranch3, "OptCondBranch3", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranchC = { OptCondBranchC, "OptCondBranchC", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptDeadCode = { OptDeadCode, "OptDeadCode", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptDeadJumps = { OptDeadJumps, "OptDeadJumps", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 }; @@ -241,8 +243,10 @@ static OptFunc* OptFuncs[] = { &DOptCmp8, &DOptCmp9, &DOptComplAX1, - &DOptCondBranches1, - &DOptCondBranches2, + &DOptCondBranch1, + &DOptCondBranch2, + &DOptCondBranch3, + &DOptCondBranchC, &DOptDeadCode, &DOptDeadJumps, &DOptDecouple, @@ -619,9 +623,6 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptPtrLoad15, 1); Changes += RunOptFunc (S, &DOptPtrLoad16, 1); Changes += RunOptFunc (S, &DOptPtrLoad17, 1); - Changes += RunOptFunc (S, &DOptBNegAX2, 1); - Changes += RunOptFunc (S, &DOptBNegAX3, 1); - Changes += RunOptFunc (S, &DOptBNegAX4, 1); Changes += RunOptFunc (S, &DOptAdd1, 1); Changes += RunOptFunc (S, &DOptAdd2, 1); Changes += RunOptFunc (S, &DOptAdd4, 1); @@ -674,8 +675,6 @@ static unsigned RunOptGroup3 (CodeSeg* S) do { C = 0; - C += RunOptFunc (S, &DOptBNegA1, 1); - C += RunOptFunc (S, &DOptBNegA2, 1); C += RunOptFunc (S, &DOptNegAX1, 1); C += RunOptFunc (S, &DOptNegAX2, 1); C += RunOptFunc (S, &DOptStackOps, 3); /* Before OptBoolUnary1 */ @@ -683,6 +682,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptBoolUnary1, 3); C += RunOptFunc (S, &DOptBoolUnary2, 3); C += RunOptFunc (S, &DOptBoolUnary3, 1); + C += RunOptFunc (S, &DOptBNegA1, 1); C += RunOptFunc (S, &DOptBNegAX1, 1); /* After OptBoolUnary2 */ C += RunOptFunc (S, &DOptShift1, 1); C += RunOptFunc (S, &DOptShift4, 1); @@ -695,16 +695,22 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptDeadJumps, 1); C += RunOptFunc (S, &DOptDeadCode, 1); - C += RunOptFunc (S, &DOptBoolCmp, 1); - C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptJumpTarget1, 1); C += RunOptFunc (S, &DOptJumpTarget2, 1); - C += RunOptFunc (S, &DOptCondBranches1, 1); - C += RunOptFunc (S, &DOptCondBranches2, 1); + C += RunOptFunc (S, &DOptCondBranch1, 1); + C += RunOptFunc (S, &DOptCondBranch2, 1); + C += RunOptFunc (S, &DOptCondBranch3, 1); + C += RunOptFunc (S, &DOptCondBranchC, 1); C += RunOptFunc (S, &DOptRTSJumps1, 1); + C += RunOptFunc (S, &DOptBoolCmp, 1); + C += RunOptFunc (S, &DOptBoolTrans, 1); + C += RunOptFunc (S, &DOptBNegA2, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX2, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX3, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX4, 1); /* After OptCondBranch's */ C += RunOptFunc (S, &DOptCmp1, 1); C += RunOptFunc (S, &DOptCmp2, 1); - C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */ + C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */ C += RunOptFunc (S, &DOptCmp3, 1); C += RunOptFunc (S, &DOptCmp4, 1); C += RunOptFunc (S, &DOptCmp5, 1); @@ -712,7 +718,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptCmp9, 1); C += RunOptFunc (S, &DOptTest1, 1); C += RunOptFunc (S, &DOptLoad1, 1); - C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */ + C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */ C += RunOptFunc (S, &DOptUnusedLoads, 1); C += RunOptFunc (S, &DOptUnusedStores, 1); C += RunOptFunc (S, &DOptDupLoads, 1); diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c index 663e4e85e..3a3b3fa7c 100644 --- a/src/cc65/coptbool.c +++ b/src/cc65/coptbool.c @@ -167,7 +167,7 @@ static void ReplaceBranchCond (CodeSeg* S, unsigned I, cmp_t Cond) /*****************************************************************************/ -/* Optimize bool comparison and transformer subroutines */ +/* Optimize bool comparison and transformer subroutines with branches */ /*****************************************************************************/ @@ -291,6 +291,62 @@ unsigned OptBoolTrans (CodeSeg* S) +unsigned OptBoolUnary (CodeSeg* S) +/* Try to remove the call to a bcastax/bnegax routines where the call is +** not really needed and change following branch condition accordingly. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a boolean transformer */ + if (E->OPC == OP65_JSR && + (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0) { + + /* Make the boolean transformer unnecessary by changing the + ** the conditional jump to evaluate the condition flags that + ** are set after the compare directly. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Check if we can replace the code by something better */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remove the call to the bool transformer */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + /*****************************************************************************/ /* Remove calls to the boolean cast/negation subroutines */ /*****************************************************************************/ diff --git a/src/cc65/coptjmp.c b/src/cc65/coptjmp.c index 9dd4a29c5..e0b53ad91 100644 --- a/src/cc65/coptjmp.c +++ b/src/cc65/coptjmp.c @@ -898,17 +898,13 @@ unsigned OptJumpTarget3 (CodeSeg* S) -unsigned OptCondBranches1 (CodeSeg* S) -/* Performs several optimization steps: -** +unsigned OptCondBranch1 (CodeSeg* S) +/* Performs some optimization steps: ** - If an immediate load of a register is followed by a conditional jump that ** is never taken because the load of the register sets the flags in such a ** manner, remove the conditional branch. ** - If the conditional branch is always taken because of the register load, ** replace it by a jmp. -** - If a conditional branch jumps around an unconditional branch, remove the -** conditional branch and make the jump a conditional branch with the -** inverse condition of the first one. */ { unsigned Changes = 0; @@ -918,7 +914,6 @@ unsigned OptCondBranches1 (CodeSeg* S) while (I < CS_GetEntryCount (S)) { CodeEntry* N; - CodeLabel* L; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); @@ -960,6 +955,35 @@ unsigned OptCondBranches1 (CodeSeg* S) } + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranch2 (CodeSeg* S) +/* If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the inverse +** condition of the first one. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ (L = E->JumpTo) != 0 && /* ..referencing a local label */ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ @@ -991,7 +1015,51 @@ unsigned OptCondBranches1 (CodeSeg* S) -unsigned OptCondBranches2 (CodeSeg* S) +unsigned OptCondBranch3 (CodeSeg* S) +/* If the conditional branch is always taken because it follows an inverse +** conditional branch, replace it by a jmp. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a conditional branch */ + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ + !CE_HasLabel (N)) { /* ..and does not have a label */ + + /* Check if the branches conditions are inverse of each other */ + if (GetInverseCond (GetBranchCond (N->OPC)) == GetBranchCond (E->OPC)) { + /* The branch is always taken, replace it by a jump */ + CE_ReplaceOPC (N, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + } + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranchC (CodeSeg* S) /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, ** we can remove the rol and branch on the state of the carry flag. */ diff --git a/src/cc65/coptjmp.h b/src/cc65/coptjmp.h index 8df53415d..194117729 100644 --- a/src/cc65/coptjmp.h +++ b/src/cc65/coptjmp.h @@ -101,13 +101,27 @@ unsigned OptJumpTarget3 (CodeSeg* S); ** done. */ -unsigned OptCondBranches1 (CodeSeg* S); -/* If an immidiate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. +unsigned OptCondBranch1 (CodeSeg* S); +/* Performs some optimization steps: +** - If an immediate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +** - If the conditional branch is always taken because of the register load, +** replace it by a jmp. */ -unsigned OptCondBranches2 (CodeSeg* S); +unsigned OptCondBranch2 (CodeSeg* S); +/* If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the inverse +** condition of the first one. +*/ + +unsigned OptCondBranch3 (CodeSeg* S); +/* If the conditional branch is always taken because it follows an inverse +** conditional branch, replace it by a jmp. +*/ + +unsigned OptCondBranchC (CodeSeg* S); /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, ** we can remove the rol and branch on the state of the carry. */ From a31b35b2a655372f161c9e7d4c45e3d7a694ad9f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 22 Oct 2023 10:42:17 +0800 Subject: [PATCH 351/520] Fixed naming of local variables in CG_TypeOfBySize and CG_TypeOf. --- src/cc65/expr.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 37611c490..7502835e0 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -106,19 +106,19 @@ unsigned CG_AddrModeFlags (const ExprDesc* Expr) static unsigned CG_TypeOfBySize (unsigned Size) /* Get the code generator replacement type of the object by its size */ { - unsigned NewType; + unsigned CG_Type; /* 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 (Size) { - case 1: NewType = CF_CHAR; break; - case 2: NewType = CF_INT; break; + case 1: CG_Type = CF_CHAR; break; + case 2: CG_Type = CF_INT; break; case 3: /* FALLTHROUGH */ - case 4: NewType = CF_LONG; break; - default: NewType = CF_NONE; break; + case 4: CG_Type = CF_LONG; break; + default: CG_Type = CF_NONE; break; } - return NewType; + return CG_Type; } @@ -126,7 +126,7 @@ static unsigned CG_TypeOfBySize (unsigned Size) unsigned CG_TypeOf (const Type* T) /* Get the code generator base type of the object */ { - unsigned NewType; + unsigned CG_Type; switch (GetUnderlyingTypeCode (T)) { @@ -163,9 +163,9 @@ unsigned CG_TypeOf (const Type* T) case T_STRUCT: case T_UNION: - NewType = CG_TypeOfBySize (SizeOf (T)); - if (NewType != CF_NONE) { - return NewType; + CG_Type = CG_TypeOfBySize (SizeOf (T)); + if (CG_Type != CF_NONE) { + return CG_Type; } /* Address of ... */ return CF_INT | CF_UNSIGNED; From df392fc10454669e04c630afb107b2f21efde16b Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 25 Oct 2023 22:38:21 +0800 Subject: [PATCH 352/520] Fixed type promotion of switch case values. --- src/cc65/swstmt.c | 88 ++++++++++++++++++----------------- test/val/bug2019-case-value.c | 77 ++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 test/val/bug2019-case-value.c diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 0466ddf4a..fc213c9a1 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -64,7 +64,7 @@ typedef struct SwitchCtrl SwitchCtrl; struct SwitchCtrl { Collection* Nodes; /* CaseNode tree */ - TypeCode ExprType; /* Basic switch expression type */ + const Type* ExprType; /* Switch controlling expression type */ unsigned Depth; /* Number of bytes the selector type has */ unsigned DefaultLabel; /* Label for the default branch */ @@ -133,7 +133,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = GetUnqualTypeCode (&SwitchExpr.Type[0]); + SwitchData.ExprType = SwitchExpr.Type; SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; @@ -152,7 +152,7 @@ void SwitchStatement (void) /* Check if we had any labels */ if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { - Warning ("No case labels"); + Warning ("No reachable case labels for switch"); } /* If the last statement did not have a break, we may have an open @@ -209,62 +209,64 @@ void CaseLabel (void) /* Handle a case label */ { ExprDesc CaseExpr; /* Case label expression */ - long Val; /* Case label value */ - unsigned CodeLabel; /* Code label for this case */ /* Skip the "case" token */ NextToken (); /* Read the selector expression */ CaseExpr = NoCodeConstAbsIntExpr (hie1); - Val = CaseExpr.IVal; /* Now check if we're inside a switch statement */ if (Switch != 0) { /* Check the range of the expression */ - switch (Switch->ExprType) { + const Type* CaseT = CaseExpr.Type; + long CaseVal = CaseExpr.IVal; + int OutOfRange = 0; + const char* DiagMsg = 0; - case T_SCHAR: - /* Signed char */ - if (Val < -128 || Val > 127) { - Error ("Range error"); - } - break; + CaseExpr.Type = IntPromotion (Switch->ExprType); + LimitExprValue (&CaseExpr, 1); - case T_UCHAR: - if (Val < 0 || Val > 255) { - Error ("Range error"); - } - break; - - case T_SHORT: - case T_INT: - if (Val < -32768 || Val > 32767) { - Error ("Range error"); - } - break; - - case T_USHORT: - case T_UINT: - if (Val < 0 || Val > 65535) { - Error ("Range error"); - } - break; - - case T_LONG: - case T_ULONG: - break; - - default: - Internal ("Invalid type: %06lX", Switch->ExprType); + if (CaseVal != CaseExpr.IVal || + (IsSignSigned (CaseT) != IsSignSigned (CaseExpr.Type) && + (IsSignSigned (CaseT) ? CaseVal < 0 : CaseExpr.IVal < 0))) { + Warning (IsSignSigned (CaseT) ? + IsSignSigned (CaseExpr.Type) ? + "Case value is implicitly converted (%ld to %ld)" : + "Case value is implicitly converted (%ld to %lu)" : + IsSignSigned (CaseExpr.Type) ? + "Case value is implicitly converted (%lu to %ld)" : + "Case value is implicitly converted (%lu to %lu)", + CaseVal, CaseExpr.IVal); } - /* Insert the case selector into the selector table */ - CodeLabel = InsertCaseValue (Switch->Nodes, Val, Switch->Depth); + /* Check the range of the expression */ + if (IsSignSigned (CaseExpr.Type)) { + if (CaseExpr.IVal < GetIntegerTypeMin (Switch->ExprType)) { + DiagMsg = "Case value (%ld) out of range for switch condition type"; + OutOfRange = 1; + } else if (IsSignSigned (Switch->ExprType) ? + CaseExpr.IVal > (long)GetIntegerTypeMax (Switch->ExprType) : + SizeOf (CaseExpr.Type) > SizeOf (Switch->ExprType) && + (unsigned long)CaseExpr.IVal > GetIntegerTypeMax (Switch->ExprType)) { + DiagMsg = "Case value (%ld) out of range for switch condition type"; + OutOfRange = 1; + } + } else if ((unsigned long)CaseExpr.IVal > GetIntegerTypeMax (Switch->ExprType)) { + DiagMsg = "Case value (%lu) out of range for switch condition type"; + OutOfRange = 1; + } - /* Define this label */ - g_defcodelabel (CodeLabel); + if (OutOfRange == 0) { + /* Insert the case selector into the selector table */ + unsigned CodeLabel = InsertCaseValue (Switch->Nodes, CaseExpr.IVal, Switch->Depth); + + /* Define this label */ + g_defcodelabel (CodeLabel); + } else { + Warning (DiagMsg, CaseExpr.IVal); + } } else { diff --git a/test/val/bug2019-case-value.c b/test/val/bug2019-case-value.c new file mode 100644 index 000000000..b604e083a --- /dev/null +++ b/test/val/bug2019-case-value.c @@ -0,0 +1,77 @@ +/* Bug #2019 - Type promotion in switch statements seems to be broken */ + +#include <limits.h> +#include <stdio.h> + +unsigned failures; + +int f1(void) +{ + unsigned char c = 0xFF; + switch (c) { + case (signed char)0xFF: break; + case (unsigned char)0xFF: return 0; + } + return -1; +} + +int f2(void) +{ + signed char c = SCHAR_MIN; + switch (c) { + case (unsigned char)SCHAR_MIN: break; + case SCHAR_MIN: return 0; + } + return -1; +} + +int f3(void) +{ + signed int c = (int)UINT_MAX; + switch (c) { + case UINT_MAX: return 0; + } + return -1; +} + +int f4(void) +{ + unsigned int c = UINT_MAX; + switch (c) { + case -1L: return 0; + } + return -1; +} + +int main(void) +{ + if (f1()) + { + ++failures; + printf("f1() failed\n"); + } + + if (f2()) + { + ++failures; + printf("f2() failed\n"); + } + + if (f3()) + { + ++failures; + printf("f3() failed\n"); + } + + if (f4()) + { + ++failures; + printf("f4() failed\n"); + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + return failures; +} From 85e63e99a66e71c561aaf6b5fdb5d794bf89acf8 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 27 Oct 2023 21:51:45 +0800 Subject: [PATCH 353/520] Fixed regression: array element of incomplete type. --- src/cc65/compile.c | 2 +- src/cc65/declare.c | 34 ++++++++++++++++++---------------- test/err/zero-size.c | 6 ++++++ 3 files changed, 25 insertions(+), 17 deletions(-) create mode 100644 test/err/zero-size.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 9d7fbe20a..edd00022c 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -511,7 +511,7 @@ void Compile (const char* FileName) /* Mark as defined; so that it will be exported, not imported */ Entry->Flags |= SC_DEF; - } else { + } else if (!IsTypeArray (Entry->Type)) { /* Tentative declared variable is still of incomplete type */ Error ("Definition of '%s' has type '%s' that is never completed", Entry->Name, diff --git a/src/cc65/declare.c b/src/cc65/declare.c index cd174c92d..cf9c13059 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -493,33 +493,35 @@ static void FixQualifiers (Type* DataType) -static void CheckArrayElementType (Type* DataType) +static void CheckArrayElementType (const Type* T) /* Check if data type consists of arrays of incomplete element types */ { - Type* T = DataType; - while (T->C != T_END) { if (IsTypeArray (T)) { + /* If the array is multi-dimensional, keep going until we get the + ** true element type. + */ ++T; - if (IsIncompleteESUType (T)) { - /* We cannot have an array of incomplete elements */ - Error ("Array of incomplete element type '%s'", GetFullTypeName (T)); - } else if (SizeOf (T) == 0) { - /* If the array is multi-dimensional, try to get the true - ** element type. - */ - if (IsTypeArray (T)) { - continue; - } - /* We could support certain 0-size element types as an extension */ - if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { - Error ("Array of 0-size element type '%s'", GetFullTypeName (T)); + if (SizeOf (T) == 0) { + if (IsTypeArray (T) || IsIncompleteESUType (T)) { + /* We cannot have an array of incomplete elements */ + if (!IsTypeArray (T) || GetElementCount (T) == UNSPECIFIED) { + Error ("Array of incomplete element type '%s'", + GetFullTypeName (T)); + return; + } + } else if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { + /* We could support certain 0-size element types as an extension */ + Error ("Array of 0-size element type '%s'", + GetFullTypeName (T)); + return; } } else { if (IsTypeStruct (T)) { SymEntry* TagEntry = GetESUTagSym (T); if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { Error ("Invalid use of struct with flexible array member"); + return; } } } diff --git a/test/err/zero-size.c b/test/err/zero-size.c new file mode 100644 index 000000000..9e7510c91 --- /dev/null +++ b/test/err/zero-size.c @@ -0,0 +1,6 @@ +char a[][] = { 0, 0 }; /* Error: Array type has incomplete element type 'char[]' */ + +int main(void) +{ + return 0; +} From 8e45a4c960b2366a0c6d1d83539be3b029b1bcbe Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 27 Oct 2023 23:46:10 +0800 Subject: [PATCH 354/520] Fixed the bug that a union type containing a struct with a flexible array member was accepted as a struct member or array element type. --- src/cc65/declare.c | 7 +++---- test/err/bug2016-fam-member.c | 7 ++++++- test/err/bug2017-fam-element.c | 14 +++++++++----- test/val/fam.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 test/val/fam.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index cf9c13059..b09c2ea17 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -517,7 +517,8 @@ static void CheckArrayElementType (const Type* T) return; } } else { - if (IsTypeStruct (T)) { + /* Elements cannot contain flexible array members themselves */ + if (IsClassStruct (T)) { SymEntry* TagEntry = GetESUTagSym (T); if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { Error ("Invalid use of struct with flexible array member"); @@ -1202,9 +1203,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { Field->Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM; - if (IsTypeStruct (Decl.Type)) { - Error ("Invalid use of struct with flexible array member"); - } + Error ("Invalid use of struct with flexible array member"); } } diff --git a/test/err/bug2016-fam-member.c b/test/err/bug2016-fam-member.c index 02c9ec275..473aae702 100644 --- a/test/err/bug2016-fam-member.c +++ b/test/err/bug2016-fam-member.c @@ -5,7 +5,12 @@ typedef struct x { int b[]; /* Ok: Flexible array member can be last */ } x; +typedef union u { + int a; + x x; /* Ok: Union member can contain flexible array member */ +} u; + struct y { - x x; /* Not ok: Contains flexible array member */ + u u; /* Not ok: Contains union that contains flexible array member */ int a; }; diff --git a/test/err/bug2017-fam-element.c b/test/err/bug2017-fam-element.c index 195ca6597..c97ae42ec 100644 --- a/test/err/bug2017-fam-element.c +++ b/test/err/bug2017-fam-element.c @@ -1,9 +1,13 @@ /* Bug #2017 - cc65 erroneously allows arrays of structs with flexible array members */ -struct z { +typedef struct x { int a; - int c; - int b[]; -}; + int b[]; /* Ok: Flexible array member can be last */ +} x; -struct z y[3]; /* Should be an error */ +typedef union u { + int a; + x x; /* Ok: Union member can contain flexible array member */ +} u; + +union u y[3]; /* Should be an error */ diff --git a/test/val/fam.c b/test/val/fam.c new file mode 100644 index 000000000..df5df2876 --- /dev/null +++ b/test/val/fam.c @@ -0,0 +1,31 @@ +/* Bug #2016 and #2017 - flexible array members */ + +typedef struct { + int a; + int b[]; /* Ok: Flexible array member can be last */ +} X; + +typedef union { + X x; /* Ok: Contains flexible array member */ + int a; +} U; + +typedef struct { + struct { + int a; + }; + int b[]; /* Ok: Flexible array member can be last */ +} Y; + +X x; +U u; +Y y; + +_Static_assert(sizeof x == sizeof (int), "sizeof x != sizeof (int)"); +_Static_assert(sizeof u == sizeof (int), "sizeof u != sizeof (int)"); +_Static_assert(sizeof y == sizeof (int), "sizeof y != sizeof (int)"); + +int main(void) +{ + return 0; +} From d424883716263569bb1828f0d2c25fe13e27313a Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 28 Oct 2023 15:24:13 +0800 Subject: [PATCH 355/520] Fixed diagnostics on qualifiers of function return types. --- src/cc65/declare.c | 118 +++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 51 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 8fc314342..4bfd52c5e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -493,8 +493,44 @@ static void FixQualifiers (Type* DataType) +static void FixFunctionReturnType (Type* T) +/* Check if the data type consists of any functions returning forbidden return +** types and remove qualifiers from the return types if they are not void. +*/ +{ + while (T->C != T_END) { + if (IsTypeFunc (T)) { + ++T; + + /* Functions may not return functions or arrays */ + if (IsTypeFunc (T)) { + Error ("Functions are not allowed to return functions"); + } else if (IsTypeArray (T)) { + Error ("Functions are not allowed to return arrays"); + } + + /* The return type must not be qualified */ + if ((GetQualifier (T) & T_QUAL_CVR) != T_QUAL_NONE) { + /* We are stricter than the standard here */ + if (GetRawTypeRank (T) == T_RANK_VOID) { + /* A qualified void type is always an error */ + Error ("Function definition has qualified void return type"); + } else { + /* For others, qualifiers are ignored */ + Warning ("Type qualifiers ignored on function return type"); + T[0].C &= ~T_QUAL_CVR; + } + } + } else { + ++T; + } + } +} + + + static void CheckArrayElementType (const Type* T) -/* Check if data type consists of arrays of incomplete element types */ +/* Check recursively if type consists of arrays of forbidden element types */ { while (T->C != T_END) { if (IsTypeArray (T)) { @@ -2061,46 +2097,39 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Do several fixes on qualifiers */ FixQualifiers (D->Type); - /* Check if the data type consists of any arrays of forbidden types */ - CheckArrayElementType (D->Type); + /* Check if the data type consists of any functions returning forbidden return + ** types and remove qualifiers from the return types if they are not void. + */ + FixFunctionReturnType (D->Type); - /* If we have a function, add a special storage class */ - if (IsTypeFunc (D->Type)) { - D->StorageClass |= SC_FUNC; - } + /* Check recursively if the data type consists of arrays of forbidden types */ + CheckArrayElementType (D->Type); /* Parse attributes for this declarator */ ParseAttribute (D); - /* Check several things for function or function pointer types */ - if (IsTypeFunc (D->Type) || IsTypeFuncPtr (D->Type)) { + /* If we have a function, add a special storage class */ + if (IsTypeFunc (D->Type)) { - /* A function. Check the return type */ - Type* RetType = GetFuncReturnTypeModifiable (D->Type); + D->StorageClass |= SC_FUNC; - /* Functions may not return functions or arrays */ - if (IsTypeFunc (RetType)) { - Error ("Functions are not allowed to return functions"); - } else if (IsTypeArray (RetType)) { - Error ("Functions are not allowed to return arrays"); - } + } else if (!IsTypeVoid (D->Type)) { + /* Check the size of the generated type */ + unsigned Size = SizeOf (D->Type); - /* The return type must not be qualified */ - if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { - - if (GetRawTypeRank (RetType) == T_RANK_VOID) { - /* A qualified void type is always an error */ - Error ("function definition has qualified void return type"); + if (Size >= 0x10000) { + if (D->Ident[0] != '\0') { + Error ("Size of '%s' is invalid (0x%06X)", D->Ident, Size); } else { - /* For others, qualifiers are ignored */ - Warning ("type qualifiers ignored on function return type"); - RetType[0].C = GetUnqualRawTypeCode (RetType); + Error ("Invalid size in declaration (0x%06X)", Size); } } + } - /* Warn about an implicit int return in the function */ - if ((Spec->Flags & DS_DEF_TYPE) != 0 && - RetType[0].C == T_INT && RetType[1].C == T_END) { + /* Check a few pre-C99 things */ + if ((Spec->Flags & DS_DEF_TYPE) != 0) { + /* Check and warn about an implicit int return in the function */ + if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) { /* Function has an implicit int return. Output a warning if we don't ** have the C89 standard enabled explicitly. */ @@ -2110,29 +2139,16 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - } - - /* For anthing that is not a function or typedef, check for an implicit - ** int declaration. - */ - if ((D->StorageClass & SC_FUNC) != SC_FUNC && - (D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - /* If the standard was not set explicitly to C89, print a warning - ** for variables with implicit int type. + /* For anthing that is not a function or typedef, check for an implicit + ** int declaration. */ - if ((Spec->Flags & DS_DEF_TYPE) != 0 && IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' is an obsolete feature"); - } - } - - if (!IsTypeFunc (D->Type) && !IsTypeVoid (D->Type)) { - /* Check the size of the generated type */ - unsigned Size = SizeOf (D->Type); - if (Size >= 0x10000) { - if (D->Ident[0] != '\0') { - Error ("Size of '%s' is invalid (0x%06X)", D->Ident, Size); - } else { - Error ("Invalid size in declaration (0x%06X)", Size); + if ((D->StorageClass & SC_FUNC) != SC_FUNC && + (D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + /* If the standard was not set explicitly to C89, print a warning + ** for variables with implicit int type. + */ + if (IS_Get (&Standard) >= STD_C99) { + Warning ("Implicit 'int' is an obsolete feature"); } } } From 94239525ca30c052b757a7938031a20189fc2fb5 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 27 Oct 2023 07:07:38 +0200 Subject: [PATCH 356/520] Apple2 SSC: Implement no flow control --- libsrc/apple2/ser/a2.ssc.s | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index f48e948ed..c8aa6e9a5 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -87,6 +87,7 @@ SendFreeCnt: .res 1 ; Number of free bytes in send buffer Stopped: .res 1 ; Flow-stopped flag RtsOff: .res 1 ; Cached value of command register with ; flow stopped +HSType: .res 1 ; Flow-control type RecvBuf: .res 256 ; Receive buffer: 256 bytes SendBuf: .res 256 ; Send buffer: 256 bytes @@ -276,13 +277,15 @@ NoDev: lda #SER_ERR_NO_DEVICE ; Check if the handshake setting is valid AciaOK: ldy #SER_PARAMS::HANDSHAKE lda (ptr1),y - cmp #SER_HS_HW ; This is all we support - beq HandshakeOK + cmp #SER_HS_SW ; Not supported + bne HandshakeOK lda #SER_ERR_INIT_FAILED bne Out HandshakeOK: + sta HSType ; Store flow control type + ldy #$00 ; Initialize buffers sty Stopped sty RecvHead @@ -473,11 +476,14 @@ SER_IRQ: bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) -Flow: ldx Index ; Assert flow control if buffer space too low -lda RtsOff +Flow: lda HSType ; Don't touch if no flow control + beq IRQDone + + ldx Index ; Assert flow control if buffer space too low + lda RtsOff sta ACIA_CMD,x sta Stopped - sec ; Interrupt handled +IRQDone:sec ; Interrupt handled Done: rts ;---------------------------------------------------------------------------- From 8a797095c1983b2704331eafac3291336454f6c9 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 3 Nov 2023 07:50:28 +0100 Subject: [PATCH 357/520] Apple 2 SSC: Handle SER_HS_NONE with no performance impact --- libsrc/apple2/ser/a2.ssc.s | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index c8aa6e9a5..9897593e3 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -87,7 +87,6 @@ SendFreeCnt: .res 1 ; Number of free bytes in send buffer Stopped: .res 1 ; Flow-stopped flag RtsOff: .res 1 ; Cached value of command register with ; flow stopped -HSType: .res 1 ; Flow-control type RecvBuf: .res 256 ; Receive buffer: 256 bytes SendBuf: .res 256 ; Send buffer: 256 bytes @@ -284,7 +283,7 @@ AciaOK: ldy #SER_PARAMS::HANDSHAKE bne Out HandshakeOK: - sta HSType ; Store flow control type + sta tmp2 ; Store flow control type ldy #$00 ; Initialize buffers sty Stopped @@ -335,8 +334,12 @@ BaudOK: sta tmp1 ora #%00001000 ; Enable receive interrupts (RTS low) sta ACIA_CMD,x + ldy tmp2 ; Check flow control type + bne Opened + sta RtsOff ; Disable flow control if required + ; Done - stx Index ; Mark port as open +Opened: stx Index ; Mark port as open lda #SER_ERR_OK Out: ldx #$00 ; Promote char return value @@ -476,14 +479,11 @@ SER_IRQ: bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) -Flow: lda HSType ; Don't touch if no flow control - beq IRQDone - - ldx Index ; Assert flow control if buffer space too low +Flow: ldx Index ; Assert flow control if buffer space too low lda RtsOff sta ACIA_CMD,x sta Stopped -IRQDone:sec ; Interrupt handled + sec ; Interrupt handled Done: rts ;---------------------------------------------------------------------------- From fe115fb621cdbaefd04c63696fa73e3bf19e8acd Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 3 Nov 2023 14:58:32 +0100 Subject: [PATCH 358/520] Revert "Apple 2 SSC: Handle SER_HS_NONE with no performance impact" This reverts commit 0eafb6d1d50a3428d0f518a849c2b45e78b542df. This was buggy, as we sta Stopped when asserting flow, and this stops SER_GET. --- libsrc/apple2/ser/a2.ssc.s | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 9897593e3..c8aa6e9a5 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -87,6 +87,7 @@ SendFreeCnt: .res 1 ; Number of free bytes in send buffer Stopped: .res 1 ; Flow-stopped flag RtsOff: .res 1 ; Cached value of command register with ; flow stopped +HSType: .res 1 ; Flow-control type RecvBuf: .res 256 ; Receive buffer: 256 bytes SendBuf: .res 256 ; Send buffer: 256 bytes @@ -283,7 +284,7 @@ AciaOK: ldy #SER_PARAMS::HANDSHAKE bne Out HandshakeOK: - sta tmp2 ; Store flow control type + sta HSType ; Store flow control type ldy #$00 ; Initialize buffers sty Stopped @@ -334,12 +335,8 @@ BaudOK: sta tmp1 ora #%00001000 ; Enable receive interrupts (RTS low) sta ACIA_CMD,x - ldy tmp2 ; Check flow control type - bne Opened - sta RtsOff ; Disable flow control if required - ; Done -Opened: stx Index ; Mark port as open + stx Index ; Mark port as open lda #SER_ERR_OK Out: ldx #$00 ; Promote char return value @@ -479,11 +476,14 @@ SER_IRQ: bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) -Flow: ldx Index ; Assert flow control if buffer space too low +Flow: lda HSType ; Don't touch if no flow control + beq IRQDone + + ldx Index ; Assert flow control if buffer space too low lda RtsOff sta ACIA_CMD,x sta Stopped - sec ; Interrupt handled +IRQDone:sec ; Interrupt handled Done: rts ;---------------------------------------------------------------------------- From cff611711ba54d6bd7b0ab02426419ceab4f7f07 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 5 Nov 2023 21:03:06 +0100 Subject: [PATCH 359/520] Optimize multiplication by zero --- src/cc65/codegen.c | 14 +++++++++++++- test/val/mult1.c | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 480cc32ea..f923ac8c9 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2726,7 +2726,12 @@ void g_mul (unsigned flags, unsigned long val) if (flags & CF_FORCECHAR) { /* Handle some special cases */ switch (val) { - + case 0: + AddCodeLine ("lda #$00"); + return; + case 1: + /* Nothing to do */ + return; case 3: AddCodeLine ("sta tmp1"); AddCodeLine ("asl a"); @@ -2764,6 +2769,13 @@ void g_mul (unsigned flags, unsigned long val) case CF_INT: switch (val) { + case 0: + AddCodeLine ("lda #$00"); + AddCodeLine ("tax"); + return; + case 1: + /* Nothing to do */ + return; case 3: AddCodeLine ("jsr mulax3"); return; diff --git a/test/val/mult1.c b/test/val/mult1.c index 95141d76d..b1a074e41 100644 --- a/test/val/mult1.c +++ b/test/val/mult1.c @@ -55,6 +55,7 @@ void m3(unsigned char uc) /* testing literal multiply with same source and destination */ vuc = uc; uc2 = 0; + uc1 = vuc; uc1 = uc1*0; if( uc1 != 0 ) failures++; uc1 = vuc; uc1 = uc1*1; if( uc1 != (uc2+=TESTLIT) ) failures++; uc1 = vuc; uc1 = uc1*2; if( uc1 != (uc2+=TESTLIT) ) failures++; uc1 = vuc; uc1 = uc1*3; if( uc1 != (uc2+=TESTLIT) ) failures++; From 23f37c40aa81d5336cf8018ba7c988fb8655f369 Mon Sep 17 00:00:00 2001 From: Brian <18603393+brian6932@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:36:58 -0500 Subject: [PATCH 360/520] Upload 64-bit Windows snapshot to Sourceforge --- .github/workflows/snapshot-on-push-master.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 2aedb0e25..571947c9b 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -135,6 +135,15 @@ jobs: port: ${{ secrets.SSH_PORT }} user: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} + - name: Upload 64-bit Windows snapshot to sourceforge + uses: nogsantos/scp-deploy@master + with: + src: cc65-snapshot-win64.zip + host: ${{ secrets.SSH_HOST }} + remote: ${{ secrets.SSH_DIR }} + port: ${{ secrets.SSH_PORT }} + user: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_KEY }} - name: Upload documents snapshot to sourceforge uses: nogsantos/scp-deploy@master with: From 83f51b1e3c5a210f84f95e6a161ce5e062de502a Mon Sep 17 00:00:00 2001 From: Brian <18603393+brian6932@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:53:14 -0500 Subject: [PATCH 361/520] mention in readme --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e04158e1..f8a0d4ff2 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,10 @@ Some of us may also be around on IRC [#cc65](https://web.libera.chat/#cc65) on l # Downloads -* [Windows Snapshot](https://sourceforge.net/projects/cc65/files/cc65-snapshot-win32.zip) +* [Windows 64bit Snapshot](https://sourceforge.net/projects/cc65/files/cc65-snapshot-win64.zip) -* [Linux Snapshot DEB and RPM](https://software.opensuse.org//download.html?project=home%3Astrik&package=cc65) +* [Windows 32bit Snapshot](https://sourceforge.net/projects/cc65/files/cc65-snapshot-win32.zip) + +* [Linux Snapshot DEB and RPM](https://software.opensuse.org/download.html?project=home%3Astrik&package=cc65) [![Snapshot Build](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml/badge.svg?branch=master)](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml) From f8d7c0cd4c4f0c111180d87852c51d9710cd3ba6 Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Wed, 8 Nov 2023 11:13:31 +0100 Subject: [PATCH 362/520] Fixed typo --- doc/smc.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/smc.sgml b/doc/smc.sgml index 71de20208..ed9b1ba20 100644 --- a/doc/smc.sgml +++ b/doc/smc.sgml @@ -104,7 +104,7 @@ placeholder has two advantages: <item> The code is better documented. It is clearly visible that the given value is about to be changed. <item> When examining an (initial) disassembly (e.g. in a debugger), these - placegolders can be better identified: They are fixed and, you may + placeholders can be better identified: They are fixed and, you may notice that below, quite eye catching defined. </enum> From 86d498b7b3f21a8475b10fe3694fbc054b0e544b Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 6 Nov 2023 20:05:35 +0100 Subject: [PATCH 363/520] Implement SER_HS_NONE on Apple IIgs serial driver --- libsrc/apple2/ser/a2.gs.s | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 7f6bc824d..20018abe4 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -61,6 +61,7 @@ SendFreeCnt: .res 1 ; Number of bytes in send buffer Stopped: .res 1 ; Flow-stopped flag RtsOff: .res 1 +HSType: .res 1 ; Flow-control type RecvBuf: .res 256 ; Receive buffers: 256 bytes SendBuf: .res 256 ; Send buffers: 256 bytes @@ -333,8 +334,10 @@ SER_OPEN: ; Check if the handshake setting is valid ldy #SER_PARAMS::HANDSHAKE ; Handshake lda (ptr1),y - cmp #SER_HS_HW ; This is all we support - bne InvParam + cmp #SER_HS_SW ; Not supported + beq InvParam + + sta HSType ; Store flow control type ; Initialize buffers ldy #$00 @@ -644,13 +647,16 @@ CheckSpecial: sec rts -Flow: ldx Channel ; Assert flow control if buffer space too low +Flow: lda HSType ; Don't touch if no flow control + beq IRQDone + + ldx Channel ; Assert flow control if buffer space too low ldy #WR_TX_CTRL lda RtsOff jsr writeSCCReg sta Stopped - sec ; Interrupt handled +IRQDone:sec ; Interrupt handled Done: rts Special:ldx Channel From d00a23feff3b1ec1277e9345554659e468b7a701 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sat, 11 Nov 2023 21:00:12 +0100 Subject: [PATCH 364/520] Update Apple 2 serial documentation Reflect the new support of SER_HS_NONE. --- doc/apple2.sgml | 14 ++++++++------ doc/apple2enh.sgml | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 063cfc71f..97724c147 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -430,9 +430,10 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. The SSC is an extension card for the II, II+, IIe; the Apple //c and //c+ have the same hardware and firmware integrated. - It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and - does interrupt driven receives. Speeds faster than 9600 baud aren't reachable - because the ROM and ProDOS IRQ handlers are too slow. + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. 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. @@ -449,9 +450,10 @@ The names in the parentheses denote the symbols to be used for static linking of <tag><tt/a2.gs.ser (a2_gs_ser)/</tag> Driver for the Apple IIgs serial ports (printer and modem). - It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and - does interrupt driven receives. Speeds faster than 9600 baud aren't reachable - because the ROM and ProDOS IRQ handlers are too slow. + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. Note that 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 30088521a..11d4feb7e 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -431,9 +431,10 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. The SSC is an extension card for the II, II+, IIe; the Apple //c and //c+ have the same hardware and firmware integrated. - It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and - does interrupt driven receives. Speeds faster than 9600 baud aren't reachable - because the ROM and ProDOS IRQ handlers are too slow. + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. 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. @@ -450,9 +451,10 @@ The names in the parentheses denote the symbols to be used for static linking of <tag><tt/a2e.gs.ser (a2e_gs_ser)/</tag> Driver for the Apple IIgs serial ports (printer and modem). - It supports up to 9600 baud, requires hardware flow control (RTS/CTS) and - does interrupt driven receives. Speeds faster than 9600 baud aren't reachable - because the ROM and ProDOS IRQ handlers are too slow. + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. Note that transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. From 0eb38770bdc18b46e6ea34c57063a8af3e3646ec Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 13 Nov 2023 17:17:46 +0800 Subject: [PATCH 365/520] Fixed const qualifiers on named structs/unions members that should prevent assignments to the whole structs/unions. Added warning on ignored qualifiers on anonymous structs/unions. --- src/cc65/assignment.c | 7 ++-- src/cc65/declare.c | 80 ++++++++++++++++++++++++++++++++----------- src/cc65/symentry.h | 13 ++++++- test/err/bug2018.c | 25 ++++++++++++++ test/val/bug2018-ok.c | 61 +++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 test/err/bug2018.c create mode 100644 test/val/bug2018-ok.c diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index e0ec80fde..e51dde9ab 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -44,6 +44,7 @@ #include "scanner.h" #include "stackptr.h" #include "stdnames.h" +#include "symentry.h" #include "typecmp.h" #include "typeconv.h" @@ -82,6 +83,8 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) { TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, "Incompatible types in assignment to '%s' from '%s'"); + } else if (SymHasConstMember (ltype->A.S)) { + Error ("Assignment to read only variable"); } /* Do we copy the value directly using the primary? */ @@ -627,7 +630,7 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) } } else if (IsQualConst (ltype)) { /* Check for assignment to const */ - Error ("Assignment to const"); + Error ("Assignment to const variable"); } } @@ -686,7 +689,7 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) Error ("Invalid lvalue in assignment"); } else if (IsQualConst (Expr->Type)) { /* The left side must not be const qualified */ - Error ("Assignment to const"); + Error ("Assignment to const variable"); } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4bfd52c5e..ccea0098a 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -994,10 +994,14 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) ** a union. */ if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { - /* This is an anonymous struct or union. Copy the fields - ** into the current level. - */ - AnonFieldName (Decl.Ident, "field", UnionTagEntry->V.S.ACount); + /* This is an anonymous struct or union */ + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); + + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; + } } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); @@ -1011,13 +1015,18 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) GetFullTypeName (Decl.Type)); } + /* Check for const types */ + if (IsQualConst (Decl.Type)) { + Flags |= SC_HAVECONST; + } + /* Handle sizes */ FieldSize = SizeOf (Decl.Type); if (FieldSize > UnionSize) { UnionSize = FieldSize; } - /* Add a field entry to the table. */ + /* Add a field entry to the table */ if (FieldWidth > 0) { /* For a union, allocate space for the type specified by the ** bit-field. @@ -1025,19 +1034,30 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, SignednessSpecified); } else if (Decl.Ident[0] != '\0') { + /* Add the new field to the table */ Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); - if (IsAnonName (Decl.Ident)) { - Field->V.A.ANumber = UnionTagEntry->V.S.ACount++; - AliasAnonStructFields (&Decl, Field); - } - /* Check if the field itself has a flexible array member */ + /* Check the new field for certain kinds of members */ if (IsClassStruct (Decl.Type)) { SymEntry* TagEntry = GetESUTagSym (Decl.Type); + + /* Alias the fields of the anonymous member on the current level */ + if (IsAnonName (Decl.Ident)) { + Field->V.A.ANumber = UnionTagEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Field); + } + + /* Check if the field itself has a flexible array member */ if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { Field->Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM; } + + /* Check if the field itself has a const member */ + if (TagEntry && SymHasConstMember (TagEntry)) { + Field->Flags |= SC_HAVECONST; + Flags |= SC_HAVECONST; + } } } @@ -1190,10 +1210,14 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) */ if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { - /* This is an anonymous struct or union. Copy the - ** fields into the current level. - */ - AnonFieldName (Decl.Ident, "field", StructTagEntry->V.S.ACount); + /* This is an anonymous struct or union */ + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); + + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; + } } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); @@ -1211,6 +1235,11 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) GetFullTypeName (Decl.Type)); } + /* Check for const types */ + if (IsQualConst (Decl.Type)) { + Flags |= SC_HAVECONST; + } + /* Add a field entry to the table */ if (FieldWidth > 0) { /* Full bytes have already been added to the StructSize, @@ -1223,24 +1252,35 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) FieldWidth, SignednessSpecified); BitOffs += FieldWidth; CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); - /* Add any full bytes to the struct size. */ + /* Add any full bytes to the struct size */ StructSize += BitOffs / CHAR_BITS; BitOffs %= CHAR_BITS; } else if (Decl.Ident[0] != '\0') { + /* Add the new field to the table */ Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); - if (IsAnonName (Decl.Ident)) { - Field->V.A.ANumber = StructTagEntry->V.S.ACount++; - AliasAnonStructFields (&Decl, Field); - } - /* Check if the field itself has a flexible array member */ + /* Check the new field for certain kinds of members */ if (IsClassStruct (Decl.Type)) { SymEntry* TagEntry = GetESUTagSym (Decl.Type); + + /* Alias the fields of the anonymous member on the current level */ + if (IsAnonName (Decl.Ident)) { + Field->V.A.ANumber = StructTagEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Field); + } + + /* Check if the field itself has a flexible array member */ if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { Field->Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM; Error ("Invalid use of struct with flexible array member"); } + + /* Check if the field itself has a const member */ + if (TagEntry && SymHasConstMember (TagEntry)) { + Field->Flags |= SC_HAVECONST; + Flags |= SC_HAVECONST; + } } if (!FlexibleMember) { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 76a4272ae..715c036d6 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -108,6 +108,7 @@ struct CodeEntry; #define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */ #define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */ #define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ +#define SC_HAVECONST 0x08000000U /* Type has a const member */ @@ -275,6 +276,16 @@ INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) # define SymHasFlexibleArrayMember(Sym) (((Sym)->Flags & SC_HAVEFAM) == SC_HAVEFAM) #endif +#if defined(HAVE_INLINE) +INLINE int SymHasConstMember (const SymEntry* Sym) +/* Return true if the given entry has a const member */ +{ + return ((Sym->Flags & SC_HAVECONST) == SC_HAVECONST); +} +#else +# define SymHasConstMember(Sym) (((Sym)->Flags & SC_HAVECONST) == SC_HAVECONST) +#endif + #if defined(HAVE_INLINE) INLINE const char* SymGetAsmName (const SymEntry* Sym) /* Return the assembler label name for the symbol (beware: may be NULL!) */ @@ -282,7 +293,7 @@ INLINE const char* SymGetAsmName (const SymEntry* Sym) return Sym->AsmName; } #else -# define SymGetAsmName(Sym) ((Sym)->AsmName) +# define SymGetAsmName(Sym) ((Sym)->AsmName) #endif const DeclAttr* SymGetAttr (const SymEntry* Sym, DeclAttrType AttrType); diff --git a/test/err/bug2018.c b/test/err/bug2018.c new file mode 100644 index 000000000..61892992e --- /dev/null +++ b/test/err/bug2018.c @@ -0,0 +1,25 @@ +/* Bug #2018 - Compiler has problems with const struct fields */ + +struct X { + struct { + int a; + } a; + union { + int a; + const int b; + } b; +}; + +struct X f(void) +{ + struct X x = { 42 }; + return x; +} + +int main(void) +{ + struct X x = { 0 }; + x = f(); /* Error since X is read only */ + + return 0; +} diff --git a/test/val/bug2018-ok.c b/test/val/bug2018-ok.c new file mode 100644 index 000000000..7046e1d1f --- /dev/null +++ b/test/val/bug2018-ok.c @@ -0,0 +1,61 @@ +/* Bug #2018 - Compiler has problems with const struct fields */ + +#include <stdio.h> + +unsigned failures; + +struct X { + const struct { /* Qualifier ignored in cc65 */ + int a; + }; + const union { /* Qualifier ignored in cc65 */ + int b; + }; +}; + +union Y { + const struct { /* Qualifier ignored in cc65 */ + int a; + }; + const union { /* Qualifier ignored in cc65 */ + int b; + }; +}; + +struct X f(struct X a) +{ + struct X x = { 42 }; + return --a.a ? a : x; +} + +union Y g(union Y a) +{ + union Y y = { 42 }; + return --a.a ? a : y; +} + +int main(void) +{ + struct X x = { 1 }; + union Y y = { 1 }; + + x = f(x); /* Allowed in cc65 since X is not read only */ + y = g(y); /* Allowed in cc65 since Y is not read only */ + + if (x.a != 42) + { + ++failures; + } + + if (y.a != 42) + { + ++failures; + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + + return failures; +} From 9242508abf6d03221837fb1063eff855aabf6c81 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 12 Nov 2023 22:15:03 +0100 Subject: [PATCH 366/520] Optimize substraction of 1 --- src/cc65/codegen.c | 70 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index f923ac8c9..0f45f66d9 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2000,25 +2000,55 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, case CF_INT: if (flags & CF_CONST) { - AddCodeLine ("lda %s", lbuf); - AddCodeLine ("sec"); - AddCodeLine ("sbc #$%02X", (unsigned char)val); - AddCodeLine ("sta %s", lbuf); - if (val < 0x100) { - unsigned L = GetLocalLabel (); - AddCodeLine ("bcs %s", LocalLabelName (L)); - AddCodeLine ("dec %s+1", lbuf); - g_defcodelabel (L); + if (val == 1) { + unsigned L = GetLocalLabel(); if ((flags & CF_NOKEEP) == 0) { - AddCodeLine ("ldx %s+1", lbuf); + if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0) { + AddCodeLine ("lda %s", lbuf); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + AddCodeLine ("dea"); + AddCodeLine ("sta %s", lbuf); + AddCodeLine ("ldx %s+1", lbuf); + } else { + AddCodeLine ("ldx %s", lbuf); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + AddCodeLine ("dex"); + AddCodeLine ("stx %s", lbuf); + AddCodeLine ("txa"); + AddCodeLine ("ldx %s+1", lbuf); + } + } else { + AddCodeLine ("lda %s", lbuf); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + AddCodeLine ("dec %s", lbuf); } } else { - AddCodeLine ("lda %s+1", lbuf); - AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); - AddCodeLine ("sta %s+1", lbuf); - if ((flags & CF_NOKEEP) == 0) { - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + AddCodeLine ("lda %s", lbuf); + AddCodeLine ("sec"); + AddCodeLine ("sbc #$%02X", (unsigned char)val); + AddCodeLine ("sta %s", lbuf); + if (val < 0x100) { + unsigned L = GetLocalLabel (); + AddCodeLine ("bcs %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + 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); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } } else { @@ -3693,7 +3723,13 @@ void g_dec (unsigned flags, unsigned long val) } else { /* Inline the code */ if (val < 0x300) { - if ((val & 0xFF) != 0) { + if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && val == 1) { + unsigned L = GetLocalLabel(); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dex"); + g_defcodelabel (L); + AddCodeLine ("dea"); + } else if ((val & 0xFF) != 0) { unsigned L = GetLocalLabel(); AddCodeLine ("sec"); AddCodeLine ("sbc #$%02X", (unsigned char) val); From 076137f41b01f8ca4da5fe2b5242b156c769dd36 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 12 Nov 2023 22:35:24 +0100 Subject: [PATCH 367/520] Optimize lda/sta/lda and friends --- src/cc65/codeopt.c | 3 +++ src/cc65/coptind.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ src/cc65/coptind.h | 3 +++ 3 files changed, 60 insertions(+) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index baa44f99e..3a2c2a35d 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -197,6 +197,7 @@ static OptFunc DOptStore3 = { OptStore3, "OptStore3", 120, 0, static OptFunc DOptStore4 = { OptStore4, "OptStore4", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptStore5 = { OptStore5, "OptStore5", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptStoreLoad = { OptStoreLoad, "OptStoreLoad", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptLoadStoreLoad= { OptLoadStoreLoad,"OptLoadStoreLoad", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptSub1 = { OptSub1, "OptSub1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSub2 = { OptSub2, "OptSub2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSub3 = { OptSub3, "OptSub3", 100, 0, 0, 0, 0, 0 }; @@ -307,6 +308,7 @@ static OptFunc* OptFuncs[] = { &DOptStore4, &DOptStore5, &DOptStoreLoad, + &DOptLoadStoreLoad, &DOptSub1, &DOptSub2, &DOptSub3, @@ -723,6 +725,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptUnusedStores, 1); C += RunOptFunc (S, &DOptDupLoads, 1); C += RunOptFunc (S, &DOptStoreLoad, 1); + C += RunOptFunc (S, &DOptLoadStoreLoad, 1); C += RunOptFunc (S, &DOptTransfers1, 1); C += RunOptFunc (S, &DOptTransfers3, 1); C += RunOptFunc (S, &DOptTransfers4, 1); diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index a080cfb20..9e9985c10 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -581,6 +581,60 @@ unsigned OptStoreLoad (CodeSeg* S) +unsigned OptLoadStoreLoad (CodeSeg* S) +/* Search for the sequence +** +** ld. xx +** st. yy +** ld. xx +** +** and remove the useless load. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if ((L[0]->OPC == OP65_LDA || + L[0]->OPC == OP65_LDX || + L[0]->OPC == OP65_LDY) && + (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP) && + !CS_RangeHasLabel (S, I+1, 3) && + CS_GetEntries (S, L+1, I+1, 2) && + (L[1]->OPC == OP65_STA || + L[1]->OPC == OP65_STX || + L[1]->OPC == OP65_STY) && + L[2]->OPC == L[0]->OPC && + L[2]->AM == L[0]->AM && + strcmp (L[0]->Arg, L[2]->Arg) == 0) { + + /* Remove the second load */ + CS_DelEntries (S, I+2, 1); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptTransfers1 (CodeSeg* S) /* Remove transfers from one register to another and back */ { diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index c7ecf4194..6d39ee1f8 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -64,6 +64,9 @@ unsigned OptDupLoads (CodeSeg* S); unsigned OptStoreLoad (CodeSeg* S); /* Remove a store followed by a load from the same location. */ +unsigned OptLoadStoreLoad (CodeSeg* S); +/* Remove a load, store followed by a reload of the same location. */ + unsigned OptTransfers1 (CodeSeg* S); /* Remove transfers from one register to another and back */ From b3eca512643ceab4790959c962e85d9678da3e8f Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 12 Nov 2023 22:48:58 +0100 Subject: [PATCH 368/520] Add int decrement tests --- test/val/sub3.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 test/val/sub3.c diff --git a/test/val/sub3.c b/test/val/sub3.c new file mode 100644 index 000000000..2a3646f9a --- /dev/null +++ b/test/val/sub3.c @@ -0,0 +1,192 @@ +/* + !!DESCRIPTION!! Subtraction Test + !!ORIGIN!! SDCC regression tests + !!LICENCE!! GPL, read COPYING.GPL +*/ + +#include <stdio.h> +#include <limits.h> + +unsigned char failures=0; + +int int0 = 5; +unsigned int int1 = 5; + +void pre_dec_test(void) +{ + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + --int0; + --int1; + + if(int0 != 4) + failures++; + if(int1 != 4) + failures++; + + --int0; + --int1; + --int0; + --int1; + --int0; + --int1; + --int0; + --int1; + + if(int0 != 0) + failures++; + if(int1 != 0) + failures++; + + --int0; + --int1; + + if(int0 != -1) + failures++; + if(int1 != 65535U) + failures++; +} + +void post_dec_test(void) +{ + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + int0--; + int1--; + + if(int0 != 4) + failures++; + if(int1 != 4) + failures++; + + int0--; + int1--; + int0--; + int1--; + int0--; + int1--; + int0--; + int1--; + + if(int0 != 0) + failures++; + if(int1 != 0) + failures++; + + int0--; + int1--; + + if(int0 != -1) + failures++; + if(int1 != 65535U) + failures++; +} + +void pre_dec_assign_test(void) +{ + int a; + unsigned int b; + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + a = --int0; + b = --int1; + + if(int0 != 4 || a != int0) + failures++; + if(int1 != 4 || b != int1) + failures++; + + a = --int0; + b = --int1; + a = --int0; + b = --int1; + a = --int0; + b = --int1; + a = --int0; + b = --int1; + + if(int0 != 0 || a != int0) + failures++; + if(int1 != 0 || b != int1) + failures++; + + a = --int0; + b = --int1; + + if(int0 != -1 || a != int0) + failures++; + if(int1 != 65535U || b != int1) + failures++; +} + +void post_dec_assign_test(void) +{ + int a; + unsigned int b; + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + a = int0--; + b = int1--; + + if(int0 != 4 || a != 5) + failures++; + if(int1 != 4 || b != 5) + failures++; + + a = int0--; + b = int1--; + a = int0--; + b = int1--; + a = int0--; + b = int1--; + a = int0--; + b = int1--; + + if(int0 != 0 || a != 1) + failures++; + if(int1 != 0 || b != 1) + failures++; + + a = int0--; + b = int1--; + + if(int0 != -1 || a != 0) + failures++; + if(int1 != 65535U || b != 0) + failures++; +} + +int main(void) +{ + int0 = 5; + int1 = 5; + pre_dec_test(); + + int0 = 5; + int1 = 5; + post_dec_test(); + + int0 = 5; + int1 = 5; + pre_dec_assign_test(); + + int0 = 5; + int1 = 5; + post_dec_assign_test(); + + printf("failures: %d\n",failures); + + return failures; +} From 913ab3607fc95e8448c6004fb0348ceb351622cb Mon Sep 17 00:00:00 2001 From: Ryan Carsten Schmidt <git@ryandesign.com> Date: Tue, 14 Nov 2023 00:22:36 -0600 Subject: [PATCH 369/520] Fix typo --- doc/sim65.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sim65.sgml b/doc/sim65.sgml index b1e5afbdc..c2740bbad 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -49,7 +49,7 @@ which is limited to an 8-bit result 0-255. An error in sim65, like bad arguments or an internal problem will exit with <tt/1/. -A timeout from <tt/-x/ will exist with <tt/2/. +A timeout from <tt/-x/ will exit with <tt/2/. <sect1>Command line options in detail<p> From e9e3cd969e6059aa2baa659bce695fa71fd91c87 Mon Sep 17 00:00:00 2001 From: Ryan Carsten Schmidt <git@ryandesign.com> Date: Tue, 14 Nov 2023 00:57:07 -0600 Subject: [PATCH 370/520] Fix typos; other copyediting --- doc/da65.sgml | 52 +++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/doc/da65.sgml b/doc/da65.sgml index 3a01ce4f5..113eb6f97 100644 --- a/doc/da65.sgml +++ b/doc/da65.sgml @@ -184,7 +184,7 @@ Here is a description of all the command line options: <label id="option--mnemonic-column"> <tag><tt>--mnemonic-column n</tt></tag> - Specifies the column where a mnemonic or pseudo instrcuction is output. + Specifies the column where a mnemonic or pseudo instruction is output. <label id="option--pagelength"> @@ -283,7 +283,7 @@ Some instructions may generate labels in the first pass, while most other instructions do not generate labels, but use them if they are available. Among others, the branch and jump instructions will generate labels for the target of the branch in the first pass. External labels (taken from the info file) -have precedence over internally generated ones, They must be valid identifiers +have precedence over internally generated ones. They must be valid identifiers as specified for the ca65 assembler. Internal labels (generated by the disassembler) have the form <tt/Labcd/, where <tt/abcd/ is the hexadecimal address of the label in upper case letters. You should probably avoid using @@ -304,7 +304,7 @@ name="next section"> for more information. The info file contains lists of specifications grouped together. Each group directive has an identifying token and an attribute list enclosed in curly braces. Attributes have a name followed by a value. The syntax of the value -depends on the type of the attribute. String attributes are places in double +depends on the type of the attribute. String attributes are placed in double quotes, numeric attributes may be specified as decimal numbers or hexadecimal with a leading dollar sign. There are also attributes where the attribute value is a keyword; in this case, the keyword is given as-is (without quotes or @@ -317,8 +317,8 @@ anything). Each attribute is terminated by a semicolon. <sect1>Comments<p> -Comments start with a hash mark (<tt/#/) or a double slash (<tt>//</tt>); -and, extend from the position of the mark to the end of the current line. +Comments start with a hash mark (<tt/#/) or a double slash (<tt>//</tt>) +and extend from the position of the mark to the end of the current line. Hash marks or double slashes inside of strings will <em/not/ start a comment, of course. @@ -359,20 +359,20 @@ following attributes are recognized: <tag><tt/HEXOFFS/</tag> - The attribute is followed by a boolean value. If true, offsets to labels are + This attribute is followed by a boolean value. If true, offsets to labels are output in hex, otherwise they're output in decimal notation. The default is false. The attribute may be changed on the command line using the <tt><ref id="option--hexoffs" name="--hexoffs"></tt> option. <tag><tt/INPUTNAME/</tag> - The attribute is followed by a string value, which gives the name of the + This attribute is followed by a string value, which gives the name of the input file to read. If it is present, the disassembler does not accept an input file name on the command line. <tag><tt/INPUTOFFS/</tag> - The attribute is followed by a numerical value that gives an offset into + This attribute is followed by a numerical value that gives an offset into the input file which is skipped before reading data. The attribute may be used to skip headers or unwanted code sections in the input file. @@ -412,7 +412,7 @@ following attributes are recognized: <label id="OUTPUTNAME"> <tag><tt/OUTPUTNAME/</tag> - The attribute is followed by string value, which gives the name of the + This attribute is followed by string value, which gives the name of the output file to write. If it is present, specification of an output file on the command line using the <tt><ref id="option-o" name="-o"></tt> option is not allowed. @@ -433,8 +433,8 @@ following attributes are recognized: This attribute may be used instead of the <tt><ref id="option--start-addr" name="--start-addr"></tt> option on the command line. It takes a numerical parameter. The default for the start address is $10000 minus the size of - the input file (this assumes that the input file is a ROM that contains the - reset and irq vectors). + the input file. (This assumes that the input file is a ROM that contains the + reset and irq vectors.) <tag><tt/TEXTCOLUMN/</tag> @@ -468,7 +468,7 @@ following attributes are recognized: <tag><tt>NAME</tt></tag> This is a convenience attribute. It takes a string argument and will cause the disassembler to define a label for the start of the range with the - given name. So a separate <tt><ref id="infofile-label" name="LABEL"></tt> + given name so a separate <tt><ref id="infofile-label" name="LABEL"></tt> directive is not needed. <tag><tt>START</tt></tag> @@ -509,8 +509,8 @@ following attributes are recognized: <tag><tt>SKIP</tt></tag> The range is simply ignored when generating the output file. Please note that this means that reassembling the output file will <em/not/ generate - the original file, not only because the missing piece in between, but also - because the following code will be located on wrong addresses. Output + the original file, not only because of the missing piece in between, but + also because the following code will be located on wrong addresses. Output generated with <tt/SKIP/ ranges will need manual rework. <tag><tt>TEXTTABLE</tt></tag> @@ -529,7 +529,7 @@ following attributes are recognized: <tag><tt>ADDRMODE</tt></tag> When disassembling 65816 code, this specifies the M and X flag states - for this range. It's a string argument of the form "mx", capital letters + for this range. It's a string argument of the form "mx". Capital letters mean the flag is enabled. </descrip> @@ -561,9 +561,9 @@ code. The following attributes are recognized: <tag><tt>NAME</tt></tag> The attribute is followed by a string value which gives the name of the - label. Empty names are allowed, in this case the disassembler will create - an unnamed label (see the assembler docs for more information about unnamed - labels). + label. Empty names are allowed; in this case the disassembler will create + an unnamed label. (See the assembler docs for more information about unnamed + labels.) <tag><tt>SIZE</tt></tag> This attribute is optional and may be used to specify the size of the data @@ -595,15 +595,18 @@ disassembled code. The following attributes are recognized: <descrip> <tag><tt>START</tt></tag> - Followed by a numerical value. Specifies the start address of the segment. + This attribute is followed by a numerical value which specifies the start + address of the segment. <tag><tt>END</tt></tag> - Followed by a numerical value. Specifies the end address of the segment. The - end address is the last address that is a part of the segment. + This attribute is followed by a numerical value which specifies the end + address of the segment. The end address is the last address that is a part of + the segment. <tag><tt>NAME</tt></tag> - The attribute is followed by a string value which gives the name of the + This attribute is followed by a string value which gives the name of the segment. + </descrip> All attributes are mandatory. Segments must not overlap. The disassembler will @@ -635,10 +638,11 @@ The following attributes are recognized: <descrip> <tag><tt>FILE</tt></tag> - Followed by a string value. Specifies the name of the file to read. + This attribute is followed by a string value. It specifies the name of the + file to read. <tag><tt>COMMENTSTART</tt></tag> - The optional attribute is followed by a character constant. It specifies the + This optional attribute is followed by a character constant. It specifies the character that starts a comment. The default value is a semicolon. This value is ignored if <tt/IGNOREUNKNOWN/ is true. From 5332eeecc24cfca8777471fd3839f26579b8e52e Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 15 Nov 2023 18:17:36 +0800 Subject: [PATCH 371/520] Fixed empty declarations in structs/unions. --- src/cc65/declare.c | 120 +++++++++++++++++++++++------------------- test/val/bug2018-ok.c | 10 ++++ 2 files changed, 76 insertions(+), 54 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ccea0098a..6615998ce 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -983,33 +983,36 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) /* Check for a bit-field declaration */ FieldWidth = ParseFieldWidth (&Decl); - /* Ignore zero sized bit fields in a union */ - if (FieldWidth == 0) { - goto NextMember; - } - - /* Check for fields without a name */ + /* Check for fields without names */ if (Decl.Ident[0] == '\0') { - /* In cc65 mode, we allow anonymous structs/unions within - ** a union. - */ - if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { - /* This is an anonymous struct or union */ - AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); + if (FieldWidth < 0) { + /* In cc65 mode, we allow anonymous structs/unions within + ** a union. + */ + SymEntry* TagEntry; + if (IS_Get (&Standard) >= STD_CC65 && + IsClassStruct (Decl.Type) && + (TagEntry = GetESUTagSym (Decl.Type)) && + SymHasAnonName (TagEntry)) { + /* This is an anonymous struct or union */ + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); - /* Ignore CVR qualifiers */ - if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { - Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); - Decl.Type[0].C &= ~T_QUAL_CVR; + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; + } + } else { + /* A non bit-field without a name is legal but useless */ + Warning ("Declaration does not declare anything"); + + goto NextMember; } - } else { - /* A non bit-field without a name is legal but useless */ - Warning ("Declaration does not declare anything"); + } else if (FieldWidth > 0) { + /* A bit-field without a name will get an anonymous one */ + AnonName (Decl.Ident, "bit-field"); } - } - - /* Check for incomplete types including 'void' */ - if (IsIncompleteType (Decl.Type)) { + } else if (IsIncompleteType (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); @@ -1020,6 +1023,11 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) Flags |= SC_HAVECONST; } + /* Ignore zero sized bit fields in a union */ + if (FieldWidth == 0) { + goto NextMember; + } + /* Handle sizes */ FieldSize = SizeOf (Decl.Type); if (FieldSize > UnionSize) { @@ -1180,36 +1188,17 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } } - /* Apart from the above, a bit field with width 0 is not processed - ** further. - */ - if (FieldWidth == 0) { - goto NextMember; - } - - /* Check if this field is a flexible array member, and - ** calculate the size of the field. - */ - if (IsTypeArray (Decl.Type) && GetElementCount (Decl.Type) == UNSPECIFIED) { - /* Array with unspecified size */ - if (StructSize == 0) { - Error ("Flexible array member cannot be first struct field"); - } - FlexibleMember = 1; - Flags |= SC_HAVEFAM; - - /* Assume zero for size calculations */ - SetElementCount (Decl.Type, FLEXIBLE); - } - /* Check for fields without names */ if (Decl.Ident[0] == '\0') { if (FieldWidth < 0) { /* In cc65 mode, we allow anonymous structs/unions within ** a struct. */ - if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { - + SymEntry* TagEntry; + if (IS_Get (&Standard) >= STD_CC65 && + IsClassStruct (Decl.Type) && + (TagEntry = GetESUTagSym (Decl.Type)) && + SymHasAnonName (TagEntry)) { /* This is an anonymous struct or union */ AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); @@ -1221,18 +1210,34 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); + + goto NextMember; } - } else { + } else if (FieldWidth > 0) { /* A bit-field without a name will get an anonymous one */ AnonName (Decl.Ident, "bit-field"); } - } + } else { + /* Check if this field is a flexible array member, and + ** calculate the size of the field. + */ + if (IsTypeArray (Decl.Type) && GetElementCount (Decl.Type) == UNSPECIFIED) { + /* Array with unspecified size */ + if (StructSize == 0) { + Error ("Flexible array member cannot be first struct field"); + } + FlexibleMember = 1; + Flags |= SC_HAVEFAM; - /* Check for incomplete types including 'void' */ - if (IsIncompleteType (Decl.Type)) { - Error ("Field '%s' has incomplete type '%s'", - Decl.Ident, - GetFullTypeName (Decl.Type)); + /* Assume zero for size calculations */ + SetElementCount (Decl.Type, FLEXIBLE); + } + + if (IsIncompleteType (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); + } } /* Check for const types */ @@ -1240,6 +1245,13 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) Flags |= SC_HAVECONST; } + /* Apart from the above, a bit field with width 0 is not processed + ** further. + */ + if (FieldWidth == 0) { + goto NextMember; + } + /* Add a field entry to the table */ if (FieldWidth > 0) { /* Full bytes have already been added to the StructSize, diff --git a/test/val/bug2018-ok.c b/test/val/bug2018-ok.c index 7046e1d1f..567beb301 100644 --- a/test/val/bug2018-ok.c +++ b/test/val/bug2018-ok.c @@ -5,6 +5,11 @@ unsigned failures; struct X { + const int; /* Useless empty declaration */ + const void; /* Useless empty declaration */ + const struct U; /* Useless(?) declaration */ + const struct V { int a; }; /* Useless(?) declaration */ + const struct { /* Qualifier ignored in cc65 */ int a; }; @@ -14,6 +19,11 @@ struct X { }; union Y { + const int; /* Useless empty declaration */ + const void; /* Useless empty declaration */ + const union W; /* Useless(?) declaration */ + const union T { int a; }; /* Useless(?) declaration */ + const struct { /* Qualifier ignored in cc65 */ int a; }; From f6c3a1b209caaa2f21b5cbd1e1c00d7f9b29d7d7 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 15 Nov 2023 21:17:05 +0800 Subject: [PATCH 372/520] Removed the extra "'}' expected" error message following a "Excess elements in struct/union initializer" error message. --- src/cc65/initdata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index ba1298db3..e9442737c 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -497,7 +497,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) if (HasCurly) { Error ("Excess elements in %s initializer", GetBasicTypeName (T)); - SkipInitializer (HasCurly); + SkipInitializer (0); } break; } From 3b7af398a989e876b1e7afb48d6b6a81fcfcb7c9 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 15 Nov 2023 21:32:24 +0800 Subject: [PATCH 373/520] Fixed initialization of union when it has an anonymous bit-field as the first member declaration. --- src/cc65/initdata.c | 38 ++++++++++++++++++++------------------ test/val/bitfield-union.c | 9 +++++++-- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index e9442737c..5401e577c 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -511,18 +511,20 @@ 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 (TagSym) && (IsAnonName (TagSym->Name))) { - /* Account for the data and output it if we have at least a full - ** byte. We may have more if there was storage unit overlap, for - ** example two consecutive 7 bit fields. Those would be packed - ** into 2 bytes. - */ - SI.ValBits += TagSym->Type->A.B.Width; - CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); - /* TODO: Generalize this so any type can be used. */ - CHECK (SI.ValBits <= LONG_BITS); - while (SI.ValBits >= CHAR_BITS) { - DefineBitFieldData (&SI); + if (SymIsBitField (TagSym) && IsAnonName (TagSym->Name)) { + if (!IsTypeUnion (T)) { + /* Account for the data and output it if we have at least a full + ** byte. We may have more if there was storage unit overlap, for + ** example two consecutive 7 bit fields. Those would be packed + ** into 2 bytes. + */ + SI.ValBits += TagSym->Type->A.B.Width; + CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); + /* TODO: Generalize this so any type can be used. */ + CHECK (SI.ValBits <= LONG_BITS); + while (SI.ValBits >= CHAR_BITS) { + DefineBitFieldData (&SI); + } } /* Avoid consuming the comma if any */ goto NextMember; @@ -628,15 +630,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Skip the comma next round */ SkipComma = 1; -NextMember: - /* Next member. For unions, only the first one can be initialized */ + /* For unions, only the first named member can be initialized */ if (IsTypeUnion (T)) { - /* Union */ TagSym = 0; - } else { - /* Struct */ - TagSym = TagSym->NextSym; + continue; } + +NextMember: + /* Next member */ + TagSym = TagSym->NextSym; } if (HasCurly) { diff --git a/test/val/bitfield-union.c b/test/val/bitfield-union.c index 1fd201456..4c01d2183 100644 --- a/test/val/bitfield-union.c +++ b/test/val/bitfield-union.c @@ -1,5 +1,5 @@ /* - Copyright 2020 The cc65 Authors + Copyright 2020-2023 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -25,6 +25,7 @@ #include <stdio.h> typedef union { + const unsigned int : 1; unsigned int bf; struct { @@ -38,8 +39,12 @@ static unsigned char failures = 0; int main (void) { - bitfield_t bitfield = {0}; + bitfield_t bitfield = { 42 }; + printf ("Bitfield: %u\n", bitfield.bf); + if (bitfield.bf != 42) failures++; + + bitfield.bf ^= 42; printf ("Bitfield: %u\n", bitfield.bf); if (bitfield.bf != 0) failures++; From b17c4d3434682e0797fbb6d2c2e24c4e957efbcb Mon Sep 17 00:00:00 2001 From: rumbledethumps <16963588+rumbledethumps@users.noreply.github.com> Date: Thu, 16 Nov 2023 18:46:16 -0800 Subject: [PATCH 374/520] add rp6502 target --- asminc/rp6502.inc | 101 ++++++++++++++ cfg/rp6502.cfg | 42 ++++++ include/rp6502.h | 246 +++++++++++++++++++++++++++++++++++ libsrc/Makefile | 1 + libsrc/rp6502/close.c | 16 +++ libsrc/rp6502/codepage.c | 14 ++ libsrc/rp6502/crt0.s | 54 ++++++++ libsrc/rp6502/getres.c | 18 +++ libsrc/rp6502/gettime.c | 20 +++ libsrc/rp6502/gettimespec.c | 22 ++++ libsrc/rp6502/gettimezone.c | 24 ++++ libsrc/rp6502/initenv.s | 16 +++ libsrc/rp6502/irq.s | 51 ++++++++ libsrc/rp6502/lrand.c | 14 ++ libsrc/rp6502/lseek.c | 19 +++ libsrc/rp6502/mainargs.s | 16 +++ libsrc/rp6502/open.c | 26 ++++ libsrc/rp6502/oserrlist.s | 86 ++++++++++++ libsrc/rp6502/oserror.s | 66 ++++++++++ libsrc/rp6502/phi2.c | 14 ++ libsrc/rp6502/randomize.c | 16 +++ libsrc/rp6502/read.c | 31 +++++ libsrc/rp6502/read_xram.c | 17 +++ libsrc/rp6502/read_xstack.c | 22 ++++ libsrc/rp6502/ria.s | 90 +++++++++++++ libsrc/rp6502/settime.c | 18 +++ libsrc/rp6502/stdin_opt.c | 16 +++ libsrc/rp6502/sysremove.c | 26 ++++ libsrc/rp6502/sysrename.c | 32 +++++ libsrc/rp6502/write.c | 31 +++++ libsrc/rp6502/write_xram.c | 17 +++ libsrc/rp6502/write_xstack.c | 20 +++ libsrc/rp6502/xreg.s | 40 ++++++ src/ca65/main.c | 4 + src/cc65/main.c | 4 + src/common/target.c | 2 + src/common/target.h | 1 + 37 files changed, 1253 insertions(+) create mode 100644 asminc/rp6502.inc create mode 100644 cfg/rp6502.cfg create mode 100644 include/rp6502.h create mode 100644 libsrc/rp6502/close.c create mode 100644 libsrc/rp6502/codepage.c create mode 100644 libsrc/rp6502/crt0.s create mode 100644 libsrc/rp6502/getres.c create mode 100644 libsrc/rp6502/gettime.c create mode 100644 libsrc/rp6502/gettimespec.c create mode 100644 libsrc/rp6502/gettimezone.c create mode 100644 libsrc/rp6502/initenv.s create mode 100644 libsrc/rp6502/irq.s create mode 100644 libsrc/rp6502/lrand.c create mode 100644 libsrc/rp6502/lseek.c create mode 100644 libsrc/rp6502/mainargs.s create mode 100644 libsrc/rp6502/open.c create mode 100644 libsrc/rp6502/oserrlist.s create mode 100644 libsrc/rp6502/oserror.s create mode 100644 libsrc/rp6502/phi2.c create mode 100644 libsrc/rp6502/randomize.c create mode 100644 libsrc/rp6502/read.c create mode 100644 libsrc/rp6502/read_xram.c create mode 100644 libsrc/rp6502/read_xstack.c create mode 100644 libsrc/rp6502/ria.s create mode 100644 libsrc/rp6502/settime.c create mode 100644 libsrc/rp6502/stdin_opt.c create mode 100644 libsrc/rp6502/sysremove.c create mode 100644 libsrc/rp6502/sysrename.c create mode 100644 libsrc/rp6502/write.c create mode 100644 libsrc/rp6502/write_xram.c create mode 100644 libsrc/rp6502/write_xstack.c create mode 100644 libsrc/rp6502/xreg.s diff --git a/asminc/rp6502.inc b/asminc/rp6502.inc new file mode 100644 index 000000000..3fa493331 --- /dev/null +++ b/asminc/rp6502.inc @@ -0,0 +1,101 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +; RIA UART +RIA_READY := $FFE0 ; TX=$80 RX=$40 +RIA_TX := $FFE1 +RIA_RX := $FFE2 + +; VSYNC from PIX VGA +RIA_VSYNC := $FFE3 + +; RIA XRAM portal 0 +RIA_RW0 := $FFE4 +RIA_STEP0 := $FFE5 +RIA_ADDR0 := $FFE6 + +; RIA XRAM portal 1 +RIA_RW1 := $FFE8 +RIA_STEP1 := $FFE9 +RIA_ADDR1 := $FFEA + +; RIA OS fastcall +RIA_XSTACK := $FFEC +RIA_ERRNO := $FFED +RIA_OP := $FFEF +RIA_IRQ := $FFF0 +RIA_SPIN := $FFF1 +RIA_BUSY := $FFF2 ; Bit $80 +RIA_A := $FFF4 +RIA_X := $FFF6 +RIA_SREG := $FFF8 + +; RIA OS operation numbers +RIA_OP_EXIT := $FF +RIA_OP_ZXSTACK := $00 +RIA_OP_XREG := $01 +RIA_OP_PHI2 := $02 +RIA_OP_CODEPAGE := $03 +RIA_OP_LRAND := $04 +RIA_OP_STDIN_OPT := $05 +RIA_OP_CLOCK_GETRES := $10 +RIA_OP_CLOCK_GETTIME := $11 +RIA_OP_CLOCK_SETTIME := $12 +RIA_OP_CLOCK_GETTIMEZONE := $13 +RIA_OP_OPEN := $14 +RIA_OP_CLOSE := $15 +RIA_OP_READ_XSTACK := $16 +RIA_OP_READ_XRAM := $17 +RIA_OP_WRITE_XSTACK := $18 +RIA_OP_WRITE_XRAM := $19 +RIA_OP_LSEEK := $1A +RIA_OP_UNLINK := $1B +RIA_OP_RENAME := $1C + +; 6522 VIA +VIA := $FFD0 ; VIA base address +VIA_PB := VIA+$0 ; Port register B +VIA_PA1 := VIA+$1 ; Port register A +VIA_PRB := VIA+$0 ; *** Deprecated *** +VIA_PRA := VIA+$1 ; *** Deprecated *** +VIA_DDRB := VIA+$2 ; Data direction register B +VIA_DDRA := VIA+$3 ; Data direction register A +VIA_T1CL := VIA+$4 ; Timer 1, low byte +VIA_T1CH := VIA+$5 ; Timer 1, high byte +VIA_T1LL := VIA+$6 ; Timer 1 latch, low byte +VIA_T1LH := VIA+$7 ; Timer 1 latch, high byte +VIA_T2CL := VIA+$8 ; Timer 2, low byte +VIA_T2CH := VIA+$9 ; Timer 2, high byte +VIA_SR := VIA+$A ; Shift register +VIA_CR := VIA+$B ; Auxiliary control register +VIA_PCR := VIA+$C ; Peripheral control register +VIA_IFR := VIA+$D ; Interrupt flag register +VIA_IER := VIA+$E ; Interrupt enable register +VIA_PA2 := VIA+$F ; Port register A w/o handshake + +; Values in ___oserror are the union of these FatFs errors and errno.inc +.enum + FR_OK = 32 ; Succeeded + FR_DISK_ERR ; A hard error occurred in the low level disk I/O layer + FR_INT_ERR ; Assertion failed + FR_NOT_READY ; The physical drive cannot work + FR_NO_FILE ; Could not find the file + FR_NO_PATH ; Could not find the path + FR_INVALID_NAME ; The path name format is invalid + FR_DENIED ; Access denied due to prohibited access or directory full + FR_EXIST ; Access denied due to prohibited access + FR_INVALID_OBJECT ; The file/directory object is invalid + FR_WRITE_PROTECTED ; The physical drive is write protected + FR_INVALID_DRIVE ; The logical drive number is invalid + FR_NOT_ENABLED ; The volume has no work area + FR_NO_FILESYSTEM ; There is no valid FAT volume + FR_MKFS_ABORTED ; The f_mkfs() aborted due to any problem + FR_TIMEOUT ; Could not get a grant to access the volume within defined period + FR_LOCKED ; The operation is rejected according to the file sharing policy + FR_NOT_ENOUGH_CORE ; LFN working buffer could not be allocated + FR_TOO_MANY_OPEN_FILES ; Number of open files > FF_FS_LOCK + FR_INVALID_PARAMETER ; Given parameter is invalid +.endenum diff --git a/cfg/rp6502.cfg b/cfg/rp6502.cfg new file mode 100644 index 000000000..a3e3b29ce --- /dev/null +++ b/cfg/rp6502.cfg @@ -0,0 +1,42 @@ +# +# Copyright (c) 2023 Rumbledethumps +# +# SPDX-License-Identifier: Zlib +# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: Unlicense +# + +SYMBOLS { + __STARTUP__: type = import; + __STACKSIZE__: type = weak, value = $0800; +} +MEMORY { + ZP: file = "", define = yes, start = $0000, size = $0100; + CPUSTACK: file = "", start = $0100, size = $0100; + RAM: file = %O, define = yes, start = $0200, size = $FD00 - __STACKSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + STARTUP: load = RAM, type = ro; + LOWCODE: load = RAM, type = ro, optional = yes; + ONCE: load = RAM, type = ro, optional = yes; + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/include/rp6502.h b/include/rp6502.h new file mode 100644 index 000000000..0c2ba3881 --- /dev/null +++ b/include/rp6502.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#ifndef _RP6502_H +#define _RP6502_H + +/* RP6502 VIA $FFD0-$FFDF */ + +#include <_6522.h> +#define VIA (*(volatile struct __6522 *)0xFFD0) + +/* RP6502 RIA $FFE0-$FFF9 */ + +struct __RP6502 +{ + const unsigned char ready; + unsigned char tx; + const unsigned char rx; + const unsigned char vsync; + unsigned char rw0; + unsigned char step0; + unsigned int addr0; + unsigned char rw1; + unsigned char step1; + unsigned int addr1; + unsigned char xstack; + unsigned char errno_lo; + unsigned char errno_hi; + unsigned char op; + unsigned char irq; + const unsigned char spin; + const unsigned char busy; + const unsigned char lda; + unsigned char a; + const unsigned char ldx; + unsigned char x; + const unsigned char rts; + unsigned int sreg; +}; +#define RIA (*(volatile struct __RP6502 *)0xFFE0) + +#define RIA_READY_TX_BIT 0x80 +#define RIA_READY_RX_BIT 0x40 +#define RIA_BUSY_BIT 0x80 + +/* XSTACK helpers */ + +void __fastcall__ ria_push_long(unsigned long val); +void __fastcall__ ria_push_int(unsigned int val); +#define ria_push_char(v) RIA.xstack = v + +long __fastcall__ ria_pop_long(void); +int __fastcall__ ria_pop_int(void); +#define ria_pop_char() RIA.xstack + +/* Set the RIA fastcall register */ + +void __fastcall__ ria_set_axsreg(unsigned long axsreg); +void __fastcall__ ria_set_ax(unsigned int ax); +#define ria_set_a(v) RIA.a = v + +/* Run an OS operation */ + +int __fastcall__ ria_call_int(unsigned char op); +long __fastcall__ ria_call_long(unsigned char op); + +/* These run _mappederrno() on error */ + +int __fastcall__ ria_call_int_errno(unsigned char op); +long __fastcall__ ria_call_long_errno(unsigned char op); + +/* OS operation numbers */ + +#define RIA_OP_EXIT 0xFF +#define RIA_OP_ZXSTACK 0x00 +#define RIA_OP_XREG 0x01 +#define RIA_OP_PHI2 0x02 +#define RIA_OP_CODEPAGE 0x03 +#define RIA_OP_LRAND 0x04 +#define RIA_OP_STDIN_OPT 0x05 +#define RIA_OP_CLOCK_GETRES 0x10 +#define RIA_OP_CLOCK_GETTIME 0x11 +#define RIA_OP_CLOCK_SETTIME 0x12 +#define RIA_OP_CLOCK_GETTIMEZONE 0x13 +#define RIA_OP_OPEN 0x14 +#define RIA_OP_CLOSE 0x15 +#define RIA_OP_READ_XSTACK 0x16 +#define RIA_OP_READ_XRAM 0x17 +#define RIA_OP_WRITE_XSTACK 0x18 +#define RIA_OP_WRITE_XRAM 0x19 +#define RIA_OP_LSEEK 0x1A +#define RIA_OP_UNLINK 0x1B +#define RIA_OP_RENAME 0x1C + +/* C API for the operating system. */ + +int __cdecl__ xreg(char device, char channel, unsigned char address, ...); +int __fastcall__ phi2(void); +int __fastcall__ codepage(void); +long __fastcall__ lrand(void); +int __fastcall__ stdin_opt(unsigned long ctrl_bits, unsigned char str_length); +int __fastcall__ read_xstack(void *buf, unsigned count, int fildes); +int __fastcall__ read_xram(unsigned buf, unsigned count, int fildes); +int __fastcall__ write_xstack(const void *buf, unsigned count, int fildes); +int __fastcall__ write_xram(unsigned buf, unsigned count, int fildes); + +/* XREG location helpers */ + +#define xreg_ria_keyboard(...) xreg(0, 0, 0, __VA_ARGS__) +#define xreg_ria_mouse(...) xreg(0, 0, 1, __VA_ARGS__) +#define xreg_vga_canvas(...) xreg(1, 0, 0, __VA_ARGS__) +#define xreg_vga_mode(...) xreg(1, 0, 1, __VA_ARGS__) + +/* XRAM structure helpers */ + +#define xram0_struct_set(addr, type, member, val) \ + RIA.addr0 = (unsigned)(&((type *)0)->member) + (unsigned)addr; \ + switch (sizeof(((type *)0)->member)) \ + { \ + case 1: \ + RIA.rw0 = val; \ + break; \ + case 2: \ + RIA.step0 = 1; \ + RIA.rw0 = val & 0xff; \ + RIA.rw0 = (val >> 8) & 0xff; \ + break; \ + case 4: \ + RIA.step0 = 1; \ + RIA.rw0 = (unsigned long)val & 0xff; \ + RIA.rw0 = ((unsigned long)val >> 8) & 0xff; \ + RIA.rw0 = ((unsigned long)val >> 16) & 0xff; \ + RIA.rw0 = ((unsigned long)val >> 24) & 0xff; \ + break; \ + } + +#define xram1_struct_set(addr, type, member, val) \ + RIA.addr1 = (unsigned)(&((type *)0)->member) + (unsigned)addr; \ + switch (sizeof(((type *)0)->member)) \ + { \ + case 1: \ + RIA.rw1 = val; \ + break; \ + case 2: \ + RIA.step1 = 1; \ + RIA.rw1 = val & 0xff; \ + RIA.rw1 = (val >> 8) & 0xff; \ + break; \ + case 4: \ + RIA.step1 = 1; \ + RIA.rw1 = (unsigned long)val & 0xff; \ + RIA.rw1 = ((unsigned long)val >> 8) & 0xff; \ + RIA.rw1 = ((unsigned long)val >> 16) & 0xff; \ + RIA.rw1 = ((unsigned long)val >> 24) & 0xff; \ + break; \ + } + +typedef struct +{ + unsigned char x_wrap; // bool + unsigned char y_wrap; // bool + int x_pos_px; + int y_pos_px; + int width_chars; + int height_chars; + unsigned xram_data_ptr; + unsigned xram_palette_ptr; + unsigned xram_font_ptr; +} vga_mode1_config_t; + +typedef struct +{ + unsigned char x_wrap; // bool + unsigned char y_wrap; // bool + int x_pos_px; + int y_pos_px; + int width_tiles; + int height_tiles; + unsigned xram_data_ptr; + unsigned xram_palette_ptr; + unsigned xram_tile_ptr; +} vga_mode2_config_t; + +typedef struct +{ + unsigned char x_wrap; // bool + unsigned char y_wrap; // bool + int x_pos_px; + int y_pos_px; + int width_px; + int height_px; + unsigned xram_data_ptr; + unsigned xram_palette_ptr; +} vga_mode3_config_t; + +typedef struct +{ + int x_pos_px; + int y_pos_px; + unsigned xram_sprite_ptr; + unsigned char log_size; + unsigned char has_opacity_metadata; // bool +} vga_mode4_sprite_t; + +typedef struct +{ + int transform[6]; + int x_pos_px; + int y_pos_px; + unsigned xram_sprite_ptr; + unsigned char log_size; + unsigned char has_opacity_metadata; // bool +} vga_mode4_asprite_t; + +/* Values in __oserror are the union of these FatFs errors and errno.h */ + +typedef enum +{ + FR_OK = 32, /* Succeeded */ + FR_DISK_ERR, /* A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* Assertion failed */ + FR_NOT_READY, /* The physical drive cannot work */ + FR_NO_FILE, /* Could not find the file */ + FR_NO_PATH, /* Could not find the path */ + FR_INVALID_NAME, /* The path name format is invalid */ + FR_DENIED, /* Access denied due to prohibited access or directory full */ + FR_EXIST, /* Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* The physical drive is write protected */ + FR_INVALID_DRIVE, /* The logical drive number is invalid */ + FR_NOT_ENABLED, /* The volume has no work area */ + FR_NO_FILESYSTEM, /* There is no valid FAT volume */ + FR_MKFS_ABORTED, /* The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER /* Given parameter is invalid */ +} FRESULT; + +#endif /* _RP6502_H */ diff --git a/libsrc/Makefile b/libsrc/Makefile index cf7389d51..8a4f11414 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -33,6 +33,7 @@ TARGETS = apple2 \ none \ osic1p \ pce \ + rp6502 \ sim6502 \ sim65c02 \ supervision \ diff --git a/libsrc/rp6502/close.c b/libsrc/rp6502/close.c new file mode 100644 index 000000000..1fdc40545 --- /dev/null +++ b/libsrc/rp6502/close.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <errno.h> + +int __fastcall__ close(int fd) +{ + ria_set_ax(fd); + return ria_call_int_errno(RIA_OP_CLOSE); +} diff --git a/libsrc/rp6502/codepage.c b/libsrc/rp6502/codepage.c new file mode 100644 index 000000000..c0914651d --- /dev/null +++ b/libsrc/rp6502/codepage.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ codepage(void) +{ + return ria_call_int(RIA_OP_CODEPAGE); +} diff --git a/libsrc/rp6502/crt0.s b/libsrc/rp6502/crt0.s new file mode 100644 index 000000000..e6406fe5e --- /dev/null +++ b/libsrc/rp6502/crt0.s @@ -0,0 +1,54 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +; Boilerplate crt0.s + +.export _init, _exit +.import _main + +.export __STARTUP__ : absolute = 1 +.import __RAM_START__, __RAM_SIZE__ + +.import copydata, zerobss, initlib, donelib + +.include "rp6502.inc" +.include "zeropage.inc" + +.segment "STARTUP" + +; Essential 6502 startup the CPU doesn't do +_init: + ldx #$FF + txs + cld + +; Set cc65 argument stack pointer + lda #<(__RAM_START__ + __RAM_SIZE__) + sta sp + lda #>(__RAM_START__ + __RAM_SIZE__) + sta sp+1 + +; Initialize memory storage + jsr zerobss ; Clear BSS segment + jsr copydata ; Initialize DATA segment + jsr initlib ; Run constructors + +; Call main() + jsr _main + +; Back from main() also the _exit entry +; Stack the exit value in case destructors call OS +_exit: + phx + pha + jsr donelib ; Run destructors + pla + sta RIA_A + plx + stx RIA_X + lda #$FF ; exit() + sta RIA_OP + stp diff --git a/libsrc/rp6502/getres.c b/libsrc/rp6502/getres.c new file mode 100644 index 000000000..c60bbd38d --- /dev/null +++ b/libsrc/rp6502/getres.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <time.h> + +extern int __clock_gettimespec(struct timespec *ts, unsigned char op); + +int clock_getres(clockid_t clock_id, struct timespec *res) +{ + ria_set_ax(clock_id); + return __clock_gettimespec(res, RIA_OP_CLOCK_GETRES); +} diff --git a/libsrc/rp6502/gettime.c b/libsrc/rp6502/gettime.c new file mode 100644 index 000000000..b40615e9e --- /dev/null +++ b/libsrc/rp6502/gettime.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <time.h> + +extern int __clock_gettimespec(struct timespec *ts, unsigned char op); + +int clock_gettime(clockid_t clock_id, struct timespec *tp) +{ + (void)clock_id; + /* time.s doesn't set the stack value for clock_id (bug?) */ + ria_set_ax(CLOCK_REALTIME); + return __clock_gettimespec(tp, RIA_OP_CLOCK_GETTIME); +} diff --git a/libsrc/rp6502/gettimespec.c b/libsrc/rp6502/gettimespec.c new file mode 100644 index 000000000..ef136c6fe --- /dev/null +++ b/libsrc/rp6502/gettimespec.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <time.h> + +int __clock_gettimespec(struct timespec *ts, unsigned char op) +/* Internal method shared by clock_getres and clock_gettime. */ +{ + int ax = ria_call_int_errno(op); + if (ax >= 0) + { + ts->tv_sec = ria_pop_long(); + ts->tv_nsec = ria_pop_long(); + } + return ax; +} diff --git a/libsrc/rp6502/gettimezone.c b/libsrc/rp6502/gettimezone.c new file mode 100644 index 000000000..db9060967 --- /dev/null +++ b/libsrc/rp6502/gettimezone.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <time.h> + +int clock_gettimezone(clockid_t clock_id, struct _timezone *tz) +{ + int ax; + ria_set_ax(clock_id); + ax = ria_call_int_errno(RIA_OP_CLOCK_GETTIMEZONE); + if (ax >= 0) + { + char i; + for (i = 0; i < sizeof(struct _timezone); i++) + ((char *)tz)[i] = ria_pop_char(); + } + return ax; +} diff --git a/libsrc/rp6502/initenv.s b/libsrc/rp6502/initenv.s new file mode 100644 index 000000000..b6546e320 --- /dev/null +++ b/libsrc/rp6502/initenv.s @@ -0,0 +1,16 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +.constructor initenv, 24 +.import __environ, __envcount, __envsize + +.segment "ONCE" + +.proc initenv + + rts + +.endproc diff --git a/libsrc/rp6502/irq.s b/libsrc/rp6502/irq.s new file mode 100644 index 000000000..ffc152aac --- /dev/null +++ b/libsrc/rp6502/irq.s @@ -0,0 +1,51 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +.export initirq, doneirq +.import callirq, _exit + +.include "rp6502.inc" + +.segment "ONCE" + +initirq: + lda #<handler + ldx #>handler + sei + sta $FFFE + stx $FFFF + cli + rts + +.code + +doneirq: + sei + rts + +.segment "LOWCODE" + +handler: + cld + phx + tsx + pha + inx + inx + lda $100,X + and #$10 + bne break + phy + jsr callirq + ply + pla + plx + rti + +break: + lda #$FF + sta RIA_A + jmp _exit diff --git a/libsrc/rp6502/lrand.c b/libsrc/rp6502/lrand.c new file mode 100644 index 000000000..9a23390e5 --- /dev/null +++ b/libsrc/rp6502/lrand.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +long __fastcall__ lrand(void) +{ + return ria_call_long(RIA_OP_LRAND); +} diff --git a/libsrc/rp6502/lseek.c b/libsrc/rp6502/lseek.c new file mode 100644 index 000000000..afa590e63 --- /dev/null +++ b/libsrc/rp6502/lseek.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <unistd.h> + +off_t __fastcall__ lseek(int fd, off_t offset, int whence) +{ + /* Modified argument order for short stacking offset */ + ria_push_long(offset); + ria_push_char(whence); + ria_set_ax(fd); + return ria_call_long_errno(RIA_OP_LSEEK); +} diff --git a/libsrc/rp6502/mainargs.s b/libsrc/rp6502/mainargs.s new file mode 100644 index 000000000..886e39e02 --- /dev/null +++ b/libsrc/rp6502/mainargs.s @@ -0,0 +1,16 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +.constructor initmainargs, 24 +.import __argc, __argv + +.segment "ONCE" + +.proc initmainargs + + rts + +.endproc diff --git a/libsrc/rp6502/open.c b/libsrc/rp6502/open.c new file mode 100644 index 000000000..962614045 --- /dev/null +++ b/libsrc/rp6502/open.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <errno.h> +#include <string.h> + +int __cdecl__ open(const char *name, int flags, ...) +{ + size_t namelen = strlen(name); + if (namelen > 255) + { + return _mappederrno(EINVAL); + } + while (namelen) + { + ria_push_char(name[--namelen]); + } + ria_set_ax(flags); + return ria_call_int_errno(RIA_OP_OPEN); +} diff --git a/libsrc/rp6502/oserrlist.s b/libsrc/rp6502/oserrlist.s new file mode 100644 index 000000000..ef322e293 --- /dev/null +++ b/libsrc/rp6502/oserrlist.s @@ -0,0 +1,86 @@ +; +; 2002-07-18, Ullrich von Bassewitz +; 2022, ChaN +; 2023, Rumbledethumps +; +; Defines the platform-specific error list. +; +; The table is built as a list of entries: +; +; .byte entrylen +; .byte errorcode +; .asciiz errormsg +; +; and, terminated by an entry with length zero that is returned if the +; error code could not be found. +; + +.export __sys_oserrlist + +.include "rp6502.inc" +.include "errno.inc" + +;---------------------------------------------------------------------------- +; Macros used to generate the list (may get moved to an include file?) + +; Regular entry +.macro sys_oserr_entry code, msg + .local Start, End +Start: + .byte End - Start + .byte code + .asciiz msg +End: +.endmacro + +; Sentinel entry +.macro sys_oserr_sentinel msg + .byte 0 ; Length is always zero + .byte 0 ; Code is unused + .asciiz msg +.endmacro + +;---------------------------------------------------------------------------- +; The error message table + +.rodata +__sys_oserrlist: + + sys_oserr_entry ENOENT , "No such file or directory" + sys_oserr_entry ENOMEM , "Out of memory" + sys_oserr_entry EACCES , "Permission denied" + sys_oserr_entry ENODEV , "No such device" + sys_oserr_entry EMFILE , "Too many open files" + sys_oserr_entry EBUSY , "Device or resource busy" + sys_oserr_entry EINVAL , "Invalid argument" + sys_oserr_entry ENOSPC , "No space left on device" + sys_oserr_entry EEXIST , "File exists" + sys_oserr_entry EAGAIN , "Try again" + sys_oserr_entry EIO , "I/O error" + sys_oserr_entry EINTR , "Interrupted system call" + sys_oserr_entry ENOSYS , "Function not implemented" + sys_oserr_entry ESPIPE , "Illegal seek" + sys_oserr_entry ERANGE , "Range error" + sys_oserr_entry EBADF , "Bad file number" + sys_oserr_entry ENOEXEC , "Exec format error" + sys_oserr_entry EUNKNOWN , "Unknown OS specific error" + sys_oserr_entry FR_DISK_ERR , "A hard error occurred in the low level disk I/O layer" + sys_oserr_entry FR_INT_ERR , "Assertion failed" + sys_oserr_entry FR_NOT_READY , "The physical drive cannot work" + sys_oserr_entry FR_NO_FILE , "Could not find the file" + sys_oserr_entry FR_NO_PATH , "Could not find the path" + sys_oserr_entry FR_INVALID_NAME , "The path name format is invalid" + sys_oserr_entry FR_DENIED , "Access denied due to prohibited access or directory full" + sys_oserr_entry FR_EXIST , "Access denied due to prohibited access" + sys_oserr_entry FR_INVALID_OBJECT , "The file/directory object is invalid" + sys_oserr_entry FR_WRITE_PROTECTED , "The physical drive is write protected" + sys_oserr_entry FR_INVALID_DRIVE , "The logical drive number is invalid" + sys_oserr_entry FR_NOT_ENABLED , "The volume has no work area" + sys_oserr_entry FR_NO_FILESYSTEM , "There is no valid FAT volume" + sys_oserr_entry FR_MKFS_ABORTED , "The f_mkfs() aborted due to any problem" + sys_oserr_entry FR_TIMEOUT , "Could not get a grant to access the volume within defined period" + sys_oserr_entry FR_LOCKED , "The operation is rejected according to the file sharing policy" + sys_oserr_entry FR_NOT_ENOUGH_CORE , "LFN working buffer could not be allocated" + sys_oserr_entry FR_TOO_MANY_OPEN_FILES , "Number of open files > FF_FS_LOCK" + sys_oserr_entry FR_INVALID_PARAMETER , "Given parameter is invalid" + sys_oserr_sentinel "Unknown error" diff --git a/libsrc/rp6502/oserror.s b/libsrc/rp6502/oserror.s new file mode 100644 index 000000000..706bcfa65 --- /dev/null +++ b/libsrc/rp6502/oserror.s @@ -0,0 +1,66 @@ +; +; 2000-05-17, Ullrich von Bassewitz +; 2022, ChaN +; 2023, Rumbledethumps +; +; int __fastcall__ __osmaperrno (unsigned char oserror); +; +; RP6502 will respond with a union of CC65 and FatFs errnos. +; This will map FatFs errors into the CC65 range for portable code. + +EFATFS_START := 32 + +.include "rp6502.inc" +.include "errno.inc" + +.code + +___osmaperrno: + cmp #EFATFS_START + bmi @L2 + + ldx #ErrTabSize +@L1: + cmp ErrTab-2,x ; Search for the error code + beq @L3 ; Jump if found + dex + dex + bne @L1 ; Next entry + +; Code not found, return EUNKNOWN + lda #<EUNKNOWN + ldx #>EUNKNOWN +@L2: + rts + +; Found the code +@L3: + lda ErrTab-1,x + ldx #$00 ; High byte always zero + rts + +.rodata + +ErrTab: + + .byte FR_DISK_ERR , EIO ; A hard error occurred in the low level disk I/O layer +; .byte FR_INT_ERR , EUNKNOWN ; Assertion failed + .byte FR_NOT_READY , EBUSY ; The physical drive cannot work + .byte FR_NO_FILE , ENOENT ; Could not find the file + .byte FR_NO_PATH , ENOENT ; Could not find the path + .byte FR_INVALID_NAME , EINVAL ; The path name format is invalid + .byte FR_DENIED , EACCES ; Access denied due to prohibited access or directory full + .byte FR_EXIST , EEXIST ; Access denied due to prohibited access + .byte FR_INVALID_OBJECT , EINVAL ; The file/directory object is invalid + .byte FR_WRITE_PROTECTED , EACCES ; The physical drive is write protected + .byte FR_INVALID_DRIVE , ENODEV ; The logical drive number is invalid +; .byte FR_NOT_ENABLED , EUNKNOWN ; The volume has no work area +; .byte FR_NO_FILESYSTEM , EUNKNOWN ; There is no valid FAT volume +; .byte FR_MKFS_ABORTED , EUNKNOWN ; The f_mkfs() aborted due to any problem +; .byte FR_TIMEOUT , EUNKNOWN ; Could not get a grant to access the volume within defined period + .byte FR_LOCKED , EBUSY ; The operation is rejected according to the file sharing policy + .byte FR_NOT_ENOUGH_CORE , ENOMEM ; LFN working buffer could not be allocated + .byte FR_TOO_MANY_OPEN_FILES , EMFILE ; Number of open files > FF_FS_LOCK + .byte FR_INVALID_PARAMETER , EINVAL ; Given parameter is invalid + +ErrTabSize = (* - ErrTab) diff --git a/libsrc/rp6502/phi2.c b/libsrc/rp6502/phi2.c new file mode 100644 index 000000000..095580c97 --- /dev/null +++ b/libsrc/rp6502/phi2.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ phi2(void) +{ + return ria_call_int(RIA_OP_PHI2); +} diff --git a/libsrc/rp6502/randomize.c b/libsrc/rp6502/randomize.c new file mode 100644 index 000000000..a2050c4aa --- /dev/null +++ b/libsrc/rp6502/randomize.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <stdlib.h> + +// Non-standard cc65 +void _randomize(void) +{ + srand(ria_call_int(RIA_OP_LRAND)); +} diff --git a/libsrc/rp6502/read.c b/libsrc/rp6502/read.c new file mode 100644 index 000000000..d0b389c4a --- /dev/null +++ b/libsrc/rp6502/read.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <unistd.h> + +int __fastcall__ read(int fildes, void *buf, unsigned count) +{ + int total = 0; + while (count) + { + unsigned blockcount = (count > 256) ? 256 : count; + int bytes_read = read_xstack(&((char *)buf)[total], blockcount, fildes); + if (bytes_read < 0) + { + return bytes_read; + } + total += bytes_read; + count -= bytes_read; + if (bytes_read < blockcount) + { + break; + } + } + return total; +} diff --git a/libsrc/rp6502/read_xram.c b/libsrc/rp6502/read_xram.c new file mode 100644 index 000000000..565744e11 --- /dev/null +++ b/libsrc/rp6502/read_xram.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ read_xram(unsigned buf, unsigned count, int fildes) +{ + ria_push_int(buf); + ria_push_int(count); + ria_set_ax(fildes); + return ria_call_int_errno(RIA_OP_READ_XRAM); +} diff --git a/libsrc/rp6502/read_xstack.c b/libsrc/rp6502/read_xstack.c new file mode 100644 index 000000000..c3b6d098e --- /dev/null +++ b/libsrc/rp6502/read_xstack.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ read_xstack(void *buf, unsigned count, int fildes) +{ + int i, ax; + ria_push_int(count); + ria_set_ax(fildes); + ax = ria_call_int_errno(RIA_OP_READ_XSTACK); + for (i = 0; i < ax; i++) + { + ((char *)buf)[i] = ria_pop_char(); + } + return ax; +} diff --git a/libsrc/rp6502/ria.s b/libsrc/rp6502/ria.s new file mode 100644 index 000000000..f040fca61 --- /dev/null +++ b/libsrc/rp6502/ria.s @@ -0,0 +1,90 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +.include "rp6502.inc" + +.export _ria_push_long, _ria_push_int +.export _ria_pop_long, _ria_pop_int +.export _ria_set_axsreg, _ria_set_ax +.export _ria_call_int, _ria_call_long +.export _ria_call_int_errno, _ria_call_long_errno + +.importzp sp, sreg +.import ___mappederrno, incsp1 + +.code + +; void __fastcall__ ria_push_long(unsigned long val); +_ria_push_long: + ldy sreg+1 + sty RIA_XSTACK + ldy sreg + sty RIA_XSTACK +; void __fastcall__ ria_push_int(unsigned int val); +_ria_push_int: + stx RIA_XSTACK + sta RIA_XSTACK + rts + +; long __fastcall__ ria_pop_long(void); +_ria_pop_long: + jsr _ria_pop_int + ldy RIA_XSTACK + sty sreg + ldy RIA_XSTACK + sty sreg+1 + rts + +; int __fastcall__ ria_pop_int(void); +_ria_pop_int: + lda RIA_XSTACK + ldx RIA_XSTACK + rts + +; void __fastcall__ ria_set_axsreg(unsigned long axsreg); +_ria_set_axsreg: + ldy sreg + sty RIA_SREG + ldy sreg+1 + sty RIA_SREG+1 +; void __fastcall__ ria_set_ax(unsigned int ax); +_ria_set_ax: + stx RIA_X + sta RIA_A + rts + +; int __fastcall__ ria_call_int(unsigned char op); +_ria_call_int: + sta RIA_OP + jmp RIA_SPIN + +; long __fastcall__ ria_call_long(unsigned char op); +_ria_call_long: + sta RIA_OP + jsr RIA_SPIN + ldy RIA_SREG + sty sreg + ldy RIA_SREG+1 + sty sreg+1 + rts + +; int __fastcall__ ria_call_int_errno(unsigned char op); +_ria_call_int_errno: + sta RIA_OP + jsr RIA_SPIN + ldx RIA_X + bmi ERROR + rts + +; long __fastcall__ ria_call_long_errno(unsigned char op); +_ria_call_long_errno: + jsr _ria_call_long + bmi ERROR + rts + +ERROR: + lda RIA_ERRNO + jmp ___mappederrno diff --git a/libsrc/rp6502/settime.c b/libsrc/rp6502/settime.c new file mode 100644 index 000000000..69d4dafe5 --- /dev/null +++ b/libsrc/rp6502/settime.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <time.h> + +int clock_settime(clockid_t clock_id, const struct timespec *tp) +{ + ria_set_ax(clock_id); + ria_push_long(tp->tv_nsec); + ria_push_long(tp->tv_sec); + return ria_call_int_errno(RIA_OP_CLOCK_SETTIME); +} diff --git a/libsrc/rp6502/stdin_opt.c b/libsrc/rp6502/stdin_opt.c new file mode 100644 index 000000000..f01d1036c --- /dev/null +++ b/libsrc/rp6502/stdin_opt.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ stdin_opt(unsigned long ctrl_bits, unsigned char str_length) +{ + ria_push_long(ctrl_bits); + ria_set_a(str_length); + return ria_call_int_errno(RIA_OP_STDIN_OPT); +} diff --git a/libsrc/rp6502/sysremove.c b/libsrc/rp6502/sysremove.c new file mode 100644 index 000000000..6b143d672 --- /dev/null +++ b/libsrc/rp6502/sysremove.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <errno.h> +#include <string.h> + +unsigned char __fastcall__ _sysremove(const char *name) +{ + size_t namelen; + namelen = strlen(name); + if (namelen > 255) + { + return _mappederrno(EINVAL); + } + while (namelen) + { + ria_push_char(name[--namelen]); + } + return ria_call_int_errno(RIA_OP_UNLINK); +} diff --git a/libsrc/rp6502/sysrename.c b/libsrc/rp6502/sysrename.c new file mode 100644 index 000000000..6d42752ae --- /dev/null +++ b/libsrc/rp6502/sysrename.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <errno.h> +#include <string.h> + +unsigned char __fastcall__ _sysrename(const char *oldpath, const char *newpath) +{ + size_t oldpathlen, newpathlen; + oldpathlen = strlen(oldpath); + newpathlen = strlen(newpath); + if (oldpathlen + newpathlen > 254) + { + return _mappederrno(EINVAL); + } + while (oldpathlen) + { + ria_push_char(oldpath[--oldpathlen]); + } + ria_push_char(0); + while (newpathlen) + { + ria_push_char(newpath[--newpathlen]); + } + return ria_call_int_errno(RIA_OP_RENAME); +} diff --git a/libsrc/rp6502/write.c b/libsrc/rp6502/write.c new file mode 100644 index 000000000..f14c91e59 --- /dev/null +++ b/libsrc/rp6502/write.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> +#include <unistd.h> + +int __fastcall__ write(int fildes, const void *buf, unsigned count) +{ + int ax, total = 0; + while (count) + { + int blockcount = (count > 256) ? 256 : count; + ax = write_xstack(&((char *)buf)[total], blockcount, fildes); + if (ax < 0) + { + return ax; + } + total += ax; + count -= ax; + if (ax < blockcount) + { + break; + } + } + return total; +} diff --git a/libsrc/rp6502/write_xram.c b/libsrc/rp6502/write_xram.c new file mode 100644 index 000000000..379e5a667 --- /dev/null +++ b/libsrc/rp6502/write_xram.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ write_xram(unsigned buf, unsigned count, int fildes) +{ + ria_push_int(buf); + ria_push_int(count); + ria_set_ax(fildes); + return ria_call_int_errno(RIA_OP_WRITE_XRAM); +} diff --git a/libsrc/rp6502/write_xstack.c b/libsrc/rp6502/write_xstack.c new file mode 100644 index 000000000..4507193ea --- /dev/null +++ b/libsrc/rp6502/write_xstack.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Rumbledethumps + * + * SPDX-License-Identifier: Zlib + * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: Unlicense + */ + +#include <rp6502.h> + +int __fastcall__ write_xstack(const void *buf, unsigned count, int fildes) +{ + unsigned i; + for (i = count; i;) + { + ria_push_char(((char *)buf)[--i]); + } + ria_set_ax(fildes); + return ria_call_int_errno(RIA_OP_WRITE_XSTACK); +} diff --git a/libsrc/rp6502/xreg.s b/libsrc/rp6502/xreg.s new file mode 100644 index 000000000..81cba6733 --- /dev/null +++ b/libsrc/rp6502/xreg.s @@ -0,0 +1,40 @@ +; Copyright (c) 2023 Rumbledethumps +; +; SPDX-License-Identifier: Zlib +; SPDX-License-Identifier: BSD-3-Clause +; SPDX-License-Identifier: Unlicense + +; CC65 will promote variadic char arguments to int. It will not demote longs. +; int __cdecl__ xreg(char device, char channel, unsigned char address, ...); + +.export _xreg +.importzp sp +.import addysp, _ria_call_int_errno + +.include "rp6502.inc" + +.code + +.proc _xreg + + ; save variadic size in X + tya + tax + +@copy: ; copy stack + dey + lda (sp),y + sta RIA_XSTACK + tya + bne @copy + + ; recover variadic size and move sp + txa + tay + jsr addysp + + ; run RIA operation + lda #RIA_OP_XREG + jmp _ria_call_int_errno + +.endproc diff --git a/src/ca65/main.c b/src/ca65/main.c index fedbb0d4b..3ec6c84ee 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -346,6 +346,10 @@ static void SetSys (const char* Sys) NewSymbol ("__KIM1__", 1); break; + case TGT_RP6502: + NewSymbol ("__RP6502__", 1); + break; + default: AbEnd ("Invalid target name: '%s'", Sys); diff --git a/src/cc65/main.c b/src/cc65/main.c index f800ac43e..bef646cdd 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -303,6 +303,10 @@ static void SetSys (const char* Sys) DefineNumericMacro ("__KIM1__", 1); break; + case TGT_RP6502: + DefineNumericMacro ("__RP6502__", 1); + break; + default: AbEnd ("Unknown target system '%s'", Sys); } diff --git a/src/common/target.c b/src/common/target.c index ad62990bd..b50478e16 100644 --- a/src/common/target.c +++ b/src/common/target.c @@ -173,6 +173,7 @@ static const TargetEntry TargetMap[] = { { "pce", TGT_PCENGINE }, { "pet", TGT_PET }, { "plus4", TGT_PLUS4 }, + { "rp6502", TGT_RP6502 }, { "sim6502", TGT_SIM6502 }, { "sim65c02", TGT_SIM65C02 }, { "supervision", TGT_SUPERVISION }, @@ -221,6 +222,7 @@ static const TargetProperties PropertyTable[TGT_COUNT] = { { "cx16", CPU_65C02, BINFMT_BINARY, CTPET }, { "sym1", CPU_6502, BINFMT_BINARY, CTNone }, { "kim1", CPU_6502, BINFMT_BINARY, CTNone }, + { "rp6502", CPU_65C02, BINFMT_BINARY, CTNone }, }; /* Target system */ diff --git a/src/common/target.h b/src/common/target.h index 0cec74b6e..730b8211e 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -88,6 +88,7 @@ typedef enum { TGT_CX16, TGT_SYM1, TGT_KIM1, + TGT_RP6502, TGT_COUNT /* Number of target systems */ } target_t; From 564c85235fa7f776915333a29205128e3fca3f74 Mon Sep 17 00:00:00 2001 From: rumbledethumps <16963588+rumbledethumps@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:08:51 -0800 Subject: [PATCH 375/520] style update and add rp6502 doc --- asminc/rp6502.inc | 6 +-- cfg/rp6502.cfg | 8 --- doc/index.sgml | 3 ++ doc/rp6502.sgml | 96 ++++++++++++++++++++++++++++++++++++ include/rp6502.h | 70 ++++++++++++++++---------- libsrc/rp6502/close.c | 14 ++---- libsrc/rp6502/codepage.c | 12 +---- libsrc/rp6502/crt0.s | 9 ++-- libsrc/rp6502/getres.c | 16 ++---- libsrc/rp6502/gettime.c | 16 ++---- libsrc/rp6502/gettimespec.c | 19 ++----- libsrc/rp6502/gettimezone.c | 21 +++----- libsrc/rp6502/initenv.s | 6 +-- libsrc/rp6502/irq.s | 7 ++- libsrc/rp6502/lrand.c | 12 +---- libsrc/rp6502/lseek.c | 18 ++----- libsrc/rp6502/mainargs.s | 7 ++- libsrc/rp6502/open.c | 26 +++------- libsrc/rp6502/phi2.c | 12 +---- libsrc/rp6502/randomize.c | 12 +---- libsrc/rp6502/read.c | 21 ++------ libsrc/rp6502/read_xram.c | 18 ++----- libsrc/rp6502/read_xstack.c | 21 +++----- libsrc/rp6502/ria.s | 7 ++- libsrc/rp6502/settime.c | 18 ++----- libsrc/rp6502/stdin_opt.c | 15 ++---- libsrc/rp6502/sysremove.c | 24 +++------ libsrc/rp6502/sysrename.c | 33 +++++-------- libsrc/rp6502/write.c | 21 ++------ libsrc/rp6502/write_xram.c | 18 ++----- libsrc/rp6502/write_xstack.c | 19 ++----- libsrc/rp6502/xreg.s | 7 +-- 32 files changed, 261 insertions(+), 351 deletions(-) create mode 100644 doc/rp6502.sgml diff --git a/asminc/rp6502.inc b/asminc/rp6502.inc index 3fa493331..7dd1b8fcd 100644 --- a/asminc/rp6502.inc +++ b/asminc/rp6502.inc @@ -1,8 +1,4 @@ -; Copyright (c) 2023 Rumbledethumps -; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense +; Picocomputer 6502 general defines ; RIA UART RIA_READY := $FFE0 ; TX=$80 RX=$40 diff --git a/cfg/rp6502.cfg b/cfg/rp6502.cfg index a3e3b29ce..ed40e467d 100644 --- a/cfg/rp6502.cfg +++ b/cfg/rp6502.cfg @@ -1,11 +1,3 @@ -# -# Copyright (c) 2023 Rumbledethumps -# -# SPDX-License-Identifier: Zlib -# SPDX-License-Identifier: BSD-3-Clause -# SPDX-License-Identifier: Unlicense -# - SYMBOLS { __STARTUP__: type = import; __STACKSIZE__: type = weak, value = $0800; diff --git a/doc/index.sgml b/doc/index.sgml index 727364028..92df5e018 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -175,6 +175,9 @@ <tag><htmlurl url="plus4.html" name="plus4.html"></tag> Topics specific to the Commodore Plus/4. + <tag><htmlurl url="rp6502.html" name="rp6502.html"></tag> + Topics specific to the Picocomputer 6502. + <tag><htmlurl url="supervision.html" name="supervision.html"></tag> Topics specific to the Watara Supervision Console. diff --git a/doc/rp6502.sgml b/doc/rp6502.sgml new file mode 100644 index 000000000..201f0a06b --- /dev/null +++ b/doc/rp6502.sgml @@ -0,0 +1,96 @@ +<!doctype linuxdoc system> + +<article> +<title>Picocomputer 6502 - specific information for cc65 +<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> + +<abstract> +An overview over the Picocomputer 6502 and its interfaces to the cc65 C +compiler. +</abstract> + +<!-- Table of contents --> +<toc> + +<!-- Begin the document --> + +<sect>Overview<p> + +The Picocomputer 6502 is a modern W65C02S computer with a custom operating +system designed to be POSIX-like. The reference design includes a W65C02S, +W65C22S, RP6502-RIA, and optionally a RP6502-VGA. Peripheral devices like +keyboards, mice, and flash storage are connected by USB to the RP6502-RIA. +Audio is generated by the RP6502-RIA. Video is generated by the RP6502-VGA. + + + +<sect>Binary format<p> + +The standard binary output format generated by the linker for the RP6502 target +is a plain machine language program without any prefix or postfix. + +The RP6502 Integrated Development Environment, based on Visual Studio Code, +will convert the cc65 binary output into RP6502 ROM files that can be loaded +directly from the RP6502 monitor or installed on the RIA to be loaded at boot. + + + +<sect>Memory layout<p> + +<descrip> + <tag/Stack/ + The C run-time stack is located at $FEFF, and grows downward. + + <tag/Heap/ + The C heap is located at the end of the program, and grows toward the C + run-time stack. + + <tag/RAM/ + RAM is located at $0000 - $FEFF. Default binaries load and + start at $0200. + + <tag/ROM/ + The RP6502 is designed with no ROM in the 6502 address space. + + <tag/VIA/ + A Versatile Interface Adapter (6522) is 16 registers located + at $FFD0. + + <tag/RIA/ + The RP6502 Interface Adapter is 32 registers located at $FFE0. + + <tag/User/ + User I/O expansion is from $FF00 to $FFCF. + +</descrip><p> + + + +<sect>Platform-specific header files<p> + +Programs containing RP6502-specific code may use the <tt/rp6502.h/ or +<tt/rp6502.inc/ include files. + + + +<sect>License<p> + +This software is provided "as-is", without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: +<enum> +<item> The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated, but is not required. +<item> Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +<item> This notice may not be removed or altered from any source + distribution. +</enum> + +</article> diff --git a/include/rp6502.h b/include/rp6502.h index 0c2ba3881..033684b72 100644 --- a/include/rp6502.h +++ b/include/rp6502.h @@ -1,10 +1,28 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ +/*****************************************************************************/ +/* */ +/* rp6502.h */ +/* */ +/* Picocomputer 6502 */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ #ifndef _RP6502_H #define _RP6502_H @@ -50,29 +68,29 @@ struct __RP6502 /* XSTACK helpers */ -void __fastcall__ ria_push_long(unsigned long val); -void __fastcall__ ria_push_int(unsigned int val); +void __fastcall__ ria_push_long (unsigned long val); +void __fastcall__ ria_push_int (unsigned int val); #define ria_push_char(v) RIA.xstack = v -long __fastcall__ ria_pop_long(void); -int __fastcall__ ria_pop_int(void); +long __fastcall__ ria_pop_long (void); +int __fastcall__ ria_pop_int (void); #define ria_pop_char() RIA.xstack /* Set the RIA fastcall register */ -void __fastcall__ ria_set_axsreg(unsigned long axsreg); -void __fastcall__ ria_set_ax(unsigned int ax); +void __fastcall__ ria_set_axsreg (unsigned long axsreg); +void __fastcall__ ria_set_ax (unsigned int ax); #define ria_set_a(v) RIA.a = v /* Run an OS operation */ -int __fastcall__ ria_call_int(unsigned char op); -long __fastcall__ ria_call_long(unsigned char op); +int __fastcall__ ria_call_int (unsigned char op); +long __fastcall__ ria_call_long (unsigned char op); /* These run _mappederrno() on error */ -int __fastcall__ ria_call_int_errno(unsigned char op); -long __fastcall__ ria_call_long_errno(unsigned char op); +int __fastcall__ ria_call_int_errno (unsigned char op); +long __fastcall__ ria_call_long_errno (unsigned char op); /* OS operation numbers */ @@ -99,15 +117,15 @@ long __fastcall__ ria_call_long_errno(unsigned char op); /* C API for the operating system. */ -int __cdecl__ xreg(char device, char channel, unsigned char address, ...); -int __fastcall__ phi2(void); -int __fastcall__ codepage(void); -long __fastcall__ lrand(void); -int __fastcall__ stdin_opt(unsigned long ctrl_bits, unsigned char str_length); -int __fastcall__ read_xstack(void *buf, unsigned count, int fildes); -int __fastcall__ read_xram(unsigned buf, unsigned count, int fildes); -int __fastcall__ write_xstack(const void *buf, unsigned count, int fildes); -int __fastcall__ write_xram(unsigned buf, unsigned count, int fildes); +int __cdecl__ xreg (char device, char channel, unsigned char address, ...); +int __fastcall__ phi2 (void); +int __fastcall__ codepage (void); +long __fastcall__ lrand (void); +int __fastcall__ stdin_opt (unsigned long ctrl_bits, unsigned char str_length); +int __fastcall__ read_xstack (void* buf, unsigned count, int fildes); +int __fastcall__ read_xram (unsigned buf, unsigned count, int fildes); +int __fastcall__ write_xstack (const void* buf, unsigned count, int fildes); +int __fastcall__ write_xram (unsigned buf, unsigned count, int fildes); /* XREG location helpers */ diff --git a/libsrc/rp6502/close.c b/libsrc/rp6502/close.c index 1fdc40545..dd7e38115 100644 --- a/libsrc/rp6502/close.c +++ b/libsrc/rp6502/close.c @@ -1,16 +1,8 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <errno.h> -int __fastcall__ close(int fd) +int __fastcall__ close (int fd) { - ria_set_ax(fd); - return ria_call_int_errno(RIA_OP_CLOSE); + ria_set_ax (fd); + return ria_call_int_errno (RIA_OP_CLOSE); } diff --git a/libsrc/rp6502/codepage.c b/libsrc/rp6502/codepage.c index c0914651d..e28726f04 100644 --- a/libsrc/rp6502/codepage.c +++ b/libsrc/rp6502/codepage.c @@ -1,14 +1,6 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -int __fastcall__ codepage(void) +int __fastcall__ codepage (void) { - return ria_call_int(RIA_OP_CODEPAGE); + return ria_call_int (RIA_OP_CODEPAGE); } diff --git a/libsrc/rp6502/crt0.s b/libsrc/rp6502/crt0.s index e6406fe5e..165ecf0a2 100644 --- a/libsrc/rp6502/crt0.s +++ b/libsrc/rp6502/crt0.s @@ -1,10 +1,7 @@ -; Copyright (c) 2023 Rumbledethumps ; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense - -; Boilerplate crt0.s +; 2023, Rumbledethumps +; +; crt0.s .export _init, _exit .import _main diff --git a/libsrc/rp6502/getres.c b/libsrc/rp6502/getres.c index c60bbd38d..394c32e76 100644 --- a/libsrc/rp6502/getres.c +++ b/libsrc/rp6502/getres.c @@ -1,18 +1,10 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <time.h> -extern int __clock_gettimespec(struct timespec *ts, unsigned char op); +extern int __clock_gettimespec (struct timespec* ts, unsigned char op); -int clock_getres(clockid_t clock_id, struct timespec *res) +int clock_getres (clockid_t clock_id, struct timespec* res) { - ria_set_ax(clock_id); - return __clock_gettimespec(res, RIA_OP_CLOCK_GETRES); + ria_set_ax (clock_id); + return __clock_gettimespec (res, RIA_OP_CLOCK_GETRES); } diff --git a/libsrc/rp6502/gettime.c b/libsrc/rp6502/gettime.c index b40615e9e..ee63c31ec 100644 --- a/libsrc/rp6502/gettime.c +++ b/libsrc/rp6502/gettime.c @@ -1,20 +1,12 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <time.h> -extern int __clock_gettimespec(struct timespec *ts, unsigned char op); +extern int __clock_gettimespec (struct timespec* ts, unsigned char op); -int clock_gettime(clockid_t clock_id, struct timespec *tp) +int clock_gettime (clockid_t clock_id, struct timespec* tp) { (void)clock_id; /* time.s doesn't set the stack value for clock_id (bug?) */ - ria_set_ax(CLOCK_REALTIME); - return __clock_gettimespec(tp, RIA_OP_CLOCK_GETTIME); + ria_set_ax (CLOCK_REALTIME); + return __clock_gettimespec (tp, RIA_OP_CLOCK_GETTIME); } diff --git a/libsrc/rp6502/gettimespec.c b/libsrc/rp6502/gettimespec.c index ef136c6fe..4dc3a0db3 100644 --- a/libsrc/rp6502/gettimespec.c +++ b/libsrc/rp6502/gettimespec.c @@ -1,22 +1,13 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <time.h> -int __clock_gettimespec(struct timespec *ts, unsigned char op) +int __clock_gettimespec (struct timespec* ts, unsigned char op) /* Internal method shared by clock_getres and clock_gettime. */ { - int ax = ria_call_int_errno(op); - if (ax >= 0) - { - ts->tv_sec = ria_pop_long(); - ts->tv_nsec = ria_pop_long(); + int ax = ria_call_int_errno (op); + if (ax >= 0) { + ts->tv_sec = ria_pop_long (); + ts->tv_nsec = ria_pop_long (); } return ax; } diff --git a/libsrc/rp6502/gettimezone.c b/libsrc/rp6502/gettimezone.c index db9060967..90b2e568f 100644 --- a/libsrc/rp6502/gettimezone.c +++ b/libsrc/rp6502/gettimezone.c @@ -1,24 +1,15 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <time.h> -int clock_gettimezone(clockid_t clock_id, struct _timezone *tz) +int clock_gettimezone (clockid_t clock_id, struct _timezone* tz) { int ax; - ria_set_ax(clock_id); - ax = ria_call_int_errno(RIA_OP_CLOCK_GETTIMEZONE); - if (ax >= 0) - { + ria_set_ax (clock_id); + ax = ria_call_int_errno (RIA_OP_CLOCK_GETTIMEZONE); + if (ax >= 0) { char i; - for (i = 0; i < sizeof(struct _timezone); i++) - ((char *)tz)[i] = ria_pop_char(); + for (i = 0; i < sizeof (struct _timezone); i++) + ((char*)tz)[i] = ria_pop_char (); } return ax; } diff --git a/libsrc/rp6502/initenv.s b/libsrc/rp6502/initenv.s index b6546e320..180b25c67 100644 --- a/libsrc/rp6502/initenv.s +++ b/libsrc/rp6502/initenv.s @@ -1,8 +1,6 @@ -; Copyright (c) 2023 Rumbledethumps ; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense +; 2023, Rumbledethumps +; .constructor initenv, 24 .import __environ, __envcount, __envsize diff --git a/libsrc/rp6502/irq.s b/libsrc/rp6502/irq.s index ffc152aac..d7d2e6ec5 100644 --- a/libsrc/rp6502/irq.s +++ b/libsrc/rp6502/irq.s @@ -1,8 +1,7 @@ -; Copyright (c) 2023 Rumbledethumps ; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense +; 2023, Rumbledethumps +; +; Enables the C IRQ tools .export initirq, doneirq .import callirq, _exit diff --git a/libsrc/rp6502/lrand.c b/libsrc/rp6502/lrand.c index 9a23390e5..6434425df 100644 --- a/libsrc/rp6502/lrand.c +++ b/libsrc/rp6502/lrand.c @@ -1,14 +1,6 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -long __fastcall__ lrand(void) +long __fastcall__ lrand (void) { - return ria_call_long(RIA_OP_LRAND); + return ria_call_long (RIA_OP_LRAND); } diff --git a/libsrc/rp6502/lseek.c b/libsrc/rp6502/lseek.c index afa590e63..29506612c 100644 --- a/libsrc/rp6502/lseek.c +++ b/libsrc/rp6502/lseek.c @@ -1,19 +1,11 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <unistd.h> -off_t __fastcall__ lseek(int fd, off_t offset, int whence) +off_t __fastcall__ lseek (int fd, off_t offset, int whence) { /* Modified argument order for short stacking offset */ - ria_push_long(offset); - ria_push_char(whence); - ria_set_ax(fd); - return ria_call_long_errno(RIA_OP_LSEEK); + ria_push_long (offset); + ria_push_char (whence); + ria_set_ax (fd); + return ria_call_long_errno (RIA_OP_LSEEK); } diff --git a/libsrc/rp6502/mainargs.s b/libsrc/rp6502/mainargs.s index 886e39e02..152020022 100644 --- a/libsrc/rp6502/mainargs.s +++ b/libsrc/rp6502/mainargs.s @@ -1,8 +1,7 @@ -; Copyright (c) 2023 Rumbledethumps ; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense +; 2023, Rumbledethumps +; +; No arguments .constructor initmainargs, 24 .import __argc, __argv diff --git a/libsrc/rp6502/open.c b/libsrc/rp6502/open.c index 962614045..ab3a374a2 100644 --- a/libsrc/rp6502/open.c +++ b/libsrc/rp6502/open.c @@ -1,26 +1,16 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <errno.h> #include <string.h> -int __cdecl__ open(const char *name, int flags, ...) +int __cdecl__ open (const char* name, int flags, ...) { - size_t namelen = strlen(name); - if (namelen > 255) - { - return _mappederrno(EINVAL); + size_t namelen = strlen (name); + if (namelen > 255) { + return _mappederrno (EINVAL); } - while (namelen) - { - ria_push_char(name[--namelen]); + while (namelen) { + ria_push_char (name[--namelen]); } - ria_set_ax(flags); - return ria_call_int_errno(RIA_OP_OPEN); + ria_set_ax (flags); + return ria_call_int_errno (RIA_OP_OPEN); } diff --git a/libsrc/rp6502/phi2.c b/libsrc/rp6502/phi2.c index 095580c97..1275e256e 100644 --- a/libsrc/rp6502/phi2.c +++ b/libsrc/rp6502/phi2.c @@ -1,14 +1,6 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -int __fastcall__ phi2(void) +int __fastcall__ phi2 (void) { - return ria_call_int(RIA_OP_PHI2); + return ria_call_int (RIA_OP_PHI2); } diff --git a/libsrc/rp6502/randomize.c b/libsrc/rp6502/randomize.c index a2050c4aa..3299e8c68 100644 --- a/libsrc/rp6502/randomize.c +++ b/libsrc/rp6502/randomize.c @@ -1,16 +1,8 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <stdlib.h> // Non-standard cc65 -void _randomize(void) +void _randomize (void) { - srand(ria_call_int(RIA_OP_LRAND)); + srand (ria_call_int (RIA_OP_LRAND)); } diff --git a/libsrc/rp6502/read.c b/libsrc/rp6502/read.c index d0b389c4a..eb96f779c 100644 --- a/libsrc/rp6502/read.c +++ b/libsrc/rp6502/read.c @@ -1,29 +1,18 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <unistd.h> -int __fastcall__ read(int fildes, void *buf, unsigned count) +int __fastcall__ read (int fildes, void* buf, unsigned count) { int total = 0; - while (count) - { + while (count) { unsigned blockcount = (count > 256) ? 256 : count; - int bytes_read = read_xstack(&((char *)buf)[total], blockcount, fildes); - if (bytes_read < 0) - { + int bytes_read = read_xstack (&((char*)buf)[total], blockcount, fildes); + if (bytes_read < 0) { return bytes_read; } total += bytes_read; count -= bytes_read; - if (bytes_read < blockcount) - { + if (bytes_read < blockcount) { break; } } diff --git a/libsrc/rp6502/read_xram.c b/libsrc/rp6502/read_xram.c index 565744e11..f88a036ee 100644 --- a/libsrc/rp6502/read_xram.c +++ b/libsrc/rp6502/read_xram.c @@ -1,17 +1,9 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -int __fastcall__ read_xram(unsigned buf, unsigned count, int fildes) +int __fastcall__ read_xram (unsigned buf, unsigned count, int fildes) { - ria_push_int(buf); - ria_push_int(count); - ria_set_ax(fildes); - return ria_call_int_errno(RIA_OP_READ_XRAM); + ria_push_int (buf); + ria_push_int (count); + ria_set_ax (fildes); + return ria_call_int_errno (RIA_OP_READ_XRAM); } diff --git a/libsrc/rp6502/read_xstack.c b/libsrc/rp6502/read_xstack.c index c3b6d098e..70eebad2f 100644 --- a/libsrc/rp6502/read_xstack.c +++ b/libsrc/rp6502/read_xstack.c @@ -1,22 +1,13 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -int __fastcall__ read_xstack(void *buf, unsigned count, int fildes) +int __fastcall__ read_xstack (void* buf, unsigned count, int fildes) { int i, ax; - ria_push_int(count); - ria_set_ax(fildes); - ax = ria_call_int_errno(RIA_OP_READ_XSTACK); - for (i = 0; i < ax; i++) - { - ((char *)buf)[i] = ria_pop_char(); + ria_push_int (count); + ria_set_ax (fildes); + ax = ria_call_int_errno (RIA_OP_READ_XSTACK); + for (i = 0; i < ax; i++) { + ((char*)buf)[i] = ria_pop_char (); } return ax; } diff --git a/libsrc/rp6502/ria.s b/libsrc/rp6502/ria.s index f040fca61..a1b53efb1 100644 --- a/libsrc/rp6502/ria.s +++ b/libsrc/rp6502/ria.s @@ -1,8 +1,7 @@ -; Copyright (c) 2023 Rumbledethumps ; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense +; 2023, Rumbledethumps +; +; Helpers for building API shims .include "rp6502.inc" diff --git a/libsrc/rp6502/settime.c b/libsrc/rp6502/settime.c index 69d4dafe5..1ba1d2e3e 100644 --- a/libsrc/rp6502/settime.c +++ b/libsrc/rp6502/settime.c @@ -1,18 +1,10 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <time.h> -int clock_settime(clockid_t clock_id, const struct timespec *tp) +int clock_settime (clockid_t clock_id, const struct timespec* tp) { - ria_set_ax(clock_id); - ria_push_long(tp->tv_nsec); - ria_push_long(tp->tv_sec); - return ria_call_int_errno(RIA_OP_CLOCK_SETTIME); + ria_set_ax (clock_id); + ria_push_long (tp->tv_nsec); + ria_push_long (tp->tv_sec); + return ria_call_int_errno (RIA_OP_CLOCK_SETTIME); } diff --git a/libsrc/rp6502/stdin_opt.c b/libsrc/rp6502/stdin_opt.c index f01d1036c..3a9ccfde4 100644 --- a/libsrc/rp6502/stdin_opt.c +++ b/libsrc/rp6502/stdin_opt.c @@ -1,16 +1,9 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ #include <rp6502.h> -int __fastcall__ stdin_opt(unsigned long ctrl_bits, unsigned char str_length) +int __fastcall__ stdin_opt (unsigned long ctrl_bits, unsigned char str_length) { - ria_push_long(ctrl_bits); - ria_set_a(str_length); - return ria_call_int_errno(RIA_OP_STDIN_OPT); + ria_push_long (ctrl_bits); + ria_set_a (str_length); + return ria_call_int_errno (RIA_OP_STDIN_OPT); } diff --git a/libsrc/rp6502/sysremove.c b/libsrc/rp6502/sysremove.c index 6b143d672..d8c1ced98 100644 --- a/libsrc/rp6502/sysremove.c +++ b/libsrc/rp6502/sysremove.c @@ -1,26 +1,16 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <errno.h> #include <string.h> -unsigned char __fastcall__ _sysremove(const char *name) +unsigned char __fastcall__ _sysremove (const char* name) { size_t namelen; - namelen = strlen(name); - if (namelen > 255) - { - return _mappederrno(EINVAL); + namelen = strlen (name); + if (namelen > 255) { + return _mappederrno (EINVAL); } - while (namelen) - { - ria_push_char(name[--namelen]); + while (namelen) { + ria_push_char (name[--namelen]); } - return ria_call_int_errno(RIA_OP_UNLINK); + return ria_call_int_errno (RIA_OP_UNLINK); } diff --git a/libsrc/rp6502/sysrename.c b/libsrc/rp6502/sysrename.c index 6d42752ae..96eca24cf 100644 --- a/libsrc/rp6502/sysrename.c +++ b/libsrc/rp6502/sysrename.c @@ -1,32 +1,21 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <errno.h> #include <string.h> -unsigned char __fastcall__ _sysrename(const char *oldpath, const char *newpath) +unsigned char __fastcall__ _sysrename (const char* oldpath, const char* newpath) { size_t oldpathlen, newpathlen; - oldpathlen = strlen(oldpath); - newpathlen = strlen(newpath); - if (oldpathlen + newpathlen > 254) - { - return _mappederrno(EINVAL); + oldpathlen = strlen (oldpath); + newpathlen = strlen (newpath); + if (oldpathlen + newpathlen > 254) { + return _mappederrno (EINVAL); } - while (oldpathlen) - { - ria_push_char(oldpath[--oldpathlen]); + while (oldpathlen) { + ria_push_char (oldpath[--oldpathlen]); } - ria_push_char(0); - while (newpathlen) - { - ria_push_char(newpath[--newpathlen]); + ria_push_char (0); + while (newpathlen) { + ria_push_char (newpath[--newpathlen]); } - return ria_call_int_errno(RIA_OP_RENAME); + return ria_call_int_errno (RIA_OP_RENAME); } diff --git a/libsrc/rp6502/write.c b/libsrc/rp6502/write.c index f14c91e59..11241dab5 100644 --- a/libsrc/rp6502/write.c +++ b/libsrc/rp6502/write.c @@ -1,29 +1,18 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> #include <unistd.h> -int __fastcall__ write(int fildes, const void *buf, unsigned count) +int __fastcall__ write (int fildes, const void* buf, unsigned count) { int ax, total = 0; - while (count) - { + while (count) { int blockcount = (count > 256) ? 256 : count; - ax = write_xstack(&((char *)buf)[total], blockcount, fildes); - if (ax < 0) - { + ax = write_xstack (&((char*)buf)[total], blockcount, fildes); + if (ax < 0) { return ax; } total += ax; count -= ax; - if (ax < blockcount) - { + if (ax < blockcount) { break; } } diff --git a/libsrc/rp6502/write_xram.c b/libsrc/rp6502/write_xram.c index 379e5a667..31a553813 100644 --- a/libsrc/rp6502/write_xram.c +++ b/libsrc/rp6502/write_xram.c @@ -1,17 +1,9 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -int __fastcall__ write_xram(unsigned buf, unsigned count, int fildes) +int __fastcall__ write_xram (unsigned buf, unsigned count, int fildes) { - ria_push_int(buf); - ria_push_int(count); - ria_set_ax(fildes); - return ria_call_int_errno(RIA_OP_WRITE_XRAM); + ria_push_int (buf); + ria_push_int (count); + ria_set_ax (fildes); + return ria_call_int_errno (RIA_OP_WRITE_XRAM); } diff --git a/libsrc/rp6502/write_xstack.c b/libsrc/rp6502/write_xstack.c index 4507193ea..b53aa95e7 100644 --- a/libsrc/rp6502/write_xstack.c +++ b/libsrc/rp6502/write_xstack.c @@ -1,20 +1,11 @@ -/* - * Copyright (c) 2023 Rumbledethumps - * - * SPDX-License-Identifier: Zlib - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-License-Identifier: Unlicense - */ - #include <rp6502.h> -int __fastcall__ write_xstack(const void *buf, unsigned count, int fildes) +int __fastcall__ write_xstack (const void* buf, unsigned count, int fildes) { unsigned i; - for (i = count; i;) - { - ria_push_char(((char *)buf)[--i]); + for (i = count; i;) { + ria_push_char (((char*)buf)[--i]); } - ria_set_ax(fildes); - return ria_call_int_errno(RIA_OP_WRITE_XSTACK); + ria_set_ax (fildes); + return ria_call_int_errno (RIA_OP_WRITE_XSTACK); } diff --git a/libsrc/rp6502/xreg.s b/libsrc/rp6502/xreg.s index 81cba6733..40d4a6705 100644 --- a/libsrc/rp6502/xreg.s +++ b/libsrc/rp6502/xreg.s @@ -1,9 +1,6 @@ -; Copyright (c) 2023 Rumbledethumps ; -; SPDX-License-Identifier: Zlib -; SPDX-License-Identifier: BSD-3-Clause -; SPDX-License-Identifier: Unlicense - +; 2023, Rumbledethumps +; ; CC65 will promote variadic char arguments to int. It will not demote longs. ; int __cdecl__ xreg(char device, char channel, unsigned char address, ...); From 3066b1f9b4a73b88a25dd5fa0fad1208df92bfa5 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 17 Nov 2023 22:49:03 +0100 Subject: [PATCH 376/520] Optimize unsigned int and long > 255 --- src/cc65/codegen.c | 18 +++++++--- test/val/compare8.c | 85 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index f923ac8c9..d98fc85cb 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -4199,10 +4199,14 @@ void g_gt (unsigned flags, unsigned long val) */ g_ne (flags, val); } else if (val < 0xFFFF) { - /* Use >= instead of > because the former gives better - ** code on the 6502 than the latter. - */ - g_ge (flags, val+1); + if (val == 0xFF) { + AddCodeLine ("cpx #$00"); + } else { + /* Use >= instead of > because the former gives better + ** code on the 6502 than the latter. + */ + g_ge (flags, val+1); + } } else { /* Never true */ Warning ("Condition is never true"); @@ -4229,6 +4233,8 @@ void g_gt (unsigned flags, unsigned long val) ** is easier to optimize. */ g_ne (flags, val); + } else if (val == 0xFF) { + AddCodeLine ("cpx #$00"); } else if (val < 0xFFFFFFFF) { /* Use >= instead of > because the former gives better ** code on the 6502 than the latter. @@ -4241,7 +4247,9 @@ void g_gt (unsigned flags, unsigned long val) } } else { /* Signed compare */ - if ((long) val < 0x7FFFFFFF) { + if (val == 0xFF) { + AddCodeLine ("cpx #$00"); + } else if ((long) val < 0x7FFFFFFF) { g_ge (flags, val+1); } else { /* Never true */ diff --git a/test/val/compare8.c b/test/val/compare8.c index 2621dad1d..3004787ff 100644 --- a/test/val/compare8.c +++ b/test/val/compare8.c @@ -23,6 +23,8 @@ bit bit0 = 0; #endif int int0 = 0; int int1 = 0; +unsigned int uint0 = 0; +unsigned int uint1 = 0; unsigned char uchar0 = 0; unsigned char uchar1 = 0; signed char char0 = 0; @@ -296,12 +298,95 @@ void int_compare2(void) c_int_gt_lit2(0xff); } +void c_uint_gt_lit1(unsigned char expected_result) +{ + result = 0; + + if(uint0 > 0) + result |= 1; + + if(uint0 > 1) + result |= 2; + + if(uint0 > 0xff) + result |= 4; + + if(uint0 > 0x100) + result |= 8; + + if(uint0 > 0x0101) + result |= 0x10; + + if(uint0 > 0x01ff) + result |= 0x20; + + if(uint0 > 0x0200) + result |= 0x40; + + if(uint0 > 0x0201) + result |= 0x80; + + if(result != expected_result) + failures=1; +} + +void uint_compare1(void) +{ + uint0 = 0; + c_uint_gt_lit1(0x00); + + uint0 = 1; + c_uint_gt_lit1(0x01); + + uint0 = 2; + c_uint_gt_lit1(0x03); + + uint0 = 0xfe; + c_uint_gt_lit1(0x03); + + uint0 = 0xff; + c_uint_gt_lit1(0x03); + + uint0 = 0x100; + c_uint_gt_lit1(0x07); + + uint0 = 0x101; + c_uint_gt_lit1(0x0f); + + uint0 = 0x102; + c_uint_gt_lit1(0x1f); + + uint0 = 0x1fe; + c_uint_gt_lit1(0x1f); + + uint0 = 0x1ff; + c_uint_gt_lit1(0x1f); + + uint0 = 0x200; + c_uint_gt_lit1(0x3f); + + uint0 = 0x201; + c_uint_gt_lit1(0x7f); + + uint0 = 0x7f00; + c_uint_gt_lit1(0xff); + + /* now check contiguous ranges */ + + for(uint0 = 2; uint0 != 0xff; uint0++) + c_uint_gt_lit1(0x03); + + for(uint0 = 0x202; uint0 != 0xffff; uint0++) + c_uint_gt_lit1(0xff); +} + int main (void) { char_compare(); int_compare1(); int_compare2(); + uint_compare1(); success = failures; done (); From f1b0fbebd53d3a94e80be5702e3855aec004c40f Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 23 Nov 2023 20:01:47 +0100 Subject: [PATCH 377/520] Remove all indexed accesses to SCC registers Some IIgs models had issues sending bytes out. Also make channel tests clearer. --- libsrc/apple2/ser/a2.gs.s | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 20018abe4..3a2db1926 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -154,6 +154,11 @@ SCCBDATA := $C03A SER_FLAG := $E10104 +; ------------------------------------------------------------------------ +; Channels +CHANNEL_B = 0 +CHANNEL_A = 1 + ; ------------------------------------------------------------------------ ; Write registers, read registers, and values that interest us @@ -377,7 +382,7 @@ SER_OPEN: ldy #WR_TX_RX_CTRL ; Setup stop & parity bits jsr writeSCCReg - cpx #$00 + cpx #CHANNEL_B bne ClockA ClockB: ldy #WR_CLOCK_CTRL @@ -470,7 +475,7 @@ BaudOK: lda SER_FLAG ; Get SerFlag's current value sta SerFlagOrig ; and save it - cpx #$00 + cpx #CHANNEL_B bne IntA IntB: ora #SER_FLAG_CH_B ; Inform firmware we want channel B IRQs @@ -568,7 +573,8 @@ SER_PUT: SER_STATUS: ldx Channel - lda SCCBREG,x + ldy #RR_INIT_STATUS + jsr readSSCReg ldx #$00 sta (ptr1) .assert SER_ERR_OK = 0, error @@ -617,7 +623,12 @@ SER_IRQ: beq CheckSpecial ldx Channel - lda SCCBDATA,x ; Get byte + beq ReadBdata + lda SCCADATA + bra ReadDone +ReadBdata: + lda SCCBDATA ; Get byte +ReadDone: ldx RecvFreeCnt ; Check if we have free space left beq Flow ; Jump if no space in receive buffer ldy RecvTail ; Load buffer pointer @@ -678,7 +689,13 @@ Special:ldx Channel rts BadChar: - lda SCCBDATA,x ; Remove char in error + cpx #CHANNEL_B + beq BadCharB + lda SCCADATA + bra BadCharDone +BadCharB: + lda SCCBDATA ; Remove char in error +BadCharDone: sec rts @@ -695,7 +712,8 @@ Again: lda SendFreeCnt ; Anything to send? bne Quit ; Bail out if it is Wait: - lda SCCBREG,x ; Check that we're ready to send + ldy #RR_INIT_STATUS + jsr readSSCReg ; Check that we're ready to send tay and #INIT_STATUS_READY beq NotReady @@ -712,8 +730,13 @@ Quit: rts Send: ldy SendHead ; Send byte lda SendBuf,y - sta SCCBDATA,x - + cpx #CHANNEL_B + beq WriteBdata + sta SCCADATA + bra WriteDone +WriteBdata: + sta SCCBDATA +WriteDone: inc SendHead inc SendFreeCnt jmp Again ; Continue flushing TX buffer From 3905e430680869169cd820589b95bb6d9974176e Mon Sep 17 00:00:00 2001 From: rumbledethumps <16963588+rumbledethumps@users.noreply.github.com> Date: Sun, 26 Nov 2023 15:12:20 -0800 Subject: [PATCH 378/520] style update --- libsrc/rp6502/gettimezone.c | 3 ++- libsrc/rp6502/randomize.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/rp6502/gettimezone.c b/libsrc/rp6502/gettimezone.c index 90b2e568f..f3cb2a061 100644 --- a/libsrc/rp6502/gettimezone.c +++ b/libsrc/rp6502/gettimezone.c @@ -8,8 +8,9 @@ int clock_gettimezone (clockid_t clock_id, struct _timezone* tz) ax = ria_call_int_errno (RIA_OP_CLOCK_GETTIMEZONE); if (ax >= 0) { char i; - for (i = 0; i < sizeof (struct _timezone); i++) + for (i = 0; i < sizeof (struct _timezone); i++) { ((char*)tz)[i] = ria_pop_char (); + } } return ax; } diff --git a/libsrc/rp6502/randomize.c b/libsrc/rp6502/randomize.c index 3299e8c68..569387d14 100644 --- a/libsrc/rp6502/randomize.c +++ b/libsrc/rp6502/randomize.c @@ -1,7 +1,6 @@ #include <rp6502.h> #include <stdlib.h> -// Non-standard cc65 void _randomize (void) { srand (ria_call_int (RIA_OP_LRAND)); From 79214530e01e751773da6627142e40f0d5a6d5c6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 27 Nov 2023 20:39:15 +0800 Subject: [PATCH 379/520] Maximum total count of errors before the compiler unconditionally bails out is now 200. If more than 20 errors occur on the same source line, the compiler will immediately bail out. --- src/cc65/error.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cc65/error.c b/src/cc65/error.c index 39b067825..2ad7133ed 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -61,6 +61,8 @@ /* Count of errors/warnings */ unsigned ErrorCount = 0; unsigned WarningCount = 0; +unsigned RecentLineNo = 0; +unsigned RecentErrorCount = 0; /* Warning and error options */ IntStack WarnEnable = INTSTACK(1); /* Enable warnings */ @@ -205,8 +207,16 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } + ++ErrorCount; - if (ErrorCount > 20) { + if (RecentLineNo != LineNo) { + RecentLineNo = LineNo; + RecentErrorCount = 0; + } else { + ++RecentErrorCount; + } + + if (RecentErrorCount > 20 || ErrorCount > 200) { Fatal ("Too many errors"); } } From ac04394254be9bc7d0ec58c572d5d07c6e808792 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 27 Nov 2023 20:39:15 +0800 Subject: [PATCH 380/520] Fixed and improved diagnostics about declaration errors. --- src/cc65/declare.c | 110 +++++++++++++++++++++++++++------------------ src/cc65/declare.h | 23 ++++++++-- 2 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4bfd52c5e..1d60053e9 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1766,7 +1766,7 @@ static void ParseAnsiParamList (FuncDesc* F) /* Allow parameters without a name, but remember if we had some to ** eventually print an error message later. */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + ParseDecl (&Spec, &Decl, DM_ACCEPT_PARAM_IDENT); if (Decl.Ident[0] == '\0') { /* Unnamed symbol. Generate a name that is not user accessible, @@ -1886,7 +1886,7 @@ static FuncDesc* ParseFuncDecl (void) -static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Recursively process direct declarators. Build a type array in reverse order. */ { /* Read optional function or pointer qualifiers that modify the identifier @@ -1903,61 +1903,49 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Skip the star */ NextToken (); + /* A pointer type cannot be used as an empty declaration */ + if (Mode == DM_ACCEPT_IDENT) { + Mode = DM_NEED_IDENT; + } + /* Allow const, restrict, and volatile qualifiers */ Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); /* Parse the type that the pointer points to */ - DirectDecl (Spec, D, Mode); + Mode = DirectDecl (Spec, D, Mode); /* Add the type */ AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); - return; + return Mode; } if (CurTok.Tok == TOK_LPAREN) { NextToken (); - DirectDecl (Spec, D, Mode); - ConsumeRParen (); - } else { - /* Things depend on Mode now: - ** - Mode == DM_NEED_IDENT means: - ** we *must* have a type and a variable identifer. - ** - Mode == DM_NO_IDENT means: - ** we must have a type but no variable identifer - ** (if there is one, it's not read). - ** - Mode == DM_ACCEPT_IDENT means: - ** we *may* have an identifier. If there is an identifier, - ** it is read, but it is no error, if there is none. + /* An empty declaration cannot contain parentheses where an identifier + ** would show up if it were a non-empty declaration. */ - if (Mode == DM_NO_IDENT) { - D->Ident[0] = '\0'; - } else if (CurTok.Tok == TOK_IDENT) { - strcpy (D->Ident, CurTok.Ident); - NextToken (); - } else { - if (Mode == DM_NEED_IDENT) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, TOK_LCURLY, TOK_RCURLY }; - - Error ("Identifier expected"); - - /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); - - /* Skip curly braces */ - if (CurTok.Tok == TOK_LCURLY) { - static const token_t CurlyToken[] = { TOK_RCURLY }; - SkipTokens (CurlyToken, sizeof(CurlyToken) / sizeof(CurlyToken[0])); - NextToken (); - } else if (CurTok.Tok == TOK_RCURLY) { - NextToken (); - } - } - D->Ident[0] = '\0'; + if (Mode == DM_ACCEPT_IDENT) { + Mode = DM_NEED_IDENT; + } + Mode = DirectDecl (Spec, D, Mode); + ConsumeRParen (); + } else if (CurTok.Tok == TOK_IDENT) { + strcpy (D->Ident, CurTok.Ident); + NextToken (); + } else { + D->Ident[0] = '\0'; + if (Mode == DM_NEED_IDENT) { + Error ("Identifier expected"); } } while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { + /* An array or function type cannot be used as an empty declaration */ + if (Mode == DM_ACCEPT_IDENT && D->Ident[0] == '\0') { + Mode = DM_NEED_IDENT; + Error ("Identifier expected"); + } + if (CurTok.Tok == TOK_LPAREN) { /* Function declarator */ @@ -2043,6 +2031,8 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) if (Qualifiers & T_QUAL_CDECL) { Error ("Invalid '__cdecl__' qualifier"); } + + return Mode; } @@ -2154,12 +2144,44 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } if (PrevErrorCount != ErrorCount) { - /* Make the declaration fictitious if is is not parsed correctly */ - D->StorageClass |= SC_FICTITIOUS; + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, TOK_LCURLY, TOK_RCURLY }; + + /* Try some smart error recovery */ + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + + /* Skip curly braces */ + if (CurTok.Tok == TOK_LCURLY) { + static const token_t CurlyToken[] = { TOK_RCURLY }; + SkipTokens (CurlyToken, sizeof (CurlyToken) / sizeof (CurlyToken[0])); + NextToken (); + } else if (CurTok.Tok == TOK_RCURLY) { + NextToken (); + } if (Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { + /* Make the declaration fictitious if is is not parsed correctly */ + D->StorageClass |= SC_FICTITIOUS; + /* Use a fictitious name for the identifier if it is missing */ - AnonName (D->Ident, "global"); + const char* Level = ""; + + switch (GetLexicalLevel ()) { + case LEX_LEVEL_GLOBAL: + Level = "global"; + break; + case LEX_LEVEL_FUNCTION: + case LEX_LEVEL_BLOCK: + Level = "local"; + break; + case LEX_LEVEL_STRUCT: + Level = "field"; + break; + default: + Level = "unknown"; + break; + } + AnonName (D->Ident, Level); } } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 89d7be7ea..ca1b88165 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -98,11 +98,26 @@ struct Declarator { unsigned Index; /* Used to build Type */ }; -/* Modes for ParseDecl */ +/* Modes for ParseDecl: +** - DM_NEED_IDENT means: +** we *must* have a type and a variable identifer. +** - DM_NO_IDENT means: +** we must have a type but no variable identifer +** (if there is one, it's not read). +** - DM_ACCEPT_IDENT means: +** we *may* have an identifier, or none. If it is the latter case, +** the type must be used as an empty declaration, or it is an error. +** Note: this is used for struct/union members. +** - DM_IGNORE_IDENT means: +** we *may* have an identifier. If there is an identifier, +** it is read, but it is no error, if there is none. +** Note: this is used for function parameter type lists. +*/ typedef enum { - DM_NEED_IDENT, /* We must have an identifier */ - DM_NO_IDENT, /* We won't read an identifier */ - DM_ACCEPT_IDENT, /* We will accept an id if there is one */ + DM_NEED_IDENT, + DM_NO_IDENT, + DM_ACCEPT_IDENT, + DM_ACCEPT_PARAM_IDENT, } declmode_t; From 7574e36e95324afa8f50559838227d9660c2afae Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 27 Nov 2023 20:39:15 +0800 Subject: [PATCH 381/520] Improved error recovery with function declarations. Fixed some rare cases when a single file-scope error could get reapeated endlessly until the maximum total count of errors allowed is reached. --- src/cc65/compile.c | 32 ++++- src/cc65/declare.c | 160 +++++++++++++++++++---- src/cc65/declare.h | 12 +- test/ref/bug1889-missing-identifier.cref | 2 - 4 files changed, 170 insertions(+), 36 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index a591a60b8..02d37c53e 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -94,6 +94,8 @@ static void Parse (void) while (CurTok.Tok != TOK_CEOF) { DeclSpec Spec; + int NeedClean = 0; + unsigned PrevErrorCount = ErrorCount; /* Check for empty statements */ if (CurTok.Tok == TOK_SEMI) { @@ -144,7 +146,11 @@ static void Parse (void) Declarator Decl; /* Read the next declaration */ - ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + if (Decl.Ident[0] == '\0') { + Sym = 0; + goto NextDecl; + } /* Check if we must reserve storage for the variable. We do this, ** @@ -310,6 +316,7 @@ static void Parse (void) } +NextDecl: /* Check for end of declaration list */ if (CurTok.Tok == TOK_COMMA) { NextToken (); @@ -325,6 +332,7 @@ static void Parse (void) /* Function */ if (CurTok.Tok == TOK_SEMI) { /* Prototype only */ + NeedClean = 0; NextToken (); } else if (CurTok.Tok == TOK_LCURLY) { /* ISO C: The type category in a function definition cannot be @@ -337,6 +345,7 @@ static void Parse (void) } /* Parse the function body anyways */ + NeedClean = 0; NewFunc (Sym, FuncDef); /* Make sure we aren't omitting any work */ @@ -345,10 +354,27 @@ static void Parse (void) } else { - /* Must be followed by a semicolon */ - ConsumeSemi (); + if (Sym) { + /* Must be followed by a semicolon */ + if (CurTok.Tok != TOK_SEMI) { + NeedClean = -1; + } + ConsumeSemi (); + } } + + /* Try some smart error recovery */ + if (PrevErrorCount != ErrorCount && NeedClean < 0) { + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_SEMI, TOK_RCURLY }; + + SmartErrorSkip (); + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + if (CurTok.Tok == TOK_SEMI || CurTok.Tok == TOK_RCURLY) { + NextToken (); + } + } } /* Done with deferred operations */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 1d60053e9..31a0df2c3 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -83,6 +83,104 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp +static void OpenBrace (Collection* C, token_t Tok) +/* Consume an opening parenthesis/bracket/curly brace and remember that */ +{ + switch (Tok) { + case TOK_LPAREN: Tok = TOK_RPAREN; break; + case TOK_LBRACK: Tok = TOK_RBRACK; break; + case TOK_LCURLY: Tok = TOK_RCURLY; break; + default: Internal ("Unexpected opening token: %02X", (unsigned)Tok); + } + CollAppend (C, (void*)Tok); + NextToken (); +} + + + +static int CloseBrace (Collection* C, token_t Tok) +/* Consume a closing parenthesis/bracket/curly brace if it is matched with an +** opening one and return 0, or bail out and return -1 if it is not matched. +*/ +{ + if (CollCount (C) > 0) { + token_t LastTok = (token_t)CollLast (C); + if (LastTok == Tok) { + CollPop (C); + NextToken (); + return 0; + } + } + + return -1; +} + + + +int SmartErrorSkip (void) +/* Try some smart error recovery. Skip tokens until either a comma or semicolon +** that is not enclosed in an open parenthesis/bracket/curly brace, or until an +** unpaired right parenthesis/bracket/curly brace is reached. Return 0 if it is +** the former case, or -1 if it is the latter case. */ +{ + Collection C = AUTO_COLLECTION_INITIALIZER; + int Res = 0; + + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, + TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY }; + + while (CurTok.Tok != TOK_CEOF) { + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + + switch (CurTok.Tok) { + case TOK_LPAREN: + case TOK_LBRACK: + case TOK_LCURLY: + OpenBrace (&C, CurTok.Tok); + break; + + case TOK_RPAREN: + case TOK_RBRACK: + if (CloseBrace (&C, CurTok.Tok)) { + Res = -1; + goto ExitPoint; + } + break; + + case TOK_RCURLY: + if (CloseBrace (&C, CurTok.Tok)) { + Res = -1; + goto ExitPoint; + } else if (CollCount (&C) == 0) { + goto ExitPoint; + } + break; + + case TOK_COMMA: + if (CollCount (&C) == 0) { + goto ExitPoint; + } + NextToken (); + break; + + case TOK_SEMI: + case TOK_CEOF: + Res = -1; + goto ExitPoint; + + default: + Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok); + } + } + +ExitPoint: + DoneCollection (&C); + return Res; +} + + + static unsigned ParseOneStorageClass (void) /* Parse and return a storage class specifier */ { @@ -1537,6 +1635,12 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp *SignednessSpecified = 1; } break; + } else if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { + /* Treat this identifier as an unknown type */ + Error ("Unknown type name '%s'", CurTok.Ident); + TypeCopy (Spec->Type, type_int); + NextToken (); + break; } } else { /* This is a label. Use the default type flag to end the loop @@ -1618,14 +1722,13 @@ static void ParseOldStyleParamList (FuncDesc* F) NextToken (); } else { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_RPAREN, TOK_SEMI }; - /* Not a parameter name */ Error ("Identifier expected for parameter name"); /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + if (SmartErrorSkip () < 0) { + break; + } } /* Check for more parameters */ @@ -1711,12 +1814,9 @@ static void ParseOldStyleParamList (FuncDesc* F) ConsumeSemi (); } - if (PrevErrorCount != ErrorCount) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI }; - + if (PrevErrorCount != ErrorCount && CurTok.Tok != TOK_LCURLY) { /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + SmartErrorSkip (); } } @@ -1731,6 +1831,7 @@ static void ParseAnsiParamList (FuncDesc* F) DeclSpec Spec; Declarator Decl; SymEntry* Param; + unsigned PrevErrorCount = ErrorCount; /* Allow an ellipsis as last parameter */ if (CurTok.Tok == TOK_ELLIPSIS) { @@ -1798,6 +1899,13 @@ static void ParseAnsiParamList (FuncDesc* F) /* Count arguments */ ++F->ParamCount; + if (PrevErrorCount != ErrorCount) { + /* Try some smart error recovery */ + if (SmartErrorSkip () < 0) { + break; + } + } + /* Check for more parameters */ if (CurTok.Tok == TOK_COMMA) { NextToken (); @@ -2065,8 +2173,10 @@ Type* ParseType (Type* T) -void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) -/* Parse a variable, type or function declarator */ +int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +/* Parse a variable, type or function declarator. Return -1 if this stops at +** an unpaired right parenthesis/bracket/curly brace. +*/ { /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; @@ -2117,7 +2227,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } /* Check a few pre-C99 things */ - if ((Spec->Flags & DS_DEF_TYPE) != 0) { + if (D->Ident[0] != '\0' && (Spec->Flags & DS_DEF_TYPE) != 0) { /* Check and warn about an implicit int return in the function */ if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) { /* Function has an implicit int return. Output a warning if we don't @@ -2129,7 +2239,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - /* For anthing that is not a function or typedef, check for an implicit + /* For anything that is not a function or typedef, check for an implicit ** int declaration. */ if ((D->StorageClass & SC_FUNC) != SC_FUNC && @@ -2144,22 +2254,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } if (PrevErrorCount != ErrorCount) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, TOK_LCURLY, TOK_RCURLY }; - - /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); - - /* Skip curly braces */ - if (CurTok.Tok == TOK_LCURLY) { - static const token_t CurlyToken[] = { TOK_RCURLY }; - SkipTokens (CurlyToken, sizeof (CurlyToken) / sizeof (CurlyToken[0])); - NextToken (); - } else if (CurTok.Tok == TOK_RCURLY) { - NextToken (); - } - - if (Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { + if ((Spec->Flags & DS_DEF_TYPE) == 0 && Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { /* Make the declaration fictitious if is is not parsed correctly */ D->StorageClass |= SC_FICTITIOUS; @@ -2183,7 +2278,14 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } AnonName (D->Ident, Level); } + + /* Try some smart error recovery */ + if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { + return SmartErrorSkip (); + } } + + return 0; } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index ca1b88165..add86594d 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -128,11 +128,19 @@ typedef enum { +int SmartErrorSkip (void); +/* Try some smart error recovery. Skip tokens until either a comma or semicolon +** that is not enclosed in an open parenthesis/bracket/curly brace, or until an +** unpaired right parenthesis/bracket/curly brace is reached. Return 0 if it is +** the former case, or -1 if it is the latter case. */ + Type* ParseType (Type* Type); /* Parse a complete type specification */ -void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); -/* Parse a variable, type or function declarator */ +int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); +/* Parse a variable, type or function declarator. Return -1 if this stops at +** an unpaired right parenthesis/bracket/curly brace. +*/ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage); /* Parse a declaration specification */ diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index acaf53f94..534c6aaba 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,5 +1,3 @@ bug1889-missing-identifier.c:3: Error: Identifier expected bug1889-missing-identifier.c:3: Error: ';' expected -bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature bug1889-missing-identifier.c:4: Error: Identifier expected -bug1889-missing-identifier.c:4: Warning: Implicit 'int' is an obsolete feature From b99ebc1256ff5400e70c7df8d97a708b45b23620 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 27 Nov 2023 20:39:15 +0800 Subject: [PATCH 382/520] Fixed diagnostic messages about undeclared identifiers. --- src/cc65/asmstmt.c | 2 +- src/cc65/expr.c | 2 +- src/cc65/symtab.c | 2 +- test/ref/custom-reference-error.cref | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 6cb6f2ef7..166d05434 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -102,7 +102,7 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) /* Did we find a symbol with this name? */ if (Sym == 0) { - Error ("Undefined symbol '%s' for argument %u", CurTok.Ident, Arg); + Error ("Undeclared symbol '%s' for argument %u", CurTok.Ident, Arg); AsmErrorSkip (); return 0; } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index f7ad5affc..6d4b04892 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1322,10 +1322,10 @@ static void Primary (ExprDesc* E) E->Name = (uintptr_t) Sym->Name; } else { /* Undeclared Variable */ + Error ("Undeclared identifier '%s'", Ident); Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0); E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->Type = type_int; - Error ("Undefined symbol: '%s'", Ident); } } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a7436b6b6..ee8e0bbf8 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1484,7 +1484,7 @@ void MakeZPSym (const char* Name) if (Entry) { Entry->Flags |= SC_ZEROPAGE; } else { - Error ("Undefined symbol: '%s'", Name); + Error ("Undeclared symbol: '%s'", Name); } } diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref index fa584f307..728cc0e15 100644 --- a/test/ref/custom-reference-error.cref +++ b/test/ref/custom-reference-error.cref @@ -1,5 +1,5 @@ custom-reference-error.c:18: Error: Call to undeclared function 'printf' -custom-reference-error.c:19: Error: Undefined symbol: 'n' +custom-reference-error.c:19: Error: Undeclared identifier 'n' custom-reference-error.c:21: Warning: Control reaches end of non-void function [-Wreturn-type] custom-reference-error.c:21: Warning: Parameter 'argc' is never used custom-reference-error.c:21: Warning: Parameter 'argv' is never used From 546be1d5ddb1238edd891f96a0d4d64436879e8d Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 27 Nov 2023 20:42:50 +0800 Subject: [PATCH 383/520] Fixed assertion failure when there is an undefined symbol used in a parameter list. --- src/cc65/declare.c | 2 ++ src/cc65/symtab.c | 13 +++++++++++-- src/cc65/symtab.h | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4bfd52c5e..376f25845 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1850,6 +1850,7 @@ static FuncDesc* ParseFuncDecl (void) } /* Parse params */ + PushLexicalLevel (LEX_LEVEL_PARAM_LIST); if ((F->Flags & FD_OLDSTYLE) == 0) { /* New-style function */ ParseAnsiParamList (F); @@ -1857,6 +1858,7 @@ static FuncDesc* ParseFuncDecl (void) /* Old-style function */ ParseOldStyleParamList (F); } + PopLexicalLevel (); /* Remember the last function parameter. We need it later for several ** purposes, for example when passing stuff to fastcall functions. Since diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a7436b6b6..47e4b3153 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1221,7 +1221,14 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs ident Ident; /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + SymEntry* Entry; + + /* HACK: only allows to add parameter symbols in a parameter list */ + if ((Flags & SC_PARAM) == 0 && GetLexicalLevel () == LEX_LEVEL_PARAM_LIST) { + return 0; + } + + Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { int CheckExtern = 0; @@ -1419,7 +1426,9 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Add an alias of the global symbol to the local symbol table */ if (Tab == SymTab0 && SymTab != SymTab0 && Entry->Owner != SymTab && Alias == 0) { Alias = AddLocalSym (Name, T, SC_ALIAS, 0); - Alias->V.A.Field = Entry; + if (Alias != 0) { + Alias->V.A.Field = Entry; + } } /* Return the entry */ diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index b711fe606..38edddcb0 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -78,6 +78,7 @@ struct LexicalLevel { #define LEX_LEVEL_FUNCTION 2U #define LEX_LEVEL_BLOCK 3U #define LEX_LEVEL_STRUCT 4U +#define LEX_LEVEL_PARAM_LIST 5U /* HACK for error recovery */ /* Forwards */ struct FuncDesc; From 6434176909002b3183a7c82d2909d8c740407bf4 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 29 Nov 2023 12:27:01 +0800 Subject: [PATCH 384/520] Fixed constness of bit-fields. --- src/cc65/datatype.c | 4 ++-- src/cc65/datatype.h | 4 ++-- src/cc65/symtab.c | 1 + test/err/bug2018-bitfield.c | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 test/err/bug2018-bitfield.c diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 81727e491..9e0652526 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -497,8 +497,8 @@ Type* NewPointerTo (const Type* T) Type* NewBitFieldOf (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. +/* Return a type string that is "unqualified T : BitWidth" aligned on BitOffs. +** The type string is allocated on the heap and may be freed after use. */ { Type* P; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 0890c4d12..4f4b6f25e 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -435,8 +435,8 @@ Type* NewPointerTo (const Type* T); */ Type* NewBitFieldOf (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. +/* Return a type string that is "unqualified T : BitWidth" aligned on BitOffs. +** The type string is allocated on the heap and may be freed after use. */ const Type* AddressOf (const Type* T); diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index d30e591c9..a4ca9e23d 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1023,6 +1023,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, } else { Entry->Type = NewBitFieldOf (T, BitOffs, BitWidth); } + Entry->Type[0].C |= GetQualifier (T) & T_MASK_QUAL; /* Add the entry to the symbol table */ AddSymEntry (FieldTab, Entry); diff --git a/test/err/bug2018-bitfield.c b/test/err/bug2018-bitfield.c new file mode 100644 index 000000000..ea2928659 --- /dev/null +++ b/test/err/bug2018-bitfield.c @@ -0,0 +1,14 @@ +/* Bug #2018 - Compiler has problems with const struct fields */ + +typedef union U { + int a : 16; + const int b : 16; +} U; + +int main(void) +{ + U x = { 42 }; + x.b = 0; + + return 0; +} From c0a2021d9ae8a68989a873452085eda76b6b7a39 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 30 Nov 2023 00:35:30 +0800 Subject: [PATCH 385/520] Fixed endlessly repeated disgnostics when there are some certain patterns of syntax errors in a struct/union declaration. --- src/cc65/compile.c | 44 ++++++++------------- src/cc65/declare.c | 95 ++++++++++++++++++++++++++++++++++------------ src/cc65/declare.h | 21 +++++++--- 3 files changed, 102 insertions(+), 58 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 02d37c53e..bef55e375 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -257,6 +257,7 @@ static void Parse (void) /* Parse the initialization */ ParseInit (Sym->Type); + } else { /* This is a declaration */ @@ -326,21 +327,19 @@ NextDecl: } } - /* Function declaration? */ - if (Sym && IsTypeFunc (Sym->Type)) { - - /* Function */ - if (CurTok.Tok == TOK_SEMI) { - /* Prototype only */ - NeedClean = 0; - NextToken (); - } else if (CurTok.Tok == TOK_LCURLY) { - /* ISO C: The type category in a function definition cannot be - ** inherited from a typedef. - */ + /* Finish the declaration */ + if (Sym) { + /* Function definition? */ + if (IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) { if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) { + /* ISO C: The type category in a function definition cannot be + ** inherited from a typedef. + */ Error ("Function cannot be defined with a typedef"); } else if (comma) { + /* ISO C: A function definition cannot shall its return type + ** specifier with other declarators. + */ Error ("';' expected after top level declarator"); } @@ -350,30 +349,19 @@ NextDecl: /* Make sure we aren't omitting any work */ CheckDeferredOpAllDone (); - } - - } else { - - if (Sym) { + } else { /* Must be followed by a semicolon */ - if (CurTok.Tok != TOK_SEMI) { + if (ConsumeSemi ()) { + NeedClean = 0; + } else { NeedClean = -1; } - ConsumeSemi (); } - } /* Try some smart error recovery */ if (PrevErrorCount != ErrorCount && NeedClean < 0) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_SEMI, TOK_RCURLY }; - - SmartErrorSkip (); - SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); - if (CurTok.Tok == TOK_SEMI || CurTok.Tok == TOK_RCURLY) { - NextToken (); - } + SmartErrorSkip (1); } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 45d031ff2..b6620785f 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -117,11 +117,22 @@ static int CloseBrace (Collection* C, token_t Tok) -int SmartErrorSkip (void) -/* Try some smart error recovery. Skip tokens until either a comma or semicolon -** that is not enclosed in an open parenthesis/bracket/curly brace, or until an -** unpaired right parenthesis/bracket/curly brace is reached. Return 0 if it is -** the former case, or -1 if it is the latter case. */ +int SmartErrorSkip (int WholeDecl) +/* Try some smart error recovery. +** +** - If WholeDecl is 0: +** Skip tokens until a comma or closing curly brace that is not enclosed in +** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or +** unpaired right parenthesis/bracket/curly brace is reached. +** +** - If WholeDecl is non-0: +** Skip tokens until a closing curly brace that is not enclosed in an open +** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached. +** +** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if +** this exits with no open parentheses/brackets/curly braces. Otherwise, return +** -1. +*/ { Collection C = AUTO_COLLECTION_INITIALIZER; int Res = 0; @@ -142,31 +153,41 @@ int SmartErrorSkip (void) case TOK_RPAREN: case TOK_RBRACK: - if (CloseBrace (&C, CurTok.Tok)) { - Res = -1; - goto ExitPoint; + if (CloseBrace (&C, CurTok.Tok) < 0) { + if (!WholeDecl) { + Res = -1; + goto ExitPoint; + } + NextToken (); } break; case TOK_RCURLY: - if (CloseBrace (&C, CurTok.Tok)) { - Res = -1; - goto ExitPoint; + if (CloseBrace (&C, CurTok.Tok) < 0) { + if (!WholeDecl) { + Res = -1; + goto ExitPoint; + } + NextToken (); } else if (CollCount (&C) == 0) { goto ExitPoint; } break; case TOK_COMMA: - if (CollCount (&C) == 0) { + if (CollCount (&C) == 0 && !WholeDecl) { goto ExitPoint; } NextToken (); break; case TOK_SEMI: + if (CollCount (&C) != 0) { + Res = -1; + } + goto ExitPoint; + case TOK_CEOF: - Res = -1; goto ExitPoint; default: @@ -1058,8 +1079,9 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) while (CurTok.Tok != TOK_RCURLY) { /* Get the type of the entry */ - DeclSpec Spec; - int SignednessSpecified = 0; + DeclSpec Spec; + int SignednessSpecified = 0; + int NeedClean = 0; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { @@ -1076,7 +1098,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) Declarator Decl; /* Get type and name of the struct field */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); /* Check for a bit-field declaration */ FieldWidth = ParseFieldWidth (&Decl); @@ -1172,7 +1194,18 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { } NextToken (); } - ConsumeSemi (); + + /* Must be followed by a semicolon */ + if (NeedClean >= 0 && ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } } /* Skip the closing brace */ @@ -1236,8 +1269,9 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) while (CurTok.Tok != TOK_RCURLY) { /* Get the type of the entry */ - DeclSpec Spec; - int SignednessSpecified = 0; + DeclSpec Spec; + int SignednessSpecified = 0; + int NeedClean = 0; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { @@ -1262,7 +1296,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } /* Get type and name of the struct field */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); /* Check for a bit-field declaration */ FieldWidth = ParseFieldWidth (&Decl); @@ -1403,7 +1437,18 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { } NextToken (); } - ConsumeSemi (); + + /* Must be followed by a semicolon */ + if (NeedClean >= 0 && ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } } if (BitOffs > 0) { @@ -1778,7 +1823,7 @@ static void ParseOldStyleParamList (FuncDesc* F) Error ("Identifier expected for parameter name"); /* Try some smart error recovery */ - if (SmartErrorSkip () < 0) { + if (SmartErrorSkip (0) < 0) { break; } } @@ -1868,7 +1913,7 @@ static void ParseOldStyleParamList (FuncDesc* F) if (PrevErrorCount != ErrorCount && CurTok.Tok != TOK_LCURLY) { /* Try some smart error recovery */ - SmartErrorSkip (); + SmartErrorSkip (0); } } @@ -1953,7 +1998,7 @@ static void ParseAnsiParamList (FuncDesc* F) if (PrevErrorCount != ErrorCount) { /* Try some smart error recovery */ - if (SmartErrorSkip () < 0) { + if (SmartErrorSkip (0) < 0) { break; } } @@ -2335,7 +2380,7 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Try some smart error recovery */ if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { - return SmartErrorSkip (); + return SmartErrorSkip (0); } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index add86594d..1ce764f7a 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -128,11 +128,22 @@ typedef enum { -int SmartErrorSkip (void); -/* Try some smart error recovery. Skip tokens until either a comma or semicolon -** that is not enclosed in an open parenthesis/bracket/curly brace, or until an -** unpaired right parenthesis/bracket/curly brace is reached. Return 0 if it is -** the former case, or -1 if it is the latter case. */ +int SmartErrorSkip (int WholeDecl); +/* Try some smart error recovery. +** +** - If WholeDecl is 0: +** Skip tokens until a comma or closing curly brace that is not enclosed in +** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or +** unpaired right parenthesis/bracket/curly brace is reached. +** +** - If WholeDecl is non-0: +** Skip tokens until a closing curly brace that is not enclosed in an open +** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached. +** +** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if +** this exits with no open parentheses/brackets/curly braces. Otherwise, return +** -1. +*/ Type* ParseType (Type* Type); /* Parse a complete type specification */ From 47e7ed2f560c991959e4d3c09254e820ecd1fe25 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 30 Nov 2023 00:36:13 +0800 Subject: [PATCH 386/520] Fixed wrong "Mixed declarations and code are not supported in cc65" error message when it should be "Expression expected". --- src/cc65/expr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 6d4b04892..54984230c 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1412,9 +1412,9 @@ static void Primary (ExprDesc* E) } else { /* Let's see if this is a C99-style declaration */ DeclSpec Spec; - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); - if (Spec.Type->C != T_END) { + if ((Spec.Flags & DS_DEF_TYPE) == 0) { /* Recognized but not supported */ Error ("Mixed declarations and code are not supported in cc65"); From d8a3938f2b8d6c53c2f75b5a07964e8beef746a5 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 13 Nov 2023 20:24:14 +0100 Subject: [PATCH 387/520] Optimize a bit static long assignation --- src/Makefile | 2 +- src/cc65.vcxproj | 4 +- src/cc65/codegen.c | 42 ++------- src/cc65/codeopt.c | 7 ++ src/cc65/coptlong.c | 210 +++++++++++++++++++++++++++++++++++++++++ src/cc65/coptlong.h | 63 +++++++++++++ test/val/long.c | 41 ++++++++ test/val/static-long.c | 41 ++++++++ 8 files changed, 374 insertions(+), 36 deletions(-) create mode 100644 src/cc65/coptlong.c create mode 100644 src/cc65/coptlong.h create mode 100644 test/val/long.c create mode 100644 test/val/static-long.c diff --git a/src/Makefile b/src/Makefile index 034a2230f..4b993c881 100644 --- a/src/Makefile +++ b/src/Makefile @@ -66,7 +66,7 @@ ifndef BUILD_ID endif $(info BUILD_ID: $(BUILD_ID)) -CFLAGS += -MMD -MP -O3 -I common \ +CFLAGS += -MMD -MP -O0 -g -I common \ -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS) \ -DCA65_INC="\"$(CA65_INC)\"" -DCC65_INC="\"$(CC65_INC)\"" -DCL65_TGT="\"$(CL65_TGT)\"" \ -DLD65_LIB="\"$(LD65_LIB)\"" -DLD65_OBJ="\"$(LD65_OBJ)\"" -DLD65_CFG="\"$(LD65_CFG)\"" \ diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 6f3f8e4e4..9c1719538 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -69,6 +69,7 @@ <ClInclude Include="cc65\coptcmp.h" /> <ClInclude Include="cc65\coptind.h" /> <ClInclude Include="cc65\coptjmp.h" /> + <ClInclude Include="cc65\coptlong.h" /> <ClInclude Include="cc65\coptmisc.h" /> <ClInclude Include="cc65\coptptrload.h" /> <ClInclude Include="cc65\coptptrstore.h" /> @@ -150,6 +151,7 @@ <ClCompile Include="cc65\coptcmp.c" /> <ClCompile Include="cc65\coptind.c" /> <ClCompile Include="cc65\coptjmp.c" /> + <ClCompile Include="cc65\coptlong.c" /> <ClCompile Include="cc65\coptmisc.c" /> <ClCompile Include="cc65\coptptrload.c" /> <ClCompile Include="cc65\coptptrstore.c" /> @@ -214,4 +216,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 2721f8e96..db487ca40 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -705,7 +705,6 @@ void g_getimmed (unsigned Flags, uintptr_t Val, long Offs) /* Load a constant into the primary register */ { unsigned char B1, B2, B3, B4; - unsigned Done; if ((Flags & CF_CONST) != 0) { @@ -731,40 +730,15 @@ void g_getimmed (unsigned Flags, uintptr_t Val, long Offs) B3 = (unsigned char) (Val >> 16); B4 = (unsigned char) (Val >> 24); - /* Remember which bytes are done */ - Done = 0; - - /* Load the value */ - AddCodeLine ("ldx #$%02X", B2); - Done |= 0x02; - if (B2 == B3) { - AddCodeLine ("stx sreg"); - Done |= 0x04; - } - if (B2 == B4) { - AddCodeLine ("stx sreg+1"); - Done |= 0x08; - } - if ((Done & 0x04) == 0 && B1 != B3) { - AddCodeLine ("lda #$%02X", B3); - AddCodeLine ("sta sreg"); - Done |= 0x04; - } - if ((Done & 0x08) == 0 && B1 != B4) { - AddCodeLine ("lda #$%02X", B4); - AddCodeLine ("sta sreg+1"); - Done |= 0x08; - } + /* Load the value. Don't be too smart here and let + * the optimizer do its job. + */ + AddCodeLine ("lda #$%02X", B4); + AddCodeLine ("sta sreg+1"); + AddCodeLine ("lda #$%02X", B3); + AddCodeLine ("sta sreg"); AddCodeLine ("lda #$%02X", B1); - Done |= 0x01; - if ((Done & 0x04) == 0) { - CHECK (B1 == B3); - AddCodeLine ("sta sreg"); - } - if ((Done & 0x08) == 0) { - CHECK (B1 == B4); - AddCodeLine ("sta sreg+1"); - } + AddCodeLine ("ldx #$%02X", B2); break; default: diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 3a2c2a35d..c9c1592bc 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -57,6 +57,7 @@ #include "coptcmp.h" #include "coptind.h" #include "coptjmp.h" +#include "coptlong.h" #include "coptmisc.h" #include "coptptrload.h" #include "coptptrstore.h" @@ -150,6 +151,8 @@ static OptFunc DOptJumpTarget3 = { OptJumpTarget3, "OptJumpTarget3", 100, 0, static OptFunc DOptLoad1 = { OptLoad1, "OptLoad1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptLoad2 = { OptLoad2, "OptLoad2", 200, 0, 0, 0, 0, 0 }; static OptFunc DOptLoad3 = { OptLoad3, "OptLoad3", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptLongAssign = { OptLongAssign, "OptLongAssign", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptLongCopy = { OptLongCopy, "OptLongCopy", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX1 = { OptNegAX1, "OptNegAX1", 165, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX2 = { OptNegAX2, "OptNegAX2", 200, 0, 0, 0, 0, 0 }; static OptFunc DOptPrecalc = { OptPrecalc, "OptPrecalc", 100, 0, 0, 0, 0, 0 }; @@ -262,6 +265,8 @@ static OptFunc* OptFuncs[] = { &DOptLoad1, &DOptLoad2, &DOptLoad3, + &DOptLongAssign, + &DOptLongCopy, &DOptNegAX1, &DOptNegAX2, &DOptPrecalc, @@ -632,6 +637,7 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptAdd6, 1); Changes += RunOptFunc (S, &DOptSub1, 1); Changes += RunOptFunc (S, &DOptSub3, 1); + Changes += RunOptFunc (S, &DOptLongAssign, 1); Changes += RunOptFunc (S, &DOptStore4, 1); Changes += RunOptFunc (S, &DOptStore5, 1); Changes += RunOptFunc (S, &DOptShift1, 1); @@ -641,6 +647,7 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptStore1, 1); Changes += RunOptFunc (S, &DOptStore2, 5); Changes += RunOptFunc (S, &DOptStore3, 5); + Changes += RunOptFunc (S, &DOptLongCopy, 1); /* Return the number of changes */ return Changes; diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c new file mode 100644 index 000000000..16f089e49 --- /dev/null +++ b/src/cc65/coptlong.c @@ -0,0 +1,210 @@ +/*****************************************************************************/ +/* */ +/* coptlong.c */ +/* */ +/* Long integers optimizations */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* (C) 2023, Colin Leroy-Mira <colin@colino.net */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* common */ +#include "cpu.h" + +/* cc65 */ +#include "codeent.h" +#include "coptind.h" +#include "codeinfo.h" +#include "codeopt.h" +#include "error.h" + + +/*****************************************************************************/ +/* Remove unused loads and stores */ +/*****************************************************************************/ + + + +unsigned OptLongAssign (CodeSeg* S) +/* Simplify long assignments. +** Recognize +** lda ... 0 +** sta sreg+1 1 +** lda ... 2 +** sta sreg 3 +** lda ... 4 +** ldx ... 5 +** sta M0002 6 +** stx M0002+1 7 +** ldy sreg 8 +** sty M0002+2 9 +** ldy sreg+1 10 +** sty M0002+3 11 +** and simplify if not used right after. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[12]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + if (CS_GetEntries (S, L+1, I+1, 11)) { + if (L[0]->OPC == OP65_LDA && + L[1]->OPC == OP65_STA && + !strcmp (L[1]->Arg, "sreg+1") && + L[2]->OPC == OP65_LDA && + L[3]->OPC == OP65_STA && + !strcmp (L[3]->Arg, "sreg") && + L[4]->OPC == OP65_LDA && + L[5]->OPC == OP65_LDX && + L[6]->OPC == OP65_STA && + L[7]->OPC == OP65_STX && + !strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") && + L[8]->OPC == OP65_LDY && + !strcmp (L[8]->Arg, "sreg") && + L[9]->OPC == OP65_STY && + !strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") && + L[10]->OPC == OP65_LDY && + !strcmp (L[10]->Arg, "sreg+1") && + L[11]->OPC == OP65_STY && + !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") && + !RegXUsed (S, I+11)) { + + L[1]->AM = L[11]->AM; + CE_SetArg(L[1], L[11]->Arg); + + L[3]->AM = L[9]->AM; + CE_SetArg(L[3], L[9]->Arg); + + CS_DelEntries (S, I+8, 4); + + /* Remember, we had changes */ + ++Changes; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + +unsigned OptLongCopy (CodeSeg* S) +/* Simplify long copies. +** Recognize +** lda XXX+3 0 +** sta sreg+1 1 +** lda XXX+2 2 +** sta sreg 3 +** ldx XXX+1 4 +** lda XXX 5 +** sta YYY 6 +** stx YYY+1 7 +** ldy sreg 8 +** sty YYY+2 9 +** ldy sreg+1 10 +** sty YYY+3 11 +** and simplify if not used right after. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[12]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + if (CS_GetEntries (S, L+1, I+1, 11)) { + if (L[0]->OPC == OP65_LDA && + !strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && + !strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") && + L[1]->OPC == OP65_STA && + !strcmp (L[1]->Arg, "sreg+1") && + L[2]->OPC == OP65_LDA && + !strncmp(L[2]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && + !strcmp(L[2]->Arg + strlen(L[5]->Arg), "+2") && + L[3]->OPC == OP65_STA && + !strcmp (L[3]->Arg, "sreg") && + L[4]->OPC == OP65_LDX && + !strncmp(L[4]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && + !strcmp(L[4]->Arg + strlen(L[5]->Arg), "+1") && + L[5]->OPC == OP65_LDA && + L[6]->OPC == OP65_STA && + L[7]->OPC == OP65_STX && + !strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") && + L[8]->OPC == OP65_LDY && + !strcmp (L[8]->Arg, "sreg") && + L[9]->OPC == OP65_STY && + !strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") && + L[10]->OPC == OP65_LDY && + !strcmp (L[10]->Arg, "sreg+1") && + L[11]->OPC == OP65_STY && + !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") && + !RegXUsed (S, I+11)) { + + L[1]->AM = L[11]->AM; + CE_SetArg(L[1], L[11]->Arg); + + L[3]->AM = L[9]->AM; + CE_SetArg(L[3], L[9]->Arg); + + CS_DelEntries (S, I+8, 4); + + /* Remember, we had changes */ + ++Changes; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptlong.h b/src/cc65/coptlong.h new file mode 100644 index 000000000..c0233c299 --- /dev/null +++ b/src/cc65/coptlong.h @@ -0,0 +1,63 @@ +/*****************************************************************************/ +/* */ +/* coptlong.h */ +/* */ +/* Long integers optimizations */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* (C) 2023, Colin Leroy-Mira <colin@colino.net */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTLONG_H +#define COPTLONG_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptLongAssign (CodeSeg* S); +/* Simplify long assigns. */ + +unsigned OptLongCopy (CodeSeg* S); +/* Simplify long copy. */ + + + +/* End of coptind.h */ + +#endif diff --git a/test/val/long.c b/test/val/long.c new file mode 100644 index 000000000..076483c9b --- /dev/null +++ b/test/val/long.c @@ -0,0 +1,41 @@ +#include <stdint.h> +#include <stdio.h> + +int res = 0; + +int main(void) +{ + long a, b; + + a = 0x12345678L; + + /* Test assignment */ + b = a; + if (b != a) { + res++; + } + + /* Test increment */ + b++; + if (b != 0x12345679L) { + res++; + } + + /* Test decrement */ + b--; + if (b != 0x12345678L) { + res++; + } + + /* Test pre-decrement with test */ + if (--b != 0x12345677L) { + res++; + } + + a = --b; + if (a != 0x12345676L) { + res++; + } + + return res; +} diff --git a/test/val/static-long.c b/test/val/static-long.c new file mode 100644 index 000000000..a2e4e53a3 --- /dev/null +++ b/test/val/static-long.c @@ -0,0 +1,41 @@ +#include <stdint.h> +#include <stdio.h> + +int res = 0; + +int main(void) +{ + static long a, b; + + a = 0x12345678L; + + /* Test assignment */ + b = a; + if (b != a) { + res++; + } + + /* Test increment */ + b++; + if (b != 0x12345679L) { + res++; + } + + /* Test decrement */ + b--; + if (b != 0x12345678L) { + res++; + } + + /* Test pre-decrement with test */ + if (--b != 0x12345677L) { + res++; + } + + a = --b; + if (a != 0x12345676L) { + res++; + } + + return res; +} From 63861766e1fc9907536ab4f211652c62b908faba Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 1 Dec 2023 14:22:30 +0100 Subject: [PATCH 388/520] Fix Makefile change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 4b993c881..034a2230f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -66,7 +66,7 @@ ifndef BUILD_ID endif $(info BUILD_ID: $(BUILD_ID)) -CFLAGS += -MMD -MP -O0 -g -I common \ +CFLAGS += -MMD -MP -O3 -I common \ -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS) \ -DCA65_INC="\"$(CA65_INC)\"" -DCC65_INC="\"$(CC65_INC)\"" -DCL65_TGT="\"$(CL65_TGT)\"" \ -DLD65_LIB="\"$(LD65_LIB)\"" -DLD65_OBJ="\"$(LD65_OBJ)\"" -DLD65_CFG="\"$(LD65_CFG)\"" \ From b7e7bb7489bb1509b4c67a8d0242a690f3a3a2c3 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 9 Dec 2023 14:34:37 +0800 Subject: [PATCH 389/520] Fixed the issue that qualifiers of pointees of function parameters were ignored for type compatibility check. --- src/cc65/typecmp.c | 8 +++++--- src/cc65/typecmp.h | 12 +++++++----- test/err/bug2286-param-qualifier.c | 4 ++++ 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 test/err/bug2286-param-qualifier.c diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index bb8bca880..e8b790a69 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -69,6 +69,7 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) /* Get the symbol types */ const Type* Type1 = Sym1->Type; const Type* Type2 = Sym2->Type; + typecmp_t CmpResult; /* If either of both functions is old style, apply the default ** promotions to the parameter type. @@ -84,9 +85,10 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) } } - /* Compare this field */ - if (TypeCmp (Type1, Type2).C < TC_EQUAL) { - /* Field types not equal */ + /* Compare types of this parameter */ + CmpResult = TypeCmp (Type1, Type2); + if (CmpResult.C < TC_EQUAL || (CmpResult.F & TCF_MASK_PARAM_DIFF) != 0) { + /* The types are not compatible */ return 0; } diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index fa97ca176..43acc7ea5 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -68,15 +68,17 @@ typedef enum { TCF_VOID_PTR_ON_LEFT = 0x01, /* lhs is a void pointer */ TCF_VOID_PTR_ON_RIGHT = 0x02, /* rhs is a void pointer */ TCF_MASK_VOID_PTR = TCF_VOID_PTR_ON_LEFT | TCF_VOID_PTR_ON_RIGHT, - TCF_QUAL_DIFF = 0x04, /* CVR qualifiers differ in a way that doesn't matter */ + TCF_QUAL_DIFF = 0x04, /* lhs doesn't have all of CVR qualifiers of rhs */ TCF_QUAL_IMPLICIT = 0x08, /* CVR qualifiers of lhs are stricter than those of rhs */ - TCF_PTR_QUAL_DIFF = 0x10, /* CVR qualifiers of pointers differ */ - TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointers are stricter on lhs than those on rhs */ - TCF_MASK_C_QUAL_DIFF = 0x3C, /* All C Standard qualifiers */ + TCF_MASK_CVR_DIFF = 0x0C, /* All CVR qualifiers */ + TCF_PTR_QUAL_DIFF = 0x10, /* lhs pointee doesn't have all of CVR qualifiers of rhs pointee */ + TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointees are stricter on lhs than those on rhs */ + TCF_MASK_PTR_QUAL_DIFF = 0x30, /* All CVR qualifiers of pointees */ TCF_ADDRSIZE_QUAL_DIFF = 0x40, /* Address size qualifiers differ */ TCF_CCONV_QUAL_DIFF = 0x80, /* Function calling conventions differ. Unused now */ TCF_INCOMPATIBLE_QUAL = TCF_ADDRSIZE_QUAL_DIFF | TCF_CCONV_QUAL_DIFF, - TCF_MASK_QUAL = TCF_MASK_C_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, + TCF_MASK_PARAM_DIFF = TCF_MASK_PTR_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, + TCF_MASK_QUAL = TCF_MASK_CVR_DIFF | TCF_MASK_PTR_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, } typecmpflag_t; typedef struct { diff --git a/test/err/bug2286-param-qualifier.c b/test/err/bug2286-param-qualifier.c new file mode 100644 index 000000000..a014d0a0c --- /dev/null +++ b/test/err/bug2286-param-qualifier.c @@ -0,0 +1,4 @@ +/* Bug #2286 - Qualifiers of pointees of function parameters ignored for type compatibility check */ + +void woo(int* p); +void woo(const int* p); /* WRONG: Should be an error */ From 98ffc031d16efe00b4338e79f903382aa54af957 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 9 Dec 2023 14:35:00 +0800 Subject: [PATCH 390/520] Fixed an iteration bug in type composition. --- src/cc65/expr.c | 3 ++- src/cc65/typeconv.c | 7 ------- test/err/bug2285-composite-type.c | 5 +++++ 3 files changed, 7 insertions(+), 8 deletions(-) create mode 100644 test/err/bug2285-composite-type.c diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 54984230c..a87335f42 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -4102,9 +4102,10 @@ static void hieQuest (ExprDesc* Expr) /* Avoid further errors */ ResultType = NewPointerTo (type_void); } else { - /* Result has the composite type */ + /* Result has the properly qualified composite type */ ResultType = TypeDup (Expr2.Type); TypeComposition (ResultType, Expr3.Type); + ResultType[1].C |= GetQualifier (Indirect (Expr3.Type)); } } } else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) { diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index e5b054148..e5b6749d6 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -485,13 +485,6 @@ void TypeComposition (Type* lhs, const Type* rhs) } else if (RightCount != UNSPECIFIED) { SetElementCount (lhs, RightCount); } - } else { - /* Combine the qualifiers */ - if (IsClassPtr (lhs)) { - ++lhs; - ++rhs; - lhs->C |= GetQualifier (rhs); - } } /* Next type string element */ diff --git a/test/err/bug2285-composite-type.c b/test/err/bug2285-composite-type.c new file mode 100644 index 000000000..18a7b80a5 --- /dev/null +++ b/test/err/bug2285-composite-type.c @@ -0,0 +1,5 @@ +/* Bug #2285 - Regression in type composition */ + +void foo(); /* OK */ +void foo(int (*)(int)); /* OK */ +void foo(int (*)(long)); /* WRONG: Should be an error */ From 87f8893886570364ce18776490f470232860772e Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 9 Dec 2023 17:33:46 +0800 Subject: [PATCH 391/520] Avoided "Variable 'XXX' is defined but never used" error message resulted from an earlier error. --- src/cc65/symtab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a4ca9e23d..86a92a019 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -178,7 +178,7 @@ static void CheckSymTable (SymTable* Tab) if (IS_Get (&WarnUnusedFunc)) { Warning ("Function '%s' is defined but never used", Entry->Name); } - } else { + } else if (!IsAnonName (Entry->Name)) { if (IS_Get (&WarnUnusedVar)) { Warning ("Variable '%s' is defined but never used", Entry->Name); } From d8a722b63826ea4a1df2bb7a7f4e1197f7707117 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 9 Dec 2023 17:34:01 +0800 Subject: [PATCH 392/520] Improved diagnostics on multiple definition of struct/union types. --- src/cc65/declare.c | 10 ---------- src/cc65/symtab.c | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index b6620785f..96e1f1074 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1220,11 +1220,6 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { Flags |= SC_FICTITIOUS; } - /* Empty union is not supported now */ - if (UnionSize == 0) { - Error ("Empty union type '%s' is not supported", Name); - } - /* Make a real entry from the forward decl and return it */ return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab, DSFlags); } @@ -1471,11 +1466,6 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { Flags |= SC_FICTITIOUS; } - /* Empty struct is not supported now */ - if (StructSize == 0) { - Error ("Empty struct type '%s' is not supported", Name); - } - /* Make a real entry from the forward decl and return it */ return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab, DSFlags); } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 86a92a019..1b5c1e0b5 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -919,14 +919,8 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl /* SCType must be struct or union */ PRECONDITION (SCType == SC_STRUCT || SCType == SC_UNION); - if ((Flags & SC_FICTITIOUS) == 0) { - /* Do we have an entry with this name already? */ - TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name)); - } else { - /* Add a fictitious symbol in the fail-safe table */ - TagEntry = 0; - CurTagTab = FailSafeTab; - } + /* Do we have an entry with this name already? */ + TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name)); if (TagEntry) { @@ -954,6 +948,15 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl if (DSFlags != 0) { *DSFlags |= DS_NEW_TYPE_DEF; } + + if ((Flags & SC_FICTITIOUS) == SC_FICTITIOUS) { + /* Add a fictitious symbol in the fail-safe table */ + TagEntry = 0; + } else if (Size == 0) { + /* Empty struct is not supported now */ + Error ("Empty %s type '%s' is not supported", SCType == SC_STRUCT ? "struct" : "union", Name); + TagEntry = 0; + } } } From d8e61552be196bee3bb7fca6d23d92b1a3ffedc4 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 9 Dec 2023 18:04:29 +0800 Subject: [PATCH 393/520] Removed outdated testcases no longer in the directory from test/misc/Makefile. --- test/misc/Makefile | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/test/misc/Makefile b/test/misc/Makefile index c708b160b..811f7f462 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -58,24 +58,6 @@ $(ISEQUAL): ../isequal.c | $(WORKDIR) define PRG_template -# should compile, but gives an error -$(WORKDIR)/int-static-1888.$1.$2.prg: int-static-1888.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/int-static-1888.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -# should compile, but gives an error -$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(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) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." @@ -106,18 +88,6 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) $(if $(QUIET),echo misc/pptest2.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# should compile, but gives an error -$(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(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) - # 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." From 2a2cc6cad69dd83ebd02b314f253119461ec7cfb Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sat, 9 Dec 2023 16:43:23 +0100 Subject: [PATCH 394/520] Fix bug introduced in #2260 bne should have applied to A, not X, but adding a cmp #$00 before makes the change less optimized than the existing. --- src/cc65/codegen.c | 8 +------- test/val/sub3.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index db487ca40..c2bdfcd63 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -3697,13 +3697,7 @@ void g_dec (unsigned flags, unsigned long val) } else { /* Inline the code */ if (val < 0x300) { - if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && val == 1) { - unsigned L = GetLocalLabel(); - AddCodeLine ("bne %s", LocalLabelName (L)); - AddCodeLine ("dex"); - g_defcodelabel (L); - AddCodeLine ("dea"); - } else if ((val & 0xFF) != 0) { + if ((val & 0xFF) != 0) { unsigned L = GetLocalLabel(); AddCodeLine ("sec"); AddCodeLine ("sbc #$%02X", (unsigned char) val); diff --git a/test/val/sub3.c b/test/val/sub3.c index 2a3646f9a..dd050224e 100644 --- a/test/val/sub3.c +++ b/test/val/sub3.c @@ -168,6 +168,31 @@ void post_dec_assign_test(void) failures++; } +void dex_tests(void) { + static unsigned int a, b; + + a = 257; + b = a - 1; + if (b != 256) { + printf("fail 257 => 256\n"); + failures++; + } + + a = 256; + b = a - 1; + if (b != 255) { + printf("fail 256 => 255\n"); + failures++; + } + + a = 255; + b = a - 1; + if (b != 254) { + printf("fail 255 => 254\n"); + failures++; + } +} + int main(void) { int0 = 5; @@ -186,6 +211,8 @@ int main(void) int1 = 5; post_dec_assign_test(); + dex_tests(); + printf("failures: %d\n",failures); return failures; From b66682a05b01ed0c1bae4e51758e26b4257ab6db Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 00:47:10 +0800 Subject: [PATCH 395/520] Fixed wrapped call when the function to wrap has already got defined before it is wrapped with the pragma. --- doc/cc65.sgml | 9 +++++++-- src/cc65/declare.c | 22 ---------------------- src/cc65/expr.c | 14 +++++--------- src/cc65/funcdesc.c | 2 -- src/cc65/funcdesc.h | 2 -- src/cc65/symentry.h | 2 ++ src/cc65/symtab.c | 19 +++++++++++++------ test/val/trampoline.c | 14 +++++++------- 8 files changed, 34 insertions(+), 50 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 37e3e493d..efe48b61b 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -1613,13 +1613,13 @@ parameter with the <tt/#pragma/. This pragma sets a wrapper for functions, often used for trampolines. - The name is a function returning <tt/void/, and taking no parameters. + The <tt/name/ is a wrapper function returning <tt/void/, and taking no parameters. It must preserve the CPU's <tt/A/ and <tt/X/ registers if it wraps any <tt/__fastcall__/ functions that have parameters. It must preserve the <tt/Y/ register if it wraps any variadic functions (they have "<tt/.../" in their prototypes). - The identifier is an 8-bit number that's set into <tt/tmp4/. If the identifier + The <tt/identifier/ is an 8-bit number that's set into <tt/tmp4/. If the <tt/identifier/ is "bank", then ca65's <tt><url url="ca65.html#.BANK" name=".bank"></tt> function will be used to determine the number from the bank attribute defined in the linker config, see <url url="ld65.html#MEMORY" name="Other MEMORY area attributes">. Note that @@ -1629,6 +1629,11 @@ parameter with the <tt/#pragma/. The address of a wrapped function is passed in <tt/ptr4/. The wrapper can call that function by using "<tt/jsr callptr4/". + All functions ever declared or defined when this pragma is in effect will be wrapped + when they are called explicitly by their names later in the same translation unit. + Invocation of these functions in any other ways, for example, that via a function + pointer or in inline assembly code, will not be wrapped. + This feature is useful, for example, with banked memory, to switch banks automatically to where a wrapped function resides, and then to restore the previous bank when it returns. diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 076e94aa8..66cae8fa3 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -61,7 +61,6 @@ #include "standard.h" #include "staticassert.h" #include "symtab.h" -#include "wrappedcall.h" #include "typeconv.h" @@ -1965,9 +1964,6 @@ static void ParseAnsiParamList (FuncDesc* F) static FuncDesc* ParseFuncDecl (void) /* Parse the argument list of a function with the enclosing parentheses */ { - SymEntry* WrappedCall; - unsigned int WrappedCallData; - /* Create a new function descriptor */ FuncDesc* F = NewFuncDesc (); @@ -2023,13 +2019,6 @@ static FuncDesc* ParseFuncDecl (void) /* Leave the lexical level remembering the symbol tables */ RememberFunctionLevel (F); - /* Did we have a WrappedCall for this function? */ - GetWrappedCall((void **) &WrappedCall, &WrappedCallData); - if (WrappedCall) { - F->WrappedCall = WrappedCall; - F->WrappedCallData = WrappedCallData; - } - /* Return the function descriptor */ return F; } @@ -2099,7 +2088,6 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Function declarator */ FuncDesc* F; - SymEntry* PrevEntry; /* Parse the function declarator */ F = ParseFuncDecl (); @@ -2110,16 +2098,6 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) Qualifiers &= ~T_QUAL_FASTCALL; } - /* Was there a previous entry? If so, copy WrappedCall info from it */ - PrevEntry = FindGlobalSym (D->Ident); - if (PrevEntry && PrevEntry->Flags & SC_FUNC) { - FuncDesc* D = GetFuncDesc (PrevEntry->Type); - if (D->WrappedCall && !F->WrappedCall) { - F->WrappedCall = D->WrappedCall; - F->WrappedCallData = D->WrappedCallData; - } - } - /* Add the function type. Be sure to bounds check the type buffer */ NeedTypeSpace (D, 1); D->Type[D->Index].C = T_FUNC | Qualifiers; diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 963ea8bd6..af39a79f9 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1057,11 +1057,6 @@ static void FunctionCall (ExprDesc* Expr) /* Special handling for function pointers */ if (IsFuncPtr) { - - if (Func->WrappedCall) { - Warning ("Calling a wrapped function via a pointer, wrapped-call will not be used"); - } - /* If the function is not a fastcall function, load the pointer to ** the function into the primary. */ @@ -1110,18 +1105,18 @@ static void FunctionCall (ExprDesc* Expr) } else { /* Normal function */ - if (Func->WrappedCall) { + if (Expr->Sym && Expr->Sym->V.F.WrappedCall) { char tmp[64]; StrBuf S = AUTO_STRBUF_INITIALIZER; - if (Func->WrappedCallData == WRAPPED_CALL_USE_BANK) { + if (Expr->Sym->V.F.WrappedCallData == WRAPPED_CALL_USE_BANK) { /* Store the bank attribute in tmp4 */ SB_AppendStr (&S, "ldy #<.bank(_"); SB_AppendStr (&S, (const char*) Expr->Name); SB_AppendChar (&S, ')'); } else { /* Store the WrappedCall data in tmp4 */ - sprintf(tmp, "ldy #%u", Func->WrappedCallData); + sprintf(tmp, "ldy #%u", Expr->Sym->V.F.WrappedCallData); SB_AppendStr (&S, tmp); } g_asmcode (&S); @@ -1154,7 +1149,7 @@ static void FunctionCall (ExprDesc* Expr) SB_Done (&S); - g_call (CG_CallFlags (Expr->Type), Func->WrappedCall->Name, ArgSize); + g_call (CG_CallFlags (Expr->Type), Expr->Sym->V.F.WrappedCall->Name, ArgSize); } else { g_call (CG_CallFlags (Expr->Type), (const char*) Expr->Name, ArgSize); } @@ -1328,6 +1323,7 @@ static void Primary (ExprDesc* E) E->Type = type_int; } + E->Sym = Sym; } break; diff --git a/src/cc65/funcdesc.c b/src/cc65/funcdesc.c index 2291b35ee..de881167d 100644 --- a/src/cc65/funcdesc.c +++ b/src/cc65/funcdesc.c @@ -61,8 +61,6 @@ FuncDesc* NewFuncDesc (void) F->ParamSize = 0; F->LastParam = 0; F->FuncDef = 0; - F->WrappedCall = 0; - F->WrappedCallData = 0; /* Return the new struct */ return F; diff --git a/src/cc65/funcdesc.h b/src/cc65/funcdesc.h index e065c7602..8d21d3080 100644 --- a/src/cc65/funcdesc.h +++ b/src/cc65/funcdesc.h @@ -70,8 +70,6 @@ struct FuncDesc { unsigned ParamSize; /* Size of the parameters */ struct SymEntry* LastParam; /* Pointer to last parameter */ struct FuncDesc* FuncDef; /* Descriptor used in definition */ - struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ - unsigned int WrappedCallData; /* The WrappedCall's user data */ }; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 715c036d6..639221625 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -159,6 +159,8 @@ struct SymEntry { struct { struct SegContext* Seg; /* SegContext for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ + struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ + unsigned int WrappedCallData; /* The WrappedCall's user data */ } F; /* Label name for static symbols */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 1b5c1e0b5..3018c910d 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -61,6 +61,7 @@ #include "symentry.h" #include "typecmp.h" #include "typeconv.h" +#include "wrappedcall.h" #include "symtab.h" @@ -1407,24 +1408,30 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } if (Entry == 0) { - /* Create a new entry */ Entry = NewSymEntry (Name, Flags); /* Set the symbol attributes */ Entry->Type = TypeDup (T); - /* If this is a function, clear additional fields */ - if (IsTypeFunc (T)) { - Entry->V.F.Seg = 0; - } - /* Add the assembler name of the symbol */ SymSetAsmName (Entry); /* Add the entry to the symbol table */ AddSymEntry (Tab, Entry); + } + /* If this is a function, do we wrap calls to it? */ + if (IsTypeFunc (Entry->Type)) { + SymEntry* WrappedCall; + unsigned int WrappedCallData; + + /* Always use the latest wrapper data for it */ + GetWrappedCall ((void**)&WrappedCall, &WrappedCallData); + if (WrappedCall) { + Entry->V.F.WrappedCall = WrappedCall; + Entry->V.F.WrappedCallData = WrappedCallData; + } } /* Add an alias of the global symbol to the local symbol table */ diff --git a/test/val/trampoline.c b/test/val/trampoline.c index 8f1e1547c..d3e73b47d 100644 --- a/test/val/trampoline.c +++ b/test/val/trampoline.c @@ -22,23 +22,23 @@ void func3() { } -#pragma wrapped-call(push, trampoline_inc, 0) - void func2() { func3(); } +#pragma wrapped-call(push, trampoline_inc, 0) + +void func2(void); + #pragma wrapped-call(push, trampoline_set, 4) -void func1(void); - -#pragma wrapped-call(pop) -#pragma wrapped-call(pop) - void func1(void) { func2(); } +#pragma wrapped-call(pop) +#pragma wrapped-call(pop) + int main(void) { flag = 0; From 79b4690077cb41ff53bf48a126f48a53c8af1600 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 15:43:24 +0800 Subject: [PATCH 396/520] Fixed missing diagnostics on empty enum/struct/union declareations without tag names. Improved error recovery with local declarations and _Static_assert. --- src/cc65/compile.c | 84 +++-- src/cc65/declare.c | 436 ++++++++++------------- src/cc65/declare.h | 44 +-- src/cc65/expr.c | 2 +- src/cc65/locals.c | 54 ++- src/cc65/scanner.c | 157 ++++++++ src/cc65/scanner.h | 29 ++ src/cc65/staticassert.c | 61 ++-- test/ref/bug1889-missing-identifier.cref | 4 +- 9 files changed, 514 insertions(+), 357 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index bef55e375..5101eccd4 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -79,7 +79,6 @@ static void Parse (void) /* Top level parser routine. */ { - int comma; SymEntry* Sym; FuncDesc* FuncDef = 0; @@ -94,8 +93,8 @@ static void Parse (void) while (CurTok.Tok != TOK_CEOF) { DeclSpec Spec; + int Comma; int NeedClean = 0; - unsigned PrevErrorCount = ErrorCount; /* Check for empty statements */ if (CurTok.Tok == TOK_SEMI) { @@ -119,7 +118,7 @@ static void Parse (void) continue; } - /* Read variable defs and functions */ + /* Read the declaration specifier */ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC); /* Don't accept illegal storage classes */ @@ -139,17 +138,19 @@ static void Parse (void) } /* Read declarations for this type */ - Sym = 0; - comma = 0; + Comma = 0; while (1) { Declarator Decl; + Sym = 0; + /* Read the next declaration */ - NeedClean = ParseDecl (&Spec, &Decl, DM_NEED_IDENT); - if (Decl.Ident[0] == '\0') { - Sym = 0; - goto NextDecl; + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; } /* Check if we must reserve storage for the variable. We do this, @@ -191,10 +192,6 @@ static void Parse (void) FuncDef->Flags = (FuncDef->Flags & ~FD_EMPTY) | FD_VOID_PARAM; } } else { - if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) { - Error ("Expected ',' or ';' after top level declarator"); - } - /* Just a declaration */ Decl.StorageClass |= SC_DECL; } @@ -317,50 +314,49 @@ static void Parse (void) } -NextDecl: /* Check for end of declaration list */ - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - comma = 1; - } else { + if (CurTok.Tok != TOK_COMMA) { break; } + Comma = 1; + Spec.Flags |= DS_NO_EMPTY_DECL; + NextToken (); } /* Finish the declaration */ - if (Sym) { - /* Function definition? */ - if (IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) { - if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) { - /* ISO C: The type category in a function definition cannot be - ** inherited from a typedef. - */ - Error ("Function cannot be defined with a typedef"); - } else if (comma) { - /* ISO C: A function definition cannot shall its return type - ** specifier with other declarators. - */ - Error ("';' expected after top level declarator"); - } + if (Sym && IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) { + /* A function definition is not terminated with a semicolon */ + if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) { + /* ISO C: The type category in a function definition cannot be + ** inherited from a typedef. + */ + Error ("Function cannot be defined with a typedef"); + } else if (Comma) { + /* ISO C: A function definition cannot shall its return type + ** specifier with other declarators. + */ + Error ("';' expected after top level declarator"); + } - /* Parse the function body anyways */ - NeedClean = 0; - NewFunc (Sym, FuncDef); + /* Parse the function body anyways */ + NeedClean = 0; + NewFunc (Sym, FuncDef); - /* Make sure we aren't omitting any work */ - CheckDeferredOpAllDone (); + /* Make sure we aren't omitting any work */ + CheckDeferredOpAllDone (); + } else if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (CurTok.Tok != TOK_SEMI) { + Error ("',' or ';' expected after top level declarator"); + NeedClean = -1; } else { - /* Must be followed by a semicolon */ - if (ConsumeSemi ()) { - NeedClean = 0; - } else { - NeedClean = -1; - } + NextToken (); + NeedClean = 0; } } /* Try some smart error recovery */ - if (PrevErrorCount != ErrorCount && NeedClean < 0) { + if (NeedClean < 0) { SmartErrorSkip (1); } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 96e1f1074..efca09d7f 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -72,7 +72,7 @@ -static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified); +static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags); /* Parse a type specifier */ @@ -83,125 +83,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp -static void OpenBrace (Collection* C, token_t Tok) -/* Consume an opening parenthesis/bracket/curly brace and remember that */ -{ - switch (Tok) { - case TOK_LPAREN: Tok = TOK_RPAREN; break; - case TOK_LBRACK: Tok = TOK_RBRACK; break; - case TOK_LCURLY: Tok = TOK_RCURLY; break; - default: Internal ("Unexpected opening token: %02X", (unsigned)Tok); - } - CollAppend (C, (void*)Tok); - NextToken (); -} - - - -static int CloseBrace (Collection* C, token_t Tok) -/* Consume a closing parenthesis/bracket/curly brace if it is matched with an -** opening one and return 0, or bail out and return -1 if it is not matched. -*/ -{ - if (CollCount (C) > 0) { - token_t LastTok = (token_t)CollLast (C); - if (LastTok == Tok) { - CollPop (C); - NextToken (); - return 0; - } - } - - return -1; -} - - - -int SmartErrorSkip (int WholeDecl) -/* Try some smart error recovery. -** -** - If WholeDecl is 0: -** Skip tokens until a comma or closing curly brace that is not enclosed in -** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or -** unpaired right parenthesis/bracket/curly brace is reached. -** -** - If WholeDecl is non-0: -** Skip tokens until a closing curly brace that is not enclosed in an open -** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached. -** -** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if -** this exits with no open parentheses/brackets/curly braces. Otherwise, return -** -1. -*/ -{ - Collection C = AUTO_COLLECTION_INITIALIZER; - int Res = 0; - - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, - TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY }; - - while (CurTok.Tok != TOK_CEOF) { - SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); - - switch (CurTok.Tok) { - case TOK_LPAREN: - case TOK_LBRACK: - case TOK_LCURLY: - OpenBrace (&C, CurTok.Tok); - break; - - case TOK_RPAREN: - case TOK_RBRACK: - if (CloseBrace (&C, CurTok.Tok) < 0) { - if (!WholeDecl) { - Res = -1; - goto ExitPoint; - } - NextToken (); - } - break; - - case TOK_RCURLY: - if (CloseBrace (&C, CurTok.Tok) < 0) { - if (!WholeDecl) { - Res = -1; - goto ExitPoint; - } - NextToken (); - } else if (CollCount (&C) == 0) { - goto ExitPoint; - } - break; - - case TOK_COMMA: - if (CollCount (&C) == 0 && !WholeDecl) { - goto ExitPoint; - } - NextToken (); - break; - - case TOK_SEMI: - if (CollCount (&C) != 0) { - Res = -1; - } - goto ExitPoint; - - case TOK_CEOF: - goto ExitPoint; - - default: - Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok); - } - } - -ExitPoint: - DoneCollection (&C); - return Res; -} - - - static unsigned ParseOneStorageClass (void) /* Parse and return a storage class specifier */ { @@ -451,20 +332,36 @@ static void OptionalInt (void) -static void OptionalSigned (int* SignednessSpecified) +static void OptionalSigned (DeclSpec* Spec) /* Eat an optional "signed" token */ { if (CurTok.Tok == TOK_SIGNED) { /* Skip it */ NextToken (); - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; + if (Spec != NULL) { + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; } } } +static void UseDefaultType (DeclSpec* Spec, typespec_t TSFlags) +/* Use the default type for the type specifier */ +{ + if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { + Spec->Flags |= DS_NO_TYPE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; + } else { + Spec->Flags |= DS_DEF_TYPE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; + } +} + + + static void InitDeclSpec (DeclSpec* Spec) /* Initialize the DeclSpec struct for use */ { @@ -1080,7 +977,6 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) /* Get the type of the entry */ DeclSpec Spec; - int SignednessSpecified = 0; int NeedClean = 0; /* Check for a _Static_assert */ @@ -1090,7 +986,17 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) } InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); + + /* Check if this is only a type declaration */ + if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) { + CheckEmptyDecl (&Spec); + NextToken (); + continue; + } + + /* Allow anonymous bit-fields */ + Spec.Flags |= DS_ALLOW_BITFIELD; /* Read fields with this type */ while (1) { @@ -1098,7 +1004,12 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) Declarator Decl; /* Get type and name of the struct field */ - NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } /* Check for a bit-field declaration */ FieldWidth = ParseFieldWidth (&Decl); @@ -1123,9 +1034,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) Decl.Type[0].C &= ~T_QUAL_CVR; } } else { - /* A non bit-field without a name is legal but useless */ - Warning ("Declaration does not declare anything"); - + /* Invalid member */ goto NextMember; } } else if (FieldWidth > 0) { @@ -1160,7 +1069,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) ** bit-field. */ AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, - SignednessSpecified); + (Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0); } else if (Decl.Ident[0] != '\0') { /* Add the new field to the table */ Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); @@ -1189,17 +1098,22 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) } } -NextMember: if (CurTok.Tok != TOK_COMMA) { +NextMember: + /* Check for end of declaration list */ + if (CurTok.Tok != TOK_COMMA) { break; } + Spec.Flags |= DS_NO_EMPTY_DECL; NextToken (); } - /* Must be followed by a semicolon */ - if (NeedClean >= 0 && ConsumeSemi ()) { - NeedClean = 0; - } else { - NeedClean = -1; + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } } /* Try some smart error recovery */ @@ -1265,7 +1179,6 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) /* Get the type of the entry */ DeclSpec Spec; - int SignednessSpecified = 0; int NeedClean = 0; /* Check for a _Static_assert */ @@ -1275,7 +1188,17 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); + + /* Check if this is only a type declaration */ + if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) { + CheckEmptyDecl (&Spec); + NextToken (); + continue; + } + + /* Allow anonymous bit-fields */ + Spec.Flags |= DS_ALLOW_BITFIELD; /* Read fields with this type */ while (1) { @@ -1291,7 +1214,12 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } /* Get type and name of the struct field */ - NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } /* Check for a bit-field declaration */ FieldWidth = ParseFieldWidth (&Decl); @@ -1335,9 +1263,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) Decl.Type[0].C &= ~T_QUAL_CVR; } } else { - /* A non bit-field without a name is legal but useless */ - Warning ("Declaration does not declare anything"); - + /* Invalid member */ goto NextMember; } } else if (FieldWidth > 0) { @@ -1387,8 +1313,8 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) ** bit-field as a char type in expressions. */ CHECK (BitOffs < CHAR_BITS); - AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, - FieldWidth, SignednessSpecified); + AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, FieldWidth, + (Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0); BitOffs += FieldWidth; CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); /* Add any full bytes to the struct size */ @@ -1427,17 +1353,22 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) } } -NextMember: if (CurTok.Tok != TOK_COMMA) { +NextMember: + /* Check for end of declaration list */ + if (CurTok.Tok != TOK_COMMA) { break; } + Spec.Flags |= DS_NO_EMPTY_DECL; NextToken (); } - /* Must be followed by a semicolon */ - if (NeedClean >= 0 && ConsumeSemi ()) { - NeedClean = 0; - } else { - NeedClean = -1; + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } } /* Try some smart error recovery */ @@ -1472,7 +1403,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { -static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified) +static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) /* Parse a type specifier. Store whether one of "signed" or "unsigned" was ** specified, so bit-fields of unspecified signedness can be treated as ** unsigned; without special handling, it would be treated as signed. @@ -1482,10 +1413,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp SymEntry* TagEntry; TypeCode Qualifiers = T_QUAL_NONE; - if (SignednessSpecified != NULL) { - *SignednessSpecified = 0; - } - /* Assume we have an explicit type */ Spec->Flags &= ~DS_DEF_TYPE; @@ -1511,15 +1438,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp case TOK_LONG: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; NextToken (); OptionalInt (); Spec->Type[0].C = T_ULONG; Spec->Type[1].C = T_END; } else { - OptionalSigned (SignednessSpecified); + OptionalSigned (Spec); OptionalInt (); Spec->Type[0].C = T_LONG; Spec->Type[1].C = T_END; @@ -1529,15 +1454,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp case TOK_SHORT: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; NextToken (); OptionalInt (); Spec->Type[0].C = T_USHORT; Spec->Type[1].C = T_END; } else { - OptionalSigned (SignednessSpecified); + OptionalSigned (Spec); OptionalInt (); Spec->Type[0].C = T_SHORT; Spec->Type[1].C = T_END; @@ -1550,10 +1473,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp Spec->Type[1].C = T_END; break; - case TOK_SIGNED: - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } + case TOK_SIGNED: + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; NextToken (); switch (CurTok.Tok) { @@ -1589,9 +1510,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp break; case TOK_UNSIGNED: - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; NextToken (); switch (CurTok.Tok) { @@ -1640,12 +1559,16 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp case TOK_UNION: NextToken (); - /* */ + /* Check for tag name */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); NextToken (); - } else { + } else if (CurTok.Tok == TOK_LCURLY) { AnonName (Ident, "union"); + } else { + Error ("Tag name identifier or '{' expected"); + UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); + break; } /* Remember we have an extra type decl */ Spec->Flags |= DS_EXTRA_TYPE; @@ -1659,12 +1582,16 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp case TOK_STRUCT: NextToken (); - /* */ + /* Check for tag name */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); NextToken (); - } else { + } else if (CurTok.Tok == TOK_LCURLY) { AnonName (Ident, "struct"); + } else { + Error ("Tag name identifier or '{' expected"); + UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); + break; } /* Remember we have an extra type decl */ Spec->Flags |= DS_EXTRA_TYPE; @@ -1678,15 +1605,16 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp case TOK_ENUM: NextToken (); - /* Named enum */ + /* Check for tag name */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); NextToken (); - } else { - if (CurTok.Tok != TOK_LCURLY) { - Error ("Identifier expected for enum tag name"); - } + } else if (CurTok.Tok == TOK_LCURLY) { AnonName (Ident, "enum"); + } else { + Error ("Tag name identifier or '{' expected"); + UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); + break; } /* Remember we have an extra type decl */ Spec->Flags |= DS_EXTRA_TYPE; @@ -1699,9 +1627,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp /* The signedness of enums is determined by the type, so say this is specified to avoid ** the int -> unsigned int handling for plain int bit-fields in AddBitField. */ - if (SignednessSpecified) { - *SignednessSpecified = 1; - } + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; break; case TOK_IDENT: @@ -1718,9 +1644,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp ** Unforunately, this will cause plain int bit-fields defined via typedefs ** to be treated as signed rather than unsigned. */ - if (SignednessSpecified) { - *SignednessSpecified = 1; - } + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; break; } else if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { /* Treat this identifier as an unknown type */ @@ -1742,15 +1666,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp /* FALL THROUGH */ default: - if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { - Spec->Flags |= DS_NO_TYPE; - Spec->Type[0].C = T_INT; - Spec->Type[1].C = T_END; - } else { - Spec->Flags |= DS_DEF_TYPE; - Spec->Type[0].C = T_INT; - Spec->Type[1].C = T_END; - } + UseDefaultType (Spec, TSFlags); break; } @@ -1839,6 +1755,9 @@ static void ParseOldStyleParamList (FuncDesc* F) /* Read the declaration specifier */ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); + /* Paremeters must have identifiers as names */ + Spec.Flags |= DS_NO_EMPTY_DECL; + /* We accept only auto and register as storage class specifiers, but ** we ignore all this, since we use auto anyway. */ @@ -1859,7 +1778,7 @@ static void ParseOldStyleParamList (FuncDesc* F) Declarator Decl; /* Read the parameter */ - ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); /* Warn about new local type declaration */ if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { @@ -2083,7 +2002,7 @@ static FuncDesc* ParseFuncDecl (void) -static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Recursively process direct declarators. Build a type array in reverse order. */ { /* Read optional function or pointer qualifiers that modify the identifier @@ -2101,19 +2020,19 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo NextToken (); /* A pointer type cannot be used as an empty declaration */ - if (Mode == DM_ACCEPT_IDENT) { - Mode = DM_NEED_IDENT; + if (Mode == DM_IDENT_OR_EMPTY) { + Spec->Flags |= DS_NO_EMPTY_DECL; } /* Allow const, restrict, and volatile qualifiers */ Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); /* Parse the type that the pointer points to */ - Mode = DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, Mode); /* Add the type */ AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); - return Mode; + return; } if (CurTok.Tok == TOK_LPAREN) { @@ -2121,28 +2040,24 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo /* An empty declaration cannot contain parentheses where an identifier ** would show up if it were a non-empty declaration. */ - if (Mode == DM_ACCEPT_IDENT) { - Mode = DM_NEED_IDENT; + if (Mode == DM_IDENT_OR_EMPTY) { + Spec->Flags |= DS_NO_EMPTY_DECL; } - Mode = DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, Mode); ConsumeRParen (); } else if (CurTok.Tok == TOK_IDENT) { strcpy (D->Ident, CurTok.Ident); NextToken (); } else { D->Ident[0] = '\0'; - if (Mode == DM_NEED_IDENT) { + if ((Spec->Flags & DS_NO_EMPTY_DECL) != 0 && + CurTok.Tok != TOK_LBRACK && + ((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) { Error ("Identifier expected"); } } while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { - /* An array or function type cannot be used as an empty declaration */ - if (Mode == DM_ACCEPT_IDENT && D->Ident[0] == '\0') { - Mode = DM_NEED_IDENT; - Error ("Identifier expected"); - } - if (CurTok.Tok == TOK_LPAREN) { /* Function declarator */ @@ -2181,6 +2096,18 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo /* Array declarator */ long Size = UNSPECIFIED; + /* An array type cannot be used as an empty declaration */ + if (Mode == DM_IDENT_OR_EMPTY) { + Spec->Flags |= DS_NO_EMPTY_DECL; + if (D->Ident[0] == '\0') { + if ((Spec->Flags & DS_DEF_TYPE) == 0) { + Error ("Identifier or ';' expected after declaration specifiers"); + } else { + Error ("Identifier expected"); + } + } + } + /* We cannot have any qualifiers for an array */ if (Qualifiers != T_QUAL_NONE) { Error ("Invalid qualifiers for array"); @@ -2228,8 +2155,6 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo if (Qualifiers & T_QUAL_CDECL) { Error ("Invalid '__cdecl__' qualifier"); } - - return Mode; } @@ -2248,7 +2173,7 @@ Type* ParseType (Type* T) /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); /* Parse additional declarators */ ParseDecl (&Spec, &Decl, DM_NO_IDENT); @@ -2262,14 +2187,23 @@ Type* ParseType (Type* T) -int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Parse a variable, type or function declarator. Return -1 if this stops at -** an unpaired right parenthesis/bracket/curly brace. +** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops +** after consuming a semicolon or closing curly brace, or reaching an EOF. +** Return 1 otherwise. */ { /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; + /* If there is no explicit type specifier, an optional identifier becomes + ** required. + */ + if (Mode == DM_IDENT_OR_EMPTY && (Spec->Flags & DS_DEF_TYPE) != 0) { + Spec->Flags |= DS_NO_EMPTY_DECL; + } + /* Initialize the Declarator struct */ InitDeclarator (D); @@ -2283,6 +2217,11 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Use the storage class from the declspec */ D->StorageClass = Spec->StorageClass; + /* If we have a function, add a special symbol type */ + if (IsTypeFunc (D->Type)) { + D->StorageClass |= SC_FUNC; + } + /* Do several fixes on qualifiers */ FixQualifiers (D->Type); @@ -2297,24 +2236,6 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Parse attributes for this declarator */ ParseAttribute (D); - /* If we have a function, add a special storage class */ - if (IsTypeFunc (D->Type)) { - - D->StorageClass |= SC_FUNC; - - } else if (!IsTypeVoid (D->Type)) { - /* Check the size of the generated type */ - unsigned Size = SizeOf (D->Type); - - if (Size >= 0x10000) { - if (D->Ident[0] != '\0') { - Error ("Size of '%s' is invalid (0x%06X)", D->Ident, Size); - } else { - Error ("Invalid size in declaration (0x%06X)", Size); - } - } - } - /* Check a few pre-C99 things */ if (D->Ident[0] != '\0' && (Spec->Flags & DS_DEF_TYPE) != 0) { /* Check and warn about an implicit int return in the function */ @@ -2331,7 +2252,7 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* For anything that is not a function or typedef, check for an implicit ** int declaration. */ - if ((D->StorageClass & SC_FUNC) != SC_FUNC && + if (!IsTypeFunc (D->Type) && (D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { /* If the standard was not set explicitly to C89, print a warning ** for variables with implicit int type. @@ -2342,11 +2263,32 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } } - if (PrevErrorCount != ErrorCount) { - if ((Spec->Flags & DS_DEF_TYPE) == 0 && Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { - /* Make the declaration fictitious if is is not parsed correctly */ - D->StorageClass |= SC_FICTITIOUS; + /* Check the size of the declared type */ + if (IsObjectType (D->Type)) { + unsigned Size = SizeOf (D->Type); + if (Size >= 0x10000) { + if (D->Ident[0] != '\0') { + Error ("Size of '%s' is too large (0x%06X)", D->Ident, Size); + } else { + Error ("Size in declaration is too large (0x%06X)", Size); + } + } + } + + /* An empty declaration must be terminated with a semicolon */ + if (PrevErrorCount == ErrorCount && + Mode == DM_IDENT_OR_EMPTY && + D->Ident[0] == '\0' && + CurTok.Tok != TOK_SEMI && + ((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) { + Error ("Identifier or ';' expected after declaration specifiers"); + } + + if (PrevErrorCount != ErrorCount) { + if ((Spec->Flags & DS_DEF_TYPE) == 0 && + (Spec->Flags & DS_NO_EMPTY_DECL) != 0 && + D->Ident[0] == '\0') { /* Use a fictitious name for the identifier if it is missing */ const char* Level = ""; @@ -2366,15 +2308,21 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) break; } AnonName (D->Ident, Level); + + /* Make the declarator fictitious */ + D->StorageClass |= SC_FICTITIOUS; } /* Try some smart error recovery */ if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { - return SmartErrorSkip (0); + /* Skip to the end of the whole declaration if it is not part of a + ** parameter list or a type cast. + */ + return SmartErrorSkip (Mode == DM_IDENT_OR_EMPTY); } } - return 0; + return 1; } @@ -2389,7 +2337,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage) Spec->Flags &= ~DS_DEF_STORAGE; /* Parse the type specifiers */ - ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL); + ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC); /* If no explicit storage class is given, use the default */ if (Spec->StorageClass == 0) { @@ -2407,6 +2355,10 @@ void CheckEmptyDecl (const DeclSpec* Spec) */ { if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { - Warning ("Useless declaration"); + Warning ("Declaration does not declare anything"); + } else if (IsClassStruct (Spec->Type) && + !IsIncompleteESUType (Spec->Type) && + SymHasAnonName (GetESUTagSym (Spec->Type))) { + Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type)); } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 1ce764f7a..36de40311 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -77,6 +77,11 @@ enum typespec_t { #define DS_NEW_TYPE_DECL 0x0010U /* New type declared */ #define DS_NEW_TYPE_DEF 0x0020U /* New type defined */ #define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF) +#define DS_EXPLICIT_SIGNEDNESS 0x0040U /* Signedness specified */ +#define DS_NO_EMPTY_DECL 0x0100U /* Disallow empty declaration */ +#define DS_ALLOW_BITFIELD 0x0200U /* Allow anonymous bit-fields */ + + /* Result of ParseDeclSpec */ typedef struct DeclSpec DeclSpec; @@ -99,24 +104,22 @@ struct Declarator { }; /* Modes for ParseDecl: -** - DM_NEED_IDENT means: -** we *must* have a type and a variable identifer. +** - DM_IDENT_OR_EMPTY means: +** we *may* have an identifier, or none. If it is the latter case, +** the type specifier must be used for an empty declaration, +** or it is an error. ** - DM_NO_IDENT means: ** we must have a type but no variable identifer ** (if there is one, it's not read). -** - DM_ACCEPT_IDENT means: -** we *may* have an identifier, or none. If it is the latter case, -** the type must be used as an empty declaration, or it is an error. -** Note: this is used for struct/union members. -** - DM_IGNORE_IDENT means: +** Note: this is used for type names. +** - DM_ACCEPT_PARAM_IDENT means: ** we *may* have an identifier. If there is an identifier, ** it is read, but it is no error, if there is none. ** Note: this is used for function parameter type lists. */ typedef enum { - DM_NEED_IDENT, + DM_IDENT_OR_EMPTY, DM_NO_IDENT, - DM_ACCEPT_IDENT, DM_ACCEPT_PARAM_IDENT, } declmode_t; @@ -128,29 +131,14 @@ typedef enum { -int SmartErrorSkip (int WholeDecl); -/* Try some smart error recovery. -** -** - If WholeDecl is 0: -** Skip tokens until a comma or closing curly brace that is not enclosed in -** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or -** unpaired right parenthesis/bracket/curly brace is reached. -** -** - If WholeDecl is non-0: -** Skip tokens until a closing curly brace that is not enclosed in an open -** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached. -** -** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if -** this exits with no open parentheses/brackets/curly braces. Otherwise, return -** -1. -*/ - Type* ParseType (Type* Type); /* Parse a complete type specification */ -int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); +int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode); /* Parse a variable, type or function declarator. Return -1 if this stops at -** an unpaired right parenthesis/bracket/curly brace. +** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops +** after consuming a semicolon or closing curly brace, or reaching an EOF. +** Return 1 otherwise. */ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a87335f42..d672b032e 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1422,7 +1422,7 @@ static void Primary (ExprDesc* E) Declarator Decl; /* Parse one declaration */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); if (CurTok.Tok == TOK_ASSIGN) { NextToken (); ParseInit (Decl.Type); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 381477faa..701dcb806 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -441,14 +441,15 @@ static void ParseStaticDecl (Declarator* Decl) -static void ParseOneDecl (const DeclSpec* Spec) +static int ParseOneDecl (DeclSpec* Spec) /* Parse one variable declarator. */ { - Declarator Decl; /* Declarator data structure */ + Declarator Decl; /* Declarator data structure */ + int NeedClean; /* Read the declarator */ - ParseDecl (Spec, &Decl, DM_NEED_IDENT); + NeedClean = ParseDecl (Spec, &Decl, DM_IDENT_OR_EMPTY); /* Check if there are any non-extern storage classes set for function ** declarations. Function can only be declared inside functions with the @@ -538,6 +539,8 @@ static void ParseOneDecl (const DeclSpec* Spec) /* Make sure we aren't missing some work */ CheckDeferredOpAllDone (); + + return NeedClean; } @@ -553,15 +556,8 @@ void DeclareLocals (void) /* Loop until we don't find any more variables */ while (1) { - - /* Check variable declarations. We need to distinguish between a - ** default int type and the end of variable declarations. So we - ** will do the following: If there is no explicit storage class - ** specifier *and* no explicit type given, *and* no type qualifiers - ** have been read, it is assumed that we have reached the end of - ** declarations. - */ DeclSpec Spec; + int NeedClean; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { @@ -569,10 +565,18 @@ void DeclareLocals (void) continue; } + /* Read the declaration specifier */ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); - if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ - (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ - GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ + + /* Check variable declarations. We need distinguish between a default + ** int type and the end of variable declarations. So we will do the + ** following: If there is no explicit storage class specifier *and* no + ** explicit type given, *and* no type qualifiers have been read, it is + ** assumed that we have reached the end of declarations. + */ + if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ + (Spec.Flags & DS_DEF_TYPE) == DS_DEF_TYPE && /* No type given */ + GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ break; } @@ -587,8 +591,11 @@ void DeclareLocals (void) /* Parse a comma separated variable list */ while (1) { - /* Parse one declaration */ - ParseOneDecl (&Spec); + /* Parse one declarator */ + NeedClean = ParseOneDecl (&Spec); + if (NeedClean <= 0) { + break; + } /* Check if there is more */ if (CurTok.Tok == TOK_COMMA) { @@ -600,8 +607,19 @@ void DeclareLocals (void) } } - /* A semicolon must follow */ - ConsumeSemi (); + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } } /* Be sure to allocate any reserved space for locals */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 00dde9e83..6af9bc4be 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -1235,6 +1235,163 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount) +static void OpenBrace (Collection* C, token_t Tok) +/* Consume an opening parenthesis/bracket/curly brace and remember that */ +{ + switch (Tok) { + case TOK_LPAREN: Tok = TOK_RPAREN; break; + case TOK_LBRACK: Tok = TOK_RBRACK; break; + case TOK_LCURLY: Tok = TOK_RCURLY; break; + default: Internal ("Unexpected opening token: %02X", (unsigned)Tok); + } + CollAppend (C, (void*)Tok); + NextToken (); +} + + + +static void PopBrace (Collection* C) +/* Close the latest open parenthesis/bracket/curly brace */ +{ + if (CollCount (C) > 0) { + CollPop (C); + } +} + + + +static int CloseBrace (Collection* C, token_t Tok) +/* Consume a closing parenthesis/bracket/curly brace if it is matched with an +** opening one to close and return 0, or bail out and return -1 if it is not +** matched. +*/ +{ + if (CollCount (C) > 0) { + token_t LastTok = (token_t)CollLast (C); + if (LastTok == Tok) { + CollPop (C); + NextToken (); + return 0; + } + } + + return -1; +} + + + +int SmartErrorSkip (int TillEnd) +/* Try some smart error recovery. +** +** - If TillEnd == 0: +** Skip tokens until a comma or closing curly brace that is not enclosed in +** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or +** unpaired right parenthesis/bracket/curly brace is reached. The closing +** curly brace is consumed in the former case. +** +** - If TillEnd != 0: +** Skip tokens until a right curly brace or semicolon is reached and consumed +** while there are no open parentheses/brackets/curly braces, or until an EOF +** is reached anytime. Any open parenthesis/bracket/curly brace is considered +** to be closed by consuming a right parenthesis/bracket/curly brace even if +** they didn't match. +** +** - Return -1: +** If this exits at a semicolon or unpaired right parenthesis/bracket/curly +** brace while there are still open parentheses/brackets/curly braces. +** +** - Return 0: +** If this exits as soon as it reaches an EOF; +** Or if this exits right after consuming a semicolon or right curly brace +** while there are no open parentheses/brackets/curly braces. +** +** - Return 1: +** If this exits at a non-EOF without consuming it. +*/ +{ + Collection C = AUTO_COLLECTION_INITIALIZER; + int Res = 0; + + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, + TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY }; + + while (CurTok.Tok != TOK_CEOF) { + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + + switch (CurTok.Tok) { + case TOK_LPAREN: + case TOK_LBRACK: + case TOK_LCURLY: + OpenBrace (&C, CurTok.Tok); + break; + + case TOK_RPAREN: + case TOK_RBRACK: + if (CloseBrace (&C, CurTok.Tok) < 0) { + if (!TillEnd) { + Res = -1; + goto ExitPoint; + } + PopBrace (&C); + NextToken (); + } + break; + + case TOK_RCURLY: + if (CloseBrace (&C, CurTok.Tok) < 0) { + if (!TillEnd) { + Res = -1; + goto ExitPoint; + } + PopBrace (&C); + NextToken (); + } + if (CollCount (&C) == 0) { + /* We consider this as a terminator as well */ + Res = 0; + goto ExitPoint; + } + break; + + case TOK_COMMA: + if (CollCount (&C) == 0 && !TillEnd) { + Res = 1; + goto ExitPoint; + } + NextToken (); + break; + + case TOK_SEMI: + if (CollCount (&C) == 0) { + if (TillEnd) { + NextToken (); + Res = 0; + } else { + Res = 1; + } + goto ExitPoint; + } + NextToken (); + break; + + case TOK_CEOF: + /* We cannot consume this */ + Res = 0; + goto ExitPoint; + + default: + Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok); + } + } + +ExitPoint: + DoneCollection (&C); + return Res; +} + + + int Consume (token_t Token, const char* ErrorMsg) /* Eat token if it is the next in the input stream, otherwise print an error ** message. Returns true if the token was found and false otherwise. diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index e6b788660..808b96c5e 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -310,6 +310,35 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount); ** This routine is used for error recovery. */ +int SmartErrorSkip (int TillEnd); +/* Try some smart error recovery. +** +** - If TillEnd == 0: +** Skip tokens until a comma or closing curly brace that is not enclosed in +** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or +** unpaired right parenthesis/bracket/curly brace is reached. The closing +** curly brace is consumed in the former case. +** +** - If TillEnd != 0: +** Skip tokens until a right curly brace or semicolon is reached and consumed +** while there are no open parentheses/brackets/curly braces, or until an EOF +** is reached anytime. Any open parenthesis/bracket/curly brace is considered +** to be closed by consuming a right parenthesis/bracket/curly brace even if +** they didn't match. +** +** - Return -1: +** If this exits at a semicolon or unpaired right parenthesis/bracket/curly +** brace while there are still open parentheses/brackets/curly braces. +** +** - Return 0: +** If this exits as soon as it reaches an EOF; +** Or if this exits right after consuming a semicolon or right curly brace +** while there are no open parentheses/brackets/curly braces. +** +** - Return 1: +** If this exits at a non-EOF without consuming it. +*/ + int Consume (token_t Token, const char* ErrorMsg); /* Eat token if it is the next in the input stream, otherwise print an error ** message. Returns true if the token was found and false otherwise. diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index abb2c57ca..9df9af7da 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -45,7 +45,7 @@ -void ParseStaticAssert () +void ParseStaticAssert (void) { /* ** static_assert-declaration ::= @@ -53,20 +53,23 @@ void ParseStaticAssert () ** _Static_assert ( constant-expression , string-literal ) ; */ ExprDesc Expr; - int failed; + unsigned PrevErrorCount = ErrorCount; + int failed = 0; /* Skip the _Static_assert token itself */ CHECK (CurTok.Tok == TOK_STATIC_ASSERT); NextToken (); /* We expect an opening paren */ - if (!ConsumeLParen ()) { - return; + if (ConsumeLParen ()) { + /* Parse assertion condition */ + Expr = NoCodeConstAbsIntExpr (hie1); + failed = !Expr.IVal; } - /* Parse assertion condition */ - Expr = NoCodeConstAbsIntExpr (hie1); - failed = !Expr.IVal; + if (PrevErrorCount != ErrorCount) { + goto ExitPoint; + } /* If there is a comma, we also have an error message. The message is optional because we ** support the C2X syntax with only an expression. @@ -84,19 +87,16 @@ void ParseStaticAssert () /* String literal */ if (CurTok.Tok != TOK_SCONST) { Error ("String literal expected for static_assert message"); - return; - } + } else { + /* Issue an error including the message if the static_assert failed. */ + if (failed) { + Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); + } - /* Issue an error including the message if the static_assert failed. */ - if (failed) { - Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); - } - - /* Consume the string constant, now that we don't need it anymore. - ** This should never fail since we checked the token type above. - */ - if (!Consume (TOK_SCONST, "String literal expected")) { - return; + /* Consume the string constant, now that we don't need it anymore. + ** This should never fail since we checked the token type above. + */ + Consume (TOK_SCONST, "String literal expected"); } } else { /* No message. */ @@ -105,7 +105,24 @@ void ParseStaticAssert () } } - /* Closing paren and semi-colon needed */ - ConsumeRParen (); - ConsumeSemi (); + /* The assertion failure error is not a syntax error */ + if (failed) { + ++PrevErrorCount; + } + + if (PrevErrorCount == ErrorCount) { + /* Closing paren needed */ + ConsumeRParen (); + } + + if (PrevErrorCount == ErrorCount) { + /* Must be followed by a semicolon */ + ConsumeSemi (); + } + +ExitPoint: + /* Try some smart error recovery */ + if (PrevErrorCount != ErrorCount) { + SmartErrorSkip (1); + } } diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index 534c6aaba..2d92ff263 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,3 +1,3 @@ -bug1889-missing-identifier.c:3: Error: Identifier expected -bug1889-missing-identifier.c:3: Error: ';' expected +bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers +bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature bug1889-missing-identifier.c:4: Error: Identifier expected From a1a060c29151c1571a93922575fabe2f582ff16c Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 15:43:24 +0800 Subject: [PATCH 397/520] Declaration specifier flags cleanup. --- src/cc65/declare.c | 23 ++++++++++++----------- src/cc65/declare.h | 13 ++++++++----- src/cc65/expr.c | 2 +- src/cc65/locals.c | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index efca09d7f..62c2bdfa3 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -350,11 +350,11 @@ static void UseDefaultType (DeclSpec* Spec, typespec_t TSFlags) /* Use the default type for the type specifier */ { if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { - Spec->Flags |= DS_NO_TYPE; + Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_NONE; Spec->Type[0].C = T_INT; Spec->Type[1].C = T_END; } else { - Spec->Flags |= DS_DEF_TYPE; + Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_DEF_TYPE; Spec->Type[0].C = T_INT; Spec->Type[1].C = T_END; } @@ -1413,8 +1413,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) SymEntry* TagEntry; TypeCode Qualifiers = T_QUAL_NONE; - /* Assume we have an explicit type */ - Spec->Flags &= ~DS_DEF_TYPE; + /* Assume we have an explicitly specified type */ + Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_EXPLICIT_TYPE; /* Read storage specifiers and/or type qualifiers if we have any */ OptionalSpecifiers (Spec, &Qualifiers, TSFlags); @@ -1767,7 +1767,7 @@ static void ParseOldStyleParamList (FuncDesc* F) } /* Type must be specified */ - if ((Spec.Flags & DS_NO_TYPE) != 0) { + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { Error ("Expected declaration specifiers"); break; } @@ -1860,7 +1860,7 @@ static void ParseAnsiParamList (FuncDesc* F) } /* Type must be specified */ - if ((Spec.Flags & DS_NO_TYPE) != 0) { + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { Error ("Type specifier missing"); } @@ -2100,7 +2100,7 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) if (Mode == DM_IDENT_OR_EMPTY) { Spec->Flags |= DS_NO_EMPTY_DECL; if (D->Ident[0] == '\0') { - if ((Spec->Flags & DS_DEF_TYPE) == 0) { + if ((Spec->Flags & DS_TYPE_MASK) != DS_NONE) { Error ("Identifier or ';' expected after declaration specifiers"); } else { Error ("Identifier expected"); @@ -2200,7 +2200,8 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* If there is no explicit type specifier, an optional identifier becomes ** required. */ - if (Mode == DM_IDENT_OR_EMPTY && (Spec->Flags & DS_DEF_TYPE) != 0) { + if (Mode == DM_IDENT_OR_EMPTY && + (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { Spec->Flags |= DS_NO_EMPTY_DECL; } @@ -2237,7 +2238,7 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) ParseAttribute (D); /* Check a few pre-C99 things */ - if (D->Ident[0] != '\0' && (Spec->Flags & DS_DEF_TYPE) != 0) { + if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { /* Check and warn about an implicit int return in the function */ if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) { /* Function has an implicit int return. Output a warning if we don't @@ -2286,8 +2287,8 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } if (PrevErrorCount != ErrorCount) { - if ((Spec->Flags & DS_DEF_TYPE) == 0 && - (Spec->Flags & DS_NO_EMPTY_DECL) != 0 && + if ((Spec->Flags & DS_TYPE_MASK) != DS_DEF_TYPE && + (Spec->Flags & DS_NO_EMPTY_DECL) != 0 && D->Ident[0] == '\0') { /* Use a fictitious name for the identifier if it is missing */ const char* Level = ""; diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 36de40311..6185a111e 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -65,15 +65,18 @@ enum typespec_t { TS_DEFAULT_TYPE_AUTO = 0x02, /* C23 type inference with auto */ /* Whether to allow certain kinds of specifiers */ - TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage storage class specifiers */ - TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */ + TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage class specifiers */ + TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */ }; /* Masks for the Flags field in DeclSpec */ +#define DS_NONE 0x0000U /* Nothing specified or used */ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */ -#define DS_NO_TYPE 0x0002U /* No type explicitly specified */ -#define DS_DEF_TYPE 0x0006U /* Default type used */ -#define DS_EXTRA_TYPE 0x0008U /* Extra type declared */ +#define DS_EXPLICIT_TYPE 0x0002U /* Type specified */ +#define DS_DEF_TYPE 0x0004U /* Implicit type used */ +#define DS_AUTO_TYPE 0x0006U /* C23 auto type used */ +#define DS_TYPE_MASK 0x0006U /* Mask for type of spec decl */ +#define DS_EXTRA_TYPE 0x0008U /* ESU type in declaration */ #define DS_NEW_TYPE_DECL 0x0010U /* New type declared */ #define DS_NEW_TYPE_DEF 0x0020U /* New type defined */ #define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index d672b032e..a0902ed82 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1414,7 +1414,7 @@ static void Primary (ExprDesc* E) DeclSpec Spec; ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); - if ((Spec.Flags & DS_DEF_TYPE) == 0) { + if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { /* Recognized but not supported */ Error ("Mixed declarations and code are not supported in cc65"); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 701dcb806..79bbd4573 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -575,7 +575,7 @@ void DeclareLocals (void) ** assumed that we have reached the end of declarations. */ if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ - (Spec.Flags & DS_DEF_TYPE) == DS_DEF_TYPE && /* No type given */ + (Spec.Flags & DS_TYPE_MASK) == DS_DEF_TYPE && /* No type given */ GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ break; } From cadf8012f6b861d67b5e68a54f7b65b9fe71c633 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 15:43:24 +0800 Subject: [PATCH 398/520] Improved error recovery with type cast and sizeof. --- src/cc65/declare.c | 31 +++++++++++++++++++++++----- src/cc65/declare.h | 2 +- src/cc65/expr.c | 20 +----------------- src/cc65/scanner.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/scanner.h | 5 +++++ src/cc65/typeconv.c | 8 +------- 6 files changed, 83 insertions(+), 32 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 62c2bdfa3..fdc481ce1 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2166,20 +2166,41 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) Type* ParseType (Type* T) -/* Parse a complete type specification */ +/* Parse a complete type specification in parentheses */ { DeclSpec Spec; Declarator Decl; + int NeedClean = -1; + + /* Skip the left paren */ + NextToken (); /* Get a type without a default */ InitDeclSpec (&Spec); ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); - /* Parse additional declarators */ - ParseDecl (&Spec, &Decl, DM_NO_IDENT); + /* Only parse further if there is a type specifier */ + if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { + /* Parse additional declarators */ + NeedClean = ParseDecl (&Spec, &Decl, DM_NO_IDENT); - /* Copy the type to the target buffer */ - TypeCopy (T, Decl.Type); + /* Copy the type to the target buffer */ + TypeCopy (T, Decl.Type); + } else { + /* Fail-safe */ + TypeCopy (T, type_int); + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SimpleErrorSkip (); + } + + /* Closing paren */ + if (!ConsumeRParen ()) { + SimpleErrorSkip (); + NextToken (); + } /* Return a pointer to the target buffer */ return T; diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 6185a111e..4cfc48c68 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -135,7 +135,7 @@ typedef enum { Type* ParseType (Type* Type); -/* Parse a complete type specification */ +/* Parse a complete type specification in parentheses */ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode); /* Parse a variable, type or function declarator. Return -1 if this stops at diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a0902ed82..963ea8bd6 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1415,24 +1415,8 @@ static void Primary (ExprDesc* E) ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { - /* Recognized but not supported */ Error ("Mixed declarations and code are not supported in cc65"); - - while (CurTok.Tok != TOK_SEMI) { - Declarator Decl; - - /* Parse one declaration */ - ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); - if (CurTok.Tok == TOK_ASSIGN) { - NextToken (); - ParseInit (Decl.Type); - } - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { - break; - } - } + SmartErrorSkip (0); } else { Error ("Expression expected"); E->Flags |= E_EVAL_MAYBE_UNUSED; @@ -2089,9 +2073,7 @@ void hie10 (ExprDesc* Expr) NextToken (); if (TypeSpecAhead ()) { Type T[MAXTYPELEN]; - NextToken (); Size = ExprCheckedSizeOf (ParseType (T)); - ConsumeRParen (); } else { /* Remember the output queue pointer */ CodeMark Mark; diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 6af9bc4be..6b5235679 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -1392,6 +1392,55 @@ ExitPoint: +int SimpleErrorSkip (void) +/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace +** is reached. Return 0 If this exits at an EOF. Otherwise return -1. +*/ +{ + Collection C = AUTO_COLLECTION_INITIALIZER; + int Res = 0; + + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { + TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY }; + + while (CurTok.Tok != TOK_CEOF) { + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + + switch (CurTok.Tok) { + case TOK_LPAREN: + case TOK_LBRACK: + case TOK_LCURLY: + OpenBrace (&C, CurTok.Tok); + break; + + case TOK_RPAREN: + case TOK_RBRACK: + case TOK_RCURLY: + if (CloseBrace (&C, CurTok.Tok) < 0) { + /* Found a terminator */ + Res = -1; + goto ExitPoint; + } + break; + + case TOK_CEOF: + /* We cannot go any farther */ + Res = 0; + goto ExitPoint; + + default: + Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok); + } + } + +ExitPoint: + DoneCollection (&C); + return Res; +} + + + int Consume (token_t Token, const char* ErrorMsg) /* Eat token if it is the next in the input stream, otherwise print an error ** message. Returns true if the token was found and false otherwise. diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 808b96c5e..ccf3a8805 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -339,6 +339,11 @@ int SmartErrorSkip (int TillEnd); ** If this exits at a non-EOF without consuming it. */ +int SimpleErrorSkip (void); +/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace +** is reached. Return 0 If this exits at an EOF. Otherwise return -1. +*/ + int Consume (token_t Token, const char* ErrorMsg); /* Eat token if it is the next in the input stream, otherwise print an error ** message. Returns true if the token was found and false otherwise. diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index e5b6749d6..76658502d 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -321,15 +321,9 @@ void TypeCast (ExprDesc* Expr) { Type NewType[MAXTYPELEN]; - /* Skip the left paren */ - NextToken (); - - /* Read the type */ + /* Read the type enclosed in parentheses */ ParseType (NewType); - /* Closing paren */ - ConsumeRParen (); - /* Read the expression we have to cast */ hie10 (Expr); From 3215d377ea27cf404126ef4c33a4a48008bbf015 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 15:46:48 +0800 Subject: [PATCH 399/520] More accurate diagnostic messages on wrong missing declaration specifiers. --- src/cc65/compile.c | 13 +++++++++++ src/cc65/declare.c | 56 +++++++++++++++++++++++++++++++++++++--------- src/cc65/locals.c | 11 +++++++++ 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 5101eccd4..0dc75273d 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -88,6 +88,7 @@ static void Parse (void) /* Fill up the next token with a bogus semicolon and start the tokenizer */ NextTok.Tok = TOK_SEMI; NextToken (); + NextToken (); /* Parse until end of input */ while (CurTok.Tok != TOK_CEOF) { @@ -98,6 +99,7 @@ static void Parse (void) /* Check for empty statements */ if (CurTok.Tok == TOK_SEMI) { + /* TODO: warn on this if we have a pedantic mode */ NextToken (); continue; } @@ -137,6 +139,16 @@ static void Parse (void) continue; } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Read declarations for this type */ Comma = 0; while (1) { @@ -355,6 +367,7 @@ static void Parse (void) } } +EndOfDecl: /* Try some smart error recovery */ if (NeedClean < 0) { SmartErrorSkip (1); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index fdc481ce1..029e22069 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -979,6 +979,13 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) DeclSpec Spec; int NeedClean = 0; + /* Check for extra semicolons */ + if (CurTok.Tok == TOK_SEMI) { + /* TODO: warn on this if we have a pedantic mode */ + NextToken (); + continue; + } + /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { ParseStaticAssert (); @@ -995,6 +1002,16 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) continue; } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Allow anonymous bit-fields */ Spec.Flags |= DS_ALLOW_BITFIELD; @@ -1107,6 +1124,7 @@ NextMember: NextToken (); } +EndOfDecl: if (NeedClean > 0) { /* Must be followed by a semicolon */ if (ConsumeSemi ()) { @@ -1181,6 +1199,13 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) DeclSpec Spec; int NeedClean = 0; + /* Check for extra semicolons */ + if (CurTok.Tok == TOK_SEMI) { + /* TODO: warn on this if we have a pedantic mode */ + NextToken (); + continue; + } + /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { ParseStaticAssert (); @@ -1197,6 +1222,16 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) continue; } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Allow anonymous bit-fields */ Spec.Flags |= DS_ALLOW_BITFIELD; @@ -1362,6 +1397,7 @@ NextMember: NextToken (); } +EndOfDecl: if (NeedClean > 0) { /* Must be followed by a semicolon */ if (ConsumeSemi ()) { @@ -1559,6 +1595,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) case TOK_UNION: NextToken (); + /* Remember we have an extra type decl */ + Spec->Flags |= DS_EXTRA_TYPE; /* Check for tag name */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); @@ -1570,8 +1608,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); break; } - /* Remember we have an extra type decl */ - Spec->Flags |= DS_EXTRA_TYPE; /* Declare the union in the current scope */ TagEntry = ParseUnionSpec (Ident, &Spec->Flags); /* Encode the union entry into the type */ @@ -1582,6 +1618,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) case TOK_STRUCT: NextToken (); + /* Remember we have an extra type decl */ + Spec->Flags |= DS_EXTRA_TYPE; /* Check for tag name */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); @@ -1593,8 +1631,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); break; } - /* Remember we have an extra type decl */ - Spec->Flags |= DS_EXTRA_TYPE; /* Declare the struct in the current scope */ TagEntry = ParseStructSpec (Ident, &Spec->Flags); /* Encode the struct entry into the type */ @@ -1605,6 +1641,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) case TOK_ENUM: NextToken (); + /* Remember we have an extra type decl */ + Spec->Flags |= DS_EXTRA_TYPE; /* Check for tag name */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); @@ -1616,8 +1654,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); break; } - /* Remember we have an extra type decl */ - Spec->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ TagEntry = ParseEnumSpec (Ident, &Spec->Flags); /* Encode the enum entry into the type */ @@ -1658,9 +1694,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) ** in DeclareLocals. The type code used here doesn't matter as ** long as it has no qualifiers. */ - Spec->Flags |= DS_DEF_TYPE; - Spec->Type[0].C = T_INT; - Spec->Type[1].C = T_END; + UseDefaultType (Spec, TS_DEFAULT_TYPE_INT); break; } /* FALL THROUGH */ @@ -2376,7 +2410,9 @@ void CheckEmptyDecl (const DeclSpec* Spec) ** warning if not. */ { - if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { + if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { + /* No declaration at all */ + } else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { Warning ("Declaration does not declare anything"); } else if (IsClassStruct (Spec->Type) && !IsIncompleteESUType (Spec->Type) && diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 79bbd4573..b8738992f 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -588,6 +588,16 @@ void DeclareLocals (void) continue; } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Parse a comma separated variable list */ while (1) { @@ -616,6 +626,7 @@ void DeclareLocals (void) } } +EndOfDecl: /* Try some smart error recovery */ if (NeedClean < 0) { SmartErrorSkip (1); From 07c71541f43f70e59cf440dc1d9c9e085f9f577c Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 10 Dec 2023 09:30:41 +0100 Subject: [PATCH 400/520] Fix #2262: Make sure there's no branching after the sequence Also better check that arguments match --- src/cc65/coptlong.c | 79 +++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c index 16f089e49..29cf4d353 100644 --- a/src/cc65/coptlong.c +++ b/src/cc65/coptlong.c @@ -49,24 +49,30 @@ /* Remove unused loads and stores */ /*****************************************************************************/ - - unsigned OptLongAssign (CodeSeg* S) /* Simplify long assignments. ** Recognize -** lda ... 0 +** lda #IMM 0 ** sta sreg+1 1 -** lda ... 2 +** lda #IMM 2 ** sta sreg 3 -** lda ... 4 -** ldx ... 5 -** sta M0002 6 -** stx M0002+1 7 +** lda #IMM 4 +** ldx #IMM 5 +** sta YYY 6 +** stx YYY+1 7 ** ldy sreg 8 -** sty M0002+2 9 +** sty YYY+2 9 ** ldy sreg+1 10 -** sty M0002+3 11 -** and simplify if not used right after. +** sty YYY+3 11 +** and simplify, if not used right after and no branching occurs, to +** lda XXX+3 +** sta YYY+3 +** lda XXX+2 +** sta YYY+2 +** ldx XXX +** lda XXX+1 +** sta YYY +** stx YYY+1 */ { unsigned Changes = 0; @@ -75,35 +81,43 @@ unsigned OptLongAssign (CodeSeg* S) unsigned I = 0; while (I < CS_GetEntryCount (S)) { - CodeEntry* L[12]; + CodeEntry* L[13]; /* Get next entry */ L[0] = CS_GetEntry (S, I); - if (CS_GetEntries (S, L+1, I+1, 11)) { - if (L[0]->OPC == OP65_LDA && + if (CS_GetEntries (S, L+1, I+1, 12)) { + if (/* Check the opcode sequence */ + L[0]->OPC == OP65_LDA && L[1]->OPC == OP65_STA && - !strcmp (L[1]->Arg, "sreg+1") && L[2]->OPC == OP65_LDA && L[3]->OPC == OP65_STA && - !strcmp (L[3]->Arg, "sreg") && L[4]->OPC == OP65_LDA && L[5]->OPC == OP65_LDX && L[6]->OPC == OP65_STA && L[7]->OPC == OP65_STX && - !strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && - !strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") && L[8]->OPC == OP65_LDY && - !strcmp (L[8]->Arg, "sreg") && L[9]->OPC == OP65_STY && - !strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && - !strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") && L[10]->OPC == OP65_LDY && - !strcmp (L[10]->Arg, "sreg+1") && L[11]->OPC == OP65_STY && - !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + /* Check the arguments match */ + L[0]->AM == AM65_IMM && + !strcmp (L[1]->Arg, "sreg+1") && + L[2]->AM == AM65_IMM && + !strcmp (L[3]->Arg, "sreg") && + L[4]->AM == AM65_IMM && + L[5]->AM == AM65_IMM && + !strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") && + !strcmp (L[8]->Arg, "sreg") && + !strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") && + !strcmp (L[10]->Arg, "sreg+1") && + !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && !strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") && - !RegXUsed (S, I+11)) { + /* Check there's nothing more */ + !RegXUsed (S, I+12) && + !CS_RangeHasLabel(S, I, 12)) { L[1]->AM = L[11]->AM; CE_SetArg(L[1], L[11]->Arg); @@ -142,7 +156,15 @@ unsigned OptLongCopy (CodeSeg* S) ** sty YYY+2 9 ** ldy sreg+1 10 ** sty YYY+3 11 -** and simplify if not used right after. +** and simplify, if not used right after and no branching occurs, to +** lda XXX+3 +** sta YYY+3 +** lda XXX+2 +** sta YYY+2 +** ldx XXX +** lda XXX+1 +** sta YYY +** stx YYY+1 */ { unsigned Changes = 0; @@ -151,12 +173,12 @@ unsigned OptLongCopy (CodeSeg* S) unsigned I = 0; while (I < CS_GetEntryCount (S)) { - CodeEntry* L[12]; + CodeEntry* L[13]; /* Get next entry */ L[0] = CS_GetEntry (S, I); - if (CS_GetEntries (S, L+1, I+1, 11)) { + if (CS_GetEntries (S, L+1, I+1, 12)) { if (L[0]->OPC == OP65_LDA && !strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && !strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") && @@ -185,7 +207,8 @@ unsigned OptLongCopy (CodeSeg* S) L[11]->OPC == OP65_STY && !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && !strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") && - !RegXUsed (S, I+11)) { + !RegXUsed (S, I+11) && + !CS_RangeHasLabel(S, I, 12)) { L[1]->AM = L[11]->AM; CE_SetArg(L[1], L[11]->Arg); From bbd542fac7fd78d6cb2b7c53cf198c3c79b3047d Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 17:01:54 +0800 Subject: [PATCH 401/520] Fixed missing diagnosis on extra identifiers in type names. --- src/cc65/declare.c | 3 +++ test/err/type-name-extra-identifier.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/err/type-name-extra-identifier.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 029e22069..076e94aa8 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2080,6 +2080,9 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) DirectDecl (Spec, D, Mode); ConsumeRParen (); } else if (CurTok.Tok == TOK_IDENT) { + if (Mode == DM_NO_IDENT) { + Error ("Unexpected identifier in type name"); + } strcpy (D->Ident, CurTok.Ident); NextToken (); } else { diff --git a/test/err/type-name-extra-identifier.c b/test/err/type-name-extra-identifier.c new file mode 100644 index 000000000..72de4778d --- /dev/null +++ b/test/err/type-name-extra-identifier.c @@ -0,0 +1,25 @@ +/* + Copyright 2023 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of type name with extra identifier +*/ + +int a = sizeof (int b); From befc9533c6195e9cc18aa4873b823d486dba0abd Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 10 Dec 2023 20:21:50 +0800 Subject: [PATCH 402/520] More accurate diagnostic messages on empty declarations without any type specifiers. --- src/cc65/declare.c | 9 ++++++--- src/cc65/expr.c | 2 +- test/ref/bug1889-missing-identifier.cref | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 076e94aa8..ec5116ead 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2087,10 +2087,13 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) NextToken (); } else { D->Ident[0] = '\0'; - if ((Spec->Flags & DS_NO_EMPTY_DECL) != 0 && - CurTok.Tok != TOK_LBRACK && + if (CurTok.Tok != TOK_LBRACK && ((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) { - Error ("Identifier expected"); + if ((Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { + Error ("Declaration specifier or identifier expected"); + } else if ((Spec->Flags & DS_NO_EMPTY_DECL) != 0) { + Error ("Identifier expected"); + } } } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 963ea8bd6..ed918b4ee 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1412,7 +1412,7 @@ static void Primary (ExprDesc* E) } else { /* Let's see if this is a C99-style declaration */ DeclSpec Spec; - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { Error ("Mixed declarations and code are not supported in cc65"); diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index 2d92ff263..7381d2032 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,3 +1,3 @@ bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature -bug1889-missing-identifier.c:4: Error: Identifier expected +bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected From b31a1c7c0c25de087e272c045eb2370a03856c93 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 10 Dec 2023 22:16:30 +0100 Subject: [PATCH 403/520] test for regression that occured after #2262 --- test/val/optimizer-bug-pr2262.c | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 test/val/optimizer-bug-pr2262.c diff --git a/test/val/optimizer-bug-pr2262.c b/test/val/optimizer-bug-pr2262.c new file mode 100644 index 000000000..f2c08a98c --- /dev/null +++ b/test/val/optimizer-bug-pr2262.c @@ -0,0 +1,53 @@ + +// optimizer bug that occured after PR #2262, fixed by PR #2295 + +#include <stdio.h> + +unsigned char n; +unsigned long fp1; + +int failures = 0; + +void test1(void) +{ + asm("lda _n"); + asm("jeq %g", L0004); + + asm("lda #$3F"); + asm("sta sreg+1"); + asm("lda #$C0"); + asm("sta sreg"); + asm("lda #$00"); + asm("ldx #$00"); + asm("jmp %g", L0005); + +L0004: + asm("lda #$3F"); + asm("sta sreg+1"); + asm("lda #$00"); + asm("sta sreg"); + asm("lda #$00"); + asm("ldx #$00"); + +L0005: + asm("sta _fp1"); + asm("stx _fp1+1"); + asm("ldy sreg"); + asm("sty _fp1+2"); + asm("ldy sreg+1"); + asm("sty _fp1+3"); +} + +int main(void) +{ + n = 0; + test1(); + printf("fp1:%08lx\n", fp1); + if (fp1 != 0x3f000000) failures++; + n = 0xff; + test1(); + printf("fp1:%08lx\n", fp1); + if (fp1 != 0x3fc00000) failures++; + printf("failures:%d\n", failures); + return failures; +} From 0b077f561f890c74045dc72598243d01c5384fc0 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 10 Dec 2023 22:43:47 +0100 Subject: [PATCH 404/520] exclude test directory from some style checks - it makes no sense to enforce these things in the test bench, we need to be able to test all kinds of spaces and tabs :) --- .github/checks/spaces.sh | 2 +- .github/checks/tabs.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/checks/spaces.sh b/.github/checks/spaces.sh index 945e9acc3..e231f6c2d 100755 --- a/.github/checks/spaces.sh +++ b/.github/checks/spaces.sh @@ -5,7 +5,7 @@ CHECK_PATH=. cd $SCRIPT_PATH/../../ -FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'` +FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "test/" | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'` cd $OLDCWD diff --git a/.github/checks/tabs.sh b/.github/checks/tabs.sh index 1c32def17..80dac3f2d 100755 --- a/.github/checks/tabs.sh +++ b/.github/checks/tabs.sh @@ -5,7 +5,7 @@ CHECK_PATH=. cd $SCRIPT_PATH/../../ -FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'` +FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "test/" | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'` cd $OLDCWD From 6b855d562aa16b1e36ef7270b0dcfdb27398bd24 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sun, 10 Dec 2023 23:18:55 +0100 Subject: [PATCH 405/520] use -std=gnu17 for the references, so the test bench will not break with GCC 14. see #2277 --- test/ref/Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/ref/Makefile b/test/ref/Makefile index abd3e9bc0..9538fdee7 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -39,8 +39,18 @@ OPTIONS = g O Os Osi Osir Osr Oi Oir Or ISEQUAL = ..$S..$Stestwrk$Sisequal$(EXE) +# NOTE: the current test bench may include K&R style C, C89 style C, C99 - and +# even things from later standards. Technically C99 removed certain C89 +# constructs - However, so far GCC would still compile them and issue a +# warning (instead of an error). Now, GCC 14 will be more strict about this, +# and by default make those things an error instead. We use -std=gnu17 here +# so we can still build the references with a modern compiler, and don't +# have to deal with special-casing individual tests that use constructs +# from those old standards. Should this become a problem in the future, we +# will have to change that, and create said special cases here. +# see discussion in https://github.com/cc65/cc65/issues/2277 CC = gcc -CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow +CFLAGS = -std=gnu17 -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow .PHONY: all clean From 9985ee7f61f9c0e1b1256d16e3fb901c87531019 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Mon, 11 Dec 2023 00:18:40 +0100 Subject: [PATCH 406/520] fix %hhn and %hn in the internal xvsnprintf function --- src/common/xsprintf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/xsprintf.c b/src/common/xsprintf.c index 556e4f359..2e811a5b1 100644 --- a/src/common/xsprintf.c +++ b/src/common/xsprintf.c @@ -352,8 +352,8 @@ static void StoreOffset (PrintfCtrl* P) /* Store the current output offset (%n format spec) */ { switch (P->LengthMod) { - case lmChar: *va_arg (P->ap, int*) = P->BufFill; break; - case lmShort: *va_arg (P->ap, int*) = P->BufFill; break; + case lmChar: *va_arg (P->ap, char*) = P->BufFill; break; + case lmShort: *va_arg (P->ap, short*) = P->BufFill; break; case lmInt: *va_arg (P->ap, int*) = P->BufFill; break; case lmLong: *va_arg (P->ap, long*) = P->BufFill; break; case lmIntMax: *va_arg (P->ap, intmax_t*) = P->BufFill; break; From b1c150249495d35b299c46579efc47a7b3cbc717 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Mon, 11 Dec 2023 00:35:07 +0100 Subject: [PATCH 407/520] MS compiler insists on those typecasts apparently --- src/common/xsprintf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/xsprintf.c b/src/common/xsprintf.c index 2e811a5b1..aa3183752 100644 --- a/src/common/xsprintf.c +++ b/src/common/xsprintf.c @@ -352,8 +352,8 @@ static void StoreOffset (PrintfCtrl* P) /* Store the current output offset (%n format spec) */ { switch (P->LengthMod) { - case lmChar: *va_arg (P->ap, char*) = P->BufFill; break; - case lmShort: *va_arg (P->ap, short*) = P->BufFill; break; + case lmChar: *va_arg (P->ap, char*) = (char)P->BufFill; break; + case lmShort: *va_arg (P->ap, short*) = (short)P->BufFill; break; case lmInt: *va_arg (P->ap, int*) = P->BufFill; break; case lmLong: *va_arg (P->ap, long*) = P->BufFill; break; case lmIntMax: *va_arg (P->ap, intmax_t*) = P->BufFill; break; From f8fe1d1560aa79d497393771fa10ef09663da7bb Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 13 Dec 2023 22:57:32 +0800 Subject: [PATCH 408/520] Fixed missing diagnosis on function parameter lists with trailing commas. --- src/cc65/declare.c | 107 +++++++++++++++++++---------- test/err/bug2301-trailing-coma-1.c | 3 + test/err/bug2301-trailing-coma-2.c | 6 ++ 3 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 test/err/bug2301-trailing-coma-1.c create mode 100644 test/err/bug2301-trailing-coma-2.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index eee6dff9f..2746d1326 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1740,10 +1740,12 @@ static const Type* ParamTypeCvt (Type* T) static void ParseOldStyleParamList (FuncDesc* F) /* Parse an old-style (K&R) parameter list */ { - unsigned PrevErrorCount = ErrorCount; + if (CurTok.Tok == TOK_RPAREN) { + return; + } /* Parse params */ - while (CurTok.Tok != TOK_RPAREN) { + while (1) { /* List of identifiers expected */ if (CurTok.Tok == TOK_IDENT) { @@ -1768,29 +1770,33 @@ static void ParseOldStyleParamList (FuncDesc* F) } /* Check for more parameters */ - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { + if (CurTok.Tok != TOK_COMMA) { break; } - } + NextToken (); - /* Skip right paren. We must explicitly check for one here, since some of - ** the breaks above bail out without checking. - */ - ConsumeRParen (); + } +} + + + +static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) +/* Parse an old-style (K&R) function declarator declaration list */ +{ + if (CurTok.Tok == TOK_SEMI) { + /* No parameter declaration list */ + return; + } /* An optional list of type specifications follows */ while (CurTok.Tok != TOK_LCURLY) { DeclSpec Spec; + int NeedClean; /* Read the declaration specifier */ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); - /* Paremeters must have identifiers as names */ - Spec.Flags |= DS_NO_EMPTY_DECL; - /* We accept only auto and register as storage class specifiers, but ** we ignore all this, since we use auto anyway. */ @@ -1799,10 +1805,14 @@ static void ParseOldStyleParamList (FuncDesc* F) Error ("Illegal storage class"); } - /* Type must be specified */ + /* If we haven't got a type specifier yet, something must be wrong */ if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { - Error ("Expected declaration specifiers"); - break; + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; } /* Parse a comma separated variable list */ @@ -1811,7 +1821,12 @@ static void ParseOldStyleParamList (FuncDesc* F) Declarator Decl; /* Read the parameter */ - ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } /* Warn about new local type declaration */ if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { @@ -1820,9 +1835,9 @@ static void ParseOldStyleParamList (FuncDesc* F) } if (Decl.Ident[0] != '\0') { - /* We have a name given. Search for the symbol */ SymEntry* Param = FindLocalSym (Decl.Ident); + if (Param) { /* Check if we already changed the type for this ** parameter. @@ -1837,25 +1852,40 @@ static void ParseOldStyleParamList (FuncDesc* F) Error ("Redefinition for parameter '%s'", Param->Name); } } else { - Error ("Unknown identifier: '%s'", Decl.Ident); + Error ("Unknown parameter '%s'", Decl.Ident); + } + + /* Initialization is not allowed */ + if (CurTok.Tok == TOK_ASSIGN) { + Error ("Parameter '%s' cannot be initialized", Decl.Ident); + + /* Try some smart error recovery */ + SmartErrorSkip (0); } } - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { + /* Check for more declarators */ + if (CurTok.Tok != TOK_COMMA) { break; } + NextToken (); } - /* Variable list must be semicolon terminated */ - ConsumeSemi (); - } +EndOfDecl: + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + } - if (PrevErrorCount != ErrorCount && CurTok.Tok != TOK_LCURLY) { /* Try some smart error recovery */ - SmartErrorSkip (0); + if (NeedClean < 0) { + SmartErrorSkip (1); + } } } @@ -1864,8 +1894,12 @@ static void ParseOldStyleParamList (FuncDesc* F) static void ParseAnsiParamList (FuncDesc* F) /* Parse a new-style (ANSI) parameter list */ { + if (CurTok.Tok == TOK_RPAREN) { + return; + } + /* Parse params */ - while (CurTok.Tok != TOK_RPAREN) { + while (1) { DeclSpec Spec; Declarator Decl; @@ -1894,7 +1928,7 @@ static void ParseAnsiParamList (FuncDesc* F) /* Type must be specified */ if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { - Error ("Type specifier missing"); + Error ("Declaration specifier or '...' expected"); } /* Warn about new local type declaration */ @@ -1945,18 +1979,12 @@ static void ParseAnsiParamList (FuncDesc* F) } } - /* Check for more parameters */ - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { + /* Check for end of parameter type list */ + if (CurTok.Tok != TOK_COMMA) { break; } + NextToken (); } - - /* Skip right paren. We must explicitly check for one here, since some of - ** the breaks above bail out without checking. - */ - ConsumeRParen (); } @@ -1998,9 +2026,12 @@ static FuncDesc* ParseFuncDecl (void) if ((F->Flags & FD_OLDSTYLE) == 0) { /* New-style function */ ParseAnsiParamList (F); + ConsumeRParen (); } else { /* Old-style function */ ParseOldStyleParamList (F); + ConsumeRParen (); + ParseOldStyleParamDeclList (F); } PopLexicalLevel (); diff --git a/test/err/bug2301-trailing-coma-1.c b/test/err/bug2301-trailing-coma-1.c new file mode 100644 index 000000000..66cd4fae3 --- /dev/null +++ b/test/err/bug2301-trailing-coma-1.c @@ -0,0 +1,3 @@ +/* Bug #2301 - Function parameter list with an extra trailing comma should not compile */ + +int foo(int a,); /* Should fail */ diff --git a/test/err/bug2301-trailing-coma-2.c b/test/err/bug2301-trailing-coma-2.c new file mode 100644 index 000000000..40ca3d0ca --- /dev/null +++ b/test/err/bug2301-trailing-coma-2.c @@ -0,0 +1,6 @@ +/* Bug #2301 - Function parameter list with an extra trailing comma should not compile */ + +int bar(a,) int a; /* Should fail */ +{ + return a; +} From 1e4d1b4311149ca84cbd35267e447d58ae47f9e6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 14 Dec 2023 02:34:03 +0800 Subject: [PATCH 409/520] Fixed function declarator parser when a parameter has a function type. Ensured check on parameter lists without types in non-definition declarations. --- src/cc65/compile.c | 8 ++++ src/cc65/declare.c | 92 +++++++++++++++++++++++++++++----------------- test/err/bug2303.c | 1 + test/val/bug2302.c | 32 ++++++++++++++++ 4 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 test/err/bug2303.c create mode 100644 test/val/bug2302.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 0dc75273d..b996c78df 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -206,6 +206,14 @@ static void Parse (void) } else { /* Just a declaration */ Decl.StorageClass |= SC_DECL; + + FuncDef = GetFuncDesc (Decl.Type); + if ((FuncDef->Flags & (FD_EMPTY | FD_OLDSTYLE)) == FD_OLDSTYLE) { + /* A parameter list without types is only allowed in a + ** function definition. + */ + Error ("Parameter names without types in function declaration"); + } } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 2746d1326..8b7512730 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1989,7 +1989,7 @@ static void ParseAnsiParamList (FuncDesc* F) -static FuncDesc* ParseFuncDecl (void) +static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) /* Parse the argument list of a function with the enclosing parentheses */ { /* Create a new function descriptor */ @@ -2009,14 +2009,17 @@ static FuncDesc* ParseFuncDecl (void) /* Parameter list declared as void */ NextToken (); F->Flags |= FD_VOID_PARAM; - } else if (CurTok.Tok == TOK_IDENT && + } else if (Mode != DM_NO_IDENT && + CurTok.Tok == TOK_IDENT && (NextTok.Tok == TOK_COMMA || NextTok.Tok == TOK_RPAREN)) { /* If the identifier is a typedef, we have a new-style parameter list; ** if it's some other identifier, it's an old-style parameter list. + ** Note: Non-empty Old-style (K&R) parameter list is not allowed in + ** type names. */ SymEntry* Sym = FindSym (CurTok.Ident); if (Sym == 0 || !SymIsTypeDef (Sym)) { - /* Old-style (K&R) function. */ + /* Old-style (K&R) function */ F->Flags |= FD_OLDSTYLE; } } @@ -2026,13 +2029,20 @@ static FuncDesc* ParseFuncDecl (void) if ((F->Flags & FD_OLDSTYLE) == 0) { /* New-style function */ ParseAnsiParamList (F); - ConsumeRParen (); } else { /* Old-style function */ ParseOldStyleParamList (F); - ConsumeRParen (); + } + + if (!ConsumeRParen ()) { + /* Try some smart error recovery */ + SimpleErrorSkip (); + NextToken (); + } else if (Mode == DM_IDENT_OR_EMPTY && (F->Flags & FD_OLDSTYLE) != 0) { + /* Parameter declaration list is only allowed in function definitions */ ParseOldStyleParamDeclList (F); } + PopLexicalLevel (); /* Remember the last function parameter. We need it later for several @@ -2040,18 +2050,27 @@ static FuncDesc* ParseFuncDecl (void) ** more symbols are added to the table, it is easier if we remember it ** now, since it is currently the last entry in the symbol table. */ - F->LastParam = GetSymTab()->SymTail; + F->LastParam = GetSymTab ()->SymTail; + + /* Leave the lexical level remembering the symbol tables */ + RememberFunctionLevel (F); /* It is allowed to use incomplete types in function prototypes, so we ** won't always get to know the parameter sizes here and may do that later. */ F->Flags |= FD_INCOMPLETE_PARAM; - /* Leave the lexical level remembering the symbol tables */ - RememberFunctionLevel (F); + /* We cannot specify fastcall for variadic functions */ + if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { + Error ("Variadic functions cannot be __fastcall__"); + Qualifiers &= ~T_QUAL_FASTCALL; + } - /* Return the function descriptor */ - return F; + /* Add the function type. Be sure to bounds check the type buffer */ + NeedTypeSpace (D, 1); + D->Type[D->Index].C = T_FUNC | Qualifiers; + D->Type[D->Index].A.F = F; + ++D->Index; } @@ -2090,15 +2109,37 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } if (CurTok.Tok == TOK_LPAREN) { - NextToken (); + SymEntry* Entry; + /* An empty declaration cannot contain parentheses where an identifier ** would show up if it were a non-empty declaration. */ if (Mode == DM_IDENT_OR_EMPTY) { Spec->Flags |= DS_NO_EMPTY_DECL; } - DirectDecl (Spec, D, Mode); - ConsumeRParen (); + + /* We have to disambiguate the meanings of 'type (identifier' when + ** the identifier can be a typedef'ed parameter type specifier or + ** a declarator enclosed in parentheses in some cases. + */ + if (Mode == DM_IDENT_OR_EMPTY || /* If we are in a declaration... */ + NextTok.Tok == TOK_LPAREN || /* or the next token is one more paren... */ + NextTok.Tok == TOK_STAR || /* or a '*' ... */ + (NextTok.Tok == TOK_IDENT && /* or an identifier that... */ + ((Entry = FindSym (NextTok.Ident)) == 0 || /* is not a typedef. */ + !SymIsTypeDef (Entry)))) { + /* Parse the direct declarator in parentheses */ + NextToken (); + DirectDecl (Spec, D, Mode); + ConsumeRParen (); + } else { + /* This is a parameter type list in parentheses */ + ParseFuncDecl (D, Mode, Qualifiers); + + /* Qualifiers now used */ + Qualifiers = T_QUAL_NONE; + } + } else if (CurTok.Tok == TOK_IDENT) { if (Mode == DM_NO_IDENT) { Error ("Unexpected identifier in type name"); @@ -2119,28 +2160,11 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) { - /* Function declarator */ - FuncDesc* F; - - /* Parse the function declarator */ - F = ParseFuncDecl (); - - /* We cannot specify fastcall for variadic functions */ - if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { - Error ("Variadic functions cannot be __fastcall__"); - Qualifiers &= ~T_QUAL_FASTCALL; - } - - /* Add the function type. Be sure to bounds check the type buffer */ - NeedTypeSpace (D, 1); - D->Type[D->Index].C = T_FUNC | Qualifiers; - D->Type[D->Index].A.F = F; - ++D->Index; + ParseFuncDecl (D, Mode, Qualifiers); /* Qualifiers now used */ Qualifiers = T_QUAL_NONE; - } else { /* Array declarator */ long Size = UNSPECIFIED; @@ -2385,9 +2409,11 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } /* Try some smart error recovery */ - if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { + if (Mode == DM_NO_IDENT) { + return SimpleErrorSkip (); + } else if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { /* Skip to the end of the whole declaration if it is not part of a - ** parameter list or a type cast. + ** parameter list. */ return SmartErrorSkip (Mode == DM_IDENT_OR_EMPTY); } diff --git a/test/err/bug2303.c b/test/err/bug2303.c new file mode 100644 index 000000000..609725ad8 --- /dev/null +++ b/test/err/bug2303.c @@ -0,0 +1 @@ +int f(a); /* Should be an error */ diff --git a/test/val/bug2302.c b/test/val/bug2302.c new file mode 100644 index 000000000..3ca8a5572 --- /dev/null +++ b/test/val/bug2302.c @@ -0,0 +1,32 @@ +/* Bug #2302 - Parameters of function types not parsed correctly */ + +#include <stdio.h> + +typedef int A; +int zoo(A ()); /* OK: int zoo(int (*)()) */ +int zoo(A (())); /* OK: int zoo(int ((*)())) aka. int zoo(int (*)()) */ +int zoo(A (A)); /* OK: int zoo(int (*)(int)) */ +int zoo(A ((A))); /* OK: int zoo(int ((*)(int))) aka. int zoo(int (*)(int)) */ +int zoo(A A(A)); /* OK: int zoo(int (*A)(int)) */ +int zoo(A (*)(A)); /* OK: int zoo(int (*)(int)) */ +int zoo(A (*A)(A)); /* OK: int zoo(int (*A)(int)) */ +int zoo(A ((*A))(A)); /* OK: int zoo(int (*A)(int)) */ +int zoo(A ((((*((fp))))(A A)))) /* OK: int zoo(int (*fp)(int A)) */ +{ + return fp(42); +} + +int bar(int a) +{ + return a ^ 42; +} + +int main(void) +{ + int a = zoo((int (*)())bar); + if (a != 0) + { + printf("failed: a = %d\n", a); + } + return a; +} From a5746227dc15b2961b91350ae52a29df13227770 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 14 Dec 2023 21:27:48 +0800 Subject: [PATCH 410/520] Added warning on static functions that are used but not defined. --- src/cc65/compile.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 0dc75273d..94e389292 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -273,11 +273,12 @@ static void Parse (void) if (IsTypeVoid (Decl.Type)) { /* We cannot declare variables of type void */ Error ("Illegal type for variable '%s'", Decl.Ident); - Sym->Flags &= ~(SC_STORAGE | SC_DEF); + Sym->Flags |= SC_DEF; } else if (Size == 0 && SymIsDef (Sym) && !IsEmptiableObjectType (Decl.Type)) { /* Size is unknown. Is it an array? */ if (!IsTypeArray (Decl.Type)) { Error ("Variable '%s' has unknown size", Decl.Ident); + Sym->Flags |= SC_DEF; } } else { /* Check for enum forward declaration. @@ -539,10 +540,16 @@ void Compile (const char* FileName) Entry->Flags |= SC_DEF; } else if (!IsTypeArray (Entry->Type)) { /* Tentative declared variable is still of incomplete type */ - Error ("Definition of '%s' has type '%s' that is never completed", + Error ("Definition of '%s' never has its type '%s' completed", Entry->Name, GetFullTypeName (Entry->Type)); } + } else if (!SymIsDef (Entry) && (Entry->Flags & SC_FUNC) == SC_FUNC) { + /* Check for undefined functions */ + if ((Entry->Flags & (SC_EXTERN | SC_STATIC)) == SC_STATIC && SymIsRef (Entry)) { + Warning ("Static function '%s' used but never defined", + Entry->Name); + } } } From 1093d169ad3f03de4b86a69dba200bad62a1f261 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 12 Dec 2023 18:03:00 +0100 Subject: [PATCH 411/520] Fix BSS obliteration by mliparam during exec(). Using mliparam at this time could lead to corruption at the start of the new executed program if BSS is real full and mliparam is over $BB00. The fix is to open the file from the loader stub instead of doing it before the C library shutdown. --- libsrc/apple2/exec.s | 65 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/libsrc/apple2/exec.s b/libsrc/apple2/exec.s index 27a6487bd..b8875e9ca 100644 --- a/libsrc/apple2/exec.s +++ b/libsrc/apple2/exec.s @@ -121,35 +121,9 @@ setbuf: lda #$00 ; Low byte dex dex - ; Set I/O buffer - sta mliparam + MLI::OPEN::IO_BUFFER - stx mliparam + MLI::OPEN::IO_BUFFER+1 - - ; PATHNAME already set - .assert MLI::OPEN::PATHNAME = MLI::INFO::PATHNAME, error - - ; Lower file level to avoid program file - ; being closed by C library shutdown code - ldx LEVEL - stx level - beq :+ - dec LEVEL - - ; Open file -: lda #OPEN_CALL - ldx #OPEN_COUNT - jsr callmli - - ; Restore file level - ldx level - stx LEVEL - bcc :+ - jmp oserr - - ; Get and save fd -: lda mliparam + MLI::OPEN::REF_NUM - sta read_ref - sta close_ref + ; Set OPEN MLI call I/O buffer parameter + sta io_buffer + stx io_buffer+1 .ifdef __APPLE2ENH__ ; Calling the 80 column firmware needs the ROM switched @@ -194,14 +168,25 @@ setbuf: lda #$00 ; Low byte ; Initiate C library shutdown jmp _exit - .bss - -level : .res 1 - .rodata +source: + ; Open program file + ; PATHNAME parameter is already set (we reuse + ; the copy at $0280); IO_BUFFER has been setup + ; before shutting down the C library + jsr $BF00 + .byte OPEN_CALL + .word open_param + bcs error + + ; Copy REF_NUM to MLI READ and CLOSE parameters + lda open_ref + sta read_ref + sta close_ref + ; Read whole program file -source: jsr $BF00 + jsr $BF00 .byte READ_CALL .word read_param bcs error @@ -254,6 +239,14 @@ jump: jmp (data_buffer) file_type = * - source + target .byte $00 +open_param = * - source + target + .byte $03 ; PARAM_COUNT + .addr $0280 ; PATHNAME +io_buffer = * - source + target + .addr $0000 ; IO_BUFFER +open_ref = * - source + target + .byte $00 ; REF_NUM + read_param = * - source + target .byte $04 ; PARAM_COUNT read_ref = * - source + target @@ -285,4 +278,8 @@ size = * - source target = DOSWARM - size + ; Make sure that the loader isn't too big, and + ; fits in $300-$3D0 + .assert target >= $300, error + dosvec: jmp quit From 08341aae3026453cf1b7777e7fc31ef2395772af Mon Sep 17 00:00:00 2001 From: paul moore <paulmoore100@hotmail.com> Date: Thu, 14 Dec 2023 14:25:35 -0800 Subject: [PATCH 412/520] second try at fixing win64 build --- .github/workflows/build-on-pull-request.yml | 16 ++++-- src/ar65.vcxproj | 33 ++++++++++++ src/ca65.vcxproj | 33 ++++++++++++ src/cc65.sln | 60 ++++++++++++++++++++- src/cc65.vcxproj | 35 +++++++++++- src/chrcvt65.vcxproj | 33 ++++++++++++ src/cl65.vcxproj | 33 ++++++++++++ src/cl65/main.c | 2 +- src/co65.vcxproj | 31 +++++++++++ src/common.vcxproj | 29 ++++++++++ src/common/filetype.c | 3 +- src/common/filetype.h | 2 +- src/da65.vcxproj | 33 ++++++++++++ src/grc65.vcxproj | 33 ++++++++++++ src/ld65.vcxproj | 39 +++++++++++++- src/ld65/main.c | 2 +- src/od65.vcxproj | 33 ++++++++++++ src/sim65.vcxproj | 33 ++++++++++++ src/sp65.vcxproj | 35 +++++++++++- 19 files changed, 504 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 55be5db1e..55ee3df2d 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -67,8 +67,16 @@ jobs: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.1 - - name: Build app (debug) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug + - name: Build app (x86 debug) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug Platform=Win32 + + - name: Build app (x86 release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release Platform=Win32 + + - name: Build app (x64 release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug Platform=x64 + + - name: Build app (x64 release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release Platform=x64 + - - name: Build app (release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release diff --git a/src/ar65.vcxproj b/src/ar65.vcxproj index 27d12dadc..0fd788e06 100644 --- a/src/ar65.vcxproj +++ b/src/ar65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{5E8C19C6-B167-440C-8BEF-3CBF109CDB49}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="ar65\add.h" /> <ClInclude Include="ar65\del.h" /> diff --git a/src/ca65.vcxproj b/src/ca65.vcxproj index 3cc6019f2..d8fd39303 100644 --- a/src/ca65.vcxproj +++ b/src/ca65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="ca65\anonname.h" /> <ClInclude Include="ca65\asserts.h" /> diff --git a/src/cc65.sln b/src/cc65.sln index 4ae2816ad..8e31ef909 100644 --- a/src/cc65.sln +++ b/src/cc65.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common.vcxproj", "{71DC1F68-BFC4-478C-8655-C8E9C9654D2B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cc65", "cc65.vcxproj", "{B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}" @@ -66,61 +68,115 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|Win32.ActiveCfg = Debug|Win32 {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|Win32.Build.0 = Debug|Win32 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|x64.ActiveCfg = Debug|x64 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|x64.Build.0 = Debug|x64 {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|Win32.ActiveCfg = Release|Win32 {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|Win32.Build.0 = Release|Win32 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|x64.ActiveCfg = Release|x64 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|x64.Build.0 = Release|x64 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|Win32.ActiveCfg = Debug|Win32 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|Win32.Build.0 = Debug|Win32 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|x64.ActiveCfg = Debug|x64 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|x64.Build.0 = Debug|x64 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|Win32.ActiveCfg = Release|Win32 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|Win32.Build.0 = Release|Win32 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|x64.ActiveCfg = Release|x64 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|x64.Build.0 = Release|x64 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|Win32.ActiveCfg = Debug|Win32 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|Win32.Build.0 = Debug|Win32 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|x64.ActiveCfg = Debug|x64 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|x64.Build.0 = Debug|x64 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|Win32.ActiveCfg = Release|Win32 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|Win32.Build.0 = Release|Win32 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|x64.ActiveCfg = Release|x64 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|x64.Build.0 = Release|x64 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|Win32.ActiveCfg = Debug|Win32 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|Win32.Build.0 = Debug|Win32 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|x64.ActiveCfg = Debug|x64 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|x64.Build.0 = Debug|x64 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|Win32.ActiveCfg = Release|Win32 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|Win32.Build.0 = Release|Win32 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|x64.ActiveCfg = Release|x64 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|x64.Build.0 = Release|x64 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|Win32.ActiveCfg = Debug|Win32 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|Win32.Build.0 = Debug|Win32 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|x64.ActiveCfg = Debug|x64 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|x64.Build.0 = Debug|x64 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|Win32.ActiveCfg = Release|Win32 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|Win32.Build.0 = Release|Win32 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|x64.ActiveCfg = Release|x64 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|x64.Build.0 = Release|x64 {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|Win32.ActiveCfg = Debug|Win32 {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|Win32.Build.0 = Debug|Win32 + {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|x64.ActiveCfg = Debug|x64 + {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|x64.Build.0 = Debug|x64 {F657912F-050A-488B-B203-50ED5715CDD7}.Release|Win32.ActiveCfg = Release|Win32 {F657912F-050A-488B-B203-50ED5715CDD7}.Release|Win32.Build.0 = Release|Win32 + {F657912F-050A-488B-B203-50ED5715CDD7}.Release|x64.ActiveCfg = Release|x64 + {F657912F-050A-488B-B203-50ED5715CDD7}.Release|x64.Build.0 = Release|x64 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|Win32.ActiveCfg = Debug|Win32 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|Win32.Build.0 = Debug|Win32 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|x64.ActiveCfg = Debug|x64 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|x64.Build.0 = Debug|x64 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|Win32.ActiveCfg = Release|Win32 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|Win32.Build.0 = Release|Win32 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|x64.ActiveCfg = Release|x64 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|x64.Build.0 = Release|x64 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|Win32.ActiveCfg = Debug|Win32 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|Win32.Build.0 = Debug|Win32 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|x64.ActiveCfg = Debug|x64 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|x64.Build.0 = Debug|x64 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|Win32.ActiveCfg = Release|Win32 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|Win32.Build.0 = Release|Win32 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|x64.ActiveCfg = Release|x64 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|x64.Build.0 = Release|x64 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|Win32.ActiveCfg = Debug|Win32 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|Win32.Build.0 = Debug|Win32 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|x64.ActiveCfg = Debug|x64 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|x64.Build.0 = Debug|x64 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|Win32.ActiveCfg = Release|Win32 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|Win32.Build.0 = Release|Win32 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|x64.ActiveCfg = Release|x64 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|x64.Build.0 = Release|x64 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|Win32.ActiveCfg = Debug|Win32 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|Win32.Build.0 = Debug|Win32 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|x64.ActiveCfg = Debug|x64 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|x64.Build.0 = Debug|x64 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|Win32.ActiveCfg = Release|Win32 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|Win32.Build.0 = Release|Win32 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|x64.ActiveCfg = Release|x64 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|x64.Build.0 = Release|x64 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|Win32.ActiveCfg = Debug|Win32 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|Win32.Build.0 = Debug|Win32 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|x64.ActiveCfg = Debug|x64 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|x64.Build.0 = Debug|x64 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|Win32.ActiveCfg = Release|Win32 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|Win32.Build.0 = Release|Win32 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|x64.ActiveCfg = Release|x64 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|x64.Build.0 = Release|x64 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|Win32.ActiveCfg = Debug|Win32 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|Win32.Build.0 = Debug|Win32 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|x64.ActiveCfg = Debug|x64 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|x64.Build.0 = Debug|x64 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|Win32.ActiveCfg = Release|Win32 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|Win32.Build.0 = Release|Win32 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|x64.ActiveCfg = Release|x64 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|x64.Build.0 = Release|x64 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|Win32.ActiveCfg = Debug|Win32 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|Win32.Build.0 = Debug|Win32 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|x64.ActiveCfg = Debug|x64 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|x64.Build.0 = Debug|x64 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|Win32.ActiveCfg = Release|Win32 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|Win32.Build.0 = Release|Win32 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|x64.ActiveCfg = Release|x64 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 9c1719538..75ee92276 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="cc65\anonname.h" /> <ClInclude Include="cc65\asmcode.h" /> @@ -216,4 +249,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project> \ No newline at end of file diff --git a/src/chrcvt65.vcxproj b/src/chrcvt65.vcxproj index 1e5c753b5..d44380c4e 100644 --- a/src/chrcvt65.vcxproj +++ b/src/chrcvt65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="chrcvt65\error.c" /> <ClCompile Include="chrcvt65\main.c" /> diff --git a/src/cl65.vcxproj b/src/cl65.vcxproj index 67b7eb087..688926557 100644 --- a/src/cl65.vcxproj +++ b/src/cl65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{F657912F-050A-488B-B203-50ED5715CDD7}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="cl65\error.c" /> <ClCompile Include="cl65\global.c" /> diff --git a/src/cl65/main.c b/src/cl65/main.c index 553fb9ca6..0148ad1e3 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -1610,7 +1610,7 @@ int main (int argc, char* argv []) } /* Determine the file type by the extension */ - switch (GetFileType (Arg)) { + switch (GetTypeOfFile (Arg)) { case FILETYPE_C: /* Compile the file */ diff --git a/src/co65.vcxproj b/src/co65.vcxproj index 9f5959d89..fa6a1e315 100644 --- a/src/co65.vcxproj +++ b/src/co65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,14 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +71,14 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="co65\convert.c" /> <ClCompile Include="co65\error.c" /> diff --git a/src/common.vcxproj b/src/common.vcxproj index df99fc4a9..6098c98a0 100644 --- a/src/common.vcxproj +++ b/src/common.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{71DC1F68-BFC4-478C-8655-C8E9C9654D2B}</ProjectGuid> @@ -22,16 +30,25 @@ <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -39,11 +56,23 @@ <PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="common\abend.h" /> <ClInclude Include="common\addrsize.h" /> diff --git a/src/common/filetype.c b/src/common/filetype.c index a5bac640d..ae8b636dc 100644 --- a/src/common/filetype.c +++ b/src/common/filetype.c @@ -92,8 +92,7 @@ static const FileId TypeTable[] = { /*****************************************************************************/ - -FILETYPE GetFileType (const char* Name) +FILETYPE GetTypeOfFile (const char* Name) /* Determine the type of the given file by looking at the name. If the file ** type could not be determined, the function returns FILETYPE_UNKOWN. */ diff --git a/src/common/filetype.h b/src/common/filetype.h index f4beae73a..0738078ba 100644 --- a/src/common/filetype.h +++ b/src/common/filetype.h @@ -63,7 +63,7 @@ typedef enum { -FILETYPE GetFileType (const char* Name); +FILETYPE GetTypeOfFile (const char* Name); /* Determine the type of the given file by looking at the name. If the file ** type could not be determined, the function returns FILETYPE_UNKOWN. */ diff --git a/src/da65.vcxproj b/src/da65.vcxproj index a40daf1d6..9d2b0ded6 100644 --- a/src/da65.vcxproj +++ b/src/da65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{0BCFB793-2B25-40E2-B265-75848824AC4C}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="da65\asminc.c" /> <ClCompile Include="da65\attrtab.c" /> diff --git a/src/grc65.vcxproj b/src/grc65.vcxproj index fbd44fa3e..742003899 100644 --- a/src/grc65.vcxproj +++ b/src/grc65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="grc65\main.c" /> </ItemGroup> diff --git a/src/ld65.vcxproj b/src/ld65.vcxproj index 9e4b08621..e2f06b198 100644 --- a/src/ld65.vcxproj +++ b/src/ld65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{26C749A0-814C-47A2-9D36-AE92AE932FE4}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -38,6 +53,19 @@ </ClCompile> <Link> <SubSystem>Console</SubSystem> + <MapFileName>ld65.map</MapFileName> + <MapExports>true</MapExports> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <MapFileName>ld65.map</MapFileName> + <MapExports>true</MapExports> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -48,6 +76,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="ld65\asserts.h" /> <ClInclude Include="ld65\bin.h" /> @@ -118,4 +155,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project> \ No newline at end of file diff --git a/src/ld65/main.c b/src/ld65/main.c index 70e9c84d1..5632f4961 100644 --- a/src/ld65/main.c +++ b/src/ld65/main.c @@ -189,7 +189,7 @@ static void LinkFile (const char* Name, FILETYPE Type) /* If we don't know the file type, determine it from the extension */ if (Type == FILETYPE_UNKNOWN) { - Type = GetFileType (Name); + Type = GetTypeOfFile (Name); } /* For known file types, search the file in the directory list */ diff --git a/src/od65.vcxproj b/src/od65.vcxproj index 1a1527067..409b5038e 100644 --- a/src/od65.vcxproj +++ b/src/od65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="od65\dump.c" /> <ClCompile Include="od65\error.c" /> diff --git a/src/sim65.vcxproj b/src/sim65.vcxproj index 97fc3855a..7bc489398 100644 --- a/src/sim65.vcxproj +++ b/src/sim65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{002A366E-2863-46A8-BDDE-DDF534AAEC73}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="sim65\6502.h" /> <ClInclude Include="sim65\error.h" /> diff --git a/src/sp65.vcxproj b/src/sp65.vcxproj index a9f0919a3..21816ee2b 100644 --- a/src/sp65.vcxproj +++ b/src/sp65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="sp65\asm.c" /> <ClCompile Include="sp65\attr.c" /> @@ -99,4 +132,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project> \ No newline at end of file From 269786a5aeefc7171dfd22662a26513443b98817 Mon Sep 17 00:00:00 2001 From: paul moore <paulmoore100@hotmail.com> Date: Thu, 14 Dec 2023 14:38:24 -0800 Subject: [PATCH 413/520] fix msbuild syntax --- .github/workflows/build-on-pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 55ee3df2d..f1dd58ee5 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -68,15 +68,15 @@ jobs: uses: microsoft/setup-msbuild@v1.1 - name: Build app (x86 debug) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug Platform=Win32 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -Platform=Win32 - name: Build app (x86 release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release Platform=Win32 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -Platform=Win32 - name: Build app (x64 release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug Platform=x64 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -Platform=x64 - name: Build app (x64 release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release Platform=x64 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -Platform=x64 From 103d4b82c5889ae3acf34c6b281f3f9879d853d4 Mon Sep 17 00:00:00 2001 From: paul moore <paulmoore100@hotmail.com> Date: Thu, 14 Dec 2023 14:43:58 -0800 Subject: [PATCH 414/520] more msbuild syntax fixes --- .github/workflows/build-on-pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index f1dd58ee5..045bc048d 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -68,15 +68,15 @@ jobs: uses: microsoft/setup-msbuild@v1.1 - name: Build app (x86 debug) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -Platform=Win32 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=Win32 - name: Build app (x86 release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -Platform=Win32 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=Win32 - name: Build app (x64 release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -Platform=x64 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=x64 - name: Build app (x64 release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -Platform=x64 + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=x64 From 0d74b84ce4c484f0ef4a23326c4327d05ac4358a Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:18:25 -0500 Subject: [PATCH 415/520] Test of .struct and .union features. Update documentation with more examples, better clarity, and fixes to incorrect data. --- doc/ca65.sgml | 76 ++++++++++++--- test/asm/val/struct.s | 222 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+), 12 deletions(-) create mode 100644 test/asm/val/struct.s diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 104c59bbd..e54b308d4 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -4086,8 +4086,10 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" <sect1><tt>.TAG</tt><label id=".TAG"><p> - Allocate space for a struct or union. - + Allocate space for a struct or union. This is equivalent to + <tt><ref id=".RES" name=".RES"></tt> with the + <tt><ref id=".SIZEOF" name=".SIZEOF"></tt> of a struct. + Example: <tscreen><verb> @@ -4100,6 +4102,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" .tag Point ; Allocate 4 bytes </verb></tscreen> + See: <ref id="structs" name=""Structs and unions""> <sect1><tt>.UNDEF, .UNDEFINE</tt><label id=".UNDEFINE"><p> @@ -4865,10 +4868,15 @@ compiler, depending on the target system selected: Structs and unions are special forms of <ref id="scopes" name="scopes">. They are, to some degree, comparable to their C counterparts. Both have a list of -members. Each member allocates storage, and optionally may have a name whose -value, in the case of a struct, usually is the storage offset from the -beginning, and in the case of a union, doesn't change, and usually is zero. +members. Each member allocates storage, and optionally may have a name. +Each named member has a constant value equal to the storage offset from the +beginning of the structure. In the case of a union, all members are placed at +the same offset, typically 0. + +Each named member also has a storage size which can be accessed with the +<ref id=".SIZEOF" name=".SIZEOF"></tt> operator. The struct or union itself also +has a <tt/.SIZEOF/ indicating its total storage size. <sect1>Declaration<p> @@ -4895,8 +4903,9 @@ A struct or union may not necessarily have a name. If it is anonymous, no local scope is opened; the identifiers used to name the members are placed into the current scope instead. -A struct may contain unnamed members and definitions of local structs/unions. -The storage allocators may contain a multiplier, as in the example below: +Storage allocators may contain a multiplier. A struct may also contain members +and definitions of local structs/unions. Example: + <tscreen><verb> .struct Circle .struct Point @@ -4905,7 +4914,8 @@ The storage allocators may contain a multiplier, as in the example below: Radius .word .endstruct </verb></tscreen> -The size of the Circle struct is 6 (three words). + +In this example the size of the Circle struct is 6 (three words). <sect1>The storage allocator keywords<p> @@ -4915,7 +4925,7 @@ The size of the Circle struct is 6 (three words). <tag/.BYTE, .RES/ Allocates multiples of 1 byte. <tt/.RES/ requires an operand. - <tag/.DBYTE, .WORD, .ADDR/ + <tag/.DBYT, .WORD, .ADDR/ Allocates multiples of 2 bytes. <tag/.FARADDR/ @@ -4924,6 +4934,15 @@ The size of the Circle struct is 6 (three words). <tag/.DWORD/ Allocates multiples of 4 bytes. + <tag/.TAG/ + Allocates a previously defined struct. + + <tag/.STRUCT, .UNION/ + Begins a nested .struct or .union definition, and allocates it. + Note that its member offset values will begin at 0, unless this nested + structure is anonymous, in which case they will instead become members of + the enclosing scope. + </descrip> @@ -4968,13 +4987,46 @@ name=".TAG"> directive. C: .tag Circle </verb></tscreen> -Currently, members are just offsets from the start of the struct or union. To +Members are just offsets from the start of the struct or union. To access a field of a struct, the member offset must be added to the address of the struct variable itself: <tscreen><verb> - lda C+Circle::Radius ; Load circle radius into A + lda C + Circle::Radius ; Load circle radius + lda C + Circle::Origin + Point::ycoord ; Load circle origin.ycoord </verb></tscreen> -That may change in a future version of the assembler. + +Nested structures or unions are treated differently depending on whether they +are anonymous. If named, a new structure definition is created within the +enclosing scope, with its offsets beginning at 0. If anonymous, the members of +the new structure are added to the enclosing scope instead. Example: + +<tscreen><verb> + .struct Object + member .byte ; Object::member = 0 + named .struct Point ; Object::named = 1 + xcoord .word ; Object::Point::xcoord = 0 + ycoord .word ; Object::Point::ycoord = 2 + .endstruct + unnamed .struct ; Object::unnamed = 5 + un1 .word ; Object::un1 = 5 + un2 .word ; Object::un2 = 7 + .endstruct + .struct + un3 .word ; Object::un3 = 9 + .endstruct + .endstruct + + lda O + Object::named + Object::Point::ycoord + lda O + Object::un2 +</verb></tscreen> + +In this example, the first nested structure is named "Point", and its member +offsets begin at 0. On the other hand, the two anonymous structures simply +continue to add members to the enclosing Object structure. + +Note that an anonymous structure does not need a member name, since all of its +members become part of the enclosing structure. The "unnamed" member in the +example is redundantly the same offset as its first member "un1. <sect1>Limitations<p> diff --git a/test/asm/val/struct.s b/test/asm/val/struct.s new file mode 100644 index 000000000..b2bf08ea0 --- /dev/null +++ b/test/asm/val/struct.s @@ -0,0 +1,222 @@ +; test .struct feature + +.code + +; exit with 0 +.export _main +_main: + lda #0 + tax + rts + +; test storage allocator sizes and offsets + +.struct Storage + mb1 .byte + mb5 .byte 5 + mr1 .res 1 + mr5 .res 5 + mdb1 .dbyt + mdb5 .dbyt 5 + mw1 .word + mw5 .word 5 + ma1 .addr + ma5 .addr 5 + mf1 .faraddr + mf5 .faraddr 5 + mdw1 .dword + mdw5 .dword 5 +.endstruct + +.assert .sizeof(Storage::mb1) = 1, error, ".struct .byte member has unexpected .sizeof" +.assert .sizeof(Storage::mb5) = 5, error, ".struct .byte 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mr1) = 1, error, ".struct .res 1 member has unexpected .sizeof" +.assert .sizeof(Storage::mr5) = 5, error, ".struct .res 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mdb1) = 2, error, ".struct .dbyt member has unexpected .sizeof" +.assert .sizeof(Storage::mdb5) = 10, error, ".struct .dbyt 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mw1) = 2, error, ".struct .word member has unexpected .sizeof" +.assert .sizeof(Storage::mw5) = 10, error, ".struct .word 5 member has unexpected .sizeof" +.assert .sizeof(Storage::ma1) = 2, error, ".struct .addr member has unexpected .sizeof" +.assert .sizeof(Storage::ma5) = 10, error, ".struct .addr 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mf1) = 3, error, ".struct .faraddr member has unexpected .sizeof" +.assert .sizeof(Storage::mf5) = 15, error, ".struct .faraddr 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mdw1) = 4, error, ".struct .dword member has unexpected .sizeof" +.assert .sizeof(Storage::mdw5) = 20, error, ".struct .dword 5 member has unexpected .sizeof" + +.assert Storage::mb1 = 0, error, ".struct storage offset is incorrect" +.assert Storage::mb5 = Storage::mb1 + .sizeof(Storage::mb1), error, ".struct storage offset is incorrect" +.assert Storage::mr1 = Storage::mb5 + .sizeof(Storage::mb5), error, ".struct storage offset is incorrect" +.assert Storage::mr5 = Storage::mr1 + .sizeof(Storage::mr1), error, ".struct storage offset is incorrect" +.assert Storage::mdb1 = Storage::mr5 + .sizeof(Storage::mr5), error, ".struct storage offset is incorrect" +.assert Storage::mdb5 = Storage::mdb1 + .sizeof(Storage::mdb1), error, ".struct storage offset is incorrect" +.assert Storage::mw1 = Storage::mdb5 + .sizeof(Storage::mdb5), error, ".struct storage offset is incorrect" +.assert Storage::mw5 = Storage::mw1 + .sizeof(Storage::mw1), error, ".struct storage offset is incorrect" +.assert Storage::ma1 = Storage::mw5 + .sizeof(Storage::mw5), error, ".struct storage offset is incorrect" +.assert Storage::ma5 = Storage::ma1 + .sizeof(Storage::ma1), error, ".struct storage offset is incorrect" +.assert Storage::mf1 = Storage::ma5 + .sizeof(Storage::ma5), error, ".struct storage offset is incorrect" +.assert Storage::mf5 = Storage::mf1 + .sizeof(Storage::mf1), error, ".struct storage offset is incorrect" +.assert Storage::mdw1 = Storage::mf5 + .sizeof(Storage::mf5), error, ".struct storage offset is incorrect" +.assert Storage::mdw5 = Storage::mdw1 + .sizeof(Storage::mdw1), error, ".struct storage offset is incorrect" +.assert .sizeof(Storage) = Storage::mdw5 + .sizeof(Storage::mdw5), error, ".struct has unexpected .sizeof" + +; test union offset and size + +.union UStorage + mb1 .byte + mb5 .byte 5 + mr1 .res 1 + mr5 .res 5 + mdb1 .dbyt + mdb5 .dbyt 5 + mw1 .word + mw5 .word 5 + ma1 .addr + ma5 .addr 5 + mf1 .faraddr + mf5 .faraddr 5 + mdw1 .dword + mdw5 .dword 5 +.endunion + +.assert .sizeof(UStorage::mb1) = 1, error, ".union .byte member has unexpected .sizeof" +.assert .sizeof(UStorage::mb5) = 5, error, ".union .byte 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mr1) = 1, error, ".union .res 1 member has unexpected .sizeof" +.assert .sizeof(UStorage::mr5) = 5, error, ".union .res 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mdb1) = 2, error, ".union .dbyt member has unexpected .sizeof" +.assert .sizeof(UStorage::mdb5) = 10, error, ".union .dbyt 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mw1) = 2, error, ".union .word member has unexpected .sizeof" +.assert .sizeof(UStorage::mw5) = 10, error, ".union .word 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::ma1) = 2, error, ".union .addr member has unexpected .sizeof" +.assert .sizeof(UStorage::ma5) = 10, error, ".union .addr 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mf1) = 3, error, ".union .faraddr member has unexpected .sizeof" +.assert .sizeof(UStorage::mf5) = 15, error, ".union .faraddr 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mdw1) = 4, error, ".union .dword member has unexpected .sizeof" +.assert .sizeof(UStorage::mdw5) = 20, error, ".union .dword 5 member has unexpected .sizeof" +.assert .sizeof(UStorage) = 20, error, ".union has unexpected .sizeof" + +.assert UStorage::mb1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mb5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mr1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mr5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdb1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdb5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mw1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mw5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::ma1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::ma5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mf1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mf5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdw1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdw5 = 0, error, ".union storage offset is incorrect" + +; test tag + +storage: .tag Storage +.assert (*-storage)=.sizeof(Storage), error, ".tag reserved size incorrect" + +; test nested structures + +.struct Point + xc .word + yc .word +.endstruct + +.struct Nested + pad .res 13 + tag .tag Point + ch .struct Child + ca .word ; offset = 0 + gch .struct Grandchild + gca .word ; offset = 0 + gcb .byte + .endstruct + cb .byte + .endstruct + anon .struct + aa .dword ; offset = Nested::anon (anonymous .struct) + ab .dword + .endstruct + chu .union Chunion + ua .byte ; offset = 0 + ub .dword + .endunion + chanon .union + uc .byte ; offset = Nested::chanon + ud .dword + .endunion + last .byte +.endstruct + +.assert Nested::pad = 0, error, "Nested .struct has unexpected starting offset" +.assert Nested::Child::ca = 0, error, "Nested .struct has unexpected starting offset" +.assert Nested::Child::Grandchild::gca = 0, error, "Nested .struct has unexpected starting offset" + +.assert .sizeof(Nested::tag) = .sizeof(Point), error, ".tag in .struct has unexpected .sizeof" +.assert .sizeof(Nested::Child::Grandchild) = 2 + 1, error, "Nested .struct has unexpected .sizeof" +.assert .sizeof(Nested::Child) = 2 + 1 + .sizeof(Nested::Child::Grandchild), error, "Nested .struct has unpexpected .sizeof" +.assert .sizeof(Nested::ch) = .sizeof(Nested::Child), error, "Nested .struct has unexpected member .sizeof" +.assert .sizeof(Nested::Child::gch) = .sizeof(Nested::Child::Grandchild), error, "Nested .struct has unexpected member .sizeof" +.assert .sizeof(Nested::anon) = 8, error, "Nested anonymous member .struct has unexpected .sizeof" +.assert .sizeof(Nested::aa) = 4, error, "Nested anonymous .struct member has unexpected .sizeof" +.assert .sizeof(Nested::ab) = 4, error, "Nested anonymous .struct member has unexpected .sizeof" +.assert .sizeof(Nested::Chunion) = 4, error, "Nested .union has unexpected .sizeof" +.assert .sizeof(Nested::chu) = .sizeof(Nested::Chunion), error, "Nested member .union has unexpected .sizeof" +.assert .sizeof(Nested::chanon) = 4, error, "Nested anonymous member .union as unexpected .sizeof" + +.assert Nested::tag = Nested::pad + .sizeof(Nested::pad), error, ".tag within .struct has unexpected offset" +.assert Nested::ch = Nested::tag + .sizeof(Nested::tag), error, "Nested .struct has unexpected offset" +.assert Nested::anon = Nested::ch + .sizeof(Nested::ch), error, "Nested anonymous member .struct has unexpected offset" +.assert Nested::aa = Nested::anon, error, "Nested anonymous .struct member has unexpected offset" +.assert Nested::ab = Nested::aa + .sizeof(Nested::aa), error, "Nested anonymous .struct member has unexpected offset" +.assert Nested::chu = Nested::ab + .sizeof(Nested::ab), error, "Nested member .union has unexpected offset" +.assert Nested::chanon = Nested::chu + .sizeof(Nested::Chunion), error, "Nested anonymous member .union has unexpected offset" +.assert Nested::uc = Nested::chanon, error, "Nested anonymous .union member has unexpected offset" +.assert Nested::ud = Nested::chanon, error, "Nested anonymous .union member has unexpected offset" +.assert Nested::last = Nested::ud + .sizeof(Nested::ud), error, ".struct member has unexpected offset after anonymous nested .struct" + +; test .org + +start: + +.struct OrgStruct + ma .byte + mb .byte + .org $1234 + mc .byte + md .byte + .struct Nested + me .byte + .org $5678 + mf .byte + mg .byte + .endstruct + mh .byte +.endstruct + +.assert start <> (OrgStruct::mh+1), error, "Fatal test error: accidental code position conflict, move OrgStruct .org to another arbitrary address." +.assert * = start, error, ".org within .struct does not return to previous location at .endstruct" +.assert OrgStruct::ma = 0, error, ".struct with .org has unexpected offset" +.assert OrgStruct::mb = 1, error, ".struct with .org has unexpected offset" +.assert OrgStruct::mc = $1234, error, ".struct with .org has unexpected offset" +.assert OrgStruct::md = $1235, error, ".struct with .org has unexpected offset" +.assert OrgStruct::Nested::me = 0, error, "Nested .struct with .org has unexpected offset" +.assert OrgStruct::Nested::mf = $5678, error, "Nested .struct with .org has unexpected offset" +.assert OrgStruct::Nested::mg = $5679, error, "Nested .struct with .org has unexpected offset" +.assert OrgStruct::mh = $1239, error, ".struct with .org has unexpected offset" +.assert .sizeof(OrgStruct) = 8, error, ".struct with .org has unexpected .sizeof" + +.union OrgUnion + ma .byte + mb .word + .org $1234 + mc .byte + md .word +.endunion + +.assert start <> OrgUnion::md, error, "Fatal test error: accidental code position conflict, move OrgUnion .org to another arbitrary address." +.assert * = start, error, ".org within .union does not return to previous location at .endunion" +.assert OrgUnion::ma = 0, error, ".union with .org has unexpected offset" +.assert OrgUnion::mb = 0, error, ".union with .org has unexpected offset" +.assert OrgUnion::mc = $1234, error, ".union with .org has unexpected offset" +.assert OrgUnion::md = $1234, error, ".union with .org has unexpected offset" +.assert .sizeof(OrgUnion) = 2, error, ".union with .org has unexpected .sizeof" From 6cb8717c24d415448f451edbe3f949e9188fe1c1 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:25:21 -0500 Subject: [PATCH 416/520] fix dangling space, mention .union in test comment --- doc/ca65.sgml | 2 +- test/asm/val/struct.s | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index e54b308d4..dde782a57 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -4089,7 +4089,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" Allocate space for a struct or union. This is equivalent to <tt><ref id=".RES" name=".RES"></tt> with the <tt><ref id=".SIZEOF" name=".SIZEOF"></tt> of a struct. - + Example: <tscreen><verb> diff --git a/test/asm/val/struct.s b/test/asm/val/struct.s index b2bf08ea0..b6ef6f45d 100644 --- a/test/asm/val/struct.s +++ b/test/asm/val/struct.s @@ -1,4 +1,4 @@ -; test .struct feature +; test .struct and .union features .code From f6838be1621f4ef57c1224209c50e41ee0bcfe4d Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:32:52 -0500 Subject: [PATCH 417/520] missing <tt> --- doc/ca65.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index dde782a57..9be63d035 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -4875,8 +4875,8 @@ beginning of the structure. In the case of a union, all members are placed at the same offset, typically 0. Each named member also has a storage size which can be accessed with the -<ref id=".SIZEOF" name=".SIZEOF"></tt> operator. The struct or union itself also -has a <tt/.SIZEOF/ indicating its total storage size. +<tt><ref id=".SIZEOF" name=".SIZEOF"></tt> operator. The struct or union itself +also has a <tt/.SIZEOF/ indicating its total storage size. <sect1>Declaration<p> From 0ff1b20f2a2420bbff8e162c54e52fa24561de3e Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:52:57 -0500 Subject: [PATCH 418/520] nested struct example needs a .tag --- doc/ca65.sgml | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 9be63d035..3d4899366 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -5016,6 +5016,7 @@ the new structure are added to the enclosing scope instead. Example: .endstruct .endstruct +O: .tag Object lda O + Object::named + Object::Point::ycoord lda O + Object::un2 </verb></tscreen> From 302c4f7409b898ea5c1d63009b883e5887c2ea78 Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 17 Dec 2023 05:01:06 -0500 Subject: [PATCH 419/520] clarify offsets of anonymous nested scope, missing quotes for names --- doc/ca65.sgml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 3d4899366..3120d9dd4 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -4998,7 +4998,8 @@ the struct variable itself: Nested structures or unions are treated differently depending on whether they are anonymous. If named, a new structure definition is created within the enclosing scope, with its offsets beginning at 0. If anonymous, the members of -the new structure are added to the enclosing scope instead. Example: +the new structure are added to the enclosing scope instead, with offsets +continuing through that scope. Example: <tscreen><verb> .struct Object @@ -5023,11 +5024,11 @@ O: .tag Object In this example, the first nested structure is named "Point", and its member offsets begin at 0. On the other hand, the two anonymous structures simply -continue to add members to the enclosing Object structure. +continue to add members to the enclosing "Object" structure. Note that an anonymous structure does not need a member name, since all of its members become part of the enclosing structure. The "unnamed" member in the -example is redundantly the same offset as its first member "un1. +example is redundantly the same offset as its first member "un1". <sect1>Limitations<p> From 9892c8f6c45f3ad61c49ce78d22881161d21dbab Mon Sep 17 00:00:00 2001 From: bbbradsmith <bbbradsmith@users.noreply.github.com> Date: Sun, 17 Dec 2023 05:40:00 -0500 Subject: [PATCH 420/520] using less generic names for the example to avoid confusion, adding cautionary example for what I think is the most error prone case --- doc/ca65.sgml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 3120d9dd4..c5c6893da 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -5003,23 +5003,29 @@ continuing through that scope. Example: <tscreen><verb> .struct Object - member .byte ; Object::member = 0 - named .struct Point ; Object::named = 1 + id .byte ; Object::id = 0 + target .struct Point ; Object::target = 1 xcoord .word ; Object::Point::xcoord = 0 ycoord .word ; Object::Point::ycoord = 2 .endstruct - unnamed .struct ; Object::unnamed = 5 - un1 .word ; Object::un1 = 5 - un2 .word ; Object::un2 = 7 + cost .struct ; Object::cost = 5 + price .word ; Object::price = 5 + tax .word ; Object::tax = 7 .endstruct .struct - un3 .word ; Object::un3 = 9 + radius .word ; Object::radius = 9 .endstruct .endstruct O: .tag Object - lda O + Object::named + Object::Point::ycoord - lda O + Object::un2 + lda O + Object::target + Object::Point::ycoord ; Named struct + lda O + Object::tax ; Anonymous + lda O + Object::radius ; Anonymous + + ; Be careful not to use a named nested structure without also adding the + ; offset to the nested structure itself. + lda O + Object::Point::ycoord ; Incorrect! + lda O + Object::target + Object::Point::ycoord ; Correct </verb></tscreen> In this example, the first nested structure is named "Point", and its member @@ -5027,8 +5033,8 @@ offsets begin at 0. On the other hand, the two anonymous structures simply continue to add members to the enclosing "Object" structure. Note that an anonymous structure does not need a member name, since all of its -members become part of the enclosing structure. The "unnamed" member in the -example is redundantly the same offset as its first member "un1". +members become part of the enclosing structure. The "cost" member in the +example is redundantly the same offset as its first member "price". <sect1>Limitations<p> From cd7c688dff5b0dfed2dec0fde9f2f24cd1b73ae9 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 18 Dec 2023 15:30:53 +0800 Subject: [PATCH 421/520] Separated C preprocessor errors from other errors. --- src/cc65/error.c | 67 +++++++++++++++++------- src/cc65/error.h | 25 +++++++-- test/ref/Makefile | 3 +- test/ref/bug2312-preprocessor-error.c | 8 +++ test/ref/bug2312-preprocessor-error.cref | 2 + test/ref/custom-reference-error.c | 5 +- test/ref/custom-reference-error.cref | 11 ++-- 7 files changed, 91 insertions(+), 30 deletions(-) create mode 100644 test/ref/bug2312-preprocessor-error.c create mode 100644 test/ref/bug2312-preprocessor-error.cref diff --git a/src/cc65/error.c b/src/cc65/error.c index 2ad7133ed..5cd29b388 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -59,8 +59,10 @@ /* Count of errors/warnings */ -unsigned ErrorCount = 0; -unsigned WarningCount = 0; +unsigned PPErrorCount = 0; /* Pre-parser errors */ +unsigned PPWarningCount = 0; /* Pre-parser warnings */ +unsigned ErrorCount = 0; /* Errors occurred in parser and later translation phases */ +unsigned WarningCount = 0; /* Warnings occurred in parser and later translation phases */ unsigned RecentLineNo = 0; unsigned RecentErrorCount = 0; @@ -197,7 +199,7 @@ void Internal (const char* Format, ...) -static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap) /* Print an error message - internal function */ { fprintf (stderr, "%s:%u: Error: ", Filename, LineNo); @@ -208,7 +210,11 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } - ++ErrorCount; + if (EC != EC_PP) { + ++ErrorCount; + } else { + ++PPErrorCount; + } if (RecentLineNo != LineNo) { RecentLineNo = LineNo; RecentErrorCount = 0; @@ -216,7 +222,7 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va ++RecentErrorCount; } - if (RecentErrorCount > 20 || ErrorCount > 200) { + if (RecentErrorCount > 20 || GetTotalErrors () > 200) { Fatal ("Too many errors"); } } @@ -228,18 +234,18 @@ void Error (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntError (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntError (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); va_end (ap); } -void LIError (const LineInfo* LI, const char* Format, ...) +void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) /* Print an error message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntError (GetInputName (LI), GetInputLine (LI), Format, ap); + IntError (EC, GetInputName (LI), GetInputLine (LI), Format, ap); va_end (ap); } @@ -250,7 +256,7 @@ void PPError (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntError (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntError (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap); va_end (ap); } @@ -262,13 +268,13 @@ void PPError (const char* Format, ...) -static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap) /* Print a warning message - internal function */ { if (IS_Get (&WarningsAreErrors)) { /* Treat the warning as an error */ - IntError (Filename, LineNo, Msg, ap); + IntError (EC, Filename, LineNo, Msg, ap); } else if (IS_Get (&WarnEnable)) { @@ -279,7 +285,12 @@ static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } - ++WarningCount; + + if (EC != EC_PP) { + ++WarningCount; + } else { + ++PPWarningCount; + } } } @@ -291,18 +302,18 @@ void Warning (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntWarning (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntWarning (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); va_end (ap); } -void LIWarning (const LineInfo* LI, const char* Format, ...) +void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) /* Print a warning message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntWarning (GetInputName (LI), GetInputLine (LI), Format, ap); + IntWarning (EC, GetInputName (LI), GetInputLine (LI), Format, ap); va_end (ap); } @@ -313,7 +324,7 @@ void PPWarning (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntWarning (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntWarning (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap); va_end (ap); } @@ -398,16 +409,34 @@ void PPNote (const char* Format, ...) /*****************************************************************************/ -/* Code */ +/* Error summary */ /*****************************************************************************/ +unsigned GetTotalErrors (void) +/* Get total count of errors of all categories */ +{ + return PPErrorCount + ErrorCount; +} + + + +unsigned GetTotalWarnings (void) +/* Get total count of warnings of all categories */ +{ + return PPWarningCount + WarningCount; +} + + + void ErrorReport (void) /* Report errors (called at end of compile) */ { - unsigned int V = (ErrorCount != 0 ? 0 : 1); - Print (stdout, V, "%u errors and %u warnings generated.\n", ErrorCount, WarningCount); + unsigned TotalErrors = GetTotalErrors (); + unsigned TotalWarnings = GetTotalWarnings (); + unsigned int V = (TotalErrors != 0 ? 0 : 1); + Print (stdout, V, "%u errors and %u warnings generated.\n", TotalErrors, TotalWarnings); } diff --git a/src/cc65/error.h b/src/cc65/error.h index 83be8c782..4dce6cf91 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -55,9 +55,20 @@ +/* Error categories */ +typedef enum errcat_t errcat_t; +enum errcat_t { + EC_PP, /* Pre-parser phases */ + EC_PARSER, /* Parser and later phases */ +}; + + + /* Count of errors/warnings */ -extern unsigned ErrorCount; -extern unsigned WarningCount; +extern unsigned PPErrorCount; /* Pre-parser errors */ +extern unsigned PPWarningCount; /* Pre-parser warnings */ +extern unsigned ErrorCount; /* Errors occurred in parser and later translation phases */ +extern unsigned WarningCount; /* Warnings occurred in parser and later translation phases */ /* Warning and error options */ extern IntStack WarnEnable; /* Enable warnings */ @@ -98,7 +109,7 @@ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1, void Error (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print an error message */ -void LIError (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3))); +void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print an error message with the line info given explicitly */ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); @@ -107,7 +118,7 @@ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); void Warning (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a warning message */ -void LIWarning (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3))); +void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print a warning message with the line info given explicitly */ void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2))); @@ -130,6 +141,12 @@ void LINote (const LineInfo* LI, const char* Format, ...) attribute ((format (pr void PPNote (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a note message. For use within the preprocessor */ +unsigned GetTotalErrors (void); +/* Get total count of errors of all categories */ + +unsigned GetTotalWarnings (void); +/* Get total count of warnings of all categories */ + void ErrorReport (void); /* Report errors (called at end of compile) */ diff --git a/test/ref/Makefile b/test/ref/Makefile index 9538fdee7..9ecb33c00 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -63,7 +63,8 @@ CUSTOMSOURCES = \ # exact error output is required ERRORSOURCES = \ custom-reference-error.c \ - bug1889-missing-identifier.c + bug1889-missing-identifier.c \ + bug2312-preprocessor-error.c SOURCES := $(filter-out $(CUSTOMSOURCES) $(ERRORSOURCES),$(wildcard *.c)) diff --git a/test/ref/bug2312-preprocessor-error.c b/test/ref/bug2312-preprocessor-error.c new file mode 100644 index 000000000..b7872f514 --- /dev/null +++ b/test/ref/bug2312-preprocessor-error.c @@ -0,0 +1,8 @@ +/* Bug #2312 - Error recovery from preprocessor errors at the end of a declaration */ + +typedef int A; /* ';' consumption triggers PP below */ + +#define /* PP error during ';' consumption */ + +A f(void); /* Should be OK */ +int A(void); /* Should be an error */ diff --git a/test/ref/bug2312-preprocessor-error.cref b/test/ref/bug2312-preprocessor-error.cref new file mode 100644 index 000000000..680950fd6 --- /dev/null +++ b/test/ref/bug2312-preprocessor-error.cref @@ -0,0 +1,2 @@ +bug2312-preprocessor-error.c:5: Error: Missing macro name +bug2312-preprocessor-error.c:8: Error: Redefinition of typedef 'A' as different kind of symbol diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c index 857145fc0..a7c1b6c56 100644 --- a/test/ref/custom-reference-error.c +++ b/test/ref/custom-reference-error.c @@ -13,7 +13,10 @@ and then "make" again to confirm */ -short main(int argc, char* argv[]) +typedef short return_t; +#error /* produce an error */ + +return_t main(int argc, char* argv[]) { printf("%02x", 0x42); /* produce an error */ n = 0; /* produce an error */ diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref index 728cc0e15..b21c72dce 100644 --- a/test/ref/custom-reference-error.cref +++ b/test/ref/custom-reference-error.cref @@ -1,5 +1,6 @@ -custom-reference-error.c:18: Error: Call to undeclared function 'printf' -custom-reference-error.c:19: Error: Undeclared identifier 'n' -custom-reference-error.c:21: Warning: Control reaches end of non-void function [-Wreturn-type] -custom-reference-error.c:21: Warning: Parameter 'argc' is never used -custom-reference-error.c:21: Warning: Parameter 'argv' is never used +custom-reference-error.c:17: Error: #error +custom-reference-error.c:21: Error: Call to undeclared function 'printf' +custom-reference-error.c:22: Error: Undeclared identifier 'n' +custom-reference-error.c:24: Warning: Control reaches end of non-void function [-Wreturn-type] +custom-reference-error.c:24: Warning: Parameter 'argc' is never used +custom-reference-error.c:24: Warning: Parameter 'argv' is never used From b876a6b213eb4fb8d821f4dba77079a3e7cb8917 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Tue, 19 Dec 2023 19:30:50 +0800 Subject: [PATCH 422/520] Fixed cc65 exitcode when there are only preprocessor errors. --- src/cc65/main.c | 4 ++-- test/err/bug2312-pperror-only.c | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test/err/bug2312-pperror-only.c diff --git a/src/cc65/main.c b/src/cc65/main.c index bef646cdd..7dc5417f6 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -1089,7 +1089,7 @@ int main (int argc, char* argv[]) Compile (InputFile); /* Create the output file if we didn't had any errors */ - if (PreprocessOnly == 0 && (ErrorCount == 0 || Debug)) { + if (PreprocessOnly == 0 && (GetTotalErrors () == 0 || Debug)) { /* Emit literals, do cleanup and optimizations */ FinishCompile (); @@ -1115,5 +1115,5 @@ int main (int argc, char* argv[]) DoneSegAddrSizes (); /* Return an apropriate exit code */ - return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS; + return (GetTotalErrors () > 0)? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/test/err/bug2312-pperror-only.c b/test/err/bug2312-pperror-only.c new file mode 100644 index 000000000..bdec33956 --- /dev/null +++ b/test/err/bug2312-pperror-only.c @@ -0,0 +1,8 @@ +/* Bug #2312 */ + +#error "Compiler should exit with failure" + +int main(void) +{ + return 0; +} From e5f9def572ae7f203ddee540f58b5e04e93567f9 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Fri, 29 Dec 2023 22:37:18 +0100 Subject: [PATCH 423/520] Added SPRCTL0 and SPRCTL1 bit definitions for ca65 --- asminc/lynx.inc | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index 403d15d07..a24044713 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -81,8 +81,42 @@ MATHJ = $FC6F ; Suzy Misc -SPRCTL0 = $FC80 -SPRCTL1 = $FC81 +SPRCTL0 = $FC80 +; Sprite bits-per-pixel definitions +BPP_MASK = %11000000 ; Mask for settings bits per pixel +BPP_1 = %00000000 +BPP_2 = %01000000 +BPP_3 = %10000000 +BPP_4 = %11000000 +; More sprite control 0 bit definitions +HFLIP = %00100000 +VFLIP = %00010000 +; Sprite types - redefined to reflect the reality caused by the shadow error +TYPE_SHADOW = %00000111 +TYPE_XOR = %00000110 +TYPE_NONCOLL = %00000101 ; Non-colliding +TYPE_NORMAL = %00000100 +TYPE_BOUNDARY = %00000011 +TYPE_BSHADOW = %00000010 ; Background shadow +TYPE_BACKNONCOLL = %00000001 ; Background non-colliding +TYPE_BACKGROUND = %00000000 + +SPRCTL1 = $FC81 +LITERAL = %10000000 +PACKED = %00000000 +ALGO3 = %01000000 ; Broken, do not set this bit! +; Sprite reload mask definitions +RELOAD_MASK = %00110000 +RENONE = %00000000 ; Reload nothing +REHV = %00010000 ; Reload hsize, vsize +REHVS = %00100000 ; Reload hsize, vsize, stretch +REHVST = %00110000 ; Reload hsize, vsize, stretch, tilt +; More sprite control 1 bit definitions +REUSEPAL = %00001000 +SKIP = %00000100 +DRAWUP = %00000010 +DRAWLEFT = %00000001 + SPRCOLL = $FC82 SPRINIT = $FC83 SUZYHREV = $FC88 From 061d907a11599cf3ad565ffbc155ce0e93156a72 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Fri, 29 Dec 2023 22:46:15 +0100 Subject: [PATCH 424/520] Fixed tabs instead of spaces --- asminc/lynx.inc | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index a24044713..198af415b 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -81,41 +81,41 @@ MATHJ = $FC6F ; Suzy Misc -SPRCTL0 = $FC80 +SPRCTL0 = $FC80 ; Sprite bits-per-pixel definitions -BPP_MASK = %11000000 ; Mask for settings bits per pixel -BPP_1 = %00000000 -BPP_2 = %01000000 -BPP_3 = %10000000 -BPP_4 = %11000000 +BPP_MASK = %11000000 ; Mask for settings bits per pixel +BPP_1 = %00000000 +BPP_2 = %01000000 +BPP_3 = %10000000 +BPP_4 = %11000000 ; More sprite control 0 bit definitions -HFLIP = %00100000 -VFLIP = %00010000 +HFLIP = %00100000 +VFLIP = %00010000 ; Sprite types - redefined to reflect the reality caused by the shadow error -TYPE_SHADOW = %00000111 -TYPE_XOR = %00000110 +TYPE_SHADOW = %00000111 +TYPE_XOR = %00000110 TYPE_NONCOLL = %00000101 ; Non-colliding -TYPE_NORMAL = %00000100 +TYPE_NORMAL = %00000100 TYPE_BOUNDARY = %00000011 TYPE_BSHADOW = %00000010 ; Background shadow TYPE_BACKNONCOLL = %00000001 ; Background non-colliding TYPE_BACKGROUND = %00000000 -SPRCTL1 = $FC81 -LITERAL = %10000000 -PACKED = %00000000 -ALGO3 = %01000000 ; Broken, do not set this bit! +SPRCTL1 = $FC81 +LITERAL = %10000000 +PACKED = %00000000 +ALGO3 = %01000000 ; Broken, do not set this bit! ; Sprite reload mask definitions -RELOAD_MASK = %00110000 -RENONE = %00000000 ; Reload nothing -REHV = %00010000 ; Reload hsize, vsize -REHVS = %00100000 ; Reload hsize, vsize, stretch -REHVST = %00110000 ; Reload hsize, vsize, stretch, tilt +RELOAD_MASK = %00110000 +RENONE = %00000000 ; Reload nothing +REHV = %00010000 ; Reload hsize, vsize +REHVS = %00100000 ; Reload hsize, vsize, stretch +REHVST = %00110000 ; Reload hsize, vsize, stretch, tilt ; More sprite control 1 bit definitions -REUSEPAL = %00001000 -SKIP = %00000100 -DRAWUP = %00000010 -DRAWLEFT = %00000001 +REUSEPAL = %00001000 +SKIP = %00000100 +DRAWUP = %00000010 +DRAWLEFT = %00000001 SPRCOLL = $FC82 SPRINIT = $FC83 From 66e354961c659bf7466542d577f0bf16811bb875 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Fri, 29 Dec 2023 22:48:36 +0100 Subject: [PATCH 425/520] Missed some tabs --- asminc/lynx.inc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index 198af415b..5ae17f6ef 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -95,22 +95,22 @@ VFLIP = %00010000 TYPE_SHADOW = %00000111 TYPE_XOR = %00000110 TYPE_NONCOLL = %00000101 ; Non-colliding -TYPE_NORMAL = %00000100 -TYPE_BOUNDARY = %00000011 +TYPE_NORMAL = %00000100 +TYPE_BOUNDARY = %00000011 TYPE_BSHADOW = %00000010 ; Background shadow TYPE_BACKNONCOLL = %00000001 ; Background non-colliding -TYPE_BACKGROUND = %00000000 +TYPE_BACKGROUND = %00000000 SPRCTL1 = $FC81 LITERAL = %10000000 PACKED = %00000000 -ALGO3 = %01000000 ; Broken, do not set this bit! +ALGO3 = %01000000 ; Broken, do not set this bit! ; Sprite reload mask definitions RELOAD_MASK = %00110000 -RENONE = %00000000 ; Reload nothing -REHV = %00010000 ; Reload hsize, vsize -REHVS = %00100000 ; Reload hsize, vsize, stretch -REHVST = %00110000 ; Reload hsize, vsize, stretch, tilt +RENONE = %00000000 ; Reload nothing +REHV = %00010000 ; Reload hsize, vsize +REHVS = %00100000 ; Reload hsize, vsize, stretch +REHVST = %00110000 ; Reload hsize, vsize, stretch, tilt ; More sprite control 1 bit definitions REUSEPAL = %00001000 SKIP = %00000100 From 096bd0b633a4a5c9086faaf50a4e86afcfee542c Mon Sep 17 00:00:00 2001 From: paul moore <paulmoore100@hotmail.com> Date: Fri, 29 Dec 2023 17:02:45 -0800 Subject: [PATCH 426/520] bug 2319 --- src/cl65/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cl65/main.c b/src/cl65/main.c index 553fb9ca6..cd245470a 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -1305,6 +1305,9 @@ static void OptStaticLocals (const char* Opt attribute ((unused)), static void OptTarget (const char* Opt attribute ((unused)), const char* Arg) /* Set the target system */ { + if (FirstInput) { + Error ("Target must be specified before input files"); + } Target = FindTarget (Arg); if (Target == TGT_UNKNOWN) { Error ("No such target system: '%s'", Arg); From c262929a626d4c191046da85c480513a6d109c5e Mon Sep 17 00:00:00 2001 From: paul moore <paulmoore100@hotmail.com> Date: Sun, 31 Dec 2023 10:14:53 -0800 Subject: [PATCH 427/520] doc cl65 change --- doc/cl65.sgml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/cl65.sgml b/doc/cl65.sgml index 24d2f5927..f48e1353c 100644 --- a/doc/cl65.sgml +++ b/doc/cl65.sgml @@ -261,6 +261,9 @@ different options for different files on the command line. As an example. translates main.c with full optimization and module.c with less optimization and debug info enabled. +Note that the target system (-t , --target) must be specified before any file +unless using the default target of c64 + The type of an input file is derived from its extension: <itemize> From f3199e430857663528a1a10b5b5c908d4ff802bb Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 14:48:03 +0800 Subject: [PATCH 428/520] Fixed type name output with K&R-style functions. --- src/cc65/datatype.c | 155 +++++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 60 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 9e0652526..e00e0c64d 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1356,6 +1356,65 @@ const char* GetFullTypeName (const Type* T) +static void GetParameterList (StrBuf* ParamList, StrBuf* Buf, const FuncDesc* D, int Detailed) +{ + /* First argument */ + const SymEntry* Param = D->SymTab->SymHead; + unsigned I; + + if ((D->Flags & FD_OLDSTYLE) == 0) { + /* ANSI style */ + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (ParamList, ", "); + } + if (Detailed) { + if (SymIsRegVar (Param)) { + SB_AppendStr (ParamList, "register "); + } + if (!SymHasAnonName (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, "..."); + } + } + } else { + /* K&R style */ + if (Detailed) { + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (ParamList, ", "); + } + if (!SymHasAnonName (Param)) { + SB_AppendStr (ParamList, Param->Name); + } + /* Next argument */ + Param = Param->NextSym; + } + } + SB_Clear (Buf); + } + SB_Terminate (ParamList); +} + + + static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBuf* East, const Type* T) /* Return the name string of the given type split into a western part and an ** eastern part. @@ -1395,34 +1454,11 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } else if (IsTypeFunc (T)) { - FuncDesc* D = GetFuncDesc (T); - struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + const FuncDesc* D = GetFuncDesc (T); - /* First argument */ - 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, ", "); - } - 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 parameter list string */ + GetParameterList (&ParamList, &Buf, D, 0); /* Join the existing West and East together */ if (!SB_IsEmpty (East)) { @@ -1600,37 +1636,8 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) /* 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 (!SymHasAnonName (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 parameter list string */ + GetParameterList (&ParamList, &Buf, D, 1); /* Get the function qualifiers */ if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { @@ -1641,16 +1648,44 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) /* 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, GetFuncReturnType (T)); SB_Append (&West, &East); + + /* Check if the function is defined in K&R style */ + if ((D->Flags & FD_OLDSTYLE) != 0 && D->ParamCount > 0) { + /* First argument */ + const SymEntry* Param = D->SymTab->SymHead; + unsigned I; + + SB_Clear (&ParamList); + SB_Clear (&Buf); + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + SB_AppendChar (&ParamList, ' '); + if (SymIsRegVar (Param)) { + SB_AppendStr (&ParamList, "register "); + } + if (!SymHasAnonName (Param)) { + SB_AppendStr (&Buf, Param->Name); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_AppendChar (&ParamList, ';'); + SB_Clear (&Buf); + + /* Next argument */ + Param = Param->NextSym; + } + SB_Append (&West, &ParamList); + } + SB_Terminate (&West); /* Output */ fprintf (F, "%s", SB_GetConstBuf (&West)); + SB_Done (&ParamList); + SB_Done (&Buf); SB_Done (&East); SB_Done (&West); } From f734f43a35d05cb5a8980febdd62fafa86eb1c6c Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 14:48:05 +0800 Subject: [PATCH 429/520] Removed extra 'const' qualifier in array-to-pointer and function-to-pointer conversions. --- src/cc65/datatype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index e00e0c64d..a8b7735b0 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -539,7 +539,7 @@ const Type* AddressOf (const Type* T) Type* P = TypeAlloc (Size + 1); /* Create the return type... */ - P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST; + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); memcpy (P+1, T, Size * sizeof (Type)); /* ...and return it */ From 0b7d9d8216a4f94d76cad6f587f9277b50410836 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 14:48:05 +0800 Subject: [PATCH 430/520] Fixed missing calling convention and address size qualifiers in diagnosis on function types. --- src/cc65/datatype.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index a8b7735b0..b2511cf1b 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1454,6 +1454,7 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } else if (IsTypeFunc (T)) { + int QualCount = 0; struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; const FuncDesc* D = GetFuncDesc (T); @@ -1467,13 +1468,27 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu SB_Clear (East); } + /* Add qualifiers */ + if ((GetQualifier (T) & ~T_QUAL_NEAR) != T_QUAL_NONE) { + QualCount = GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR); + if (QualCount > 0) { + SB_AppendChar (&Buf, ' '); + } + } + if (SB_IsEmpty (West)) { - /* Just use the param list */ - SB_Printf (West, "(%s)", SB_GetConstBuf (&ParamList)); + /* Use no parentheses */ + SB_Terminate (&Buf); + + /* Append the param list to the West */ + SB_Printf (West, "%s(%s)", SB_GetConstBuf (&Buf), SB_GetConstBuf (&ParamList)); } else { - /* Append the param list to the existing West */ - SB_Printf (&Buf, "(%s)(%s)", SB_GetConstBuf (West), SB_GetConstBuf (&ParamList)); - SB_Printf (West, "%s", SB_GetConstBuf (&Buf)); + /* Append the existing West */ + SB_Append (&Buf, West); + SB_Terminate (&Buf); + + /* Append the param list to the West */ + SB_Printf (West, "(%s)(%s)", SB_GetConstBuf (&Buf), SB_GetConstBuf (&ParamList)); } SB_Done (&ParamList); From 4e820677ee1b6cce52505da7ff042eb8a9520ff7 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 15:03:45 +0800 Subject: [PATCH 431/520] ED_AddrExpr() and ED_IndExpr() need no return values. --- src/cc65/exprdesc.c | 6 ++---- src/cc65/exprdesc.h | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index a1af0bb8b..a21e56623 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -418,7 +418,7 @@ ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) -ExprDesc* ED_AddrExpr (ExprDesc* Expr) +void ED_AddrExpr (ExprDesc* Expr) /* Take address of Expr. The result is always an rvalue */ { switch (Expr->Flags & E_MASK_LOC) { @@ -447,12 +447,11 @@ ExprDesc* ED_AddrExpr (ExprDesc* Expr) } break; } - return Expr; } -ExprDesc* ED_IndExpr (ExprDesc* Expr) +void ED_IndExpr (ExprDesc* Expr) /* Dereference Expr */ { switch (Expr->Flags & E_MASK_LOC) { @@ -486,7 +485,6 @@ ExprDesc* ED_IndExpr (ExprDesc* Expr) } break; } - return Expr; } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 93a8604c9..148485764 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -637,10 +637,10 @@ INLINE void ED_MarkExprAsRVal (ExprDesc* Expr) # define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) #endif -ExprDesc* ED_AddrExpr (ExprDesc* Expr); +void ED_AddrExpr (ExprDesc* Expr); /* Take address of Expr */ -ExprDesc* ED_IndExpr (ExprDesc* Expr); +void ED_IndExpr (ExprDesc* Expr); /* Dereference Expr */ #if defined(HAVE_INLINE) From 88246f852d73bc52e4ac37ea1927e3d9e9c70c3d Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 15:04:50 +0800 Subject: [PATCH 432/520] Removed RefineFuncDesc() as an unnecessary wrapper. --- src/cc65/symtab.c | 2 +- src/cc65/typeconv.c | 36 ++++-------------------------------- src/cc65/typeconv.h | 3 --- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 3018c910d..c2c6bab27 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -741,7 +741,7 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) /* Refine the existing composite prototype with this new ** one. */ - RefineFuncDesc (Sym->Type, T); + TypeComposition (Sym->Type, T); } } diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 76658502d..25b693511 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -427,10 +427,6 @@ void TypeComposition (Type* lhs, const Type* rhs) ** type or this fails with a critical check. */ { - FuncDesc* F1; - FuncDesc* F2; - long LeftCount, RightCount; - /* Compose two types */ while (lhs->C != T_END) { @@ -445,8 +441,8 @@ void TypeComposition (Type* lhs, const Type* rhs) /* Check for special type elements */ if (IsTypeFunc (lhs)) { /* Compose the function descriptors */ - F1 = GetFuncDesc (lhs); - F2 = GetFuncDesc (rhs); + FuncDesc* F1 = GetFuncDesc (lhs); + FuncDesc* F2 = GetFuncDesc (rhs); /* If F1 has an empty parameter list (which does also mean, it is ** not a function definition, because the flag is reset in this @@ -470,8 +466,8 @@ void TypeComposition (Type* lhs, const Type* rhs) } } else if (IsTypeArray (lhs)) { /* Check member count */ - LeftCount = GetElementCount (lhs); - RightCount = GetElementCount (rhs); + long LeftCount = GetElementCount (lhs); + long RightCount = GetElementCount (rhs); /* Set composite type if it is requested */ if (LeftCount != UNSPECIFIED) { @@ -485,28 +481,4 @@ void TypeComposition (Type* lhs, const Type* rhs) ++lhs; ++rhs; } - - return; -} - - - -FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType) -/* Refine the existing function descriptor with a new one */ -{ - FuncDesc* Old = GetFuncDesc (OldType); - FuncDesc* New = GetFuncDesc (NewType); - - CHECK (Old != 0 && New != 0); - - if ((New->Flags & FD_EMPTY) == 0) { - if ((Old->Flags & FD_EMPTY) == 0) { - TypeComposition (OldType, NewType); - } else { - TypeCopy (OldType, NewType); - Old->Flags &= ~FD_EMPTY; - } - } - - return Old; } diff --git a/src/cc65/typeconv.h b/src/cc65/typeconv.h index 839c5e43e..1d79a446c 100644 --- a/src/cc65/typeconv.h +++ b/src/cc65/typeconv.h @@ -63,9 +63,6 @@ void TypeComposition (Type* lhs, const Type* rhs); ** type or this fails with a critical check. */ -FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType); -/* Refine the existing function descriptor with a new one */ - /* End of typeconv.h */ From acbd87b5764005b7798ae9b3f29c8a7b9e3eaf6b Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 15:27:57 +0800 Subject: [PATCH 433/520] Renamed GetUnqualTypeCode() to GetUnderlyingTypeCode() for consistency with GetUnderlyingType(). --- src/cc65/datatype.c | 12 ++++++------ src/cc65/datatype.h | 14 +++++++------- src/cc65/expr.c | 6 +++--- src/cc65/initdata.c | 4 ++-- src/cc65/typecmp.c | 8 ++++---- src/cc65/typeconv.c | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 9e0652526..46f62e922 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -216,7 +216,7 @@ unsigned BitSizeOf (const Type* T) unsigned SizeOf (const Type* T) /* Compute size (in bytes) of object represented by type array */ { - switch (GetUnqualTypeCode (T)) { + switch (GetUnderlyingTypeCode (T)) { case T_VOID: /* A void variable is a cc65 extension. @@ -368,7 +368,7 @@ static unsigned GetMinimalTypeSizeByBitWidth (unsigned BitWidth) -TypeCode GetUnqualTypeCode (const Type* Type) +TypeCode GetUnderlyingTypeCode (const Type* Type) /* Get the type code of the unqualified underlying type of Type. ** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ @@ -725,7 +725,7 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) const Type* GetSignedType (const Type* T) /* Get signed counterpart of the integral type */ { - switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + switch (GetUnderlyingTypeCode (T) & T_MASK_RANK) { case T_RANK_CHAR: return type_schar; @@ -739,7 +739,7 @@ const Type* GetSignedType (const Type* T) return type_long; default: - Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); return T; } } @@ -749,7 +749,7 @@ const Type* GetSignedType (const Type* T) const Type* GetUnsignedType (const Type* T) /* Get unsigned counterpart of the integral type */ { - switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + switch (GetUnderlyingTypeCode (T) & T_MASK_RANK) { case T_RANK_CHAR: return type_uchar; @@ -763,7 +763,7 @@ const Type* GetUnsignedType (const Type* T) return type_ulong; default: - Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); return T; } } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 4f4b6f25e..dbe0eedaa 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -328,7 +328,7 @@ INLINE TypeCode GetQualifier (const Type* T) # define GetQualifier(T) ((T)->C & T_MASK_QUAL) #endif -TypeCode GetUnqualTypeCode (const Type* Type); +TypeCode GetUnderlyingTypeCode (const Type* Type); /* Get the type code of the unqualified underlying type of Type. ** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ @@ -359,30 +359,30 @@ INLINE TypeCode GetTypeClass (const Type* T) INLINE TypeCode GetTypeRank (const Type* T) /* Get the type rank of a type */ { - return (GetUnqualTypeCode (T) & T_MASK_RANK); + return (GetUnderlyingTypeCode (T) & T_MASK_RANK); } #else -# define GetTypeRank(T) (GetUnqualTypeCode (T) & T_MASK_RANK) +# define GetTypeRank(T) (GetUnderlyingTypeCode (T) & T_MASK_RANK) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetSignedness (const Type* T) /* Get the signedness of a type */ { - return (GetUnqualTypeCode (T) & T_MASK_SIGN); + return (GetUnderlyingTypeCode (T) & T_MASK_SIGN); } #else -# define GetSignedness(T) (GetUnqualTypeCode (T) & T_MASK_SIGN) +# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetSizeModifier (const Type* T) /* Get the size modifier of a type */ { - return (GetUnqualTypeCode (T) & T_MASK_SIZE); + return (GetUnderlyingTypeCode (T) & T_MASK_SIZE); } #else -# define GetSizeModifier(T) (GetUnqualTypeCode (T) & T_MASK_SIZE) +# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE) #endif #if defined(HAVE_INLINE) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index cfddfa24e..0f3a6e110 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -129,7 +129,7 @@ unsigned CG_TypeOf (const Type* T) { unsigned CG_Type; - switch (GetUnqualTypeCode (T)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: return CF_CHAR; @@ -188,7 +188,7 @@ unsigned CG_TypeOf (const Type* T) unsigned CG_CallFlags (const Type* T) /* Get the code generator flags for calling the function */ { - if (GetUnqualTypeCode (T) == T_FUNC) { + if (GetUnderlyingTypeCode (T) == T_FUNC) { return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; } else { Error ("Illegal function type %04lX", T->C); @@ -291,7 +291,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) void LimitExprValue (ExprDesc* Expr, int WarnOverflow) /* Limit the constant value of the expression to the range of its type */ { - switch (GetUnqualTypeCode (Expr->Type)) { + switch (GetUnderlyingTypeCode (Expr->Type)) { case T_INT: case T_SHORT: if (WarnOverflow && ((Expr->IVal < -0x8000) || (Expr->IVal > 0x7FFF))) { diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 5401e577c..82cebefc2 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -679,7 +679,7 @@ static unsigned ParseVoidInit (Type* T) Size = 0; do { ExprDesc Expr = NoCodeConstExpr (hie1); - switch (GetUnqualTypeCode (&Expr.Type[0])) { + switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -747,7 +747,7 @@ static unsigned ParseVoidInit (Type* T) static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { - switch (GetUnqualTypeCode (T)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: case T_UCHAR: diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index e8b790a69..a09c80304 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -259,8 +259,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } /* Get the ranks of the left and right hands */ - LeftRank = (GetUnqualTypeCode (lhs) & T_MASK_RANK); - RightRank = (GetUnqualTypeCode (rhs) & T_MASK_RANK); + LeftRank = (GetUnderlyingTypeCode (lhs) & T_MASK_RANK); + RightRank = (GetUnderlyingTypeCode (rhs) & T_MASK_RANK); /* Bit-fields are considered compatible if they have the same ** signedness, bit-offset and bit-width. @@ -344,10 +344,10 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) case T_RANK_PTR: ++Result->Indirections; if (Result->Indirections == 1) { - if ((GetUnqualTypeCode (lhs + 1) & T_MASK_RANK) == T_RANK_VOID) { + if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_LEFT; } - if ((GetUnqualTypeCode (rhs + 1) & T_MASK_RANK) == T_RANK_VOID) { + if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_RIGHT; } } else { diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 25b693511..6e9fad69a 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -436,7 +436,7 @@ void TypeComposition (Type* lhs, const Type* rhs) } /* Check for sanity */ - CHECK (GetUnqualTypeCode (lhs) == GetUnqualTypeCode (rhs)); + CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); /* Check for special type elements */ if (IsTypeFunc (lhs)) { From 7aab84628dad035e3e14d0b837d99496723a1c67 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 1 Jan 2024 16:11:30 +0800 Subject: [PATCH 434/520] Fixed calling convention parsing in type names and function parameter types. --- src/cc65/declare.c | 112 +++++++++++++++++++++++----------- test/val/bug2327-cconv-type.c | 111 +++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 test/val/bug2327-cconv-type.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 8b7512730..2666a8d31 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -501,6 +501,31 @@ static void FixQualifiers (Type* DataType) T[0].C |= CodeAddrSizeQualifier (); } + } else { + + /* If we have remaining qualifiers, flag them as invalid */ + Q = T[0].C; + + if (Q & T_QUAL_NEAR) { + Error ("Invalid '__near__' qualifier"); + Q &= ~T_QUAL_NEAR; + } + if (Q & T_QUAL_FAR) { + Error ("Invalid '__far__' qualifier"); + Q &= ~T_QUAL_FAR; + } + if (Q & T_QUAL_FASTCALL) { + Error ("Invalid '__fastcall__' qualifier"); + Q &= ~T_QUAL_FASTCALL; + } + if (Q & T_QUAL_CDECL) { + Error ("Invalid '__cdecl__' qualifier"); + Q &= ~T_QUAL_CDECL; + } + + /* Clear the invalid qualifiers */ + T[0].C &= Q; + } ++T; } @@ -1990,7 +2015,7 @@ static void ParseAnsiParamList (FuncDesc* F) static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) -/* Parse the argument list of a function with the enclosing parentheses */ +/* Parse the argument list of a function with the closing parenthesis */ { /* Create a new function descriptor */ FuncDesc* F = NewFuncDesc (); @@ -1998,9 +2023,6 @@ static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) /* Enter a new lexical level */ EnterFunctionLevel (); - /* Skip the opening paren */ - NextToken (); - /* Check for several special parameter lists */ if (CurTok.Tok == TOK_RPAREN) { /* Parameter list is empty (K&R-style) */ @@ -2075,19 +2097,18 @@ static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) -static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) +static void DirectDecl (DeclSpec* Spec, Declarator* D, TypeCode* RemQ, declmode_t Mode) /* Recursively process direct declarators. Build a type array in reverse order. */ { /* Read optional function or pointer qualifiers that modify the identifier - ** or token to the right. For convenience, we allow a calling convention - ** also for pointers here. If it's a pointer-to-function, the qualifier - ** later will be transfered to the function itself. If it's a pointer to - ** something else, it will be flagged as an error. + ** or token to the right. */ - TypeCode Qualifiers = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); + TypeCode Qualifiers = *RemQ | OptionalQualifiers (*RemQ, T_QUAL_ADDRSIZE | T_QUAL_CCONV); /* Pointer to something */ if (CurTok.Tok == TOK_STAR) { + /* Qualifiers on the pointer itself */ + TypeCode Q = T_QUAL_NONE; /* Skip the star */ NextToken (); @@ -2098,17 +2119,30 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } /* Allow const, restrict, and volatile qualifiers */ - Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); + Q |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); - /* Parse the type that the pointer points to */ - DirectDecl (Spec, D, Mode); + /* For convenience, we allow a calling convention also for pointers + ** here. If it's a pointer-to-function, the qualifier later will be + ** transfered to the function itself. If it's a pointer to something + ** else, it will be flagged as an error. + */ + *RemQ = T_QUAL_NONE; + + /* Parse the type that derives from the pointer */ + DirectDecl (Spec, D, RemQ, Mode); /* Add the type */ - AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); + AddTypeCodeToDeclarator (D, T_PTR | Q | *RemQ); + + /* Return the calling convention and address size specifiers on the + ** pointee type. + */ + *RemQ = Qualifiers; return; } if (CurTok.Tok == TOK_LPAREN) { + int Nested = 0; SymEntry* Entry; /* An empty declaration cannot contain parentheses where an identifier @@ -2118,19 +2152,33 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) Spec->Flags |= DS_NO_EMPTY_DECL; } + /* Skip the opening paren */ + NextToken (); + /* We have to disambiguate the meanings of 'type (identifier' when ** the identifier can be a typedef'ed parameter type specifier or ** a declarator enclosed in parentheses in some cases. */ if (Mode == DM_IDENT_OR_EMPTY || /* If we are in a declaration... */ - NextTok.Tok == TOK_LPAREN || /* or the next token is one more paren... */ - NextTok.Tok == TOK_STAR || /* or a '*' ... */ - (NextTok.Tok == TOK_IDENT && /* or an identifier that... */ - ((Entry = FindSym (NextTok.Ident)) == 0 || /* is not a typedef. */ - !SymIsTypeDef (Entry)))) { + CurTok.Tok == TOK_LPAREN || /* or the next token is one more paren... */ + CurTok.Tok == TOK_STAR || /* or a '*' ... */ + (CurTok.Tok == TOK_IDENT && /* or an identifier that... */ + ((Entry = FindSym (CurTok.Ident)) == 0 || /* is not a typedef. */ + !SymIsTypeDef (Entry)))) { + Nested = 1; + } else { + /* Check for qualifiers */ + TypeCode Q = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); + + if (Q != T_QUAL_NONE) { + Qualifiers |= Q; + Nested = 1; + } + } + + if (Nested) { /* Parse the direct declarator in parentheses */ - NextToken (); - DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, &Qualifiers, Mode); ConsumeRParen (); } else { /* This is a parameter type list in parentheses */ @@ -2160,6 +2208,9 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) { + /* Skip the opening paren */ + NextToken (); + /* Function declarator */ ParseFuncDecl (D, Mode, Qualifiers); @@ -2215,19 +2266,7 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } } - /* If we have remaining qualifiers, flag them as invalid */ - if (Qualifiers & T_QUAL_NEAR) { - Error ("Invalid '__near__' qualifier"); - } - if (Qualifiers & T_QUAL_FAR) { - Error ("Invalid '__far__' qualifier"); - } - if (Qualifiers & T_QUAL_FASTCALL) { - Error ("Invalid '__fastcall__' qualifier"); - } - if (Qualifiers & T_QUAL_CDECL) { - Error ("Invalid '__cdecl__' qualifier"); - } + *RemQ = Qualifiers; } @@ -2288,6 +2327,8 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) ** Return 1 otherwise. */ { + TypeCode Q = T_QUAL_NONE; + /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; @@ -2303,11 +2344,12 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) InitDeclarator (D); /* Get additional derivation of the declarator and the identifier */ - DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, &Q, Mode); /* Add the base type */ NeedTypeSpace (D, TypeLen (Spec->Type) + 1); /* Bounds check */ TypeCopy (D->Type + D->Index, Spec->Type); + D->Type[D->Index].C |= Q; /* Use the storage class from the declspec */ D->StorageClass = Spec->StorageClass; diff --git a/test/val/bug2327-cconv-type.c b/test/val/bug2327-cconv-type.c new file mode 100644 index 000000000..678727278 --- /dev/null +++ b/test/val/bug2327-cconv-type.c @@ -0,0 +1,111 @@ +/* Bug #2327 - Calling conventions and address size specifiers of functions in type names and parameter types are not parsed correctly */ + +#include <stdint.h> +#include <stdio.h> + +unsigned failures; +int need_header = 1; +const char* test_header; + +/* Helpers */ +void set_header(const char* name) +{ + if (need_header == 0) + { + printf("\n"); + } + test_header = name; + need_header = 1; +} + +void print_header(void) +{ + if (need_header) + { + need_header = 0; + printf("<%s test>\n", test_header); + } +} + +#define CHECK(R, E) \ + do { \ + if ((R) != (E)) { \ + ++failures; \ + print_header(); \ + printf(" fail: %s = %d\n", #R, (R)); \ + } \ + } while (0); + +#define CHECK_RV() CHECK(rv, 42) +#define CHECK_SP() CHECK(x - (intptr_t)&x, 0) + +#define FUNC_QUAL __cdecl__ __near__ + +typedef int hoo_t(int __far__ __cdecl__ ()); +typedef int hoo_t(int __far__ (__cdecl__)()); /* Question: should this be rejected? */ +typedef int hoo_t(int __far__ (__cdecl__ ())); +typedef int hoo_t(int __far__ (__cdecl__ *)()); +typedef int hoo_t(int __far__ (__cdecl__ (*)())); +typedef int hoo_t(int __far__ ((__cdecl__ *)())); + +typedef int hoo_t(int __cdecl__ __far__ ()); +typedef int hoo_t(int (__cdecl__ __far__)()); /* Question: should this be rejected? */ +typedef int hoo_t(int (__cdecl__ __far__ ())); +typedef int hoo_t(int (__cdecl__ __far__ *)()); +typedef int hoo_t(int (__cdecl__ (__far__ *)())); +typedef int hoo_t(int ((__cdecl__ __far__ *)())); + +typedef int (FUNC_QUAL foo_t)(int, int); +typedef int (FUNC_QUAL *pfoo_t)(int, int); + +int FUNC_QUAL foo(int a, int b) +{ + return a * b; +} + +int (FUNC_QUAL * const pfoo)() = (int (FUNC_QUAL *)())foo; + +/* Incompatible and not working for cc65 if used as-is */ +int (*qfoo)(int, ...) = foo; + +int main(void) +{ + int rv; + intptr_t x; + + set_header("init"); + x = (intptr_t)&x; + CHECK_SP() + + set_header("foo"); + rv = foo((int8_t)-3, (int32_t)-14); + CHECK_RV() + CHECK_SP() + + set_header("pfoo"); +#if 0 + /* This would fail */ + rv = pfoo((int8_t)-6, (int32_t)-7); +#else + rv = ((pfoo_t)pfoo)((int8_t)-6, (int32_t)-7); +#endif + CHECK_RV() + CHECK_SP() + + set_header("qfoo"); +#if 0 + /* This would fail */ + rv = (qfoo)((int32_t)-6, (int8_t)-7); +#else + rv = ((foo_t *)qfoo)((int32_t)-6, (int8_t)-7); +#endif + CHECK_RV() + CHECK_SP() + + if (failures > 0) + { + printf("\nfailures: %u\n", failures); + } + + return failures; +} From 75461e13190976534eec4a1b8ce89f6ed03773ac Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 28 Dec 2023 21:50:13 +0100 Subject: [PATCH 435/520] Apple2: implement stat(2) and statvfs(3) --- asminc/stat.inc | 64 ++++++++++ asminc/statvfs.inc | 46 +++++++ doc/apple2.sgml | 37 +++++- doc/apple2enh.sgml | 39 +++++- doc/funcref.sgml | 171 +++++++++++++++++++++++++++ include/apple2.h | 42 +++++-- include/dirent.h | 36 ++---- include/sys/stat.h | 37 +++++- include/sys/statvfs.h | 74 ++++++++++++ include/sys/types.h | 43 ++++++- include/time.h | 12 +- libsrc/apple2/exec.s | 26 ++-- libsrc/apple2/filename.s | 11 +- libsrc/apple2/gettime.s | 56 ++------- libsrc/apple2/gmtime_dt.s | 73 ++++++++++++ libsrc/apple2/mktime_dt.s | 37 ++++++ libsrc/apple2/mli.inc | 7 +- libsrc/apple2/mli_file_info.s | 33 ++++++ libsrc/apple2/mli_file_info_direct.s | 22 ++++ libsrc/apple2/open.s | 1 + libsrc/apple2/stat.s | 129 ++++++++++++++++++++ libsrc/apple2/statvfs.s | 125 ++++++++++++++++++++ libsrc/apple2/targetutil/convert.c | 2 +- 23 files changed, 999 insertions(+), 124 deletions(-) create mode 100644 asminc/stat.inc create mode 100644 asminc/statvfs.inc create mode 100644 include/sys/statvfs.h create mode 100644 libsrc/apple2/gmtime_dt.s create mode 100644 libsrc/apple2/mktime_dt.s create mode 100644 libsrc/apple2/mli_file_info.s create mode 100644 libsrc/apple2/mli_file_info_direct.s create mode 100644 libsrc/apple2/stat.s create mode 100644 libsrc/apple2/statvfs.s diff --git a/asminc/stat.inc b/asminc/stat.inc new file mode 100644 index 000000000..e5248f06d --- /dev/null +++ b/asminc/stat.inc @@ -0,0 +1,64 @@ +;**************************************************************************** +;* * +;* stat.inc * +;* * +;* Stat struct * +;* * +;* * +;* * +;*(C) 2023 Colin Leroy-Mira <colin@colino.net> * +;* * +;* * +;*This software is provided 'as-is', without any expressed or implied * +;*warranty. In no event will the authors be held liable for any damages * +;*arising from the use of this software. * +;* * +;*Permission is granted to anyone to use this software for any purpose, * +;*including commercial applications, and to alter it and redistribute it * +;*freely, subject to the following restrictions: * +;* * +;*1. The origin of this software must not be misrepresented; you must not * +;* claim that you wrote the original software. If you use this software * +;* in a product, an acknowledgment in the product documentation would be * +;* appreciated but is not required. * +;*2. Altered source versions must be plainly marked as such, and must not * +;* be misrepresented as being the original software. * +;*3. This notice may not be removed or altered from any source * +;* distribution. * +;* * +;**************************************************************************** + + .include "time.inc" + +;------------------------------------------------------------------------------ +; st_mode values + +S_IFDIR = $01 +S_IFREG = $02 + +;------------------------------------------------------------------------------ +; struct stat + +.struct stat + st_dev .dword + st_ino .dword + st_mode .byte + st_nlink .dword + st_uid .byte + st_gid .byte + st_size .dword + st_atim .tag timespec + st_ctim .tag timespec + st_mtim .tag timespec + .ifdef __APPLE2__ + st_access .byte + st_type .byte + st_auxtype .word + st_storagetype .byte + st_blocks .word + st_mod_date .word + st_mod_time .word + st_create_date .word + st_create_time .word + .endif +.endstruct diff --git a/asminc/statvfs.inc b/asminc/statvfs.inc new file mode 100644 index 000000000..8674b045d --- /dev/null +++ b/asminc/statvfs.inc @@ -0,0 +1,46 @@ +;**************************************************************************** +;* * +;* statvfs.inc * +;* * +;* Statvfs struct * +;* * +;* * +;* * +;*(C) 2023 Colin Leroy-Mira <colin@colino.net> * +;* * +;* * +;*This software is provided 'as-is', without any expressed or implied * +;*warranty. In no event will the authors be held liable for any damages * +;*arising from the use of this software. * +;* * +;*Permission is granted to anyone to use this software for any purpose, * +;*including commercial applications, and to alter it and redistribute it * +;*freely, subject to the following restrictions: * +;* * +;*1. The origin of this software must not be misrepresented; you must not * +;* claim that you wrote the original software. If you use this software * +;* in a product, an acknowledgment in the product documentation would be * +;* appreciated but is not required. * +;*2. Altered source versions must be plainly marked as such, and must not * +;* be misrepresented as being the original software. * +;*3. This notice may not be removed or altered from any source * +;* distribution. * +;* * +;**************************************************************************** + +;------------------------------------------------------------------------------ +; struct statvfs + +.struct statvfs + f_bsize .dword + f_frsize .dword + f_blocks .dword + f_bfree .dword + f_bavail .dword + f_files .dword + f_ffree .dword + f_favail .dword + f_fsid .dword + f_flag .dword + f_namemax .dword +.endstruct diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 97724c147..f1603c428 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -321,14 +321,15 @@ Programs containing Apple ][ specific code may use the <sect1>Apple ][ specific functions<p> -The functions listed below are special for the Apple ][. See -the <url url="funcref.html" name="function reference"> for declaration and +The functions and variables listed below are special for the Apple ][. +See the <url url="funcref.html" name="function reference"> for declaration and usage. <itemize> <item>_auxtype <item>_dos_type <item>_filetype +<item>_datetime <item>get_ostype <item>rebootafterexit <item>ser_apple2_slot @@ -569,6 +570,28 @@ program. See the discussion of the <tt/.CONDES/ feature in the <url url="ca65.html" name="assembler manual">. +<sect1>ProDOS date/time manipulation<p> + +<descrip> +The readdir and stat function return ProDOS timestamps in their file +creation/modification time attributes. You can convert them to more portable +time representations using either: + +<tag/struct tm/ +<tt/struct tm* __fastcall__ gmtime_dt (const struct datetime* dt);/ + +Converts a <tt/struct datetime/ into a <tt/struct tm/. Returns NULL in case +of error and sets errno. + +<tag/time_t/ +<tt/time_t __fastcall__ mktime_dt (const struct datetime* dt);/ + +Parses a <tt/struct datetime/ and returns a UNIX timestamp. Returns 0 on error and +sets errno. + +</descrip> + + <sect1>DIO<p> <descrip> @@ -630,6 +653,16 @@ url="ca65.html" name="assembler manual">. that can be used to set these variables. It is included in <tt/apple2.h/. + The global variable <tt/_datetime/ allows the file creation date/time + to be set before a call to <tt/fopen()/ + or <tt/open()/ that creates the file. It is defined in <tt/apple2.h/: + + <tscreen> + <verb> + extern struct datetime _datetime; + </verb> + </tscreen> + <tag>Example</tag> A text file cannot be created with just the diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 11d4feb7e..e27501577 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -322,14 +322,15 @@ Programs containing enhanced Apple //e specific code may use the <sect1>Enhanced Apple //e specific functions<p> -The functions listed below are special for the enhanced Apple //e. See -the <url url="funcref.html" name="function reference"> for declaration and +The functions and variables listed below are special for the Apple ][. +See the <url url="funcref.html" name="function reference"> for declaration and usage. <itemize> <item>_auxtype <item>_dos_type <item>_filetype +<item>_datetime <item>get_ostype <item>rebootafterexit <item>ser_apple2_slot @@ -575,6 +576,28 @@ program. See the discussion of the <tt/.CONDES/ feature in the <url url="ca65.html" name="assembler manual">. +<sect1>ProDOS date/time manipulation<p> + +<descrip> +The readdir and stat function return ProDOS timestamps in their file +creation/modification time attributes. You can convert them to more portable +time representations using either: + +<tag/struct tm/ +<tt/struct tm* __fastcall__ gmtime_dt (const struct datetime* dt);/ + +Converts a <tt/struct datetime/ into a <tt/struct tm/. Returns -1 in case +of error and sets errno, 0 on success. + +<tag/time_t/ +<tt/time_t __fastcall__ mktime_dt (const struct datetime* dt);/ + +Parses a <tt/struct datetime/ and returns a UNIX timestamp. Returns 0 on error and +sets errno. + +</descrip> + + <sect1>DIO<p> <descrip> @@ -619,7 +642,7 @@ url="ca65.html" name="assembler manual">. auxiliary type. Therefore, some additional mechanism for specifying the file types is needed. - <tag>Specifying the File Type and Auxiliary Type</tag> + <tag>Specifying the File Type, Auxiliary Type and creation date</tag> There are two global variables provided that allow the file type and auxiliary type to be specified before a call to <tt/fopen()/ @@ -636,6 +659,16 @@ url="ca65.html" name="assembler manual">. that can be used to set these variables. It is included in <tt/apple2.h/, which is in turn included in <tt/apple2enh.h/. + The global variable <tt/_datetime/ allows the file creation date/time + to be set before a call to <tt/fopen()/ + or <tt/open()/ that creates the file. It is defined in <tt/apple2.h/: + + <tscreen> + <verb> + extern struct datetime _datetime; + </verb> + </tscreen> + <tag>Example</tag> A text file cannot be created with just the diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 524818b19..c8a818295 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -93,6 +93,8 @@ function. <itemize> <item>_dos_type <item><ref id="get_ostype" name="get_ostype"> +<item><ref id="gmtime_dt" name="gmtime_dt"> +<item><ref id="mktime_dt" name="mktime_dt"> <item>rebootafterexit </itemize> @@ -846,6 +848,20 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>. (incomplete) +<sect1><tt/stat.h/<label id="sys/stat.h"><p> + +<itemize> +<item><ref id="stat" name="stat"> +</itemize> + + +<sect1><tt/statvfs.h/<label id="sys/statvfs.h"><p> + +<itemize> +<item><ref id="statvfs" name="statvfs"> +</itemize> + + <sect1><tt/vic20.h/<label id="vic20.h"><p> (incomplete) @@ -2851,6 +2867,79 @@ setting the time may not work. See also the platform-specific information. </quote> +<sect1>gmtime_dt<label id="gmtime_dt"><p> + +<quote> +<descrip> +<tag/Function/Converts a ProDOS date to a struct tm. +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ +<tag/Declaration/<tt/struct tm* __fastcall__ gmtime_dt (const struct datetime* dt);/ +<tag/Description/The <tt/gmtime_dt/ function converts the given +proDOS date/time to a struct tm. On error, NULL is returned and <tt/errno/ is set +to an error code describing the reason for the failure. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>This function is only available on Apple II. +<item>On Apple II, you can't stat() an opened file. stat() before opening. +</itemize> +<tag/Availability/cc65 +<tag/Example/ +<verb> + +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +int main(void) +{ + struct stat st; + struct tm* tm; + if (stat ("/disk/file", &st) == 0) { + tm = gmtime_dt (&st.st_ctime); + if (tm) + printf ("File created on %s\n", asctime(tm)); + } +} +</verb> +</descrip> +</quote> + + +<sect1>mktime_dt<label id="mktime_dt"><p> + +<quote> +<descrip> +<tag/Function/Converts a ProDOS date to a time_t. +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ +<tag/Declaration/<tt/time_t __fastcall__ mktime_dt (const struct datetime* dt);/ +<tag/Description/The <tt/mktime_dt/ function parses the given +proDOS date/time and returns a time_t timestamp. On error, 0 is returned, +and errno is set. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>This function is only available on Apple II. +</itemize> +<tag/Availability/cc65 +<tag/Example/ +<verb> + +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +int main(void) +{ + struct stat st; + if (stat ("/disk/file", &st) == 0) { + printf ("File created on %s\n", + localtime (mktime_dt (&st.st_ctime))); + } +} +</verb> +</descrip> +</quote> + + <sect1>clrscr<label id="clrscr"><p> <quote> @@ -6229,6 +6318,9 @@ be used in presence of a prototype. <item>The returned pointer may point to a statically allocated instance of <tt/struct dirent/, so it may get overwritten by subsequent calls to <tt/readdir/. +<item>On the Apple II platform, the d_ctime and d_mtime returned are in the +ProDOS format. You can convert them to more portable time representations using +the ProDOS datetime conversion functions. <item>On several platforms, namely the CBMs and the Atari, the disk drives get confused when opening/closing files between directory reads. So for example a program that reads the list of files on a disk, and after each call to @@ -7075,6 +7167,85 @@ be used in presence of a prototype. </quote> +<sect1>stat<label id="stat"><p> + +<quote> +<descrip> +<tag/Function/Get file status. +<tag/Header/<tt/<ref id="sys/stat.h" name="sys/stat.h">/ +<tag/Declaration/<tt/int __fastcall__ stat (const char* pathname, struct stat* statbuf);/ +<tag/Description/<tt/stat/ gets information for the file with the given name. On success, +zero is returned. On error, -1 is returned and <tt/errno/ is set to an error +code describing the reason for the failure. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>On the Apple II platform, the st_ctim, st_mtim and st_atim members are left +to zero, for size and performance reasons. The ProDOS creation and modification dates +are returned in the ProDOS format in st_ctime and st_mtime. The access date does +not exist. You can convert them to POSIX-style time representations using +the <url url="apple2.html#ss9.3" name="ProDOS datetime conversion functions">. +</itemize> +<tag/Availability/POSIX 1003.1 +<tag/See also/ +<ref id="statvfs" name="statvfs"> +<tag/Example/ +<verb> +#include <sys/stat.h> + +#define FILENAME "helloworld" +struct stat stbuf; +if (stat (FILENAME, &stbuf) == 0) { + printf ("%s size is %lu bytes (created on %s)\n", FILENAME, stbuf.st_size, +#ifndef __APPLE2__ + localtime (&stbuf.st_ctim.tv_sec) +#else + localtime (mktime_dt (&stbuf.st_ctime)) +#endif + ); +} else { + printf ("There was a problem stat'ing %s: %d\n", FILENAME, errno); +} +</verb> +</descrip> +</quote> + + +<sect1>statvfs<label id="statvfs"><p> + +<quote> +<descrip> +<tag/Function/Get filesystem statistics. +<tag/Header/<tt/<ref id="sys/statvfs.h" name="sys/statvfs.h">/ +<tag/Declaration/<tt/int __fastcall__ statvfs (const char* pathname, struct statvfs* buf);/ +<tag/Description/<tt/statvfs/ gets information for the filesytem on which the given file +resides. On success, +zero is returned. On error, -1 is returned and <tt/errno/ is set to an error +code describing the reason for the failure. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>The function requires an absolute pathname. +</itemize> +<tag/Availability/POSIX 1003.1 +<tag/See also/ +<ref id="stat" name="stat"> +<tag/Example/ +<verb> +#include <sys/statvfs.h> + +#define FILENAME "/disk/helloworld" +struct statvfs stvbuf; +if (statvfs (FILENAME, &stvbuf) == 0) { + printf ("%s filesystem has %u blocks of %u size, %u of them free.\n", FILENAME, stvbuf.f_blocks, stvbuf.f_bsize, stvbuf.f_bfree); +} else { + printf ("There was a problem statvfs'ing %s: %d\n", FILENAME, errno); +} +</verb> +</descrip> +</quote> + + <sect1>strcasecmp<label id="strcasecmp"><p> <quote> diff --git a/include/apple2.h b/include/apple2.h index 8b9a3e0ea..25995eec2 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -41,6 +41,7 @@ # error This module may only be used when compiling for the Apple ][! #endif +#include <time.h> #include <apple2_filetype.h> @@ -142,6 +143,27 @@ extern unsigned char _dos_type; ** ProDOS 8 2.4.x - 0x24 */ +/* struct stat.st_mode values */ +#define S_IFDIR 0x01 +#define S_IFREG 0x02 +#define S_IFBLK 0xFF +#define S_IFCHR 0xFF +#define S_IFIFO 0xFF +#define S_IFLNK 0xFF +#define S_IFSOCK 0xFF + +struct datetime { + struct { + unsigned day :5; + unsigned mon :4; + unsigned year :7; + } date; + struct { + unsigned char min; + unsigned char hour; + } time; +}; + /*****************************************************************************/ @@ -151,20 +173,10 @@ extern unsigned char _dos_type; /* The file stream implementation and the POSIX I/O functions will use the -** following struct to set the date and time stamp on files. This specificially +** following struct to set the date and time stamp on files. This specifically ** applies to the open and fopen functions. */ -extern struct { - struct { - unsigned day :5; - unsigned mon :4; - unsigned year :7; - } createdate; /* Current date: 0 */ - struct { - unsigned char min; - unsigned char hour; - } createtime; /* Current time: 0 */ -} _datetime; +extern struct datetime _datetime; /* The addresses of the static drivers */ #if !defined(__APPLE2ENH__) @@ -211,6 +223,12 @@ void rebootafterexit (void); #define _cpeekcolor() COLOR_WHITE #define _cpeekrevers() 0 +struct tm* __fastcall__ gmtime_dt (const struct datetime* dt); +/* Converts a ProDOS date/time structure to a struct tm */ + +time_t __fastcall__ mktime_dt (const struct datetime* dt); +/* Converts a ProDOS date/time structure to a time_t UNIX timestamp */ + /* End of apple2.h */ diff --git a/include/dirent.h b/include/dirent.h index 124c7f224..b95646833 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -33,6 +33,8 @@ #ifndef _DIRENT_H #define _DIRENT_H +#include <target.h> + /*****************************************************************************/ @@ -46,31 +48,15 @@ typedef struct DIR DIR; #if defined(__APPLE2__) struct dirent { - char d_name[16]; - unsigned d_ino; - unsigned d_blocks; - unsigned long d_size; - unsigned char d_type; - struct { - unsigned day :5; - unsigned mon :4; - unsigned year :7; - } d_cdate; - struct { - unsigned char min; - unsigned char hour; - } d_ctime; - unsigned char d_access; - unsigned d_auxtype; - struct { - unsigned day :5; - unsigned mon :4; - unsigned year :7; - } d_mdate; - struct { - unsigned char min; - unsigned char hour; - } d_mtime; + char d_name[16]; + unsigned d_ino; + unsigned d_blocks; + unsigned long d_size; + unsigned char d_type; + struct datetime d_ctime; + unsigned char d_access; + unsigned d_auxtype; + struct datetime d_mtime; }; #define _DE_ISREG(t) ((t) != 0x0F) diff --git a/include/sys/stat.h b/include/sys/stat.h index d8fc09c75..0e1589d52 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -2,7 +2,7 @@ /* */ /* stat.h */ /* */ -/* Constants for the mode argument of open and creat */ +/* stat(2) definition */ /* */ /* */ /* */ @@ -11,6 +11,9 @@ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ +/* (C) 2023 Colin Leroy-Mira */ +/* EMail: colin@colino.net */ +/* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ @@ -36,6 +39,10 @@ #ifndef _STAT_H #define _STAT_H +#include <time.h> +#include <target.h> +#include <sys/types.h> + /*****************************************************************************/ @@ -47,6 +54,30 @@ #define S_IREAD 0x01 #define S_IWRITE 0x02 +#define S_IFMT 0x03 + +struct stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + off_t st_size; + struct timespec st_atim; + struct timespec st_ctim; + struct timespec st_mtim; + #ifdef __APPLE2__ + unsigned char st_access; + unsigned char st_type; + unsigned int st_auxtype; + unsigned char st_storagetype; + unsigned int st_blocks; + struct datetime st_mtime; + struct datetime st_ctime; + #endif +}; + /*****************************************************************************/ @@ -55,5 +86,9 @@ +int __fastcall__ stat (const char* pathname, struct stat* statbuf); + + + /* End of stat.h */ #endif diff --git a/include/sys/statvfs.h b/include/sys/statvfs.h new file mode 100644 index 000000000..d9edc2f23 --- /dev/null +++ b/include/sys/statvfs.h @@ -0,0 +1,74 @@ +/*****************************************************************************/ +/* */ +/* statvfs.h */ +/* */ +/* statvfs(3) definition */ +/* */ +/* */ +/* */ +/* (C) 2023 Colin Leroy-Mira */ +/* EMail: colin@colino.net */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef _STATVFS_H +#define _STATVFS_H + +#include <sys/types.h> + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +struct statvfs { + unsigned long f_bsize; + unsigned long f_frsize; + fsblkcnt_t f_blocks; + fsblkcnt_t f_bfree; + fsblkcnt_t f_bavail; + fsfilcnt_t f_files; + fsfilcnt_t f_ffree; + fsfilcnt_t f_favail; + unsigned long f_fsid; + unsigned long f_flag; + unsigned long f_namemax; +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int __fastcall__ statvfs (const char* pathname, struct statvfs* buf); + + + +/* End of statvfs.h */ +#endif diff --git a/include/sys/types.h b/include/sys/types.h index e75dd7d46..89b91c5b4 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -50,6 +50,46 @@ typedef long int off_t; #endif +#ifndef _HAVE_dev_t +#define _HAVE_dev_t +typedef unsigned long int dev_t; +#endif + +#ifndef _HAVE_ino_t +#define _HAVE_ino_t +typedef unsigned long int ino_t; +#endif + +#ifndef _HAVE_nlink_t +#define _HAVE_nlink_t +typedef unsigned long int nlink_t; +#endif + +#ifndef _HAVE_uid_t +#define _HAVE_uid_t +typedef unsigned char uid_t; +#endif + +#ifndef _HAVE_gid_t +#define _HAVE_gid_t +typedef unsigned char gid_t; +#endif + +#ifndef _HAVE_mode_t +#define _HAVE_mode_t +typedef unsigned char mode_t; +#endif + +#ifndef _HAVE_fsblkcnt_t +#define _HAVE_fsblkcnt_t +typedef unsigned long int fsblkcnt_t; +#endif + +#ifndef _HAVE_fsfilcnt_t +#define _HAVE_fsfilcnt_t +typedef unsigned long int fsfilcnt_t; +#endif + /*****************************************************************************/ @@ -60,6 +100,3 @@ typedef long int off_t; /* End of types.h */ #endif - - - diff --git a/include/time.h b/include/time.h index bfc2ac435..6cd0c8068 100644 --- a/include/time.h +++ b/include/time.h @@ -37,6 +37,15 @@ #define _TIME_H +/* Forward declaration for target.h */ +typedef unsigned long time_t; +typedef unsigned long clock_t; + + + +#include <target.h> + + /* NULL pointer */ #ifndef NULL @@ -49,9 +58,6 @@ typedef unsigned size_t; #endif -typedef unsigned long time_t; -typedef unsigned long clock_t; - /* Structure for broken down time */ struct tm { int tm_sec; diff --git a/libsrc/apple2/exec.s b/libsrc/apple2/exec.s index b8875e9ca..ec90f19bb 100644 --- a/libsrc/apple2/exec.s +++ b/libsrc/apple2/exec.s @@ -5,8 +5,8 @@ ; .export _exec - .import pushname, popname - .import popax, done, _exit + .import mli_file_info_direct + .import pushname, popname, popax, done, _exit .include "zeropage.inc" .include "errno.inc" @@ -17,13 +17,12 @@ typerr: lda #$4A ; "Incompatible file format" ; Cleanup name -oserr: jsr popname ; Preserves A - ; Set ___oserror - jmp ___mappederrno +mlierr: jsr popname +oserr: jmp ___mappederrno _exec: - ; Save cmdline + ; Store cmdline sta ptr4 stx ptr4+1 @@ -32,6 +31,9 @@ _exec: jsr pushname bne oserr + jsr mli_file_info_direct + bcs mlierr + ; ProDOS TechRefMan, chapter 5.1.5.1: ; "The complete or partial pathname of the system program ; is stored at $280, starting with a length byte." @@ -46,18 +48,6 @@ _exec: dey bpl :- - ; Set pushed name - lda sp - ldx sp+1 - sta mliparam + MLI::INFO::PATHNAME - stx mliparam + MLI::INFO::PATHNAME+1 - - ; Get file_type and aux_type - lda #GET_INFO_CALL - ldx #GET_INFO_COUNT - jsr callmli - bcs oserr - ; If we get here the program file at least exists so we copy ; the loader stub right now and patch it later to set params ldx #size - 1 diff --git a/libsrc/apple2/filename.s b/libsrc/apple2/filename.s index aaef6ec2d..0d4b6bedd 100644 --- a/libsrc/apple2/filename.s +++ b/libsrc/apple2/filename.s @@ -8,6 +8,7 @@ .import subysp, addysp, decsp1 .include "zeropage.inc" + .include "apple2.inc" .include "mli.inc" pushname: @@ -15,7 +16,7 @@ pushname: stx ptr1+1 ; Alloc pathname buffer - ldy #64+1 ; Max pathname length + zero + ldy #FILENAME_MAX jsr subysp ; Check for full pathname @@ -71,14 +72,14 @@ copy: lda (ptr1),y sta (sp),y beq setlen iny - cpy #64+1 ; Max pathname length + zero + cpy #FILENAME_MAX bcc copy ; Load oserror code lda #$40 ; "Invalid pathname" ; Free pathname buffer -addsp65:ldy #64+1 +addsp65:ldy #FILENAME_MAX bne addsp ; Branch always ; Alloc and set length byte @@ -93,5 +94,5 @@ setlen: tya popname: ; Cleanup stack - ldy #1 + 64+1 ; Length byte + max pathname length + zero -addsp: jmp addysp ; Preserves A + ldy #1 + FILENAME_MAX +addsp: jmp addysp ; Preserves A and X diff --git a/libsrc/apple2/gettime.s b/libsrc/apple2/gettime.s index 7467b0189..829aaab3b 100644 --- a/libsrc/apple2/gettime.s +++ b/libsrc/apple2/gettime.s @@ -4,7 +4,8 @@ ; int __fastcall__ clock_gettime (clockid_t clk_id, struct timespec *tp); ; - .import pushax, steaxspidx, incsp1, incsp3, return0 + .import pushax, incsp1, incsp3, steaxspidx, return0 + .import _mktime_dt .include "time.inc" .include "zeropage.inc" @@ -29,42 +30,12 @@ _clock_gettime: jsr callmli bcs oserr - ; Get date - lda DATELO+1 - lsr - php ; Save month msb - cmp #70 ; Year < 70? - bcs :+ ; No, leave alone - adc #100 ; Move 19xx to 20xx -: sta TM + tm::tm_year - lda DATELO - tax ; Save day - plp ; Restore month msb - ror - lsr - lsr - lsr - lsr - beq erange ; [1..12] allows for validity check - tay - dey ; Move [1..12] to [0..11] - sty TM + tm::tm_mon - txa ; Restore day - and #%00011111 - sta TM + tm::tm_mday + ; Convert DATELO/TIMELO to time_t + lda #<DATELO + ldx #>DATELO + jsr _mktime_dt - ; Get time - lda TIMELO+1 - sta TM + tm::tm_hour - lda TIMELO - sta TM + tm::tm_min - - ; Make time_t - lda #<TM - ldx #>TM - jsr _mktime - - ; Store tv_sec + ; Store ldy #timespec::tv_sec jsr steaxspidx @@ -74,21 +45,8 @@ _clock_gettime: ; Return success jmp return0 - ; Load errno code -erange: lda #ERANGE - - ; Cleanup stack - jsr incsp3 ; Preserves A - - ; Set __errno - jmp ___directerrno - ; Cleanup stack oserr: jsr incsp3 ; Preserves A ; Set ___oserror jmp ___mappederrno - - .bss - -TM: .tag tm diff --git a/libsrc/apple2/gmtime_dt.s b/libsrc/apple2/gmtime_dt.s new file mode 100644 index 000000000..a0b8e9f4d --- /dev/null +++ b/libsrc/apple2/gmtime_dt.s @@ -0,0 +1,73 @@ +; +; Oliver Schmidt, 14.08.2018 +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; struct tm * __fastcall__ gmtime_dt(const struct datetime *dt) +; + + .export _gmtime_dt, tm_buf + + .include "time.inc" + .include "zeropage.inc" + .include "errno.inc" + .include "mli.inc" + + ; Convert ProDOS date/time to a struct tm + ; source date address in AX + ; on stack: + ; destination struct + +_gmtime_dt: + sta ptr1 + stx ptr1+1 + + ; Get time + ldy #$03 + lda (ptr1),y + sta tm_buf + tm::tm_hour + dey + lda (ptr1),y + sta tm_buf + tm::tm_min + + ; Get date + dey + lda (ptr1),y + lsr + php ; Save month msb + cmp #70 ; Year < 70? + bcs :+ ; No, leave alone + adc #100 ; Move 19xx to 20xx +: sta tm_buf + tm::tm_year + + dey + lda (ptr1),y + tax ; Save day + plp ; Restore month msb + ror + lsr + lsr + lsr + lsr + beq erange ; [1..12] allows for validity check + tay + dey ; Move [1..12] to [0..11] + sty tm_buf + tm::tm_mon + txa ; Restore day + and #%00011111 + sta tm_buf + tm::tm_mday + + lda #<tm_buf ; Return pointer to tm_buf + ldx #>tm_buf + rts + + ; Load errno code and return NULL +erange: lda #ERANGE + sta ___errno + lda #$00 + tax + rts + + .bss + +tm_buf: + .tag tm diff --git a/libsrc/apple2/mktime_dt.s b/libsrc/apple2/mktime_dt.s new file mode 100644 index 000000000..415f52b9e --- /dev/null +++ b/libsrc/apple2/mktime_dt.s @@ -0,0 +1,37 @@ +; +; Oliver Schmidt, 14.08.2018 +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; time_t __fastcall__ mktime_dt(const struct datetime *dt) +; + + .import steaxspidx, pushax, incsp2, _gmtime_dt + .import tm_buf + .export _mktime_dt + + .include "time.inc" + .include "zeropage.inc" + .include "errno.inc" + .include "mli.inc" + + ; Convert ProDOS date/time to UNIX timestamp + ; source date address in AX + +_mktime_dt: + ; Convert to internal tm + jsr _gmtime_dt + cpx #$00 + bne :+ + cmp #$00 + beq err + + ; Make time_t +: lda #<tm_buf + ldx #>tm_buf + jmp _mktime + +err: lda #$00 + tax + sta sreg + sta sreg+1 + rts diff --git a/libsrc/apple2/mli.inc b/libsrc/apple2/mli.inc index 42363d9c9..382a071b0 100644 --- a/libsrc/apple2/mli.inc +++ b/libsrc/apple2/mli.inc @@ -83,8 +83,8 @@ EOF_COUNT = 2 AUX_TYPE .word STORAGE_TYPE .byte BLOCKS .word - MODE_DATE .word - MODE_TIME .word + MOD_DATE .word + MOD_TIME .word CREATE_DATE .word CREATE_TIME .word .endstruct @@ -139,3 +139,6 @@ LEVEL := $BF94 ; File level: used in open, flush, close MACHID := $BF98 ; Machine identification PFIXPTR := $BF9A ; If = 0, no prefix active KVERSION:= $BFFF ; Kernel version number + +; Max filename length +FILENAME_MAX = 64+1 diff --git a/libsrc/apple2/mli_file_info.s b/libsrc/apple2/mli_file_info.s new file mode 100644 index 000000000..16e01c07f --- /dev/null +++ b/libsrc/apple2/mli_file_info.s @@ -0,0 +1,33 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; + + .export mli_file_info + .import pushname, popname, mli_file_info_direct + .import popax + .include "zeropage.inc" + .include "errno.inc" + .include "mli.inc" + + ; Calls ProDOS MLI GET_FILE_INFO on the filename + ; stored as C string in AX at top of stack + ; Returns with carry set on error, and sets errno +mli_file_info: + ; Get pathname + jsr popax + jsr pushname + bne oserr + + jsr mli_file_info_direct + php ; Save return status + + jsr popname ; Preserves A + + plp + bcs oserr + rts + +oserr: + jsr ___mappederrno + sec + rts diff --git a/libsrc/apple2/mli_file_info_direct.s b/libsrc/apple2/mli_file_info_direct.s new file mode 100644 index 000000000..c15ebc28f --- /dev/null +++ b/libsrc/apple2/mli_file_info_direct.s @@ -0,0 +1,22 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; + + .export mli_file_info_direct + .include "zeropage.inc" + .include "mli.inc" + + ; Calls ProDOS MLI GET_FILE_INFO on the ProDOS style + ; filename stored on top of stack + ; Returns with carry set on error, and sets errno +mli_file_info_direct: + ; Set pushed name + lda sp + ldx sp+1 + sta mliparam + MLI::INFO::PATHNAME + stx mliparam + MLI::INFO::PATHNAME+1 + + ; Get file information + lda #GET_INFO_CALL + ldx #GET_INFO_COUNT + jmp callmli diff --git a/libsrc/apple2/open.s b/libsrc/apple2/open.s index 68c203cd6..38793a13e 100644 --- a/libsrc/apple2/open.s +++ b/libsrc/apple2/open.s @@ -18,6 +18,7 @@ .include "fcntl.inc" .include "mli.inc" .include "filedes.inc" + .include "time.inc" .segment "ONCE" diff --git a/libsrc/apple2/stat.s b/libsrc/apple2/stat.s new file mode 100644 index 000000000..f655b3e3f --- /dev/null +++ b/libsrc/apple2/stat.s @@ -0,0 +1,129 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; int __fastcall__ stat(const char *pathname, struct stat *statbuf); +; + + .export _stat + .import __errno, _open,_close + .import mli_file_info + .import popax, pushax, pusha0, incsp2 + .include "zeropage.inc" + .include "errno.inc" + .include "fcntl.inc" + .include "filedes.inc" + .include "mli.inc" + .include "stat.inc" + +_stat: + ; Store statbuf pointer + sta ptr4 + sta stbuf + stx ptr4+1 + stx stbuf+1 + + ; Clear statbuf + lda #$00 + ldy #.sizeof(stat)-1 +: sta (ptr4),y + dey + bpl :- + + ; Reset errno + sta ___errno + + ; Store pathname + jsr popax + jsr pushax ; Push it back for mli_file_info + jsr pushax ; and for open + + jsr mli_file_info + + bcc got_info + jmp incsp2 ; Drop filename copy for open + +got_info: + ; st_dev + lda DEVNUM + lsr ; Shift right to cc65 representation + lsr + lsr + lsr + ldy #stat::st_dev + sta (ptr4),y + + ; st_mode (S_IFDIR/S_IFREG only) + lda mliparam + MLI::INFO::FILE_TYPE + ldy #stat::st_mode + cmp #$0f + bne is_reg + lda #S_IFDIR + bne set_st_mode + +is_reg: lda #S_IFREG + +set_st_mode: + sta (ptr4),y + + ; st_access through st_create_time + ldx #MLI::INFO::ACCESS + ldy #stat::st_access +: lda mliparam,x + sta (ptr4),y + inx + iny + cpy #stat::st_create_time + .sizeof(stat::st_create_time) + bne :- + + ; st_size + lda #O_RDONLY + jsr pusha0 + ldy #$04 + jsr _open + cmp #$FF + beq done + pha ; Save file descriptor for closing + + ; Get ProDOS's REF_NUM from file descriptor + jsr getfd + ; Get file information + sta mliparam + MLI::EOF::REF_NUM + lda #GET_EOF_CALL + ldx #EOF_COUNT + jsr callmli + bcs eoferr + + ; Get struct stat in ptr4 back, open destroyed it + lda stbuf + ldx stbuf+1 + sta ptr4 + stx ptr4+1 + + ; Store size + ldy #stat::st_size + lda mliparam + MLI::EOF::EOF + sta (ptr4),y + lda mliparam + MLI::EOF::EOF+1 + iny + sta (ptr4),y + lda mliparam + MLI::EOF::EOF+2 + iny + sta (ptr4),y + + ; Close file +eoferr: + pla + ldx #$00 + jsr _close + + ; Set return value if we had an error + lda ___errno + beq done + lda #$FF +done: + tax + rts + + .bss + +stbuf: .res 2 diff --git a/libsrc/apple2/statvfs.s b/libsrc/apple2/statvfs.s new file mode 100644 index 000000000..6274bb52b --- /dev/null +++ b/libsrc/apple2/statvfs.s @@ -0,0 +1,125 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; int __fastcall__ statvfs(const char *pathname, struct statvfs *statvfsbuf); +; + + .export _statvfs + .import _dio_query_sectsize + .import mli_file_info, pushax, popax, popptr1 + .include "zeropage.inc" + .include "apple2.inc" + .include "errno.inc" + .include "mli.inc" + .include "statvfs.inc" + +_statvfs: + ; Store statbuf + sta ptr4 + stx ptr4+1 + + ; Clear statbuf + lda #$00 + ldy #.sizeof(statvfs)-1 +: sta (ptr4),y + dey + bpl :- + + ; Store pathname, keeping only volume name + jsr popptr1 + ldy #$00 + sty vol_sep + lda (ptr1),y + cmp #'/' ; Is the path absolute? + beq :+ + lda #EINVAL + jmp ___directerrno + +: iny + lda (ptr1),y + beq :+ ; End of string, no other / + cpy #FILENAME_MAX + beq :+ ; Max filename length reached + cmp #'/' + bne :- ; Not a slash, keep looking + sty vol_sep ; Register '/' index + lda #$00 + sta (ptr1),y ; Cut pathname at first slash +: lda ptr1 + ldx ptr1+1 + jsr pushax + + jsr mli_file_info + + php + ldy vol_sep ; Put slash back in pathname + lda #'/' + sta (ptr1),y + plp + + bcc got_info + + jmp ___mappederrno + +got_info: + ; f_fsid + lda DEVNUM + lsr ; Shift right to cc65 representation + lsr + lsr + lsr + ldy #statvfs::f_fsid + sta (ptr4),y + + ; total number of blocks + lda mliparam + MLI::INFO::AUX_TYPE + ldy #statvfs::f_blocks + sta (ptr4),y + lda mliparam + MLI::INFO::AUX_TYPE+1 + iny + sta (ptr4),y + + ; blocks free & avail + sec + lda mliparam + MLI::INFO::AUX_TYPE + sbc mliparam + MLI::INFO::BLOCKS + ldy #statvfs::f_bfree + sta (ptr4),y + ldy #statvfs::f_bavail + sta (ptr4),y + + lda mliparam + MLI::INFO::AUX_TYPE+1 + sbc mliparam + MLI::INFO::BLOCKS+1 + iny + sta (ptr4),y + ldy #statvfs::f_bfree+1 + sta (ptr4),y + + ; block sizes + jsr _dio_query_sectsize + ; low bytes + ldy #statvfs::f_bsize + sta (ptr4),y + ldy #statvfs::f_frsize + sta (ptr4),y + ; f_frsize high byte + iny + txa + sta (ptr4),y + ; f_bsize high byte + ldy #statvfs::f_bsize+1 + sta (ptr4),y + + ; f_namemax + lda #FILENAME_MAX + ldy #statvfs::f_namemax + sta (ptr4),y + + lda #$00 + sta ___errno + tax + rts + + .bss + +vol_sep:.res 1 diff --git a/libsrc/apple2/targetutil/convert.c b/libsrc/apple2/targetutil/convert.c index ea9273fc3..52dffa745 100644 --- a/libsrc/apple2/targetutil/convert.c +++ b/libsrc/apple2/targetutil/convert.c @@ -108,7 +108,7 @@ static unsigned get_dir_entry(char* p_name) } /* Field header_pointer directly follows field last_mod */ - cur_addr = *(unsigned*)(&dirent->d_mtime.hour + 1); + cur_addr = *(unsigned*)(&dirent->d_mtime.time.hour + 1); dhandle = dio_open(getcurrentdevice()); if (!dhandle) { From 075ece5fafef1834cf2c349c1e08658f452410d5 Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Wed, 3 Jan 2024 16:35:12 +0100 Subject: [PATCH 436/520] Clean-up void is always fast --- include/rp6502.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/rp6502.h b/include/rp6502.h index 033684b72..2b40cfc71 100644 --- a/include/rp6502.h +++ b/include/rp6502.h @@ -72,8 +72,8 @@ void __fastcall__ ria_push_long (unsigned long val); void __fastcall__ ria_push_int (unsigned int val); #define ria_push_char(v) RIA.xstack = v -long __fastcall__ ria_pop_long (void); -int __fastcall__ ria_pop_int (void); +long ria_pop_long (void); +int ria_pop_int (void); #define ria_pop_char() RIA.xstack /* Set the RIA fastcall register */ @@ -118,9 +118,9 @@ long __fastcall__ ria_call_long_errno (unsigned char op); /* C API for the operating system. */ int __cdecl__ xreg (char device, char channel, unsigned char address, ...); -int __fastcall__ phi2 (void); -int __fastcall__ codepage (void); -long __fastcall__ lrand (void); +int phi2 (void); +int codepage (void); +long lrand (void); int __fastcall__ stdin_opt (unsigned long ctrl_bits, unsigned char str_length); int __fastcall__ read_xstack (void* buf, unsigned count, int fildes); int __fastcall__ read_xram (unsigned buf, unsigned count, int fildes); From dc9d2f0dbd2a7969e5e86501fa6727421512a8a1 Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Wed, 3 Jan 2024 16:46:07 +0100 Subject: [PATCH 437/520] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f8a0d4ff2..eff5049f9 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ External contributors: * [Stephan Mühlstrasser](https://github.com/smuehlst): osic1p target * [Wayne Parham](https://github.com/WayneParham): Sym-1 target * [Dave Plummer](https://github.com/davepl): KIM-1 target +* [rumbledethumps](https://github.com/rumbledethumps): Picocomputer target *(The above list is incomplete, if you feel left out - please speak up or add yourself in a PR)* From 726b70a5342e57fe7744db55ed98a6e9c5cc884b Mon Sep 17 00:00:00 2001 From: jedeoric <plifplouf78@hotmail.com> Date: Fri, 5 Jan 2024 00:45:03 +0100 Subject: [PATCH 438/520] add XMAINARGS and XGETARGV for Telestrat --- asminc/telestrat.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/asminc/telestrat.inc b/asminc/telestrat.inc index c57bd3de8..7623c4d05 100644 --- a/asminc/telestrat.inc +++ b/asminc/telestrat.inc @@ -257,8 +257,11 @@ XBINDX = $28 ; Convert a number into hex and displays on chan XDECIM = $29 XHEXA = $2A ; Convert a number into hex +XMAINARGS = $2C ; Only available for Orix (Alternative OS) + XEDT = $2D ; Launch editor XINSER = $2E +XGETARGV = $2E ; Only available for Orix (Alternative OS) XSCELG = $2F ; Search a line in editor mode XOPEN = $30 ; Only in Orix From bcea5dfa8f353c0aa64c90cdbc4a05e19540d5ff Mon Sep 17 00:00:00 2001 From: jedeoric <plifplouf78@hotmail.com> Date: Fri, 5 Jan 2024 00:46:15 +0100 Subject: [PATCH 439/520] fix comment telestrat --- asminc/telestrat.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asminc/telestrat.inc b/asminc/telestrat.inc index 7623c4d05..bbfabdf40 100644 --- a/asminc/telestrat.inc +++ b/asminc/telestrat.inc @@ -257,11 +257,11 @@ XBINDX = $28 ; Convert a number into hex and displays on chan XDECIM = $29 XHEXA = $2A ; Convert a number into hex -XMAINARGS = $2C ; Only available for Orix (Alternative OS) +XMAINARGS = $2C ; Only available for Orix XEDT = $2D ; Launch editor XINSER = $2E -XGETARGV = $2E ; Only available for Orix (Alternative OS) +XGETARGV = $2E ; Only available for Orix XSCELG = $2F ; Search a line in editor mode XOPEN = $30 ; Only in Orix From 169c9c0da0c86a2067a927af34a64f528ec2d31e Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 5 Jan 2024 19:38:51 +0100 Subject: [PATCH 440/520] Add strdup tests --- test/val/lib_common_strdup.c | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/val/lib_common_strdup.c diff --git a/test/val/lib_common_strdup.c b/test/val/lib_common_strdup.c new file mode 100644 index 000000000..748317017 --- /dev/null +++ b/test/val/lib_common_strdup.c @@ -0,0 +1,60 @@ +#include <string.h> +#include "unittest.h" + +#define SHORT_STR "abcdefghijklmnopqrstuvwxyz" + +#define MID_STR_LEN 700 /* Two pages and something */ +#define LONG_STR_LEN 40000UL /* Two long to duplicate */ +TEST +{ + char *dst; + char *src; + int i; + + dst = strdup(""); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL") + ASSERT_IsTrue(!strcmp(dst, ""), "strings differ"); + free(dst); + + dst = strdup(SHORT_STR); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL"); + ASSERT_IsTrue(strlen(dst) == strlen(SHORT_STR), "string lengths differ"); + ASSERT_IsTrue(!strcmp(dst, SHORT_STR), "strings differ"); + free(dst); + + src = malloc(MID_STR_LEN+1); + ASSERT_IsTrue(src != NULL, "Could not allocate source string"); + memset(src, 'a', MID_STR_LEN-1); + src[MID_STR_LEN] = '\0'; + + dst = strdup(src); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL"); + printf("strlens %zu %zu\n", strlen(src), strlen(dst)); + ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ"); + ASSERT_IsTrue(!strcmp(dst, src), "strings differ"); + free(dst); + free(src); + + src = malloc(LONG_STR_LEN+1); + ASSERT_IsTrue(src != NULL, "Could not allocate source string"); + memset(src, 'a', LONG_STR_LEN-1); + src[LONG_STR_LEN] = '\0'; + + dst = strdup(src); + ASSERT_IsTrue(dst == NULL, "strdup did not return NULL"); + free(src); + + for (i = 254; i < 258; i++) { + src = malloc(i+1); + memset(src, 'a', i-1); + src[i] = '\0'; + + dst = strdup(src); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL"); + ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ"); + ASSERT_IsTrue(!strcmp(dst, src), "strings differ"); + free (dst); + free(src); + } +} +ENDTEST From 29801a2fde4a78c9bc489d5367e109957411e5eb Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:53:24 +0100 Subject: [PATCH 441/520] remove extra format specifier, fixed #2330 --- test/val/bug1178-struct-copy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/val/bug1178-struct-copy.c b/test/val/bug1178-struct-copy.c index 7fb7e7803..322002c02 100644 --- a/test/val/bug1178-struct-copy.c +++ b/test/val/bug1178-struct-copy.c @@ -62,7 +62,7 @@ void dotest2(void) StructArray2[0] = test2; - printf ("test2: %d, %d, %d, %d, %d\n", + printf ("test2: %d, %d, %d, %d\n", (int)StructArray2[0].a, (int)StructArray2[0].b, (int)StructArray2[0].c, (int)StructArray2[0].d); if ((StructArray2[0].a != 42) || From 7ce982cc68212977cf86ed2cf0f123439393751e Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 7 Jan 2024 10:19:48 +0100 Subject: [PATCH 442/520] Remove non-standard (and useless) include Fixes #2337 --- include/time.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/time.h b/include/time.h index 6cd0c8068..f8977ab0c 100644 --- a/include/time.h +++ b/include/time.h @@ -43,10 +43,6 @@ typedef unsigned long clock_t; -#include <target.h> - - - /* NULL pointer */ #ifndef NULL #define NULL ((void *) 0) From 3a439e0e1b9c98fba7c3791d3bfb1074296ecb61 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 5 Jan 2024 19:39:03 +0100 Subject: [PATCH 443/520] Little strdup optimisation -12 bytes on disk -20 cycles per strdup -6 cycles per strlen called from strdup --- libsrc/common/strcspn.s | 8 ++-- libsrc/common/strdup.s | 93 ++++++++++++++++------------------------- libsrc/common/strlen.s | 15 +++---- libsrc/common/strspn.s | 8 ++-- 4 files changed, 51 insertions(+), 73 deletions(-) diff --git a/libsrc/common/strcspn.s b/libsrc/common/strcspn.s index 4bb01479a..418bf6ac2 100644 --- a/libsrc/common/strcspn.s +++ b/libsrc/common/strcspn.s @@ -7,13 +7,13 @@ .export _strcspn .import popptr1, _strlen - .importzp ptr1, ptr2, tmp1, tmp2 + .importzp ptr1, ptr4, tmp1, tmp2 _strcspn: - jsr _strlen ; get length in a/x and transfer s2 to ptr2 + jsr _strlen ; get length in a/x and transfer s2 to ptr4 ; Note: It does not make sense to ; have more than 255 test chars, so - ; we don't support a high byte here! (ptr2+1 is + ; we don't support a high byte here! (ptr4+1 is ; also unchanged in strlen then (important!)) ; -> the original implementation also ; ignored this case @@ -38,7 +38,7 @@ checkNext: iny check: cpy tmp1 ; compare with length of test character string beq endOfTestChars - cmp (ptr2),y ; found matching char? + cmp (ptr4),y ; found matching char? bne checkNext leave: txa ; restore position of finding diff --git a/libsrc/common/strdup.s b/libsrc/common/strdup.s index 3ab07bda1..94f2cd338 100644 --- a/libsrc/common/strdup.s +++ b/libsrc/common/strdup.s @@ -1,85 +1,62 @@ ; ; Ullrich von Bassewitz, 18.07.2000 +; Colin Leroy-Mira, 05.01.2024 ; ; char* __fastcall__ strdup (const char* S); ; -; Note: The code knowns which zero page locations are used by malloc. +; Note: The code knowns which zero page locations are used by malloc, +; memcpy and strlen. ; - .importzp sp, tmp1, ptr4 - .import pushax, decsp4, incsp4 - .import _strlen, _malloc, _memcpy + .importzp ptr2, ptr3, ptr4, tmp1, tmp2, tmp3 + .import _strlen_ptr4, _malloc, _memcpy, pushax .export _strdup .macpack cpu - .macpack generic _strdup: + ; Get length (and store source in ptr4) + sta ptr4 + stx ptr4+1 + stx tmp1 ; Backup high byte, which + jsr _strlen_ptr4 ; strlen may increment -; Since we need some place to store the intermediate results, allocate a -; stack frame. To make this somewhat more efficient, create the stackframe -; as needed for the final call to the memcpy function. - - pha ; decsp will destroy A (but not X) - jsr decsp4 ; Target/source - -; Store the pointer into the source slot - - ldy #1 - txa - sta (sp),y - pla -.if (.cpu .bitand CPU_ISET_65SC02) - sta (sp) + ; Add null byte for terminator +.if (.cpu .bitand ::CPU_ISET_65SC02) + inc a .else - dey - sta (sp),y + clc + adc #1 .endif - -; Get length of S (which is still in a/x) - - jsr _strlen - -; Calculate strlen(S)+1 (the space needed) - - add #1 - bcc @L1 + bne :+ inx -; Save the space we're about to allocate in ptr4 - -@L1: sta ptr4 - stx ptr4+1 - -; Allocate memory. _malloc will not use ptr4 + ; Store length +: sta tmp2 + stx tmp3 + ; Allocate memory jsr _malloc -; Store the result into the target stack slot - - ldy #2 - sta (sp),y ; Store low byte - sta tmp1 - txa ; Get high byte - iny - sta (sp),y ; Store high byte - -; Check for a NULL pointer - - ora tmp1 + ; Check for NULL + bne :+ + cpx #$00 beq OutOfMemory -; Copy the string. memcpy will return the target string which is exactly -; what we need here. It will also drop the allocated stack frame. + ; Push dest +: jsr pushax + ; Push source lda ptr4 - ldx ptr4+1 ; Load size - jmp _memcpy ; Copy string, drop stackframe + ldx tmp1 + jsr pushax -; Out of memory, return NULL (A = 0) + ; Push length + lda tmp2 + ldx tmp3 + + ; Copy and return the dest pointer + jmp _memcpy OutOfMemory: - tax - jmp incsp4 ; Drop stack frame - - + rts diff --git a/libsrc/common/strlen.s b/libsrc/common/strlen.s index 8d5bc20fc..c20ab78f9 100644 --- a/libsrc/common/strlen.s +++ b/libsrc/common/strlen.s @@ -2,19 +2,20 @@ ; Ullrich von Bassewitz, 31.05.1998 ; ; Note: strspn & strcspn call internally this function and rely on -; the usage of only ptr2 here! Keep in mind when appling changes +; the usage of only ptr4 here! Keep in mind when appling changes ; and check the other implementations too! ; ; size_t __fastcall__ strlen (const char* s); ; - .export _strlen - .importzp ptr2 + .export _strlen, _strlen_ptr4 + .importzp ptr4 .macpack cpu _strlen: - sta ptr2 ; Save s - stx ptr2+1 + sta ptr4 ; Save s + stx ptr4+1 +_strlen_ptr4: .if (.cpu .bitand ::CPU_ISET_HUC6280) clx cly @@ -27,11 +28,11 @@ _strlen: .endif .endif -L1: lda (ptr2),y +L1: lda (ptr4),y beq L9 iny bne L1 - inc ptr2+1 + inc ptr4+1 inx bne L1 diff --git a/libsrc/common/strspn.s b/libsrc/common/strspn.s index 6fda716be..7e3f707d1 100644 --- a/libsrc/common/strspn.s +++ b/libsrc/common/strspn.s @@ -7,13 +7,13 @@ .export _strspn .import popptr1, _strlen - .importzp ptr1, ptr2, tmp1, tmp2 + .importzp ptr1, ptr4, tmp1, tmp2 _strspn: - jsr _strlen ; get length in a/x and transfer s2 to ptr2 + jsr _strlen ; get length in a/x and transfer s2 to ptr4 ; Note: It does not make sense to ; have more than 255 test chars, so - ; we don't support a high byte here! (ptr2+1 is + ; we don't support a high byte here! (ptr4+1 is ; also unchanged in strlen then (important!)) ; -> the original implementation also ; ignored this case @@ -38,7 +38,7 @@ checkNext: iny check: cpy tmp1 ; compare with length of test character string beq leave - cmp (ptr2),y ; found matching char? + cmp (ptr4),y ; found matching char? bne checkNext foundTestChar: From df193c0947b890ea433e590e9042b0a9c6b537c0 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 7 Jan 2024 22:58:45 +0100 Subject: [PATCH 444/520] Rework time functions a bit - mktime: Work unsigned as time_t's type implies (shifting Y2K38 bug to 2106) - mktime: Add unit tests - gmtime/localtime: factorize - gmtime/localtime: Add unit tests - mktime/gmtime/localtime: Size optimisation (-130 bytes wrt master) - mktime: Speed optimisation (from 23M cycles on the unit test to 2M) --- libsrc/common/_is_leap_year.h | 22 ++++++ libsrc/common/_is_leap_year.s | 23 ++++++ libsrc/common/{gmtime.c => _time_t_to_tm.c} | 11 +-- libsrc/common/gmtime.s | 20 +++++ libsrc/common/localtime.c | 60 --------------- libsrc/common/localtime.s | 29 +++++++ libsrc/common/mktime.c | 64 +++++++--------- test/val/lib_common_gmtime_localtime.c | 84 +++++++++++++++++++++ test/val/lib_common_mktime.c | 61 +++++++++++++++ 9 files changed, 268 insertions(+), 106 deletions(-) create mode 100644 libsrc/common/_is_leap_year.h create mode 100644 libsrc/common/_is_leap_year.s rename libsrc/common/{gmtime.c => _time_t_to_tm.c} (94%) create mode 100644 libsrc/common/gmtime.s delete mode 100644 libsrc/common/localtime.c create mode 100644 libsrc/common/localtime.s create mode 100644 test/val/lib_common_gmtime_localtime.c create mode 100644 test/val/lib_common_mktime.c diff --git a/libsrc/common/_is_leap_year.h b/libsrc/common/_is_leap_year.h new file mode 100644 index 000000000..378c462ff --- /dev/null +++ b/libsrc/common/_is_leap_year.h @@ -0,0 +1,22 @@ +/* +** _is_leap_year.h +** +** (C) Copyright 2024, Colin Leroy-Mira <colin@colino.net> +** +*/ + + + +#ifndef __IS_LEAP_YEAR_H +#define __IS_LEAP_YEAR_H + + + +unsigned char __fastcall__ IsLeapYear (unsigned char Year); +/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206, + * without 1900 added */ + + + +/* End of _is_leap_year.h */ +#endif diff --git a/libsrc/common/_is_leap_year.s b/libsrc/common/_is_leap_year.s new file mode 100644 index 000000000..d3136c1c8 --- /dev/null +++ b/libsrc/common/_is_leap_year.s @@ -0,0 +1,23 @@ +; +; Colin Leroy-Mira, 2024 +; +; unsigned char __fastcall__ IsLeapYear (unsigned char Year) +; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206, +; without 1900 added. +; + + .export _IsLeapYear + +_IsLeapYear: + ldx #$00 ; Prepare X for rts + cmp #$00 ; Y 0 (1900) is not a leap year + beq NotLeap + cmp #$C8 ; Y 200 (2100) is not a leap year + beq NotLeap + and #$03 ; Year % 4 == 0 means leap year + bne NotLeap + lda #$01 ; Return 1 + rts +NotLeap: + lda #$00 ; Return 0 + rts diff --git a/libsrc/common/gmtime.c b/libsrc/common/_time_t_to_tm.c similarity index 94% rename from libsrc/common/gmtime.c rename to libsrc/common/_time_t_to_tm.c index 85e9de3d0..684cff752 100644 --- a/libsrc/common/gmtime.c +++ b/libsrc/common/_time_t_to_tm.c @@ -42,18 +42,9 @@ /*****************************************************************************/ - -struct tm* __fastcall__ gmtime (const time_t* timep) +struct tm* __fastcall__ _time_t_to_tm (const time_t t) { static struct tm timebuf; - time_t t; - - /* Check the argument */ - if (timep == 0 || (long) (t = *timep) < 0) { - /* Invalid arg */ - return 0; - } - /* Since our ints are just 16 bits, split the given time into seconds, ** hours and days. Each of the values will fit in a 16 bit variable. ** The mktime routine will then do the rest. diff --git a/libsrc/common/gmtime.s b/libsrc/common/gmtime.s new file mode 100644 index 000000000..288b285eb --- /dev/null +++ b/libsrc/common/gmtime.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ gmtime (const time_t* timep); +; + + .export _gmtime + .import __time_t_to_tm + .import ldeaxi + +_gmtime: + cpx #$00 ; Check for null pointer + bne :+ + cmp #$00 + beq no_pointer +: jsr ldeaxi ; Load value from pointer + jmp __time_t_to_tm ; Convert it + +no_pointer: + rts ; A/X already set diff --git a/libsrc/common/localtime.c b/libsrc/common/localtime.c deleted file mode 100644 index 48931ea62..000000000 --- a/libsrc/common/localtime.c +++ /dev/null @@ -1,60 +0,0 @@ -/*****************************************************************************/ -/* */ -/* localtime.c */ -/* */ -/* Convert calendar time into broken down local time */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include <time.h> - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -struct tm* __fastcall__ localtime (const time_t* timep) -{ - time_t t; - - /* Check for a valid time spec */ - if (timep == 0) { - return 0; - } - - /* Get the time and correct for the time zone offset */ - t = *timep + _tz.timezone; - - /* Use gmtime for conversion */ - return gmtime (&t); -} diff --git a/libsrc/common/localtime.s b/libsrc/common/localtime.s new file mode 100644 index 000000000..279442c9d --- /dev/null +++ b/libsrc/common/localtime.s @@ -0,0 +1,29 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ localtime (const time_t* timep); +; + + .export _localtime + .import __time_t_to_tm, __tz + .import ldeaxi, tosaddeax, pusheax + .importzp sreg + +_localtime: + cpx #$00 ; Check for null pointer + bne :+ + cmp #$00 + beq no_pointer +: jsr ldeaxi ; Load value + jsr pusheax ; Push it + lda __tz+1+3 + sta sreg+1 + lda __tz+1+2 + sta sreg + ldx __tz+1+1 + lda __tz+1 + jsr tosaddeax ; Add _tz.timezone + jmp __time_t_to_tm ; Convert to struct tm + +no_pointer: + rts ; A/X already set diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c index 275589dbb..c9ac1652c 100644 --- a/libsrc/common/mktime.c +++ b/libsrc/common/mktime.c @@ -36,7 +36,7 @@ #include <limits.h> #include <stdlib.h> #include <time.h> - +#include "_is_leap_year.h" /*****************************************************************************/ @@ -67,14 +67,6 @@ static const unsigned MonthDays [] = { -static unsigned char __fastcall__ IsLeapYear (unsigned Year) -/* Returns 1 if the given year is a leap year */ -{ - return (((Year % 4) == 0) && ((Year % 100) != 0 || (Year % 400) == 0)); -} - - - time_t __fastcall__ mktime (register struct tm* TM) /* Make a time in seconds since 1/1/1970 from the broken down time in TM. ** A call to mktime does also correct the time in TM to contain correct @@ -82,13 +74,13 @@ time_t __fastcall__ mktime (register struct tm* TM) */ { register div_t D; - int Max; - unsigned DayCount; + static int Max; + static unsigned DayCount; /* Check if TM is valid */ if (TM == 0) { /* Invalid data */ - goto Error; + return (time_t) -1L; } /* Adjust seconds. */ @@ -96,27 +88,29 @@ time_t __fastcall__ mktime (register struct tm* TM) TM->tm_sec = D.rem; /* Adjust minutes */ - if (TM->tm_min + D.quot < 0) { - goto Error; - } TM->tm_min += D.quot; D = div (TM->tm_min, 60); TM->tm_min = D.rem; /* Adjust hours */ - if (TM->tm_hour + D.quot < 0) { - goto Error; - } TM->tm_hour += D.quot; D = div (TM->tm_hour, 24); TM->tm_hour = D.rem; /* Adjust days */ - if (TM->tm_mday + D.quot < 0) { - goto Error; - } TM->tm_mday += D.quot; + /* Adjust year */ + while (1) { + Max = 365UL + IsLeapYear (TM->tm_year); + if ((unsigned int)TM->tm_mday > Max) { + ++TM->tm_year; + TM->tm_mday -= Max; + } else { + break; + } + } + /* Adjust month and year. This is an iterative process, since changing ** the month will change the allowed days for this month. */ @@ -125,20 +119,17 @@ time_t __fastcall__ mktime (register struct tm* TM) /* Make sure, month is in the range 0..11 */ D = div (TM->tm_mon, 12); TM->tm_mon = D.rem; - if (TM->tm_year + D.quot < 0) { - goto Error; - } TM->tm_year += D.quot; /* Now check if mday is in the correct range, if not, correct month ** and eventually year and repeat the process. */ - if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year + 1900)) { + if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) { Max = 29; } else { Max = MonthLength[TM->tm_mon]; } - if (TM->tm_mday > Max) { + if ((unsigned int)TM->tm_mday > Max) { /* Must correct month and eventually, year */ if (TM->tm_mon == DECEMBER) { TM->tm_mon = JANUARY; @@ -157,19 +148,27 @@ time_t __fastcall__ mktime (register struct tm* TM) ** year. */ TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1; - if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year + 1900)) { + if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) { ++TM->tm_yday; } /* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to - ** somewhere in 2038) all years dividable by 4 are leap years, so - ** dividing by 4 gives the days that must be added cause of leap years. + ** somewhere in 2106) all years dividable by 4 are leap years(1), + ** so dividing by 4 gives the days that must be added because of leap years. ** (and the last leap year before 1970 was 1968) + ** (1): Exception on 2100, which is not leap, and handled just after. */ DayCount = ((unsigned) (TM->tm_year-70)) * 365U + (((unsigned) (TM->tm_year-(68+1))) / 4) + TM->tm_yday; + /* Handle the 2100 exception */ + if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) { + DayCount--; + } else if (TM->tm_year > 200) { + DayCount--; + } + /* Calculate the weekday */ TM->tm_wday = (JAN_1_1970 + DayCount) % 7; @@ -182,11 +181,4 @@ time_t __fastcall__ mktime (register struct tm* TM) ((unsigned) TM->tm_min) * 60U + ((unsigned) TM->tm_sec) - _tz.timezone; - -Error: - /* Error exit */ - return (time_t) -1L; } - - - diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c new file mode 100644 index 000000000..143d15831 --- /dev/null +++ b/test/val/lib_common_gmtime_localtime.c @@ -0,0 +1,84 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> + +int fails = 0; + +time_t timestamps[] = { + 0, + 0x2FFFFFFF, + 0x6FFFFFFF, + 0xF48656FF, + 0xF4865700, + 0xFC5A3EFF, + 0x6D6739FF, + 0x6D673A00, + 0xFFFFFFFF, +}; + +/* Values checked against glibc 2.37's implementation of ctime() */ +const char *dates_gmt[] = { + "Thu Jan 1 00:00:00 1970\n", + "Sun Jul 9 16:12:47 1995\n", + "Wed Jul 18 05:49:51 2029\n", + "Thu Dec 31 23:59:59 2099\n", + "Fri Jan 1 00:00:00 2100\n", + "Fri Feb 29 23:59:59 2104\n", + "Tue Feb 29 23:59:59 2028\n", + "Wed Mar 1 00:00:00 2028\n", + "Sun Feb 7 06:28:15 2106\n", + NULL +}; + +const char *dates_gmt_plus_one[] = { + "Thu Jan 1 01:00:00 1970\n", + "Sun Jul 9 17:12:47 1995\n", + "Wed Jul 18 06:49:51 2029\n", + "Fri Jan 1 00:59:59 2100\n", + "Fri Jan 1 01:00:00 2100\n", + "Sat Mar 1 00:59:59 2104\n", + "Wed Mar 1 00:59:59 2028\n", + "Wed Mar 1 01:00:00 2028\n", + "Thu Jan 1 00:59:59 1970\n", + NULL +}; + +int main (void) +{ + int i; + + for (i = 0; dates_gmt[i] != NULL; i++) { + struct tm *tm; + char *str; + + /* Check gmtime */ + tm = gmtime(×tamps[i]); + str = asctime(tm); + if (strcmp(str, dates_gmt[i])) { + fails++; + printf("gmtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", + timestamps[i], dates_gmt[i], str); + } + + /* Check localtime with UTC timezone */ + _tz.timezone = 0; + tm = localtime(×tamps[i]); + str = asctime(tm); + if (strcmp(str, dates_gmt[i])) { + fails++; + printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", + timestamps[i], dates_gmt[i], str); + } + + /* Check localtime at UTC+1 */ + _tz.timezone = 3600; + tm = localtime(×tamps[i]); + str = asctime(tm); + if (strcmp(str, dates_gmt_plus_one[i])) { + fails++; + printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", + timestamps[i], dates_gmt_plus_one[i], str); + } + } + return fails; +} diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c new file mode 100644 index 000000000..832ce3834 --- /dev/null +++ b/test/val/lib_common_mktime.c @@ -0,0 +1,61 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> + +int fails = 0; + +time_t timestamps[] = { + 0, + 0x2FFFFFFF, + 0x6FFFFFFF, + 0xF48656FF, + 0xF4865700, + 0xFC5A3EFF, + 0x6D6739FF, + 0x6D673A00, + 0xFFFFFFFF, +}; + +/* Values checked against glibc 2.37's implementation of ctime() */ +const char *dates[] = { + "Thu Jan 1 00:00:00 1970\n", + "Sun Jul 9 16:12:47 1995\n", + "Wed Jul 18 05:49:51 2029\n", + "Thu Dec 31 23:59:59 2099\n", + "Fri Jan 1 00:00:00 2100\n", + "Fri Feb 29 23:59:59 2104\n", + "Tue Feb 29 23:59:59 2028\n", + "Wed Mar 1 00:00:00 2028\n", + "Sun Feb 7 06:28:15 2106\n", + NULL +}; + +int main (void) +{ + struct tm tm; + time_t t; + int i; + + /* Verify conversion both ways */ + for (t = 0x0FFFFFFF; ; t += 0x10000000) { + struct tm *tm = gmtime(&t); + time_t r = mktime(tm); + if (t != r) { + fails++; + printf("Unexpected result for t %lx: %lx\n", t, r); + } + if (t == 0xFFFFFFFF) { + break; + } + } + + for (i = 0; dates[i] != NULL; i++) { + char *str = ctime(×tamps[i]); + if (strcmp(str, dates[i])) { + fails++; + printf("Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", + timestamps[i], dates[i], str); + } + } + return fails; +} From 2564aaa12c92e188f748ae59b7aae58c0c1dc4bd Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 10 Jan 2024 04:48:27 +0800 Subject: [PATCH 445/520] Refix for diagnosis on expected expressions. --- src/cc65/expr.c | 2 +- test/ref/bug1889-missing-identifier.c | 2 ++ test/ref/bug1889-missing-identifier.cref | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 0f3a6e110..3ef141300 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1408,7 +1408,7 @@ static void Primary (ExprDesc* E) } else { /* Let's see if this is a C99-style declaration */ DeclSpec Spec; - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { Error ("Mixed declarations and code are not supported in cc65"); diff --git a/test/ref/bug1889-missing-identifier.c b/test/ref/bug1889-missing-identifier.c index d9cf4aa52..a8140565f 100644 --- a/test/ref/bug1889-missing-identifier.c +++ b/test/ref/bug1889-missing-identifier.c @@ -3,6 +3,8 @@ int enum { a } x; inline enum { b }; +_Static_assert(); + int main(void) { return 0; diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index 7381d2032..6317657d1 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,3 +1,4 @@ bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected +bug1889-missing-identifier.c:6: Error: Expression expected From 94dfc08c0e439b46b0f1a0e625bca9609b02ff94 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 10 Jan 2024 04:43:50 +0800 Subject: [PATCH 446/520] Fixed false "Non constant initializers" error messages on wrong places, which could be resulted from failed array declarations etc. --- src/cc65/expr.c | 12 +++++++++++- test/ref/custom-reference-error.c | 5 ++++- test/ref/custom-reference-error.cref | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 3ef141300..9cf05d2dc 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -4305,8 +4305,13 @@ ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*)) if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) { Error ("Constant expression expected"); /* To avoid any compiler errors, make the expression a valid const */ - Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_RESULT; + Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_MAKE; Expr.Flags |= E_LOC_NONE; + + /* Remove any non-constant code generated */ + if (!ED_CodeRangeIsEmpty (&Expr)) { + RemoveCodeRange (&Expr.Start, &Expr.End); + } } /* Return by value */ @@ -4331,6 +4336,11 @@ ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*)) Error ("Constant integer expression expected"); /* To avoid any compiler errors, make the expression a valid const */ ED_MakeConstAbsInt (&Expr, 1); + + /* Remove any non-constant code generated */ + if (!ED_CodeRangeIsEmpty (&Expr)) { + RemoveCodeRange (&Expr.Start, &Expr.End); + } } /* Return by value */ diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c index a7c1b6c56..e98fb024d 100644 --- a/test/ref/custom-reference-error.c +++ b/test/ref/custom-reference-error.c @@ -14,7 +14,7 @@ */ typedef short return_t; -#error /* produce an error */ +#error This is an/* produce an error */error return_t main(int argc, char* argv[]) { @@ -22,3 +22,6 @@ return_t main(int argc, char* argv[]) n = 0; /* produce an error */ /* produce a warning */ } + +int arr[main(0, 0)]; /* produce an error */ +int b = 0; diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref index b21c72dce..9ffa581cd 100644 --- a/test/ref/custom-reference-error.cref +++ b/test/ref/custom-reference-error.cref @@ -1,6 +1,7 @@ -custom-reference-error.c:17: Error: #error +custom-reference-error.c:17: Error: #error: This is an error custom-reference-error.c:21: Error: Call to undeclared function 'printf' custom-reference-error.c:22: Error: Undeclared identifier 'n' custom-reference-error.c:24: Warning: Control reaches end of non-void function [-Wreturn-type] custom-reference-error.c:24: Warning: Parameter 'argc' is never used custom-reference-error.c:24: Warning: Parameter 'argv' is never used +custom-reference-error.c:26: Error: Constant integer expression expected From 8e43c4706f0b7cffa34155af8a2705a2c7ef8aae Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 10 Jan 2024 04:50:42 +0800 Subject: [PATCH 447/520] Added hierarchy info about source file inclusion in diagnostic output. Fixed presumed names of source files in disgnosis. Fixed line number of source files in debug output. --- src/cc65/codeseg.c | 2 +- src/cc65/error.c | 123 +++++++++++++++++++++++---------- src/cc65/error.h | 7 +- src/cc65/input.c | 165 +++++++++++++++++++++++++++++++++++++++----- src/cc65/input.h | 24 +++++-- src/cc65/lineinfo.c | 66 ++++++++++++++---- src/cc65/lineinfo.h | 41 ++++++++--- src/cc65/preproc.c | 8 +-- 8 files changed, 347 insertions(+), 89 deletions(-) diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index 9f1bf4cc5..f4970b586 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -1471,7 +1471,7 @@ void CS_Output (CodeSeg* S) /* Add line debug info */ if (DebugInfo) { WriteOutput ("\t.dbg\tline, \"%s\", %u\n", - GetInputName (LI), GetInputLine (LI)); + GetActualFileName (LI), GetActualLineNum (LI)); } } /* Output the code */ diff --git a/src/cc65/error.c b/src/cc65/error.c index 5cd29b388..d6e7d7cbd 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -120,13 +120,46 @@ Collection DiagnosticStrBufs; +void PrintFileInclusionInfo (const LineInfo* LI) +/* Print hierarchy of file inclusion */ +{ + if (LI->IncFiles != 0) { + unsigned FileCount = CollCount (LI->IncFiles); + if (FileCount > 0) { + const char* Str = "In file included from %s:%u%c\n"; + + while (FileCount-- > 0) { + LineInfoFile* LIF = CollAtUnchecked (LI->IncFiles, FileCount); + char C = FileCount > 0 ? ',' : ':'; + + fprintf (stderr, Str, LIF->Name, LIF->LineNum, C); + Str = " from %s:%u%c\n"; + } + } + } +} + + + +static LineInfo* GetDiagnosticLI (void) +/* Get the line info where the diagnostic info refers to */ +{ + if (CurTok.LI) { + return CurTok.LI; + } else { + return GetCurLineInfo (); + } +} + + + static const char* GetDiagnosticFileName (void) /* Get the source file name where the diagnostic info refers to */ { if (CurTok.LI) { - return GetInputName (CurTok.LI); + return GetPresumedFileName (CurTok.LI); } else { - return GetCurrentFilename (); + return GetCurrentFileName (); } } @@ -136,7 +169,7 @@ static unsigned GetDiagnosticLineNum (void) /* Get the source line number where the diagnostic info refers to */ { if (CurTok.LI) { - return GetInputLine (CurTok.LI); + return GetPresumedLineNum (CurTok.LI); } else { return GetCurrentLineNum (); } @@ -199,10 +232,18 @@ void Internal (const char* Format, ...) -static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntError (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap) /* Print an error message - internal function */ { - fprintf (stderr, "%s:%u: Error: ", Filename, LineNo); + unsigned LineNo = GetPresumedLineNum (LI); + + /* Print file inclusion if appropriate */ + if (HasFileInclusionChanged (LI)) { + PrintFileInclusionInfo (LI); + } + RememberCheckedLI (LI); + + fprintf (stderr, "%s:%u: Error: ", GetPresumedFileName (LI), LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); @@ -229,23 +270,23 @@ static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const -void Error (const char* Format, ...) -/* Print an error message */ +void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) +/* Print an error message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntError (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntError (EC, LI, Format, ap); va_end (ap); } -void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) -/* Print an error message with the line info given explicitly */ +void Error (const char* Format, ...) +/* Print an error message */ { va_list ap; va_start (ap, Format); - IntError (EC, GetInputName (LI), GetInputLine (LI), Format, ap); + IntError (EC_PARSER, GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -256,7 +297,7 @@ void PPError (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntError (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntError (EC_PP, GetCurLineInfo (), Format, ap); va_end (ap); } @@ -268,17 +309,25 @@ void PPError (const char* Format, ...) -static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntWarning (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap) /* Print a warning message - internal function */ { if (IS_Get (&WarningsAreErrors)) { /* Treat the warning as an error */ - IntError (EC, Filename, LineNo, Msg, ap); + IntError (EC, LI, Msg, ap); } else if (IS_Get (&WarnEnable)) { - fprintf (stderr, "%s:%u: Warning: ", Filename, LineNo); + unsigned LineNo = GetPresumedLineNum (LI); + + /* Print file inclusion if appropriate */ + if (HasFileInclusionChanged (LI)) { + PrintFileInclusionInfo (LI); + } + RememberCheckedLI (LI); + + fprintf (stderr, "%s:%u: Warning: ", GetPresumedFileName (LI), LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); @@ -297,23 +346,23 @@ static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, cons -void Warning (const char* Format, ...) -/* Print a warning message */ +void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) +/* Print a warning message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntWarning (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntWarning (EC, LI, Format, ap); va_end (ap); } -void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) -/* Print a warning message with the line info given explicitly */ +void Warning (const char* Format, ...) +/* Print a warning message */ { va_list ap; va_start (ap, Format); - IntWarning (EC, GetInputName (LI), GetInputLine (LI), Format, ap); + IntWarning (EC_PARSER, GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -324,7 +373,7 @@ void PPWarning (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntWarning (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntWarning (EC_PP, GetCurLineInfo (), Format, ap); va_end (ap); } @@ -365,33 +414,33 @@ void ListWarnings (FILE* F) -static void IntNote (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntNote (const LineInfo* LI, const char* Msg, va_list ap) /* Print a note message - internal function */ { - fprintf (stderr, "%s:%u: Note: ", Filename, LineNo); + fprintf (stderr, "%s:%u: Note: ", GetPresumedFileName (LI), GetPresumedLineNum (LI)); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); } -void Note (const char* Format, ...) -/* Print a note message */ -{ - va_list ap; - va_start (ap, Format); - IntNote (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); - va_end (ap); -} - - - void LINote (const LineInfo* LI, const char* Format, ...) /* Print a note message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntNote (GetInputName (LI), GetInputLine (LI), Format, ap); + IntNote (LI, Format, ap); + va_end (ap); +} + + + +void Note (const char* Format, ...) +/* Print a note message */ +{ + va_list ap; + va_start (ap, Format); + IntNote (GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -402,7 +451,7 @@ void PPNote (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntNote (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntNote (GetDiagnosticLI (), Format, ap); va_end (ap); } diff --git a/src/cc65/error.h b/src/cc65/error.h index 4dce6cf91..5af862f9f 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -100,6 +100,9 @@ struct StrBuf; +void PrintFileInclusionInfo (const LineInfo* LI); +/* Print hierarchy of file inclusion */ + void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2))); /* Print a message about a fatal error and die */ @@ -109,7 +112,7 @@ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1, void Error (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print an error message */ -void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); +void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print an error message with the line info given explicitly */ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); @@ -118,7 +121,7 @@ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); void Warning (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a warning message */ -void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); +void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print a warning message with the line info given explicitly */ void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2))); diff --git a/src/cc65/input.c b/src/cc65/input.c index 89c471687..fcf7f32f3 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -91,10 +91,11 @@ struct IFile { /* Struct that describes an active input file */ typedef struct AFile AFile; struct AFile { - unsigned Line; /* Line number for this file */ + unsigned LineNum; /* Actual line number for this file */ FILE* F; /* Input file stream */ IFile* Input; /* Points to corresponding IFile */ int SearchPath; /* True if we've added a path for this file */ + unsigned LineOffs; /* Offset to presumed line number for this file */ char* PName; /* Presumed name of the file */ PPIfStack IfStack; /* PP #if stack */ int MissingNL; /* Last input line was missing a newline */ @@ -111,6 +112,7 @@ static Collection* CurrentInputStack; /* Counter for the __COUNTER__ macro */ static unsigned MainFileCounter; +LineInfo* PrevDiagnosticLI; @@ -163,10 +165,11 @@ static AFile* NewAFile (IFile* IF, FILE* F) AFile* AF = (AFile*) xmalloc (sizeof (AFile)); /* Initialize the fields */ - AF->Line = 0; - AF->F = F; - AF->Input = IF; - AF->PName = 0; + AF->LineNum = 0; + AF->F = F; + AF->Input = IF; + AF->LineOffs = 0; + AF->PName = 0; AF->IfStack.Index = -1; AF->MissingNL = 0; @@ -285,7 +288,7 @@ void OpenMainFile (const char* Name) /* Update the line infos, so we have a valid line info even at start of ** the main file before the first line is read. */ - UpdateLineInfo (MainFile->Input, MainFile->Line, Line); + UpdateCurrentLineInfo (Line); /* Initialize the __COUNTER__ counter */ MainFileCounter = 0; @@ -553,7 +556,7 @@ int NextLine (void) if (!Input->MissingNL || SB_NotEmpty (Line)) { /* Accept files without a newline at the end */ - ++Input->Line; + ++Input->LineNum; /* Assume no new line */ Input->MissingNL = 1; @@ -569,7 +572,7 @@ int NextLine (void) if (C == '\n') { /* We got a new line */ - ++Input->Line; + ++Input->LineNum; /* If the \n is preceeded by a \r, remove the \r, so we can read ** DOS/Windows files under *nix. @@ -605,7 +608,7 @@ int NextLine (void) InitLine (Line); /* Create line information for this line */ - UpdateLineInfo (Input->Input, Input->Line, Line); + UpdateCurrentLineInfo (Line); /* Done */ return C != EOF || SB_NotEmpty (Line); @@ -645,15 +648,145 @@ int PreprocessNextLine (void) -const char* GetInputFile (const struct IFile* IF) -/* Return a filename from an IFile struct */ +static LineInfoFile* NewLineInfoFile (const AFile* AF) +{ + const char* Name = AF->PName == 0 ? AF->Input->Name : AF->PName; + unsigned Len = strlen (Name); + + /* Allocate memory for the file info and the file name */ + LineInfoFile* LIF = xmalloc (sizeof (LineInfoFile) + Len); + + /* Copy info */ + LIF->InputFile = AF->Input; + LIF->LineNum = AF->LineNum + AF->LineOffs; + memcpy (LIF->Name, Name, Len + 1); + + return LIF; +} + + + +void GetFileInclusionInfo (struct LineInfo* LI) +/* Get info about source file inclusion for LineInfo struct */ +{ + unsigned FileCount = CollCount (&AFiles); + + CHECK (FileCount > 0); + + /* Get the correct index */ + --FileCount; + + if (LI->IncFiles != 0) { + FreeFileInclusionInfo (LI); + } + LI->IncFiles = 0; + + if (LI->File != 0) { + xfree (LI->File); + } + + /* Copy info from the AFile */ + LI->File = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount)); + + /* Remember the actual line number */ + LI->ActualLineNum = ((AFile*)CollAtUnchecked (&AFiles, FileCount))->LineNum; + + if (FileCount > 0) { + /* The file is included from another */ + + /* Always use a new collection */ + LI->IncFiles = NewCollection (); + + while (FileCount-- > 0) { + /* Copy info from the AFile */ + LineInfoFile* LIF = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount)); + + /* Add this file */ + CollAppend (LI->IncFiles, LIF); + } + } +} + + + +void FreeFileInclusionInfo (struct LineInfo* LI) +/* Free info about source file inclusion for LineInfo struct */ +{ + if (LI->File != 0) { + xfree (LI->File); + LI->File = 0; + } + + if (LI->IncFiles != 0) { + unsigned I; + for (I = 0; I < CollCount (LI->IncFiles); ++I) { + CollAtUnchecked (LI->IncFiles, I); + } + FreeCollection (LI->IncFiles); + LI->IncFiles = 0; + } +} + + + +static int IsDifferentLineInfoFile (const LineInfoFile* Lhs, const LineInfoFile* Rhs) +/* Return true if the two files are different */ +{ + /* If the input files are the same but their presumed names are different, + ** we still consider the files same. + */ + return Lhs->InputFile != Rhs->InputFile || Lhs->LineNum != Rhs->LineNum; +} + + + +int HasFileInclusionChanged (const struct LineInfo* LI) +/* Return true if file inclusion has changed from last time */ +{ + if (LI->File != 0) { + LineInfo* PrevLI = GetPrevCheckedLI (); + + if (LI == PrevLI) { + return 0; + } + + if (PrevLI == 0) { + return 1; + } + + if (LI->IncFiles != 0) { + unsigned I; + + if (PrevLI->IncFiles == 0 || + CollCount (LI->IncFiles) != CollCount (PrevLI->IncFiles)) { + return 1; + } + + for (I = 0; I < CollCount (LI->IncFiles); ++I) { + /* If this refers to a different file, then the inclusion has changed */ + if (IsDifferentLineInfoFile (CollAtUnchecked (LI->IncFiles, I), + CollAtUnchecked (PrevLI->IncFiles, I))) { + return 1; + } + } + } + } + + /* Unchanged */ + return 0; +} + + + +const char* GetInputFileName (const struct IFile* IF) +/* Return the name of the file from an IFile struct */ { return IF->Name; } -const char* GetCurrentFilename (void) +const char* GetCurrentFileName (void) /* Return the name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); @@ -674,7 +807,7 @@ unsigned GetCurrentLineNum (void) unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { const AFile* AF = CollLast (&AFiles); - return AF->Line; + return AF->LineNum + AF->LineOffs; } else { /* No open file */ return 0; @@ -684,18 +817,18 @@ unsigned GetCurrentLineNum (void) void SetCurrentLineNum (unsigned LineNum) -/* Set the line number in the current input file */ +/* Set the presumed line number in the current input file */ { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { AFile* AF = CollLast (&AFiles); - AF->Line = LineNum; + AF->LineOffs = LineNum - AF->LineNum; } } -void SetCurrentFilename (const char* Name) +void SetCurrentFileName (const char* Name) /* Set the presumed name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); diff --git a/src/cc65/input.h b/src/cc65/input.h index 9457bdf9b..9a5a76949 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -52,6 +52,10 @@ +/* Forwards */ +struct IFile; +struct LineInfo; + /* An enum that describes different types of input files. The members are ** choosen so that it is possible to combine them to bitsets */ @@ -61,9 +65,6 @@ typedef enum { IT_USRINC = 0x04, /* User include file (using "") */ } InputType; -/* Forward for an IFile structure */ -struct IFile; - /* The current input line */ extern StrBuf* Line; @@ -125,10 +126,19 @@ int PreprocessNextLine (void); ** main file. */ -const char* GetInputFile (const struct IFile* IF); -/* Return a filename from an IFile struct */ +void GetFileInclusionInfo (struct LineInfo* LI); +/* Get info about source file inclusion for LineInfo struct */ -const char* GetCurrentFilename (void); +void FreeFileInclusionInfo (struct LineInfo* LI); +/* Free info about source file inclusion for LineInfo struct */ + +int HasFileInclusionChanged (const struct LineInfo* LI); +/* Return true if file inclusion has changed from last time */ + +const char* GetInputFileName (const struct IFile* IF); +/* Return the name of the file from an IFile struct */ + +const char* GetCurrentFileName (void); /* Return the name of the current input file */ unsigned GetCurrentLineNum (void); @@ -137,7 +147,7 @@ unsigned GetCurrentLineNum (void); void SetCurrentLineNum (unsigned LineNum); /* Set the line number in the current input file */ -void SetCurrentFilename (const char* Name); +void SetCurrentFileName (const char* Name); /* Set the presumed name of the current input file */ unsigned GetCurrentCounter (void); diff --git a/src/cc65/lineinfo.c b/src/cc65/lineinfo.c index f5c2e2689..8639d27e9 100644 --- a/src/cc65/lineinfo.c +++ b/src/cc65/lineinfo.c @@ -56,6 +56,9 @@ /* Global pointer to line information for the current line */ static LineInfo* CurLineInfo = 0; +/* Global pointer to previously checked line information about file inclusion hierarchy */ +static LineInfo* PrevCheckedLI = 0; + /*****************************************************************************/ @@ -64,7 +67,7 @@ static LineInfo* CurLineInfo = 0; -static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) +static LineInfo* NewLineInfo (const StrBuf* Line) /* Create and return a new line info. Ref count will be 1. */ { unsigned Len; @@ -87,8 +90,9 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L /* Initialize the fields */ LI->RefCount = 1; - LI->InputFile = F; - LI->LineNum = LineNum; + LI->File = 0; + LI->IncFiles = 0; + GetFileInclusionInfo (LI); /* Copy the line, replacing tabs by spaces in the given line since tabs ** will give rather arbitrary results when used in the output later, and @@ -117,6 +121,7 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L static void FreeLineInfo (LineInfo* LI) /* Free a LineInfo structure */ { + FreeFileInclusionInfo (LI); xfree (LI); } @@ -156,8 +161,8 @@ LineInfo* GetCurLineInfo (void) -void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) -/* Update the line info - called if a new line is read */ +void UpdateCurrentLineInfo (const StrBuf* Line) +/* Update the current line info - called if a new line is read */ { /* If a current line info exists, release it */ if (CurLineInfo) { @@ -172,23 +177,60 @@ void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) } /* Create a new line info */ - CurLineInfo = NewLineInfo (F, LineNum, Line); + CurLineInfo = NewLineInfo (Line); } -const char* GetInputName (const LineInfo* LI) -/* Return the file name from a line info */ +void RememberCheckedLI (LineInfo* LI) +/* Remember the latest checked line info struct */ +{ + if (PrevCheckedLI != LI) { + if (PrevCheckedLI != 0) { + ReleaseLineInfo (PrevCheckedLI); + } + PrevCheckedLI = UseLineInfo (LI); + } +} + + + +LineInfo* GetPrevCheckedLI (void) +/* Get the latest checked line info struct */ +{ + return PrevCheckedLI; +} + + + +const char* GetPresumedFileName (const LineInfo* LI) +/* Return the presumed file name from a line info */ { PRECONDITION (LI != 0); - return GetInputFile (LI->InputFile); + return LI->File->Name; } -unsigned GetInputLine (const LineInfo* LI) -/* Return the line number from a line info */ +unsigned GetPresumedLineNum (const LineInfo* LI) +/* Return the presumed line number from a line info */ { PRECONDITION (LI != 0); - return LI->LineNum; + return LI->File->LineNum; +} + + + +const char* GetActualFileName (const struct LineInfo* LI) +/* Return the actual name of the source file from a line info struct */ +{ + return LI->File != 0 ? GetInputFileName (LI->File->InputFile) : "<out of filescope>"; +} + + + +unsigned GetActualLineNum (const struct LineInfo* LI) +/* Return the actual line number of the source file from a line info struct */ +{ + return LI->ActualLineNum; } diff --git a/src/cc65/lineinfo.h b/src/cc65/lineinfo.h index f365b4f01..02e77cd9c 100644 --- a/src/cc65/lineinfo.h +++ b/src/cc65/lineinfo.h @@ -60,15 +60,24 @@ struct IFile; +/* Struct that describes an input file for line info */ +typedef struct LineInfoFile LineInfoFile; +struct LineInfoFile { + struct IFile* InputFile; /* Points to corresponding IFile */ + unsigned LineNum; /* Presumed line number for this file */ + char Name[1]; /* Presumed name of the file */ +}; + /* The text for the actual line is allocated at the end of the structure, so ** the size of the structure varies. */ typedef struct LineInfo LineInfo; struct LineInfo { - unsigned RefCount; /* Reference counter */ - struct IFile* InputFile; /* Input file for this line */ - unsigned LineNum; /* Line number */ - char Line[1]; /* Source code line */ + unsigned RefCount; /* Reference counter */ + LineInfoFile* File; /* Presumed input files for this line */ + unsigned ActualLineNum; /* Actual line number for this file */ + struct Collection* IncFiles; /* Presumed inclusion input files */ + char Line[1]; /* Text of source code line */ }; @@ -92,14 +101,26 @@ LineInfo* GetCurLineInfo (void); ** increased, use UseLineInfo for that purpose. */ -void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line); -/* Update the line info - called if a new line is read */ +void UpdateCurrentLineInfo (const StrBuf* Line); +/* Update the current line info - called if a new line is read */ -const char* GetInputName (const LineInfo* LI); -/* Return the file name from a line info */ +void RememberCheckedLI (struct LineInfo* LI); +/* Remember the latest checked line info struct */ -unsigned GetInputLine (const LineInfo* LI); -/* Return the line number from a line info */ +LineInfo* GetPrevCheckedLI (void); +/* Get the latest checked line info struct */ + +const char* GetPresumedFileName (const LineInfo* LI); +/* Return the presumed file name from a line info */ + +unsigned GetPresumedLineNum (const LineInfo* LI); +/* Return the presumed line number from a line info */ + +const char* GetActualFileName (const struct LineInfo* LI); +/* Return the actual name of the source file from a line info struct */ + +unsigned GetActualLineNum (const struct LineInfo* LI); +/* Return the actual line number of the source file from a line info struct */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 0a9b94bf2..66cbb2a9d 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -842,7 +842,7 @@ static void AddPreLine (StrBuf* Str) SB_AppendChar (Str, '\n'); } SB_Printf (&Comment, "#line %u \"%s\"\n", - GetCurrentLineNum () - ContinuedLines, GetCurrentFilename ()); + GetCurrentLineNum () - ContinuedLines, GetCurrentFileName ()); SB_Append (Str, &Comment); } else { /* Output new lines */ @@ -2943,7 +2943,7 @@ static void DoLine (void) StrBuf Filename = AUTO_STRBUF_INITIALIZER; if (SB_GetString (Line, &Filename)) { SB_Terminate (&Filename); - SetCurrentFilename (SB_GetConstBuf (&Filename)); + SetCurrentFileName (SB_GetConstBuf (&Filename)); } else { PPError ("Invalid filename for #line directive"); LineNum = 0; @@ -3220,7 +3220,7 @@ void HandleSpecialMacro (Macro* M, const char* Name) } else if (strcmp (Name, "__FILE__") == 0) { /* Replace __FILE__ with the current filename */ StrBuf B = AUTO_STRBUF_INITIALIZER; - SB_InitFromString (&B, GetCurrentFilename ()); + SB_InitFromString (&B, GetCurrentFileName ()); SB_Clear (&M->Replacement); Stringize (&B, &M->Replacement); SB_Done (&B); @@ -3332,7 +3332,7 @@ void Preprocess (void) PLine = InitLine (PLine); if (Verbosity > 1 && SB_NotEmpty (Line)) { - printf ("%s:%u: %.*s\n", GetCurrentFilename (), GetCurrentLineNum (), + printf ("%s:%u: %.*s\n", GetCurrentFileName (), GetCurrentLineNum (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } From 2682fc0b7934743f59b564e550b7bc3d262f494e Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 10 Jan 2024 04:51:20 +0800 Subject: [PATCH 448/520] Fixed regression on comparison to null pointer. --- src/cc65/expr.c | 18 ++++++------ src/cc65/exprdesc.c | 41 ++++++++++++++++++++++---- src/cc65/exprdesc.h | 16 +++++++++- test/val/nullptr.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 test/val/nullptr.c diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 0f3a6e110..74005cb04 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1991,7 +1991,7 @@ void hie10 (ExprDesc* Expr) if (ED_IsConstAbs (Expr)) { /* Constant numeric expression */ Expr->IVal = !Expr->IVal; - } else if (ED_IsAddrExpr (Expr)) { + } else if (ED_IsEntityAddr (Expr)) { /* Address != NULL, so !Address == 0 */ ED_MakeConstBool (Expr, 0); } else { @@ -2445,8 +2445,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } /* Check for numeric constant operands */ - if ((ED_IsAddrExpr (Expr) && ED_IsNullPtr (&Expr2)) || - (ED_IsNullPtr (Expr) && ED_IsAddrExpr (&Expr2))) { + if ((ED_IsEntityAddr (Expr) && ED_IsNullPtr (&Expr2)) || + (ED_IsNullPtr (Expr) && ED_IsEntityAddr (&Expr2))) { /* Object addresses are inequal to null pointer */ Expr->IVal = (Tok != TOK_EQ); @@ -2477,8 +2477,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ pop (ltype); } - } else if (ED_IsAddrExpr (Expr) && - ED_IsAddrExpr (&Expr2) && + } else if (ED_IsEntityAddr (Expr) && + ED_IsEntityAddr (&Expr2) && Expr->Sym == Expr2.Sym) { /* Evaluate the result for static addresses */ @@ -3944,10 +3944,10 @@ static void hieQuest (ExprDesc* Expr) NextToken (); /* Parse second expression. Remember for later if it is a NULL pointer - ** expression, then load it into the primary. + ** constant expression, then load it into the primary. */ ExprWithCheck (hie0, &Expr2); - Expr2IsNULL = ED_IsNullPtr (&Expr2); + Expr2IsNULL = ED_IsNullPtrConstant (&Expr2); if (!IsTypeVoid (Expr2.Type) && ED_YetToLoad (&Expr2) && (!ConstantCond || !ED_IsConst (&Expr2))) { @@ -3991,10 +3991,10 @@ static void hieQuest (ExprDesc* Expr) ConsumeColon (); /* Parse third expression. Remember for later if it is a NULL pointer - ** expression, then load it into the primary. + ** constant expression, then load it into the primary. */ ExprWithCheck (hieQuest, &Expr3); - Expr3IsNULL = ED_IsNullPtr (&Expr3); + Expr3IsNULL = ED_IsNullPtrConstant (&Expr3); if (!IsTypeVoid (Expr3.Type) && ED_YetToLoad (&Expr3) && (!ConstantCond || !ED_IsConst (&Expr3))) { diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index a21e56623..08be1091d 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -255,7 +255,7 @@ int ED_IsConstTrue (const ExprDesc* Expr) { /* Non-zero arithmetics and objects addresses are boolean true */ return (ED_IsConstAbsInt (Expr) && Expr->IVal != 0) || - (ED_IsAddrExpr (Expr)); + ED_IsEntityAddr (Expr); } @@ -331,12 +331,41 @@ int ED_IsZPInd (const ExprDesc* Expr) int ED_IsNullPtr (const ExprDesc* Expr) -/* Return true if the given expression is a NULL pointer constant */ +/* Return true if the given expression is a null pointer. +** Note: A null pointer constant converted to a pointer type is a null pointer. +*/ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == - (E_LOC_NONE|E_RTYPE_RVAL) && - Expr->IVal == 0 && - IsClassInt (Expr->Type); + return ED_IsConstAbs (Expr) && + Expr->IVal == 0 && + (IsClassInt (Expr->Type) || IsTypePtr (Expr->Type)); +} + + + +int ED_IsNullPtrConstant (const ExprDesc* Expr) +/* Return true if the given expression is a null pointer constant. +** Note: An integer constant expression with value 0, or such an +** expression cast to void* is a null pointer constant. However, a +** null pointer constant converted to a pointer type is just a null +** pointer, not necessarily a constant in ISO C. +*/ +{ + return ED_IsConstAbs (Expr) && + Expr->IVal == 0 && + (IsClassInt (Expr->Type) || + (IsTypePtr (Expr->Type) && IsTypeVoid (Expr->Type + 1) && + GetQualifier (Expr->Type + 1) == T_QUAL_NONE)); +} + + + +int ED_IsEntityAddr (const ExprDesc* Expr) +/* Return true if the expression denotes the address of an object or function. +*/ +{ + return ED_IsAddrExpr (Expr) && + Expr->Sym != 0 && + (IsClassPtr (Expr->Type) || IsTypeFunc (Expr->Type)); } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 148485764..f6009a8a9 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -582,7 +582,21 @@ int ED_IsZPInd (const ExprDesc* Expr); /* Return true if the expression is located on the zeropage */ int ED_IsNullPtr (const ExprDesc* Expr); -/* Return true if the given expression is a NULL pointer constant */ +/* Return true if the given expression is a null pointer. +** Note: A null pointer constant converted to a pointer type is a null pointer. +*/ + +int ED_IsNullPtrConstant (const ExprDesc* Expr); +/* Return true if the given expression is a null pointer constant. +** Note: An integer constant expression with value 0, or such an +** expression cast to void* is a null pointer constant. However, a +** null pointer constant converted to a pointer type is just a null +** pointer, not necessarily a constant in ISO C. +*/ + +int ED_IsEntityAddr (const ExprDesc* Expr); +/* Return true if the expression denotes the address of an object or function. +*/ int ED_IsBool (const ExprDesc* Expr); /* Return true if the expression can be treated as a boolean, that is, it can diff --git a/test/val/nullptr.c b/test/val/nullptr.c new file mode 100644 index 000000000..e64b82ee2 --- /dev/null +++ b/test/val/nullptr.c @@ -0,0 +1,71 @@ +/* Bug # - Pointer compared to null pointer constant */ + +#include <stdio.h> + +unsigned failures; + +struct S { + char a[4]; +} *p; + +#define TEST_NULL(E) \ + do { \ + a = (E) == 0 && !(E); \ + if (!a) \ + { \ + ++failures; \ + printf("failed: " #E " should be null\n"); \ + } \ + } while(0); + +#define TEST_NON_NULL(E) \ + do { \ + a = (E) != 0 && !!(E) && (E); \ + if (!a) \ + { \ + ++failures; \ + printf("failed: " #E " should be non-null\n"); \ + } \ + } while(0); + +int main() +{ + int a; + + /* Null pointer constant (per ISO C) compared equal to null pointer constant */ + TEST_NULL((void*)0) + + /* Null pointer compared equal to null pointer constant */ + TEST_NULL((char*)0) + + /* Null pointer obtained with -> */ + TEST_NULL(((struct S*)0)->a) + + /* Null pointer obtained with -> */ + TEST_NULL(p->a) + + /* Null pointer obtained with cast and -> */ + TEST_NULL(((struct S*)(a = 0))->a) + + /* Null pointer obtained with cast and -> */ + TEST_NULL((a = 0, ((struct S*)a)->a)) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL(((struct S*)(long)(a = 0x1234))->a) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL((a = 0x1234, ((struct S*)a)->a)) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL(((struct S*)&a)->a) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL(((struct S*)&main)->a) + + if (failures != 0) + { + printf("failures: %u\n", failures); + } + + return failures; +} From 38dac907e8f3afcf269601a977f65a55cb44ad42 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Wed, 10 Jan 2024 04:51:59 +0800 Subject: [PATCH 449/520] Cleanup for symbol types and flags. --- src/cc65/asmstmt.c | 25 +++---- src/cc65/compile.c | 57 ++++++++------- src/cc65/declare.c | 38 +++++----- src/cc65/expr.c | 31 ++++---- src/cc65/exprdesc.h | 2 +- src/cc65/goto.c | 2 +- src/cc65/locals.c | 47 ++++++------ src/cc65/pragma.c | 2 +- src/cc65/symentry.c | 97 ++++++++++++++++--------- src/cc65/symentry.h | 142 ++++++++++++++++++++++++++----------- src/cc65/symtab.c | 169 ++++++++++++++++++++++++-------------------- src/cc65/symtab.h | 7 +- 12 files changed, 364 insertions(+), 255 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 166d05434..8521f0a81 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -80,9 +80,9 @@ static void AsmErrorSkip (void) -static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) -/* Find the symbol with the name currently in NextTok. The symbol must be of -** the given type. On errors, NULL is returned. +static SymEntry* AsmGetSym (unsigned Arg, int OnStack) +/* Find the symbol with the name currently in NextTok. The symbol must be on +** the stack if OnStack is true. On errors, NULL is returned. */ { SymEntry* Sym; @@ -110,8 +110,8 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) /* We found the symbol - skip the name token */ NextToken (); - /* Check if we have a global symbol */ - if ((Sym->Flags & Type) != Type) { + /* Check if the symbol is on the stack */ + if ((Sym->Flags & SC_STORAGEMASK) != SC_AUTO ? OnStack : !OnStack) { Error ("Type of argument %u differs from format specifier", Arg); AsmErrorSkip (); return 0; @@ -218,23 +218,24 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) */ { /* Parse the symbol name parameter and check the type */ - SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); + SymEntry* Sym = AsmGetSym (Arg, 0); if (Sym == 0) { /* Some sort of error */ return; } - /* Check for external linkage */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) { - /* External linkage or a function */ + /* Get the correct asm name */ + if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC || SymIsGlobal (Sym)) { + /* External or internal linkage or a function */ SB_AppendChar (T, '_'); SB_AppendStr (T, Sym->Name); - } else if (Sym->Flags & SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { + /* Register variable */ char Buf[32]; xsprintf (Buf, sizeof (Buf), "regbank+%d", Sym->V.R.RegOffs); SB_AppendStr (T, Buf); } else { - /* Static variable */ + /* Local static variable */ SB_AppendStr (T, LocalDataLabelName (Sym->V.L.Label)); } } @@ -248,7 +249,7 @@ static void ParseLVarArg (StrBuf* T, unsigned Arg) char Buf [16]; /* Parse the symbol name parameter and check the type */ - SymEntry* Sym = AsmGetSym (Arg, SC_AUTO); + SymEntry* Sym = AsmGetSym (Arg, 1); if (Sym == 0) { /* Some sort of error */ return; diff --git a/src/cc65/compile.c b/src/cc65/compile.c index aaa017453..108c80a28 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -121,15 +121,13 @@ static void Parse (void) } /* Read the declaration specifier */ - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_NONE); /* Don't accept illegal storage classes */ - if ((Spec.StorageClass & SC_TYPEMASK) == 0) { - if ((Spec.StorageClass & SC_AUTO) != 0 || - (Spec.StorageClass & SC_REGISTER) != 0) { - Error ("Illegal storage class"); - Spec.StorageClass = SC_EXTERN | SC_STATIC; - } + if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO || + (Spec.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { + Error ("Illegal storage class"); + Spec.StorageClass &= ~SC_STORAGEMASK; } /* Check if this is only a type declaration */ @@ -172,26 +170,26 @@ static void Parse (void) ** - if the storage class is explicitly specified as static, ** - or if there is an initialization. ** - ** This means that "extern int i;" will not get storage allocated. + ** This means that "extern int i;" will not get storage allocated + ** in this translation unit. */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && + if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - if ((Spec.Flags & DS_DEF_STORAGE) != 0 || - (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || - ((Decl.StorageClass & SC_EXTERN) != 0 && + /* The variable is visible in the file scope */ + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE || + (Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC || + ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN && CurTok.Tok == TOK_ASSIGN)) { - /* We will allocate storage */ - Decl.StorageClass |= SC_STORAGE; - } else { - /* It's a declaration */ - Decl.StorageClass |= SC_DECL; + /* We will allocate storage in this translation unit */ + Decl.StorageClass |= SC_TU_STORAGE; } } /* If this is a function declarator that is not followed by a comma ** or semicolon, it must be followed by a function body. */ - if ((Decl.StorageClass & SC_FUNC) != 0) { + if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* The function is now visible in the file scope */ if (CurTok.Tok == TOK_LCURLY) { /* A definition */ Decl.StorageClass |= SC_DEF; @@ -205,8 +203,6 @@ static void Parse (void) } } else { /* Just a declaration */ - Decl.StorageClass |= SC_DECL; - FuncDef = GetFuncDesc (Decl.Type); if ((FuncDef->Flags & (FD_EMPTY | FD_OLDSTYLE)) == FD_OLDSTYLE) { /* A parameter list without types is only allowed in a @@ -224,7 +220,7 @@ static void Parse (void) SymUseAttr (Sym, &Decl); /* Reserve storage for the variable if we need to */ - if (Decl.StorageClass & SC_STORAGE) { + if (Decl.StorageClass & SC_TU_STORAGE) { /* Get the size of the variable */ unsigned Size = SizeOf (Decl.Type); @@ -327,9 +323,13 @@ static void Parse (void) } /* Make the symbol zeropage according to the segment address size */ - if ((Sym->Flags & SC_STATIC) != 0) { - if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { - Sym->Flags |= SC_ZEROPAGE; + if ((Sym->Flags & SC_TYPEMASK) == SC_NONE) { + if (SymIsGlobal (Sym) || + (Sym->Flags & SC_STORAGEMASK) == SC_STATIC || + (Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { + if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { + Sym->Flags |= SC_ZEROPAGE; + } } } @@ -517,7 +517,10 @@ void Compile (const char* FileName) ** global variables. */ for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { - if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { + /* Is it a global (with or without static) tentative declaration of + ** an uninitialized variable? + */ + if ((Entry->Flags & (SC_TU_STORAGE | SC_DEF)) == SC_TU_STORAGE) { /* Assembly definition of uninitialized global variable */ SymEntry* TagSym = GetESUTagSym (Entry->Type); unsigned Size = SizeOf (Entry->Type); @@ -552,9 +555,9 @@ void Compile (const char* FileName) Entry->Name, GetFullTypeName (Entry->Type)); } - } else if (!SymIsDef (Entry) && (Entry->Flags & SC_FUNC) == SC_FUNC) { + } else if (!SymIsDef (Entry) && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) { /* Check for undefined functions */ - if ((Entry->Flags & (SC_EXTERN | SC_STATIC)) == SC_STATIC && SymIsRef (Entry)) { + if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) { Warning ("Static function '%s' used but never defined", Entry->Name); } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 2666a8d31..f93305f01 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -91,7 +91,7 @@ static unsigned ParseOneStorageClass (void) switch (CurTok.Tok) { case TOK_EXTERN: - StorageClass = SC_EXTERN | SC_STATIC; + StorageClass = SC_EXTERN; NextToken (); break; @@ -101,7 +101,7 @@ static unsigned ParseOneStorageClass (void) break; case TOK_REGISTER: - StorageClass = SC_REGISTER | SC_STATIC; + StorageClass = SC_REGISTER; NextToken (); break; @@ -136,9 +136,9 @@ static int ParseStorageClass (DeclSpec* Spec) } while (StorageClass != 0) { - if (Spec->StorageClass == 0) { - Spec->StorageClass = StorageClass; - } else if (Spec->StorageClass == StorageClass) { + if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { + Spec->StorageClass |= StorageClass; + } else if ((Spec->StorageClass & SC_STORAGEMASK) == StorageClass) { Warning ("Duplicate storage class specifier"); } else { Error ("Conflicting storage class specifier"); @@ -618,12 +618,12 @@ static SymEntry* ForwardESU (const char* Name, unsigned Flags, unsigned* DSFlags */ SymEntry* TagEntry = FindTagSym (Name); if (TagEntry == 0) { - if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { + if ((Flags & SC_TYPEMASK) != SC_ENUM) { TagEntry = AddStructSym (Name, Flags, 0, 0, DSFlags); } else { TagEntry = AddEnumSym (Name, Flags, 0, 0, DSFlags); } - } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { + } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_TYPEMASK)) { /* Already defined, but not the same type class */ Error ("Symbol '%s' is already different kind", Name); } @@ -798,7 +798,7 @@ static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) } /* Add an entry of the enumerator to the symbol table */ - AddConstSym (Ident, NewType, SC_ENUMERATOR | SC_CONST, EnumVal); + AddConstSym (Ident, NewType, SC_DEF | SC_ENUMERATOR, EnumVal); /* Use this type for following members */ MemberType = NewType; @@ -1825,8 +1825,8 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) /* We accept only auto and register as storage class specifiers, but ** we ignore all this, since we use auto anyway. */ - if ((Spec.StorageClass & SC_AUTO) == 0 && - (Spec.StorageClass & SC_REGISTER) == 0) { + if ((Spec.StorageClass & SC_STORAGEMASK) != SC_AUTO && + (Spec.StorageClass & SC_STORAGEMASK) != SC_REGISTER) { Error ("Illegal storage class"); } @@ -1942,12 +1942,12 @@ static void ParseAnsiParamList (FuncDesc* F) ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); /* We accept only auto and register as storage class specifiers */ - if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) { - Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; - } else if ((Spec.StorageClass & SC_REGISTER) == SC_REGISTER) { - Spec.StorageClass = SC_REGISTER | SC_STATIC | SC_PARAM | SC_DEF; + if ((Spec.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { + Spec.StorageClass = SC_REGISTER | SC_PARAM | SC_DEF; } else { - Error ("Illegal storage class"); + if ((Spec.StorageClass & SC_STORAGEMASK) != SC_AUTO) { + Error ("Illegal storage class"); + } Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } @@ -2355,7 +2355,9 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) D->StorageClass = Spec->StorageClass; /* If we have a function, add a special symbol type */ - if (IsTypeFunc (D->Type)) { + if (Mode != DM_ACCEPT_PARAM_IDENT && + IsTypeFunc (D->Type) && + (D->StorageClass & SC_TYPEMASK) == SC_NONE) { D->StorageClass |= SC_FUNC; } @@ -2479,9 +2481,9 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage) ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC); /* If no explicit storage class is given, use the default */ - if (Spec->StorageClass == 0) { + if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { Spec->Flags |= DS_DEF_STORAGE; - Spec->StorageClass = DefStorage; + Spec->StorageClass |= DefStorage; } } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 0f3a6e110..64b6d30bf 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1223,8 +1223,8 @@ static void Primary (ExprDesc* E) NextToken (); /* Check for illegal symbol types */ - CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL); - if (Sym->Flags & SC_ESUTYPEMASK) { + CHECK ((Sym->Flags & SC_TYPEMASK) != SC_LABEL); + if ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF) { /* Cannot use type symbols */ Error ("Variable identifier expected"); /* Assume an int type to make E valid */ @@ -1244,7 +1244,7 @@ static void Primary (ExprDesc* E) /* Enum or some other numeric constant */ E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->IVal = Sym->V.ConstVal; - } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_AUTO) { /* Local variable. If this is a parameter for a variadic ** function, we have to add some address calculations, and the ** address is not const. @@ -1258,26 +1258,25 @@ static void Primary (ExprDesc* E) E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->IVal = Sym->V.Offs; } - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC) { /* Function */ E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; E->Name = (uintptr_t) Sym->Name; - } else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { /* Register variable, zero page based */ E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL; E->Name = Sym->V.R.RegOffs; - } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { - /* Static variable */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) { - E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; - E->Name = (uintptr_t) Sym->Name; - } else { - E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; - E->Name = Sym->V.L.Label; - } - } else { + } else if (SymIsGlobal (Sym)) { + /* Global variable */ + E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; + E->Name = (uintptr_t) Sym->Name; + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_STATIC) { /* Local static variable */ E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; + E->Name = Sym->V.L.Label; + } else { + /* Other */ + E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; E->Name = Sym->V.Offs; } @@ -1311,7 +1310,7 @@ static void Primary (ExprDesc* E) } else { Warning ("Call to undeclared function '%s'", Ident); } - Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF | SC_FUNC); + Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_REF | SC_FUNC); E->Type = Sym->Type; E->Flags = E_LOC_GLOBAL | E_RTYPE_RVAL; E->Name = (uintptr_t) Sym->Name; diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 148485764..936bd591d 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -98,7 +98,7 @@ enum { E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ - E_LOC_STATIC = 0x0004, /* Static variable */ + E_LOC_STATIC = 0x0004, /* Local static variable */ E_LOC_REGISTER = 0x0008, /* Register variable */ E_LOC_STACK = 0x0010, /* Value on the stack */ E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ diff --git a/src/cc65/goto.c b/src/cc65/goto.c index 44ae0595e..e96ad6c4c 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -92,7 +92,7 @@ void GotoStatement (void) /* Find array size */ if (!IsTypeArray (arr->Type) || SizeOf (arr->Type) == 0 || - !(arr->Flags & SC_STATIC) || + (arr->Flags & SC_STORAGEMASK) != SC_STATIC || SizeOf (GetElementType(arr->Type)) != 2) { Error ("Expected a static array"); } else if (GetElementCount (arr->Type) > 127) { diff --git a/src/cc65/locals.c b/src/cc65/locals.c index b8738992f..28e263bb8 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -304,7 +304,7 @@ static void ParseAutoDecl (Declarator* Decl) /* Static local variables. */ - Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; + Decl->StorageClass = (Decl->StorageClass & ~SC_STORAGEMASK) | SC_STATIC; /* Generate a label, but don't define it */ DataLabel = GetLocalDataLabel (); @@ -451,24 +451,27 @@ static int ParseOneDecl (DeclSpec* Spec) /* Read the declarator */ NeedClean = ParseDecl (Spec, &Decl, DM_IDENT_OR_EMPTY); - /* Check if there are any non-extern storage classes set for function - ** declarations. Function can only be declared inside functions with the - ** 'extern' storage class specifier or no storage class specifier at all. + /* Check if there are explicitly specified non-external storage classes + ** for function declarations. */ - if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { - - /* Check if there are explicitly specified non-external storage classes */ + if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* Function can only be declared inside functions with the 'extern' + ** storage class specifier or no storage class specifier at all. + ** Note: this declaration is always checked for compatibility with + ** other declarations of the same symbol, but does not necessarily + ** make the symbol globally visible. This is tricky. + */ if ((Spec->Flags & DS_DEF_STORAGE) != DS_DEF_STORAGE && - (Decl.StorageClass & SC_EXTERN) == 0 && + (Decl.StorageClass & SC_STORAGEMASK) != SC_EXTERN && (Decl.StorageClass & SC_STORAGEMASK) != 0) { Error ("Illegal storage class on function"); } /* The default storage class could be wrong. Just clear them */ Decl.StorageClass &= ~SC_STORAGEMASK; - - /* This is always an extern declaration */ - Decl.StorageClass |= SC_DECL | SC_EXTERN; + } else if ((Decl.StorageClass & SC_STORAGEMASK) != SC_EXTERN) { + /* If the symbol is not marked as external, it will be defined now */ + Decl.StorageClass |= SC_DEF; } /* If we don't have a name, this was flagged as an error earlier. @@ -478,12 +481,6 @@ static int ParseOneDecl (DeclSpec* Spec) AnonName (Decl.Ident, "param"); } - /* If the symbol is not marked as external, it will be defined now */ - if ((Decl.StorageClass & SC_DECL) == 0 && - (Decl.StorageClass & SC_EXTERN) == 0) { - Decl.StorageClass |= SC_DEF; - } - /* Handle anything that needs storage (no functions, no typdefs) */ if ((Decl.StorageClass & SC_DEF) == SC_DEF && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { @@ -492,20 +489,20 @@ static int ParseOneDecl (DeclSpec* Spec) ** convert the declaration to "auto" if this is not possible. */ int Reg = 0; /* Initialize to avoid gcc complains */ - if ((Decl.StorageClass & SC_REGISTER) != 0 && + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_REGISTER && (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) { /* No space for this register variable, convert to auto */ - Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO; + Decl.StorageClass = (Decl.StorageClass & ~SC_STORAGEMASK) | SC_AUTO; } /* Check the variable type */ - if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { /* Register variable */ ParseRegisterDecl (&Decl, Reg); - } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) { + } else if ((Decl.StorageClass & SC_STORAGEMASK) == SC_AUTO) { /* Auto variable */ ParseAutoDecl (&Decl); - } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) { + } else if ((Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC) { /* Static variable */ ParseStaticDecl (&Decl); } else { @@ -514,7 +511,7 @@ static int ParseOneDecl (DeclSpec* Spec) } else { - if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN) { /* External identifier - may not get initialized */ if (CurTok.Tok == TOK_ASSIGN) { Error ("Cannot initialize extern variable '%s'", Decl.Ident); @@ -524,8 +521,8 @@ static int ParseOneDecl (DeclSpec* Spec) } } - if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || - (Decl.StorageClass & SC_FUNC) == SC_FUNC) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN || + (Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { /* Add the global symbol to both of the global and local symbol ** tables. */ diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index db306040d..b9394494b 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -630,7 +630,7 @@ static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B) Entry = FindSym(Name); /* Check if the name is valid */ - if (Entry && (Entry->Flags & SC_FUNC) == SC_FUNC) { + if (Entry && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) { PushWrappedCall(Entry, (unsigned int) Val); Entry->Flags |= SC_REF; diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 30ebe7dd8..56f6ffbb0 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -88,7 +88,7 @@ void FreeSymEntry (SymEntry* E) TypeFree (E->Type); xfree (E->AsmName); - if (E->Flags & SC_LABEL) { + if ((E->Flags & SC_TYPEMASK) == SC_LABEL) { for (i = 0; i < CollCount (E->V.L.DefsOrRefs); i++) { xfree (CollAt (E->V.L.DefsOrRefs, i)); } @@ -109,21 +109,17 @@ void DumpSymEntry (FILE* F, const SymEntry* E) unsigned Val; } SCFlagTable; - static SCFlagTable ESUTypes[] = { - { "SC_TYPEDEF", SC_TYPEDEF }, - { "SC_UNION", SC_UNION }, - { "SC_STRUCT", SC_STRUCT }, - { "SC_ENUM", SC_ENUM }, - }; - static SCFlagTable Types[] = { - { "SC_BITFIELD", SC_BITFIELD }, - { "SC_STRUCTFIELD", SC_STRUCTFIELD }, - { "SC_ENUMERATOR", SC_ENUMERATOR }, - { "SC_CONST", SC_CONST }, + { "SC_NONE", SC_NONE }, + { "SC_STRUCT", SC_STRUCT }, + { "SC_UNION", SC_UNION }, + { "SC_ENUM", SC_ENUM }, { "SC_LABEL", SC_LABEL }, - { "SC_PARAM", SC_PARAM }, + { "SC_BITFIELD", SC_BITFIELD }, + { "SC_TYPEDEF", SC_TYPEDEF }, + { "SC_ENUMERATOR", SC_ENUMERATOR }, { "SC_FUNC", SC_FUNC }, + { "SC_ARRAY", SC_ARRAY }, }; static SCFlagTable Storages[] = { @@ -131,11 +127,31 @@ void DumpSymEntry (FILE* F, const SymEntry* E) { "SC_REGISTER", SC_REGISTER }, { "SC_STATIC", SC_STATIC }, { "SC_EXTERN", SC_EXTERN }, - { "SC_STORAGE", SC_STORAGE }, + }; + + static SCFlagTable Properties[] = { + { "SC_CONST", SC_CONST }, + { "SC_STRUCTFIELD", SC_STRUCTFIELD }, + { "SC_PARAM", SC_PARAM }, + { "SC_DEFTYPE", SC_DEFTYPE }, { "SC_ZEROPAGE", SC_ZEROPAGE }, - { "SC_DECL", SC_DECL }, + { "SC_HAVEALIGN", SC_HAVEALIGN }, + { "SC_HAVEATTR", SC_HAVEATTR }, + { "SC_TU_STORAGE", SC_TU_STORAGE }, + { "SC_ASSIGN_INIT", SC_ASSIGN_INIT }, + { "SC_ALIAS", SC_ALIAS }, + { "SC_FICTITIOUS", SC_FICTITIOUS }, + { "SC_HAVEFAM", SC_HAVEFAM }, + { "SC_HAVECONST", SC_HAVECONST }, + }; + + static SCFlagTable Status[] = { { "SC_DEF", SC_DEF }, { "SC_REF", SC_REF }, + { "SC_GOTO", SC_GOTO }, + { "SC_GOTO_IND", SC_GOTO_IND }, + { "SC_LOCALSCOPE", SC_LOCALSCOPE }, + { "SC_NOINLINEDEF", SC_NOINLINEDEF }, }; unsigned I; @@ -152,28 +168,38 @@ void DumpSymEntry (FILE* F, const SymEntry* E) /* Print the flags */ SymFlags = E->Flags; fprintf (F, " Flags:"); - /* Enum, struct, union and typedefs */ - if ((SymFlags & SC_ESUTYPEMASK) != 0) { - for (I = 0; I < sizeof (ESUTypes) / sizeof (ESUTypes[0]); ++I) { - if ((SymFlags & SC_ESUTYPEMASK) == ESUTypes[I].Val) { - SymFlags &= ~SC_ESUTYPEMASK; - fprintf (F, " %s", ESUTypes[I].Name); + /* Symbol types */ + if ((SymFlags & SC_TYPEMASK) != 0) { + for (I = 0; I < sizeof (Types) / sizeof (Types[0]); ++I) { + if ((SymFlags & SC_TYPEMASK) == Types[I].Val) { + SymFlags &= ~SC_TYPEMASK; + fprintf (F, " %s", Types[I].Name); break; } } } - /* Other type flags */ - for (I = 0; I < sizeof (Types) / sizeof (Types[0]) && SymFlags != 0; ++I) { - if ((SymFlags & Types[I].Val) == Types[I].Val) { - SymFlags &= ~Types[I].Val; - fprintf (F, " %s", Types[I].Name); + /* Storage classes */ + if ((SymFlags & SC_STORAGEMASK) != 0) { + for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]); ++I) { + if ((SymFlags & SC_STORAGEMASK) == Storages[I].Val) { + SymFlags &= ~SC_STORAGEMASK; + fprintf (F, " %s", Storages[I].Name); + break; + } } } - /* Storage flags */ - for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]) && SymFlags != 0; ++I) { - if ((SymFlags & Storages[I].Val) == Storages[I].Val) { - SymFlags &= ~Storages[I].Val; - fprintf (F, " %s", Storages[I].Name); + /* Special property flags */ + for (I = 0; I < sizeof (Properties) / sizeof (Properties[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Properties[I].Val) == Properties[I].Val) { + SymFlags &= ~Properties[I].Val; + fprintf (F, " %s", Properties[I].Name); + } + } + /* Status flags */ + for (I = 0; I < sizeof (Status) / sizeof (Status[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Status[I].Val) == Status[I].Val) { + SymFlags &= ~Status[I].Val; + fprintf (F, " %s", Status[I].Name); } } if (SymFlags != 0) { @@ -199,9 +225,10 @@ int SymIsOutputFunc (const SymEntry* Sym) /* Symbol must be a function which is defined and either extern or ** static and referenced. */ - return IsTypeFunc (Sym->Type) && - SymIsDef (Sym) && - (Sym->Flags & (SC_REF | SC_EXTERN)); + return IsTypeFunc (Sym->Type) && + SymIsDef (Sym) && + ((Sym->Flags & SC_REF) || + (Sym->Flags & SC_STORAGEMASK) != SC_STATIC); } @@ -272,7 +299,7 @@ void SymCvtRegVarToAuto (SymEntry* Sym) /* Convert a register variable to an auto variable */ { /* Change the storage class */ - Sym->Flags = (Sym->Flags & ~(SC_REGISTER | SC_STATIC | SC_EXTERN)) | SC_AUTO; + Sym->Flags = (Sym->Flags & ~SC_STORAGEMASK) | SC_AUTO; /* Transfer the stack offset from register save area to actual offset */ Sym->V.Offs = Sym->V.R.SaveOffs; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 639221625..7bfc18ea4 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -68,47 +68,88 @@ struct CodeEntry; -/* Storage classes and flags */ +/* Symbol types and flags */ #define SC_NONE 0x0000U /* Nothing */ -#define SC_STRUCT 0x0001U /* Struct */ -#define SC_UNION 0x0002U /* Union */ -#define SC_ENUM 0x0003U /* Enum */ -#define SC_TYPEDEF 0x0004U /* Typedef */ -#define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ -#define SC_ENUMERATOR 0x0008U /* An enumerator */ -#define SC_BITFIELD 0x0010U /* A bit-field inside a struct or union */ -#define SC_TYPEMASK 0x001FU /* Mask for above types */ -#define SC_FUNC 0x0020U /* A function */ -#define SC_LABEL 0x0040U /* A goto code label */ -#define SC_CONST 0x0080U /* A numeric constant with a type */ -#define SC_PARAM 0x0100U /* A function parameter */ -#define SC_DEFTYPE 0x0200U /* Parameter has default type (=int, old style) */ -#define SC_STRUCTFIELD 0x0400U /* Struct or union field */ +/* Types of symbols */ +#define SC_STRUCT 0x0001U /* Struct tag */ +#define SC_UNION 0x0002U /* Union tag */ +#define SC_ENUM 0x0003U /* Enum tag */ +#define SC_LABEL 0x0004U /* A goto code label */ +#define SC_BITFIELD 0x0005U /* A bit-field inside a struct or union */ +#define SC_TYPEDEF 0x0006U /* A typedef */ +#define SC_ENUMERATOR 0x0007U /* An enumerator */ -#define SC_ZEROPAGE 0x0800U /* Symbol marked as zeropage */ +/* Note: These symbol types might be checked as bit-flags occasionally. +** So don't share their unique bits with other symbol types. +*/ +#define SC_FUNC 0x0008U /* A function */ +#define SC_ARRAY 0x0010U /* UNUSED: An array */ +#define SC_TYPEMASK 0x001FU /* Mask for symbol types all above */ -#define SC_DEF 0x1000U /* Symbol is defined */ -#define SC_REF 0x2000U /* Symbol is referenced */ -#define SC_DECL 0x4000U /* Symbol is declared in global scope */ -#define SC_STORAGE 0x8000U /* Symbol with associated storage */ +/* Additional property of the symbols */ +#define SC_CONST 0x0020U /* A numeric constant with a type */ +#define SC_STRUCTFIELD 0x0040U /* A struct or union field */ +#define SC_PARAM 0x0080U /* A function parameter */ +#define SC_DEFTYPE 0x0100U /* An old-style parameter with default type (=int) */ -#define SC_AUTO 0x010000U /* Auto variable */ -#define SC_REGISTER 0x020000U /* Register variable */ -#define SC_STATIC 0x040000U /* Static - not to be confused with other *_STATIC */ -#define SC_EXTERN 0x080000U /* Extern linkage */ -#define SC_STORAGEMASK 0x0F0000U /* Storage type mask */ +/* Address property of the symbol */ +#define SC_ZEROPAGE 0x0200U /* Symbol marked as on zeropage */ -#define SC_HAVEATTR 0x100000U /* Symbol has attributes */ +/* Additional attributes of the symbol */ +#define SC_HAVEALIGN 0x0400U /* UNUSED: Symbol has special alignment */ +#define SC_HAVEATTR 0x0800U /* Symbol has attributes */ -#define SC_GOTO 0x200000U -#define SC_SPADJUSTMENT 0x400000U -#define SC_GOTO_IND 0x800000U /* Indirect goto */ +/* Special property of declaration */ +#define SC_TU_STORAGE 0x1000U /* Symbol has allocated storage in the TU */ +#define SC_ASSIGN_INIT 0x2000U /* Symbol is to be initialized with assignment code */ -#define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */ -#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */ -#define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ -#define SC_HAVECONST 0x08000000U /* Type has a const member */ +#define SC_ALIAS 0x4000U /* Symbol is an alias */ +#define SC_FICTITIOUS 0x8000U /* Symbol is fictitious (for error recovery) */ +#define SC_HAVEFAM 0x010000U /* Struct/union has a Flexible Array Member */ +#define SC_HAVECONST 0x020000U /* Struct/union has a const member */ + +/* Status of the symbol */ +#define SC_DEF 0x040000U /* Symbol is defined */ +#define SC_REF 0x080000U /* Symbol is referenced */ +#define SC_GOTO 0x100000U /* Symbol is destination of a goto */ +#define SC_GOTO_IND 0x200000U /* Symbol is destination of an indirect goto */ +#define SC_LOCALSCOPE 0x400000U /* Symbol is invisible in file scope */ +#define SC_NOINLINEDEF 0x800000U /* Symbol may never have an inline definition */ + +/* To figure out the linkage of an object or function symbol Sym: +** - external linkage: +** SymIsGlobal (Sym) && (Sym->Flags & SC_STORAGEMASK) != SC_STATIC +** - internal linkage: +** SymIsGlobal (Sym) && (Sym->Flags & SC_STORAGEMASK) == SC_STATIC +** - no linkage: +** !SymIsGlobal (Sym) +** +** To figure out the storage class of a symbol by its SC_ flags: +** +** - no explicit storage class specifiers (in file scope): +** (flags & SC_STORAGEMASK) == SC_NONE +** - no explicit storage class specifiers (in block scope): +** (flags & SC_STORAGEMASK) == SC_AUTO +** - extern: +** (flags & SC_STORAGEMASK) == SC_EXTERN +** - static: +** (flags & SC_STORAGEMASK) == SC_STATIC +** - auto: +** (flags & SC_STORAGEMASK) == SC_AUTO +** - register: +** (flags & SC_STORAGEMASK) == SC_REGISTER +** - typedef (per ISO C): +** (flags & SC_TYPEMASK) == SC_TYPEDEF +** +** Note: SC_TYPEDEF can be also used as a flag. +*/ +#define SC_AUTO 0x01000000U /* Auto storage class */ +#define SC_REGISTER 0x02000000U /* Register storage class */ +#define SC_STATIC 0x03000000U /* Static storage class */ +#define SC_EXTERN 0x04000000U /* Extern storage class */ +#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */ +#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */ @@ -214,14 +255,37 @@ void FreeSymEntry (SymEntry* E); void DumpSymEntry (FILE* F, const SymEntry* E); /* Dump the given symbol table entry to the file in readable form */ +int SymIsOutputFunc (const SymEntry* Sym); +/* Return true if this is a function that must be output */ + +#if defined(HAVE_INLINE) +INLINE int SymIsArray (const SymEntry* Sym) +/* Return true if the given entry is an array entry */ +{ + return ((Sym->Flags & SC_TYPEMASK) == SC_ARRAY); +} +#else +# define SymIsArray(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_ARRAY) +#endif + #if defined(HAVE_INLINE) INLINE int SymIsBitField (const SymEntry* Sym) /* Return true if the given entry is a bit-field entry */ { - return ((Sym->Flags & SC_BITFIELD) == SC_BITFIELD); + return ((Sym->Flags & SC_TYPEMASK) == SC_BITFIELD); } #else -# define SymIsBitField(Sym) (((Sym)->Flags & SC_BITFIELD) == SC_BITFIELD) +# define SymIsBitField(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_BITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int SymIsLabel (const SymEntry* Sym) +/* Return true if the given entry is a label entry */ +{ + return ((Sym)->Flags & SC_TYPEMASK) == SC_LABEL; +} +#else +# define SymIsLabel(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_LABEL) #endif #if defined(HAVE_INLINE) @@ -257,17 +321,13 @@ INLINE int SymIsRef (const SymEntry* Sym) #if defined(HAVE_INLINE) INLINE int SymIsRegVar (const SymEntry* Sym) /* Return true if the given entry is a register variable */ -/* ### HACK! Fix the ugly type flags! */ { - return ((Sym->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER); + return ((Sym->Flags & (SC_STORAGEMASK | SC_TYPEMASK)) == (SC_REGISTER | SC_NONE)); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_STORAGEMASK | SC_TYPEMASK)) == (SC_REGISTER | SC_NONE)) #endif -int SymIsOutputFunc (const SymEntry* Sym); -/* Return true if this is a function that must be output */ - #if defined(HAVE_INLINE) INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) /* Return true if the given entry has a flexible array member */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index c2c6bab27..a76e60450 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -165,22 +165,22 @@ static void CheckSymTable (SymTable* Tab) /* Ignore typedef entries */ if (!SymIsTypeDef (Entry)) { - /* Check if the symbol is one with storage, and it if it was - ** defined but not used. - */ - if (((Flags & SC_AUTO) || (Flags & SC_STATIC)) && (Flags & SC_EXTERN) == 0) { + /* Check if the symbol has non-external linkage and is defined but not used */ + if (!SymIsGlobal (Entry) || (Flags & SC_STORAGEMASK) == SC_STATIC) { if (SymIsDef (Entry) && !SymIsRef (Entry) && !SymHasAttr (Entry, atUnused)) { if (Flags & SC_PARAM) { if (IS_Get (&WarnUnusedParam)) { Warning ("Parameter '%s' is never used", Entry->Name); } - } else if (Flags & SC_FUNC) { + } else if ((Flags & SC_TYPEMASK) == SC_FUNC) { if (IS_Get (&WarnUnusedFunc)) { Warning ("Function '%s' is defined but never used", Entry->Name); } - } else if (!IsAnonName (Entry->Name)) { - if (IS_Get (&WarnUnusedVar)) { + } else if ((Flags & SC_TYPEMASK) == SC_NONE) { + if (IS_Get (&WarnUnusedVar) && + !IsAnonName (Entry->Name) && + (Flags & SC_CONST) != SC_CONST) { Warning ("Variable '%s' is defined but never used", Entry->Name); } } @@ -188,7 +188,7 @@ static void CheckSymTable (SymTable* Tab) } /* If the entry is a label, check if it was defined in the function */ - if (Flags & SC_LABEL) { + if ((Flags & SC_TYPEMASK) == SC_LABEL) { if (!SymIsDef (Entry)) { /* Undefined label */ Error ("Undefined label: '%s'", Entry->Name); @@ -716,7 +716,7 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) Sym = 0; } - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC) { /* In case of a function, use the new type descriptor, since it ** contains pointers to the new symbol tables that are needed if @@ -863,7 +863,6 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab } else { TagEntry->V.E.SymTab = Tab; TagEntry->V.E.Type = Type; - TagEntry->Flags &= ~SC_DECL; TagEntry->Flags |= SC_DEF; /* Remember this is the first definition of this type */ @@ -1047,7 +1046,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); if (Entry) { - if ((Entry->Flags & SC_CONST) != SC_CONST) { + if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_TYPEMASK)) { Error ("Symbol '%s' is already different kind", Name); } else { Error ("Multiple definition for constant '%s'", Name); @@ -1056,7 +1055,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val } /* Create a new entry */ - Entry = NewSymEntry (Name, Flags); + Entry = NewSymEntry (Name, Flags | SC_CONST); /* We only have integer constants for now */ Entry->Type = TypeDup (T); @@ -1167,7 +1166,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Optimizer will need the information about the value of SP adjustment ** later, so let's preserve it. */ - E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); + E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), 0); E->V.SPAdjustment = StackPtr - DOR->StackPtr; AddSymEntry (SPAdjustTab, E); } @@ -1236,38 +1235,32 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { - int CheckExtern = 0; if ((Flags & SC_STRUCTFIELD) == 0) { while (Entry && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { /* Get the aliased entry */ Entry = Entry->V.A.Field; - /* Check for conflict with local storage class */ - CheckExtern = 1; } } /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; - } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { + } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { /* Redefinitions are not allowed */ if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { Error ("Multiple definition of '%s'", Entry->Name); Entry = 0; - } else if (CheckExtern) { - if ((Flags & (SC_AUTO | SC_REGISTER)) != 0) { - Error ("Declaration of '%s' with no linkage follows extern declaration", Name); - Entry = 0; - } else if ((Flags & SC_DEF) != 0 && (Flags & SC_EXTERN) == 0) { - /* If a static declaration follows a non-static declaration, - ** then it is an error. - */ - Error ("Static declaration of '%s' follows extern declaration", Name); - Entry = 0; - } } else if ((Flags & SC_STRUCTFIELD) != 0) { Error ("Duplicate member '%s'", Entry->Name); Entry = 0; + } else if (Entry->Owner == SymTab0) { + if ((Flags & SC_STORAGEMASK) == SC_AUTO || + (Flags & SC_STORAGEMASK) == SC_REGISTER || + (Flags & SC_STORAGEMASK) == SC_STATIC) { + Error ("Declaration of '%s' with no linkage follows extern declaration", + Name); + Entry = 0; + } } } @@ -1290,20 +1283,20 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry->Type = TypeDup (T); if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD || - (Flags & SC_ESUTYPEMASK) == SC_TYPEDEF) { + (Flags & SC_TYPEMASK) == SC_TYPEDEF) { if ((Flags & SC_ALIAS) != SC_ALIAS) { Entry->V.Offs = Offs; } - } else if ((Flags & SC_AUTO) == SC_AUTO) { + } else if ((Flags & SC_STORAGEMASK) == SC_AUTO) { Entry->V.Offs = Offs; - } else if ((Flags & SC_REGISTER) == SC_REGISTER) { + } else if ((Flags & SC_STORAGEMASK) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; Entry->V.R.SaveOffs = StackPtr; - } else if ((Flags & SC_EXTERN) == SC_EXTERN || - (Flags & SC_FUNC) == SC_FUNC) { + } else if ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_TYPEMASK) == SC_FUNC) { Entry->V.L.Label = Offs; SymSetAsmName (Entry); - } else if ((Flags & SC_STATIC) == SC_STATIC) { + } else if ((Flags & SC_STORAGEMASK) == SC_STATIC) { /* Generate the assembler name from the data label number */ Entry->V.L.Label = Offs; Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label)); @@ -1348,49 +1341,63 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Entry = FindGlobalSym (Name); } + /* Do we have a symbol with this name already? */ if (Entry) { - /* We have a symbol with this name already */ + /* Check if the symbol refers to some different type of things */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; - } else if ((Entry->Flags & (SC_AUTO | SC_REGISTER)) != 0) { - /* Check for local storage class conflict */ + } else if (Entry->Owner != SymTab0) { + /* The previous declaration has no linkage. The current declaration + ** has either external or internal linkage. Either way it is an + ** error since the two declarations would be referring to different + ** objects with the same identifier. + */ Error ("Extern declaration of '%s' follows declaration with no linkage", Name); Entry = 0; - } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { - /* If a static declaration follows a non-static declaration, then - ** the result is undefined. - ** Most compilers choose to either give an error at compile time, - ** or remove the extern property for a link time error if used. + } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { + /* The C standard specifies that the result is undefined if the + ** same thing has both internal and external linkage. Most + ** compilers choose to either give an error at compile time, or + ** remove the external linkage for a link time error if used + ** outside the current translation unit. We choose to give an + ** error at compile time in this case. */ - if (SymTab == SymTab0 && - (Flags & SC_EXTERN) == 0 && - (Entry->Flags & SC_EXTERN) != 0) { - Error ("Static declaration of '%s' follows non-static declaration", Name); - Entry = 0; - } else if ((Flags & SC_EXTERN) != 0 && - (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && - (Entry->Flags & SC_EXTERN) == 0) { - /* It is OK if a global extern declaration follows a global - ** non-static declaration, but an error if either of them is - ** local, as the two would be referring to different objects. - ** It is an error as well if a global non-static declaration - ** follows a global static declaration. + if ((Entry->Flags & SC_STORAGEMASK) != SC_STATIC) { + /* The previous declaration is a non-static declaration of an + ** object or function that has external linkage. */ - if (Entry->Owner == SymTab0) { - if ((Flags & SC_STORAGE) == 0) { - /* The C standard specifies that a later extern declaration will keep - ** the previously declared internal or external linkage unchanged. - ** Though not required by the standard, we are warning on this case. - */ - Flags &= ~SC_EXTERN; - Warning ("Extern declaration of '%s' follows static declaration, linkage unchanged", Name); - } else { - Error ("Non-static declaration of '%s' follows static declaration", Name); - Entry = 0; - } - } else { - Error ("Extern declaration of '%s' follows static declaration", Name); + if ((Flags & SC_STORAGEMASK) == SC_STATIC) { + /* It is a static declaration of an object or function that + ** has internal linkage. Conflicted wih the previous one. + */ + Error ("Static declaration of '%s' follows non-static declaration", + Name); + Entry = 0; + } + } else if ((Flags & SC_STORAGEMASK) != SC_STATIC) { + /* The previous declaration is a static declaration of an + ** object or function that has internal linkage. + */ + if ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_TYPEMASK) == SC_FUNC) { + /* The C standard specifies that an extern declaration + ** shall keep the previously declared internal linkage + ** unchanged. For a function declaration with no storage + ** class specifiers, it is treated as if with 'extern'. + ** We give a warning although it is not required by the + ** standard. + */ + Flags &= ~SC_STORAGEMASK; + Warning ("Extern declaration of '%s' follows static declaration", + Name); + } else if ((Flags & SC_STORAGEMASK) == SC_NONE) { + /* It is a non-extern-or-static declaration of an object in + ** file scope that has external linkage. Conflicted wih the + ** previous one. + */ + Error ("Non-static declaration of '%s' follows static declaration", + Name); Entry = 0; } } @@ -1486,14 +1493,22 @@ SymTable* GetLabelSymTab (void) -int SymIsLocal (SymEntry* Sym) -/* Return true if the symbol is defined in the highest lexical level */ +int SymIsLocal (const SymEntry* Sym) +/* Return true if the symbol is declared in the highest lexical level */ { return (Sym->Owner == SymTab || Sym->Owner == TagTab); } +int SymIsGlobal (const SymEntry* Sym) +/* Return true if the symbol is declared in the file scope level */ +{ + return (Sym->Owner == SymTab0 || Sym->Owner == TagTab0); +} + + + void MakeZPSym (const char* Name) /* Mark the given symbol as zero page symbol */ { @@ -1553,7 +1568,9 @@ void EmitExternals (void) Entry = SymTab->SymHead; while (Entry) { unsigned Flags = Entry->Flags; - if (Flags & SC_EXTERN) { + if (Entry->Owner == SymTab0 && + (Flags & SC_STORAGEMASK) != SC_STATIC && + ((Flags & SC_TYPEMASK) == SC_FUNC || (Flags & SC_TYPEMASK) == SC_NONE)) { /* Only defined or referenced externs */ if (SymIsRef (Entry) && !SymIsDef (Entry)) { /* An import */ @@ -1587,18 +1604,18 @@ void EmitDebugInfo (void) } Sym = SymTab->SymHead; while (Sym) { - if ((Sym->Flags & (SC_CONST | SC_TYPEMASK)) == 0) { - if (Sym->Flags & SC_AUTO) { + if ((Sym->Flags & SC_TYPEMASK) == 0) { + if ((Sym->Flags & SC_STORAGEMASK) == SC_AUTO) { AddTextLine ("%s, \"%s\", \"00\", auto, %d", Head, Sym->Name, Sym->V.Offs); - } else if (Sym->Flags & SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { AddTextLine ("%s, \"%s\", \"00\", register, \"regbank\", %d", Head, Sym->Name, Sym->V.R.RegOffs); } else if (SymIsRef (Sym) && !SymIsDef (Sym)) { AddTextLine ("%s, \"%s\", \"00\", %s, \"%s\"", Head, Sym->Name, - (Sym->Flags & SC_EXTERN)? "extern" : "static", + (Sym->Flags & SC_STORAGEMASK) != SC_STATIC ? "extern" : "static", Sym->AsmName); } } diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 38edddcb0..53b0df4eb 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -211,8 +211,11 @@ SymTable* GetFieldSymTab (void); SymTable* GetLabelSymTab (void); /* Return the label symbol table */ -int SymIsLocal (SymEntry* Sym); -/* Return true if the symbol is defined in the highest lexical level */ +int SymIsLocal (const SymEntry* Sym); +/* Return true if the symbol is declared in the highest lexical level */ + +int SymIsGlobal (const SymEntry* Sym); +/* Return true if the symbol is declared in the file scope level */ void MakeZPSym (const char* Name); /* Mark the given symbol as zero page symbol */ From 44b2e48e3ef556b9b462106152ad2a4bc89bf39c Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Thu, 11 Jan 2024 17:40:27 +0100 Subject: [PATCH 450/520] mention John Dunning as original author --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eff5049f9..500299730 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ For details look at the [Website](https://cc65.github.io). ## People -Project founder: +Project founders: -* Ullrich von Bassewitz +* John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted +* Ullrich von Bassewitz: move the code to modern systems, completely rewritten runtime library Core team members: From 6ab3c0c75fe921e55f841cc357832173044bfe81 Mon Sep 17 00:00:00 2001 From: Christian Groessler <chris@groessler.org> Date: Thu, 11 Jan 2024 19:40:41 +0100 Subject: [PATCH 451/520] elaborate more about Uz's contributions --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 500299730..11c3bb0ff 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,10 @@ For details look at the [Website](https://cc65.github.io). Project founders: * John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted -* Ullrich von Bassewitz: move the code to modern systems, completely rewritten runtime library +* Ullrich von Bassewitz: + * move the code to modern systems + * rewrite most parts of the compiler + * complete rewrite of the runtime library Core team members: From 3d0dc5815329de351f72b75afe9f01184464ff3b Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 13 Jan 2024 00:46:14 +0800 Subject: [PATCH 452/520] Fixed visibility of undeclared functions and objects. --- src/cc65/compile.c | 23 +++++++++++------------ src/cc65/symtab.c | 30 +++++++++++++++++++++++------- src/cc65/symtab.h | 4 ++-- test/err/bug2304-var-use.c | 15 +++++++++++++++ test/misc/Makefile | 6 ++++++ test/misc/bug2304-implicit-func.c | 21 +++++++++++++++++++++ 6 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 test/err/bug2304-var-use.c create mode 100644 test/misc/bug2304-implicit-func.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 108c80a28..f14774658 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -163,19 +163,19 @@ static void Parse (void) break; } - /* Check if we must reserve storage for the variable. We do this, - ** - ** - if it is not a typedef or function, - ** - if we don't had a storage class given ("int i") - ** - if the storage class is explicitly specified as static, - ** - or if there is an initialization. - ** - ** This means that "extern int i;" will not get storage allocated - ** in this translation unit. - */ + /* The symbol is now visible in the file scope */ if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - /* The variable is visible in the file scope */ + /* Check if we must reserve storage for the variable. We do this, + ** + ** - if it is not a typedef or function, + ** - if we don't had a storage class given ("int i") + ** - if the storage class is explicitly specified as static, + ** - or if there is an initialization. + ** + ** This means that "extern int i;" will not get storage allocated + ** in this translation unit. + */ if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE || (Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC || ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN && @@ -189,7 +189,6 @@ static void Parse (void) ** or semicolon, it must be followed by a function body. */ if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { - /* The function is now visible in the file scope */ if (CurTok.Tok == TOK_LCURLY) { /* A definition */ Decl.StorageClass |= SC_DEF; diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a76e60450..69484456f 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -557,8 +557,10 @@ static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned H -static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) -/* Find the symbol with the given name in the table tree that starts with T */ +static SymEntry* FindVisibleSymInTree (const SymTable* Tab, const char* Name) +/* Find the visible symbol with the given name in the table tree that starts +** with Tab. +*/ { /* Get the hash over the name */ unsigned Hash = HashStr (Name); @@ -574,7 +576,7 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) } /* Bail out if we found it */ - if (E != 0) { + if (E != 0 && (Tab != SymTab0 || (E->Flags & SC_LOCALSCOPE) == 0)) { return E; } @@ -589,9 +591,9 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) SymEntry* FindSym (const char* Name) -/* Find the symbol with the given name */ +/* Find with the given name the symbol visible in the current scope */ { - return FindSymInTree (SymTab, Name); + return FindVisibleSymInTree (SymTab, Name); } @@ -613,9 +615,9 @@ SymEntry* FindLocalSym (const char* Name) SymEntry* FindTagSym (const char* Name) -/* Find the symbol with the given name in the tag table */ +/* Find with the given name the tag symbol visible in the current scope */ { - return FindSymInTree (TagTab, Name); + return FindVisibleSymInTree (TagTab, Name); } @@ -1356,6 +1358,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Name); Entry = 0; } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { + /* If we are adding the symbol in the file scope, it is now + ** visible there. + */ + if (SymTab == SymTab0) { + Entry->Flags &= ~SC_LOCALSCOPE; + } + /* The C standard specifies that the result is undefined if the ** same thing has both internal and external linkage. Most ** compilers choose to either give an error at compile time, or @@ -1415,6 +1424,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } if (Entry == 0) { + /* Hide the symbol in the file scope if we are declaring it in a + ** local scope. + */ + if (Tab == SymTab0 && SymTab != SymTab0) { + Flags |= SC_LOCALSCOPE; + } + /* Create a new entry */ Entry = NewSymEntry (Name, Flags); diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 53b0df4eb..236bc090a 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -142,7 +142,7 @@ void LeaveStructLevel (void); SymEntry* FindSym (const char* Name); -/* Find the symbol with the given name */ +/* Find with the given name the symbol visible in the current scope */ SymEntry* FindGlobalSym (const char* Name); /* Find the symbol with the given name in the global symbol table only */ @@ -151,7 +151,7 @@ SymEntry* FindLocalSym (const char* Name); /* Find the symbol with the given name in the current symbol table only */ SymEntry* FindTagSym (const char* Name); -/* Find the symbol with the given name in the tag table */ +/* Find with the given name the tag symbol visible in the current scope */ SymEntry FindStructField (const Type* TypeArray, const char* Name); /* Find a struct/union field in the fields list. diff --git a/test/err/bug2304-var-use.c b/test/err/bug2304-var-use.c new file mode 100644 index 000000000..8a88405e2 --- /dev/null +++ b/test/err/bug2304-var-use.c @@ -0,0 +1,15 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +void f1(void) +{ + extern int a; +} + +/* 'a' is still invisible in the file scope */ + +int main(void) +{ + return a * 0; /* Usage of 'a' should be an error */ +} + +int a = 42; diff --git a/test/misc/Makefile b/test/misc/Makefile index 811f7f462..cfcae0530 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -133,6 +133,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref +# this one requires failure with --std=c89, it fails with --std=cc65 due to +# stricter checks +$(WORKDIR)/bug2304-implicit-func.$1.$2.prg: bug2304-implicit-func.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug2304-implicit-func.$1.$2.prg) + $(NOT) $(CC65) --standard c89 -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should not compile until 3-byte struct by value tests are re-enabled $(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR) $(if $(QUIET),echo misc/struct-by-value.$1.$2.prg) diff --git a/test/misc/bug2304-implicit-func.c b/test/misc/bug2304-implicit-func.c new file mode 100644 index 000000000..f6b7450ff --- /dev/null +++ b/test/misc/bug2304-implicit-func.c @@ -0,0 +1,21 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +/* This one should fail even in C89 */ + +void f1(void) +{ + extern unsigned int f(); +} + +/* 'f' is still invisible in the file scope */ + +int main(void) +{ + f(); /* Should be a conflict since the implicit function type is incompatible */ + return 0; +} + +unsigned int f() +{ + return 42; +} From 7e80e55b6d12138024d44557ed878fa3c50c6fda Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sat, 13 Jan 2024 16:40:44 +0800 Subject: [PATCH 453/520] Added a warning on implicit int in typedefs. --- src/cc65/declare.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index f93305f01..7f1d8b948 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2388,16 +2388,26 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - /* For anything that is not a function or typedef, check for an implicit - ** int declaration. + /* For anything that is not a function, check for an implicit int + ** declaration. */ - if (!IsTypeFunc (D->Type) && - (D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - /* If the standard was not set explicitly to C89, print a warning - ** for variables with implicit int type. - */ - if (IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' is an obsolete feature"); + if (!IsTypeFunc (D->Type) && IsRankInt (D->Type)) { + if ((D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + /* If the standard was not set explicitly to C89, print a warning + ** for variables with implicit int type. + */ + if (IS_Get (&Standard) >= STD_C99) { + Warning ("Implicit 'int' is an obsolete feature"); + } + } else { + /* If the standard was not set explicitly to C89, print a warning + ** for typedefs with implicit int type. + */ + if (IS_Get (&Standard) >= STD_C99) { + Warning ("Type defaults to 'int' in typedef of '%s'", + D->Ident); + Note ("Implicit 'int' is an obsolete feature"); + } } } } From 0b06c34dfc89271a135c24573352715da1aaf187 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 14 Jan 2024 00:08:41 +0800 Subject: [PATCH 454/520] Added primitive support for the ISO C99 inline feature as well as the __inline__ extension. No inlining is actually done but that part is not required by the standard. --- src/cc65/compile.c | 6 +- src/cc65/declare.c | 84 ++++++++++++++++++++++-- src/cc65/expr.c | 2 +- src/cc65/function.c | 6 ++ src/cc65/locals.c | 2 +- src/cc65/scanner.h | 1 + src/cc65/symentry.h | 4 ++ src/cc65/symtab.c | 12 +++- test/ref/Makefile | 1 + test/ref/bug1889-missing-identifier.cref | 2 +- test/ref/inline-error.c | 36 ++++++++++ test/ref/inline-error.cref | 20 ++++++ test/val/inline-func.c | 20 ++++++ 13 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 test/ref/inline-error.c create mode 100644 test/ref/inline-error.cref create mode 100644 test/val/inline-func.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 108c80a28..b24751b59 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -121,7 +121,7 @@ static void Parse (void) } /* Read the declaration specifier */ - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_NONE); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE); /* Don't accept illegal storage classes */ if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO || @@ -560,6 +560,10 @@ void Compile (const char* FileName) if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) { Warning ("Static function '%s' used but never defined", Entry->Name); + } else if ((Entry->Flags & SC_INLINE) != 0) { + Warning ("Inline function '%s' %s but never defined", + Entry->Name, + SymIsRef (Entry) ? "used" : "declared"); } } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index f93305f01..6173b5460 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -124,9 +124,37 @@ static unsigned ParseOneStorageClass (void) +static unsigned ParseOneFuncSpec (void) +/* Parse and return a function specifier */ +{ + unsigned FuncSpec = 0; + + /* Check the function specifier given */ + switch (CurTok.Tok) { + + case TOK_INLINE: + FuncSpec = SC_INLINE; + NextToken (); + break; + + case TOK_NORETURN: + FuncSpec = SC_NORETURN; + NextToken (); + break; + + default: + break; + } + + return FuncSpec; +} + + + static int ParseStorageClass (DeclSpec* Spec) /* Parse storage class specifiers. Return true if a specifier is read even if -** it was duplicated or disallowed. */ +** it was duplicated or disallowed. +*/ { /* Check the storage class given */ unsigned StorageClass = ParseOneStorageClass (); @@ -151,6 +179,31 @@ static int ParseStorageClass (DeclSpec* Spec) +static int ParseFuncSpecClass (DeclSpec* Spec) +/* Parse function specifiers. Return true if a specifier is read even if it +** was duplicated or disallowed. +*/ +{ + /* Check the function specifiers given */ + unsigned FuncSpec = ParseOneFuncSpec (); + + if (FuncSpec == 0) { + return 0; + } + + while (FuncSpec != 0) { + if ((Spec->StorageClass & FuncSpec) != 0) { + Warning ("Duplicate function specifier"); + } + Spec->StorageClass |= FuncSpec; + FuncSpec = ParseOneFuncSpec (); + } + + return 1; +} + + + static void DuplicateQualifier (const char* Name) /* Print an error message */ { @@ -303,7 +356,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t */ { TypeCode Q = T_QUAL_NONE; - int Continue; + int HasStorageClass; + int HasFuncSpec; do { /* There may be type qualifiers *before* any storage class specifiers */ @@ -311,11 +365,17 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t *Qualifiers |= Q; /* Parse storage class specifiers anyway then check */ - Continue = ParseStorageClass (Spec); - if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) { + HasStorageClass = ParseStorageClass (Spec); + if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) { Error ("Unexpected storage class specified"); } - } while (Continue || Q != T_QUAL_NONE); + + /* Parse function specifiers anyway then check */ + HasFuncSpec = ParseFuncSpecClass (Spec); + if (HasFuncSpec && (TSFlags & TS_FUNCTION_SPEC) == 0) { + Error ("Unexpected function specifiers"); + } + } while (Q != T_QUAL_NONE || HasStorageClass || HasFuncSpec); } @@ -2375,6 +2435,14 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Parse attributes for this declarator */ ParseAttribute (D); + /* 'inline' is only allowed on functions */ + if (Mode != DM_ACCEPT_PARAM_IDENT && + (D->StorageClass & SC_TYPEMASK) != SC_FUNC && + (D->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'inline' on non-function declaration"); + D->StorageClass &= ~SC_INLINE; + } + /* Check a few pre-C99 things */ if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { /* Check and warn about an implicit int return in the function */ @@ -2478,7 +2546,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage) Spec->Flags &= ~DS_DEF_STORAGE; /* Parse the type specifiers */ - ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC); + ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC); /* If no explicit storage class is given, use the default */ if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { @@ -2495,7 +2563,9 @@ void CheckEmptyDecl (const DeclSpec* Spec) ** warning if not. */ { - if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { + if ((Spec->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'inline' on empty declaration"); + } else if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { /* No declaration at all */ } else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { Warning ("Declaration does not declare anything"); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index e5e5cc62e..a855e5b3c 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1407,7 +1407,7 @@ static void Primary (ExprDesc* E) } else { /* Let's see if this is a C99-style declaration */ DeclSpec Spec; - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE | TS_FUNCTION_SPEC, SC_AUTO); if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { Error ("Mixed declarations and code are not supported in cc65"); diff --git a/src/cc65/function.c b/src/cc65/function.c index d570c2dde..596f9b617 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -518,6 +518,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D) Error ("'main' cannot be declared as __fastcall__"); } + /* main() cannot be an inline function */ + if ((Func->Flags & SC_INLINE) == SC_INLINE) { + Error ("'main' cannot be declared inline"); + Func->Flags &= ~SC_INLINE; + } + /* Check return type */ if (GetUnqualRawTypeCode (ReturnType) == T_INT) { /* Determine if this is a main function in a C99 environment that diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 28e263bb8..777f6b8b9 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -563,7 +563,7 @@ void DeclareLocals (void) } /* Read the declaration specifier */ - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_AUTO); /* Check variable declarations. We need distinguish between a default ** int type and the end of variable declarations. So we will do the diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index ccf3a8805..6fc3e5370 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -76,6 +76,7 @@ typedef enum token_t { /* Function specifiers */ TOK_INLINE, + TOK_NORETURN, TOK_FASTCALL, TOK_CDECL, diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 7bfc18ea4..7871b9ade 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -151,6 +151,10 @@ struct CodeEntry; #define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */ #define SC_STORAGEMASK 0x0F000000U /* Storage type mask */ +/* Function specifiers */ +#define SC_INLINE 0x10000000U /* Inline function */ +#define SC_NORETURN 0x20000000U /* Noreturn function */ + /* Label definition or reference */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a76e60450..f5ef2a15c 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1439,6 +1439,16 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Entry->V.F.WrappedCall = WrappedCall; Entry->V.F.WrappedCallData = WrappedCallData; } + + /* A files cope function declaration with the 'extern' storage + ** class or without the 'inline' specifier ensures that the + ** function definition (if any) is a non-inline definition. + */ + if (SymTab == SymTab0 && + ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_INLINE) == 0)) { + Entry->Flags |= SC_NOINLINEDEF; + } } /* Add an alias of the global symbol to the local symbol table */ @@ -1575,7 +1585,7 @@ void EmitExternals (void) if (SymIsRef (Entry) && !SymIsDef (Entry)) { /* An import */ g_defimport (Entry->Name, Flags & SC_ZEROPAGE); - } else if (SymIsDef (Entry)) { + } else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) { /* An export */ g_defexport (Entry->Name, Flags & SC_ZEROPAGE); } diff --git a/test/ref/Makefile b/test/ref/Makefile index 9ecb33c00..5c189c6cb 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -63,6 +63,7 @@ CUSTOMSOURCES = \ # exact error output is required ERRORSOURCES = \ custom-reference-error.c \ + inline-error.c \ bug1889-missing-identifier.c \ bug2312-preprocessor-error.c diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index 6317657d1..e77c1a7a1 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,4 +1,4 @@ bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature -bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected +bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration bug1889-missing-identifier.c:6: Error: Expression expected diff --git a/test/ref/inline-error.c b/test/ref/inline-error.c new file mode 100644 index 000000000..d8191025a --- /dev/null +++ b/test/ref/inline-error.c @@ -0,0 +1,36 @@ +/* C99 inline in declarations */ + +inline typedef int; /* Error */ +static inline int; /* Error */ +inline static int a1; /* Error */ +int inline (*fp1)(void); /* Error */ +typedef inline int f1_t(void); /* Error */ +inline int f1a(void); /* OK here warning later */ +inline extern int f1b(void); /* OK here warning later */ +extern inline int f1b(void); /* Same as above */ +inline static int f1c(void); /* OK here warning later */ +static inline int f1c(void); /* Same as above */ + +void foo(inline int x); /* Error */ +int a = sizeof (inline int); /* TODO: better error message */ +int b = sizeof (inline int (int)); /* TODO: better error message */ + +inline int main(void) /* Error */ +{ + inline typedef int; /* Error */ + static inline int; /* Error */ + extern inline int a2; /* Error */ + int inline (*fp2)(void); /* Error */ + typedef inline int f2_t(void); /* Error */ + inline int f2a(void); /* OK here warning later */ + inline extern int f2b(void); /* OK here warning later */ + extern inline int f2b(void); /* Same as above */ + + f1a(); /* Still imported */ + f1b(); /* Still imported */ + f1c(); /* Not imported */ + f2a(); /* Still imported */ + f2b(); /* Still imported */ +} + +/* Warning: non-external inline functions declared but undefined in TU */ diff --git a/test/ref/inline-error.cref b/test/ref/inline-error.cref new file mode 100644 index 000000000..abfdcdddd --- /dev/null +++ b/test/ref/inline-error.cref @@ -0,0 +1,20 @@ +inline-error.c:3: Error: 'inline' on empty declaration +inline-error.c:4: Error: 'inline' on empty declaration +inline-error.c:5: Error: 'inline' on non-function declaration +inline-error.c:6: Error: 'inline' on non-function declaration +inline-error.c:7: Error: 'inline' on non-function declaration +inline-error.c:14: Error: Unexpected function specifiers +inline-error.c:15: Error: Mixed declarations and code are not supported in cc65 +inline-error.c:16: Error: Mixed declarations and code are not supported in cc65 +inline-error.c:19: Error: 'main' cannot be declared inline +inline-error.c:20: Error: 'inline' on empty declaration +inline-error.c:21: Error: 'inline' on empty declaration +inline-error.c:22: Error: 'inline' on non-function declaration +inline-error.c:23: Error: 'inline' on non-function declaration +inline-error.c:24: Error: 'inline' on non-function declaration +inline-error.c:34: Warning: Variable 'fp2' is defined but never used +inline-error.c:37: Warning: Inline function 'f1a' used but never defined +inline-error.c:37: Warning: Inline function 'f1b' used but never defined +inline-error.c:37: Warning: Static function 'f1c' used but never defined +inline-error.c:37: Warning: Inline function 'f2a' used but never defined +inline-error.c:37: Warning: Inline function 'f2b' used but never defined diff --git a/test/val/inline-func.c b/test/val/inline-func.c new file mode 100644 index 000000000..b9e127aae --- /dev/null +++ b/test/val/inline-func.c @@ -0,0 +1,20 @@ +/* C99 inline */ + +#include <stdlib.h> + +inline static int f(int x, ...) +{ + return x * 2; +} + +extern inline int g(int x); + +int main(void) +{ + return f(g(7)) == 42 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int g(int x) +{ + return x * 3; +} From de3087a7e9b7590ab8122d547e8ae9ac0d191b21 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Sun, 14 Jan 2024 00:19:11 +0800 Subject: [PATCH 455/520] Removed the extra "unused parameter" warning when the parameter had an duplicated identifier error. --- src/cc65/symtab.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a76e60450..d3544bfc4 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -170,7 +170,8 @@ static void CheckSymTable (SymTable* Tab) if (SymIsDef (Entry) && !SymIsRef (Entry) && !SymHasAttr (Entry, atUnused)) { if (Flags & SC_PARAM) { - if (IS_Get (&WarnUnusedParam)) { + if (IS_Get (&WarnUnusedParam) && + !IsAnonName (Entry->Name)) { Warning ("Parameter '%s' is never used", Entry->Name); } } else if ((Flags & SC_TYPEMASK) == SC_FUNC) { From afdf398a0b628704b23166cde21d986b23d168b6 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 15 Jan 2024 23:56:11 +0800 Subject: [PATCH 456/520] Fixed repeated diagnosis when reading EOF in certain cases. --- src/cc65/declare.c | 6 +++--- src/cc65/locals.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 7f1d8b948..c3c1160a6 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -997,7 +997,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) /* Parse union fields */ UnionSize = 0; - while (CurTok.Tok != TOK_RCURLY) { + while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { /* Get the type of the entry */ DeclSpec Spec; @@ -1217,7 +1217,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) FlexibleMember = 0; StructSize = 0; BitOffs = 0; - while (CurTok.Tok != TOK_RCURLY) { + while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { /* Get the type of the entry */ DeclSpec Spec; @@ -1814,7 +1814,7 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) } /* An optional list of type specifications follows */ - while (CurTok.Tok != TOK_LCURLY) { + while (CurTok.Tok != TOK_LCURLY && CurTok.Tok != TOK_CEOF) { DeclSpec Spec; int NeedClean; diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 28e263bb8..8bf7aa1d2 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -551,7 +551,9 @@ void DeclareLocals (void) /* A place to store info about potential initializations of auto variables */ CollAppend (&CurrentFunc->LocalsBlockStack, 0); - /* Loop until we don't find any more variables */ + /* Loop until we don't find any more variables. EOF is handled in the loop + ** as well. + */ while (1) { DeclSpec Spec; int NeedClean; From 07e349c517a4702c8c2af26064691dcf05c5e580 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 15 Jan 2024 23:56:39 +0800 Subject: [PATCH 457/520] Skipped anonymous tag names in diagnosis on empty structs/unions. --- src/cc65/symtab.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index d3544bfc4..8ae49cdf3 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -955,7 +955,13 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl TagEntry = 0; } else if (Size == 0) { /* Empty struct is not supported now */ - Error ("Empty %s type '%s' is not supported", SCType == SC_STRUCT ? "struct" : "union", Name); + if (!IsAnonName (Name)) { + Error ("Empty %s type '%s' is not supported", + SCType == SC_STRUCT ? "struct" : "union", Name); + } else { + Error ("Empty %s type is not supported", + SCType == SC_STRUCT ? "struct" : "union"); + } TagEntry = 0; } } From e9bd9330c0b09f646a52e36c6cbf198015887d2f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Mon, 15 Jan 2024 23:56:42 +0800 Subject: [PATCH 458/520] Added warning on some code patterns of faulty attempt to declare anonymous structs/unions. Removed unnecessary warning on tagless enum/struct/unions that would be invisible out of a function declaration. --- src/cc65/datatype.c | 18 ++++++++++++ src/cc65/datatype.h | 6 ++++ src/cc65/declare.c | 68 ++++++++++++++++++++------------------------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 9c82e6773..4d6cb25a5 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -989,6 +989,24 @@ int IsIncompleteESUType (const Type* T) +int IsAnonESUType (const Type* T) +/* Return true if this is an anonymous ESU type */ +{ + SymEntry* TagSym = GetESUTagSym (T); + + return TagSym != 0 && SymHasAnonName (TagSym); +} + + + +int IsAnonStructClass (const Type* T) +/* Return true if this is an anonymous struct or union type */ +{ + return IsClassStruct (T) && IsAnonESUType (T); +} + + + int IsPassByRefType (const Type* T) /* Return true if this is a large struct/union type that doesn't fit in the ** primary. This returns false for the void value extension type since it is diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index dbe0eedaa..8446fb914 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -792,6 +792,12 @@ int IsESUType (const Type* T); int IsIncompleteESUType (const Type* T); /* Return true if this is an incomplete ESU type */ +int IsAnonESUType (const Type* T); +/* Return true if this is an anonymous ESU type */ + +int IsAnonStructClass (const Type* T); +/* Return true if this is an anonymous struct or union type */ + int IsPassByRefType (const Type* T); /* Return true if this is a large struct/union type that doesn't fit in the ** primary. This returns false for the void value extension type since it is diff --git a/src/cc65/declare.c b/src/cc65/declare.c index c3c1160a6..20e2e6879 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1020,7 +1020,8 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); /* Check if this is only a type declaration */ - if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) { + if (CurTok.Tok == TOK_SEMI && + !(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) { CheckEmptyDecl (&Spec); NextToken (); continue; @@ -1061,22 +1062,12 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) /* In cc65 mode, we allow anonymous structs/unions within ** a union. */ - SymEntry* TagEntry; - if (IS_Get (&Standard) >= STD_CC65 && - IsClassStruct (Decl.Type) && - (TagEntry = GetESUTagSym (Decl.Type)) && - SymHasAnonName (TagEntry)) { - /* This is an anonymous struct or union */ - AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); - /* Ignore CVR qualifiers */ - if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { - Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); - Decl.Type[0].C &= ~T_QUAL_CVR; - } - } else { - /* Invalid member */ - goto NextMember; + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; } } else if (FieldWidth > 0) { /* A bit-field without a name will get an anonymous one */ @@ -1240,7 +1231,8 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); /* Check if this is only a type declaration */ - if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) { + if (CurTok.Tok == TOK_SEMI && + !(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) { CheckEmptyDecl (&Spec); NextToken (); continue; @@ -1308,22 +1300,12 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) /* In cc65 mode, we allow anonymous structs/unions within ** a struct. */ - SymEntry* TagEntry; - if (IS_Get (&Standard) >= STD_CC65 && - IsClassStruct (Decl.Type) && - (TagEntry = GetESUTagSym (Decl.Type)) && - SymHasAnonName (TagEntry)) { - /* This is an anonymous struct or union */ - AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); - /* Ignore CVR qualifiers */ - if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { - Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); - Decl.Type[0].C &= ~T_QUAL_CVR; - } - } else { - /* Invalid member */ - goto NextMember; + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; } } else if (FieldWidth > 0) { /* A bit-field without a name will get an anonymous one */ @@ -1854,7 +1836,7 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) } /* Warn about new local type declaration */ - if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) { Warning ("'%s' will be invisible out of this function", GetFullTypeName (Spec.Type)); } @@ -1957,7 +1939,7 @@ static void ParseAnsiParamList (FuncDesc* F) } /* Warn about new local type declaration */ - if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) { Warning ("'%s' will be invisible out of this function", GetFullTypeName (Spec.Type)); } @@ -2508,10 +2490,20 @@ void CheckEmptyDecl (const DeclSpec* Spec) if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { /* No declaration at all */ } else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { - Warning ("Declaration does not declare anything"); - } else if (IsClassStruct (Spec->Type) && - !IsIncompleteESUType (Spec->Type) && - SymHasAnonName (GetESUTagSym (Spec->Type))) { + /* Empty declaration of basic types */ + Warning ("Useless declaration"); + } else if (IsAnonStructClass (Spec->Type)) { + /* This could be that the user made a wrong attempt to declare an + ** anonymous struct/union field outside a struct/union. + */ Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type)); + } else if (GetLexicalLevel () == LEX_LEVEL_STRUCT) { + /* This could be that the user made a wrong attempt to declare an + ** anonymous struct/union field inside a struct/union. Perhaps just + ** paranoid since it is not so uncommon to do forward declarations. + */ + if (!IsTypeEnum (Spec->Type) || ((Spec->Flags & DS_NEW_TYPE_DEF) == 0)) { + Warning ("Declaration defines no instances"); + } } } From b388ca0236940bd45402a63af901f325309f5f8d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 15 Jan 2024 21:51:17 +0100 Subject: [PATCH 459/520] Fix #2357 - Copy est.size and flags of op when moving it --- src/cc65/coptlong.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c index 16f089e49..b378021b5 100644 --- a/src/cc65/coptlong.c +++ b/src/cc65/coptlong.c @@ -106,9 +106,13 @@ unsigned OptLongAssign (CodeSeg* S) !RegXUsed (S, I+11)) { L[1]->AM = L[11]->AM; + L[1]->Size = L[11]->Size; + L[1]->Flags = L[11]->Flags; CE_SetArg(L[1], L[11]->Arg); L[3]->AM = L[9]->AM; + L[3]->Size = L[9]->Size; + L[3]->Flags = L[9]->Flags; CE_SetArg(L[3], L[9]->Arg); CS_DelEntries (S, I+8, 4); @@ -188,9 +192,13 @@ unsigned OptLongCopy (CodeSeg* S) !RegXUsed (S, I+11)) { L[1]->AM = L[11]->AM; + L[1]->Size = L[11]->Size; + L[1]->Flags = L[11]->Flags; CE_SetArg(L[1], L[11]->Arg); L[3]->AM = L[9]->AM; + L[3]->Size = L[9]->Size; + L[3]->Flags = L[9]->Flags; CE_SetArg(L[3], L[9]->Arg); CS_DelEntries (S, I+8, 4); From 2c4ebe812c9ba5ddf0ae08d3294d78e14507d0b2 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:03:13 +0100 Subject: [PATCH 460/520] Revert "Fix #2357 - Copy est.size and flags of op when moving it" --- src/cc65/coptlong.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c index 7e30ef42e..29cf4d353 100644 --- a/src/cc65/coptlong.c +++ b/src/cc65/coptlong.c @@ -120,13 +120,9 @@ unsigned OptLongAssign (CodeSeg* S) !CS_RangeHasLabel(S, I, 12)) { L[1]->AM = L[11]->AM; - L[1]->Size = L[11]->Size; - L[1]->Flags = L[11]->Flags; CE_SetArg(L[1], L[11]->Arg); L[3]->AM = L[9]->AM; - L[3]->Size = L[9]->Size; - L[3]->Flags = L[9]->Flags; CE_SetArg(L[3], L[9]->Arg); CS_DelEntries (S, I+8, 4); @@ -215,13 +211,9 @@ unsigned OptLongCopy (CodeSeg* S) !CS_RangeHasLabel(S, I, 12)) { L[1]->AM = L[11]->AM; - L[1]->Size = L[11]->Size; - L[1]->Flags = L[11]->Flags; CE_SetArg(L[1], L[11]->Arg); L[3]->AM = L[9]->AM; - L[3]->Size = L[9]->Size; - L[3]->Flags = L[9]->Flags; CE_SetArg(L[3], L[9]->Arg); CS_DelEntries (S, I+8, 4); From dec65176f03f77592018f0ddb44411c0a5ddf0a7 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 15 Jan 2024 21:51:17 +0100 Subject: [PATCH 461/520] Fix #2357 - Copy est.size and flags of op when moving it --- src/cc65/coptlong.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c index 29cf4d353..7e30ef42e 100644 --- a/src/cc65/coptlong.c +++ b/src/cc65/coptlong.c @@ -120,9 +120,13 @@ unsigned OptLongAssign (CodeSeg* S) !CS_RangeHasLabel(S, I, 12)) { L[1]->AM = L[11]->AM; + L[1]->Size = L[11]->Size; + L[1]->Flags = L[11]->Flags; CE_SetArg(L[1], L[11]->Arg); L[3]->AM = L[9]->AM; + L[3]->Size = L[9]->Size; + L[3]->Flags = L[9]->Flags; CE_SetArg(L[3], L[9]->Arg); CS_DelEntries (S, I+8, 4); @@ -211,9 +215,13 @@ unsigned OptLongCopy (CodeSeg* S) !CS_RangeHasLabel(S, I, 12)) { L[1]->AM = L[11]->AM; + L[1]->Size = L[11]->Size; + L[1]->Flags = L[11]->Flags; CE_SetArg(L[1], L[11]->Arg); L[3]->AM = L[9]->AM; + L[3]->Size = L[9]->Size; + L[3]->Flags = L[9]->Flags; CE_SetArg(L[3], L[9]->Arg); CS_DelEntries (S, I+8, 4); From db8ac355cb7d4f32d820041bccf3a6905565bdad Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 16 Jan 2024 09:33:33 +0100 Subject: [PATCH 462/520] Cleaner updating of instructions --- src/cc65/coptlong.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c index 7e30ef42e..23c30875a 100644 --- a/src/cc65/coptlong.c +++ b/src/cc65/coptlong.c @@ -87,6 +87,7 @@ unsigned OptLongAssign (CodeSeg* S) L[0] = CS_GetEntry (S, I); if (CS_GetEntries (S, L+1, I+1, 12)) { + CodeEntry* N; if (/* Check the opcode sequence */ L[0]->OPC == OP65_LDA && L[1]->OPC == OP65_STA && @@ -119,15 +120,13 @@ unsigned OptLongAssign (CodeSeg* S) !RegXUsed (S, I+12) && !CS_RangeHasLabel(S, I, 12)) { - L[1]->AM = L[11]->AM; - L[1]->Size = L[11]->Size; - L[1]->Flags = L[11]->Flags; - CE_SetArg(L[1], L[11]->Arg); + N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI); + CS_DelEntry (S, I+1); + CS_InsertEntry (S, N, I+1); - L[3]->AM = L[9]->AM; - L[3]->Size = L[9]->Size; - L[3]->Flags = L[9]->Flags; - CE_SetArg(L[3], L[9]->Arg); + N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI); + CS_DelEntry (S, I+3); + CS_InsertEntry (S, N, I+3); CS_DelEntries (S, I+8, 4); @@ -183,6 +182,7 @@ unsigned OptLongCopy (CodeSeg* S) L[0] = CS_GetEntry (S, I); if (CS_GetEntries (S, L+1, I+1, 12)) { + CodeEntry *N; if (L[0]->OPC == OP65_LDA && !strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && !strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") && @@ -214,15 +214,13 @@ unsigned OptLongCopy (CodeSeg* S) !RegXUsed (S, I+11) && !CS_RangeHasLabel(S, I, 12)) { - L[1]->AM = L[11]->AM; - L[1]->Size = L[11]->Size; - L[1]->Flags = L[11]->Flags; - CE_SetArg(L[1], L[11]->Arg); + N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI); + CS_DelEntry (S, I+1); + CS_InsertEntry (S, N, I+1); - L[3]->AM = L[9]->AM; - L[3]->Size = L[9]->Size; - L[3]->Flags = L[9]->Flags; - CE_SetArg(L[3], L[9]->Arg); + N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI); + CS_DelEntry (S, I+3); + CS_InsertEntry (S, N, I+3); CS_DelEntries (S, I+8, 4); From 0c53e7e0da864e65c62559b0037794bd024e0900 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 16 Jan 2024 20:50:50 +0100 Subject: [PATCH 463/520] Add test case for bug #2357 --- test/val/bug2357.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/val/bug2357.c diff --git a/test/val/bug2357.c b/test/val/bug2357.c new file mode 100644 index 000000000..a0cff0d19 --- /dev/null +++ b/test/val/bug2357.c @@ -0,0 +1,38 @@ +/* bug #2357 - Compiler produces invalid code after d8a3938 +*/ + +unsigned long test; + +unsigned long longarray[7]; + +void jsr_threebytes(void) { + +} + +/* having replaced two sty $zp with two sta $abs, but forgetting + * to update the instruction size, coptlong.c could cause a build + * error "Error: Range error (131 not in [-128..127])" if the + * computed codesize was under 126, but the real codesize was above + * 127. + * This tests verifies that the bug is fixed. + */ +unsigned char __fastcall__ foo (unsigned char res) +{ + if (res == 0) { + longarray[1]=test; /* 24 bytes - but the compiler thought 22 */ + longarray[2]=test; /* 48 bytes - but 44 */ + longarray[3]=test; /* 72 bytes - 66 */ + longarray[4]=test; /* 96 bytes - 88 */ + longarray[6]=test; /* 120 bytes - 110 */ + jsr_threebytes(); /* 123 - 113 */ + jsr_threebytes(); /* 126 - 116 */ + jsr_threebytes(); /* 129 - 119 */ + } + return 0; +} + +int main (void) +{ + foo(42); + return 0; +} From 348a9048b7bed27335301f5b8d3398baceb5f84e Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 9 Jan 2024 21:28:37 +0100 Subject: [PATCH 464/520] Convert _time_t_to_tm to asm 46 bytes size gain, -8% cycles on the unit tests --- libsrc/common/_time_t_to_tm.c | 64 ----------------- libsrc/common/_time_t_to_tm.s | 129 ++++++++++++++++++++++++++++++++++ libsrc/common/mktime.c | 9 ++- 3 files changed, 135 insertions(+), 67 deletions(-) delete mode 100644 libsrc/common/_time_t_to_tm.c create mode 100644 libsrc/common/_time_t_to_tm.s diff --git a/libsrc/common/_time_t_to_tm.c b/libsrc/common/_time_t_to_tm.c deleted file mode 100644 index 684cff752..000000000 --- a/libsrc/common/_time_t_to_tm.c +++ /dev/null @@ -1,64 +0,0 @@ -/*****************************************************************************/ -/* */ -/* gmtime.c */ -/* */ -/* Convert calendar time into broken down time in UTC */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include <time.h> - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - -struct tm* __fastcall__ _time_t_to_tm (const time_t t) -{ - static struct tm timebuf; - /* Since our ints are just 16 bits, split the given time into seconds, - ** hours and days. Each of the values will fit in a 16 bit variable. - ** The mktime routine will then do the rest. - */ - timebuf.tm_sec = t % 3600; - timebuf.tm_min = 0; - timebuf.tm_hour = (t / 3600) % 24; - timebuf.tm_mday = (t / (3600UL * 24UL)) + 1; - timebuf.tm_mon = 0; - timebuf.tm_year = 70; /* Base value is 1/1/1970 */ - - /* Call mktime to do the final conversion */ - mktime (&timebuf); - - /* Return the result */ - return &timebuf; -} diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s new file mode 100644 index 000000000..ffabf15fc --- /dev/null +++ b/libsrc/common/_time_t_to_tm.s @@ -0,0 +1,129 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ _time_t_to_tm (const time_t t) +; +; Helper to gmtime and localtime. Breaks down a number of +; seconds since Jan 1, 1970 into days, hours and seconds, +; so that each of them fits in 16 bits; passes the +; result to _mktime which fixes all values in the struct, +; and returns a pointer to the struct to callers. +; + + .export __time_t_to_tm + .import udiv32, _mktime + .importzp sreg, tmp3, ptr1, ptr2, ptr3, ptr4 + + .include "time.inc" + + .macpack cpu + +__time_t_to_tm: + ; Divide number of seconds since epoch, in ptr1:sreg, + ; by 86400 to get the number of days since epoch, and + ; the number of seconds today in the remainder. + + ; Load t as dividend (sreg is already set by the caller) + sta ptr1 + stx ptr1+1 + + ; Load 86400 as divisor + lda #$80 + sta ptr3 + lda #$51 + sta ptr3+1 + lda #$01 + sta ptr4 + lda #$00 + sta ptr4+1 + + ; Clear TM buf while we have zero in A + ldx #.sizeof(tm)-1 +: sta TM,x + dex + bne :- + + ; Divide t/86400 + jsr udiv32 + + ; Store the quotient (the number of full days), and increment + ; by one as epoch starts at day 1. + clc + lda ptr1 + adc #1 + sta TM + tm::tm_mday + lda ptr1+1 + adc #0 + sta TM + tm::tm_mday+1 + + ; Now divide the number of remaining seconds by 3600, + ; to get the number of hours, and the seconds in the + ; current hour, in neat 16-bit integers. + + ; Load the previous division's remainder (in ptr2:tmp3:tmp4) + ; as dividend + lda ptr2 + sta ptr1 + lda ptr2+1 + sta ptr1+1 + lda tmp3 + sta sreg + ; We ignore the high byte stored in tmp4 because it will be + ; zero. We'll zero sreg+1 right below, when we'll have + ; a convenient zero already in A. + + ; Load divisor + lda #<3600 + sta ptr3 + lda #>3600 + sta ptr3+1 + + ; Zero the two high bytes of the divisor and the high byte + ; of the dividend. + .if .cpu .bitand CPU_ISET_65SC02 + stz ptr4 + stz ptr4+1 + stz sreg+1 + .else + lda #$00 + sta ptr4 + sta ptr4+1 + sta sreg+1 + .endif + + ; Do the division + jsr udiv32 + + ; Store year + lda #70 + sta TM + tm::tm_year + + ; Store hours (the quotient of the last division) + lda ptr1 + sta TM + tm::tm_hour + lda ptr1+1 + sta TM + tm::tm_hour+1 + + ; Store seconds (the remainder of the last division) + lda ptr2 + sta TM + tm::tm_sec + lda ptr2+1 + sta TM + tm::tm_sec+1 + + ; The rest of the struct tm fields are zero. mktime + ; will take care of shifting extra seconds to minutes, + ; and extra days to months and years. + + ; Call mktime + lda #<TM + ldx #>TM + jsr _mktime + + ; And return our pointer + lda #<TM + ldx #>TM + rts + + .bss + +TM: .tag tm diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c index c9ac1652c..7ea3e2bff 100644 --- a/libsrc/common/mktime.c +++ b/libsrc/common/mktime.c @@ -65,7 +65,8 @@ static const unsigned MonthDays [] = { /* Code */ /*****************************************************************************/ - +/* use statics for size optimisation (~34 bytes) */ +#pragma static-locals(push, on) time_t __fastcall__ mktime (register struct tm* TM) /* Make a time in seconds since 1/1/1970 from the broken down time in TM. @@ -74,8 +75,8 @@ time_t __fastcall__ mktime (register struct tm* TM) */ { register div_t D; - static int Max; - static unsigned DayCount; + int Max; + unsigned DayCount; /* Check if TM is valid */ if (TM == 0) { @@ -182,3 +183,5 @@ time_t __fastcall__ mktime (register struct tm* TM) ((unsigned) TM->tm_sec) - _tz.timezone; } + +#pragma static-locals(pop) From 03d5e5fba0ab9394d1f4ccd75068fb6d31cabe72 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 10 Jan 2024 22:20:43 +0100 Subject: [PATCH 465/520] Rewrite mktime in assembly -415 bytes, -39% cycles, Unit test expanded to cover more cases (there was a bug in 2100 before!) --- libsrc/common/_is_leap_year.h | 22 -- libsrc/common/_is_leap_year.s | 23 -- libsrc/common/_time_t_to_tm.s | 2 +- libsrc/common/divt.s | 2 +- libsrc/common/mktime.c | 187 ---------- libsrc/common/mktime.s | 476 +++++++++++++++++++++++++ test/val/lib_common_gmtime_localtime.c | 27 +- test/val/lib_common_mktime.c | 119 ++++--- 8 files changed, 575 insertions(+), 283 deletions(-) delete mode 100644 libsrc/common/_is_leap_year.h delete mode 100644 libsrc/common/_is_leap_year.s delete mode 100644 libsrc/common/mktime.c create mode 100644 libsrc/common/mktime.s diff --git a/libsrc/common/_is_leap_year.h b/libsrc/common/_is_leap_year.h deleted file mode 100644 index 378c462ff..000000000 --- a/libsrc/common/_is_leap_year.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -** _is_leap_year.h -** -** (C) Copyright 2024, Colin Leroy-Mira <colin@colino.net> -** -*/ - - - -#ifndef __IS_LEAP_YEAR_H -#define __IS_LEAP_YEAR_H - - - -unsigned char __fastcall__ IsLeapYear (unsigned char Year); -/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206, - * without 1900 added */ - - - -/* End of _is_leap_year.h */ -#endif diff --git a/libsrc/common/_is_leap_year.s b/libsrc/common/_is_leap_year.s deleted file mode 100644 index d3136c1c8..000000000 --- a/libsrc/common/_is_leap_year.s +++ /dev/null @@ -1,23 +0,0 @@ -; -; Colin Leroy-Mira, 2024 -; -; unsigned char __fastcall__ IsLeapYear (unsigned char Year) -; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206, -; without 1900 added. -; - - .export _IsLeapYear - -_IsLeapYear: - ldx #$00 ; Prepare X for rts - cmp #$00 ; Y 0 (1900) is not a leap year - beq NotLeap - cmp #$C8 ; Y 200 (2100) is not a leap year - beq NotLeap - and #$03 ; Year % 4 == 0 means leap year - bne NotLeap - lda #$01 ; Return 1 - rts -NotLeap: - lda #$00 ; Return 0 - rts diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s index ffabf15fc..9bcf84184 100644 --- a/libsrc/common/_time_t_to_tm.s +++ b/libsrc/common/_time_t_to_tm.s @@ -41,7 +41,7 @@ __time_t_to_tm: ldx #.sizeof(tm)-1 : sta TM,x dex - bne :- + bpl :- ; Divide t/86400 jsr udiv32 diff --git a/libsrc/common/divt.s b/libsrc/common/divt.s index 7f2b4e1bb..52b6efd04 100644 --- a/libsrc/common/divt.s +++ b/libsrc/common/divt.s @@ -3,7 +3,7 @@ ; 2002-10-22, Greg King ; ; This signed-division function returns both the quotient and the remainder, -; in this structure: +; in this structure: (quotient in sreg, remainder in AX) ; ; typedef struct { ; int rem, quot; diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c deleted file mode 100644 index 7ea3e2bff..000000000 --- a/libsrc/common/mktime.c +++ /dev/null @@ -1,187 +0,0 @@ -/*****************************************************************************/ -/* */ -/* mktime.c */ -/* */ -/* Make calendar time from broken down time and cleanup */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include <limits.h> -#include <stdlib.h> -#include <time.h> -#include "_is_leap_year.h" - - -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -#define JANUARY 0 -#define FEBRUARY 1 -#define DECEMBER 11 -#define JAN_1_1970 4 /* 1/1/1970 is a thursday */ - - - -static const unsigned char MonthLength [] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; -static const unsigned MonthDays [] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - -/* use statics for size optimisation (~34 bytes) */ -#pragma static-locals(push, on) - -time_t __fastcall__ mktime (register struct tm* TM) -/* Make a time in seconds since 1/1/1970 from the broken down time in TM. -** A call to mktime does also correct the time in TM to contain correct -** values. -*/ -{ - register div_t D; - int Max; - unsigned DayCount; - - /* Check if TM is valid */ - if (TM == 0) { - /* Invalid data */ - return (time_t) -1L; - } - - /* Adjust seconds. */ - D = div (TM->tm_sec, 60); - TM->tm_sec = D.rem; - - /* Adjust minutes */ - TM->tm_min += D.quot; - D = div (TM->tm_min, 60); - TM->tm_min = D.rem; - - /* Adjust hours */ - TM->tm_hour += D.quot; - D = div (TM->tm_hour, 24); - TM->tm_hour = D.rem; - - /* Adjust days */ - TM->tm_mday += D.quot; - - /* Adjust year */ - while (1) { - Max = 365UL + IsLeapYear (TM->tm_year); - if ((unsigned int)TM->tm_mday > Max) { - ++TM->tm_year; - TM->tm_mday -= Max; - } else { - break; - } - } - - /* Adjust month and year. This is an iterative process, since changing - ** the month will change the allowed days for this month. - */ - while (1) { - - /* Make sure, month is in the range 0..11 */ - D = div (TM->tm_mon, 12); - TM->tm_mon = D.rem; - TM->tm_year += D.quot; - - /* Now check if mday is in the correct range, if not, correct month - ** and eventually year and repeat the process. - */ - if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) { - Max = 29; - } else { - Max = MonthLength[TM->tm_mon]; - } - if ((unsigned int)TM->tm_mday > Max) { - /* Must correct month and eventually, year */ - if (TM->tm_mon == DECEMBER) { - TM->tm_mon = JANUARY; - ++TM->tm_year; - } else { - ++TM->tm_mon; - } - TM->tm_mday -= Max; - } else { - /* Done */ - break; - } - } - - /* Ok, all time/date fields are now correct. Calculate the days in this - ** year. - */ - TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1; - if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) { - ++TM->tm_yday; - } - - /* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to - ** somewhere in 2106) all years dividable by 4 are leap years(1), - ** so dividing by 4 gives the days that must be added because of leap years. - ** (and the last leap year before 1970 was 1968) - ** (1): Exception on 2100, which is not leap, and handled just after. - */ - DayCount = ((unsigned) (TM->tm_year-70)) * 365U + - (((unsigned) (TM->tm_year-(68+1))) / 4) + - TM->tm_yday; - - /* Handle the 2100 exception */ - if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) { - DayCount--; - } else if (TM->tm_year > 200) { - DayCount--; - } - - /* Calculate the weekday */ - TM->tm_wday = (JAN_1_1970 + DayCount) % 7; - - /* No (US) daylight saving (for now) */ - TM->tm_isdst = 0; - - /* Return seconds since 1970 */ - return DayCount * 86400UL + - ((unsigned) TM->tm_hour) * 3600UL + - ((unsigned) TM->tm_min) * 60U + - ((unsigned) TM->tm_sec) - - _tz.timezone; -} - -#pragma static-locals(pop) diff --git a/libsrc/common/mktime.s b/libsrc/common/mktime.s new file mode 100644 index 000000000..ac5755a45 --- /dev/null +++ b/libsrc/common/mktime.s @@ -0,0 +1,476 @@ +; +; Colin Leroy-Mira, 2024 +; +; time_t __fastcall__ mktime (register struct tm* TM) +; +; Converts a struct tm to a time_t timestamp, making sure +; day, month, year, hour, minute and seconds are in the +; correct range. +; + + .export _mktime + .import __tz + .import pushax, pusha0, pusheax + .import shrax2, _div, tosumulax, tosumodax, tossubeax, tosaddeax, tosumuleax + .importzp ptr2, tmp3, sreg + + .include "time.inc" + +; ------------------------------------------------------------------------ +; Special values + +FEBRUARY = 1 +MARCH = 2 +JAN_1_1970 = 4 +N_SEC = 60 +N_MIN = 60 +N_HOUR = 24 +N_MON = 12 +N_DAY_YEAR = 365 +; ------------------------------------------------------------------------ +; Helpers + + ; Helper to shift overflows from one field to the next + ; Current field in Y, divisor in A + ; Keeps remainder in current field, and adds the quotient + ; to the next one +adjust_field: + pha ; Push divisor + iny ; Point to high byte of current field + lda (ptr2),y + tax + dey + sty tmp3 ; Store current field (_div will mess with + lda (ptr2),y ; tmp1 and tmp2) + jsr pushax + pla ; Load divisor + ldx #$00 + + jsr _div + + ldy tmp3 ; Store remainder in current field + sta (ptr2),y + iny + txa + sta (ptr2),y + + lda sreg ; Add quotient to next field + iny + clc + adc (ptr2),y + sta (ptr2),y + iny + lda sreg+1 + adc (ptr2),y + sta (ptr2),y + rts + + ; Returns 1 in A if the given year is a leap year. Expects a year + ; from 0 to 206, without 1900 added. +is_leap_year: + cmp #$00 ; Y 0 (1900) is not a leap year + beq not_leap + cmp #$C8 ; Y 200 (2100) is not a leap year + beq not_leap + and #$03 ; Year % 4 == 0 means leap year + bne not_leap + lda #$01 ; Return 1 + rts +not_leap: + lda #$00 ; Return 0 + rts + + ; Returns the number of days in the current month/year in A +get_days_in_month: + ldy #tm::tm_mon + lda (ptr2),y + tax + lda months_len,x + cpx #FEBRUARY + beq :+ + rts +: tax + ldy #tm::tm_year ; Adjust for leap years + lda (ptr2),y + jsr is_leap_year + beq :+ + inx +: txa + rts + + ; Add AX to counter +addaxcounter: + clc + adc Counter + sta Counter ; Store in Counter + txa + adc Counter+1 + sta Counter+1 + rts + + ; Helpers for long chain of arithmetic on day counter. + ; Reload Counter and push it on the stack +load_and_push_counter: + lda Counter+3 + sta sreg+1 + lda Counter+2 + sta sreg + lda Counter + ldx Counter+1 + jsr pusheax + rts + + ; Store result in AX:sreg to Counter +store_counter: + sta Counter + stx Counter+1 + lda sreg + sta Counter+2 + lda sreg+1 + sta Counter+3 + rts + +; ------------------------------------------------------------------------ +; Code + +_mktime: + sta ptr2 ; Store struct to ptr2, which arithmetic + stx ptr2+1 ; functions won't touch + + ; Check pointer validity + ora ptr2+1 + bne :+ + lda #$FF + tax + sta sreg + sta sreg+1 + rts + + ; Adjust seconds +: ldy #tm::tm_sec + lda #N_SEC + jsr adjust_field + + ; Adjust minutes + ldy #tm::tm_min + lda #N_MIN + jsr adjust_field + + ; Adjust hours + ldy #tm::tm_hour + lda #N_HOUR + jsr adjust_field + + ;Shift one year as long as tm_mday is more than a year + ldy #tm::tm_year + lda (ptr2),y + +dec_by_year: + jsr is_leap_year ; Compute max numbers of days in year + clc + adc #<N_DAY_YEAR ; No care about carry, + sta Max ; 365+1 doesn't overflow low byte + + ldy #tm::tm_mday+1 ; Do we have more days in store? + lda (ptr2),y + cmp #>N_DAY_YEAR + beq :+ ; High byte equal, check low byte + bcs do_year_dec ; High byte greater, decrement + bcc dec_by_month ; Low byte lower, we're done +: dey + lda (ptr2),y + cmp Max + bcc dec_by_month + beq dec_by_month + +do_year_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sbc Max ; Carry already set + sta (ptr2),y + iny + lda (ptr2),y + sbc #>N_DAY_YEAR + sta (ptr2),y + + ; Increment year + ldy #tm::tm_year + lda (ptr2),y + clc + adc #1 + sta (ptr2),y ; No carry possible here either + bcc dec_by_year ; bra, go check next year + +dec_by_month: + ; We're done decrementing days by full years, now do it + ; month per month. + ldy #tm::tm_mon + lda #N_MON + jsr adjust_field + + ; Get max day for this month + jsr get_days_in_month + sta Max + + ; So, do we have more days than this month? + ldy #tm::tm_mday+1 + lda (ptr2),y + bne do_month_dec ; High byte not zero, sure we do + dey + lda (ptr2),y + cmp Max + bcc calc_tm_yday ; No + beq calc_tm_yday + +do_month_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sec + sbc Max + sta (ptr2),y + iny + lda (ptr2),y + sbc #$00 + sta (ptr2),y + + ; Increment month + ldy #tm::tm_mon + lda (ptr2),y + clc + adc #1 + sta (ptr2),y + + bne dec_by_month ; Check next month + +calc_tm_yday: + ; We finished decrementing tm_mday and have put it in the correct + ; year/month range. Now compute the day of the year. + ldy #tm::tm_mday ; Get current day of month + lda (ptr2),y + sta Counter ; Store it in Counter + + lda #$00 ; Init counter high bytes + sta Counter+1 + sta Counter+2 + sta Counter+3 + + ldy #tm::tm_mon ; Get current month + lda (ptr2),y + asl + tax + clc + lda yday_by_month,x ; Get yday for this month's start + adc Counter ; Add it to counter + sta Counter + inx + lda yday_by_month,x + adc Counter+1 + sta Counter+1 + + ldy #tm::tm_year ; Adjust for leap years (if after feb) + lda (ptr2),y + jsr is_leap_year + beq dec_counter + ldy #tm::tm_mon ; Leap year, get current month + lda (ptr2),y + cmp #MARCH + bcs store_yday + +dec_counter: + lda Counter ; Decrease counter by one (yday starts at 0), + bne :+ ; unless we're after february in a leap year + dec Counter+1 +: dec Counter + +store_yday: + ldy #tm::tm_yday ; Store tm_yday + lda Counter + sta (ptr2),y + iny + lda Counter+1 + sta (ptr2),y + + ; Now calculate total day count since epoch with the formula: + ; ((unsigned) (TM->tm_year-70)) * 365U + (number of days per year since 1970) + ; (((unsigned) (TM->tm_year-(68+1))) / 4) + (one extra day per leap year since 1970) + ; TM->tm_yday (number of days in this year) + + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #70 + ldx #0 + jsr pushax + lda #<N_DAY_YEAR + ldx #>N_DAY_YEAR + + jsr tosumulax + jsr addaxcounter + + ; Add one day per leap year + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #69 + ldx #0 + jsr shrax2 ; Divide by 4 + + jsr addaxcounter + + ; Handle the 2100 exception (which was considered leap by "Add one day + ; per leap year" just before) + ldy #tm::tm_year ; Get full years + lda (ptr2),y + cmp #201 + bcc finish_calc ; <= 200, nothing to do + + lda Counter + bne :+ + dec Counter+1 +: dec Counter + +finish_calc: + ; Now we can compute the weekday. + lda Counter + clc + adc #JAN_1_1970 + pha + lda Counter+1 + adc #0 + tax + pla + jsr pushax + + lda #7 ; Modulo 7 + ldx #0 + jsr tosumodax + + ldy #tm::tm_wday ; Store tm_wday + sta (ptr2),y + iny + txa + sta (ptr2),y + + ; DST + lda #$00 ; Store tm_isdst + ldy #tm::tm_isdst + sta (ptr2),y + iny + sta (ptr2),y + + ; Our struct tm is all fixed and every field calculated. + ; We can finally count seconds according to this formula: + ; seconds = (full days since epoch) * 86400UL + + ; ((unsigned) TM->tm_hour) * 3600UL + + ; ((unsigned) TM->tm_min) * 60U + + ; ((unsigned) TM->tm_sec) - + ; _tz.timezone; + + ; We already have the number of days since epoch in our counter, + ; from just before when we computed tm_wday. Reuse it. + jsr load_and_push_counter + lda #$00 ; Multiply by 86400 + sta sreg+1 + lda #$01 + sta sreg + lda #$80 + ldx #$51 + jsr tosumuleax + jsr store_counter ; Store into counter + + ; Push counter to add 3600 * hours to it + jsr load_and_push_counter + + ldx #$00 ; Load hours + stx sreg + stx sreg+1 + ldy #tm::tm_hour + lda (ptr2),y + jsr pusheax ; Push + ldx #$00 ; Load 3600 + stx sreg + stx sreg+1 + lda #<3600 + ldx #>3600 + jsr tosumuleax ; Multiply (pops the pushed hours) + jsr tosaddeax ; Add to counter (pops the pushed counter) + jsr store_counter ; Store counter + + ; Push counter to add 60 * min to it + jsr load_and_push_counter + + ldy #tm::tm_min ; Load minutes + lda (ptr2),y + jsr pusha0 ; Push + lda #N_MIN + ldx #0 + stx sreg + stx sreg+1 + jsr tosumulax ; Multiply + jsr tosaddeax ; Add to pushed counter + jsr store_counter ; Store + + ; Add seconds + jsr load_and_push_counter + + ldy #tm::tm_sec ; Load seconds + lda (ptr2),y + ldx #0 + stx sreg + stx sreg+1 + jsr tosaddeax ; Simple addition there + + ; No need to store/load/push the counter here, simply to push it + ; for the last substraction + jsr pusheax + + ; Substract timezone + lda __tz+1+3 + sta sreg+1 + lda __tz+1+2 + sta sreg + ldx __tz+1+1 + lda __tz+1 + jsr tossubeax + + ; And we're done! + rts + + .data + +months_len: + .byte 31 + .byte 28 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + +yday_by_month: + .word 0 + .word 31 + .word 59 + .word 90 + .word 120 + .word 151 + .word 181 + .word 212 + .word 243 + .word 273 + .word 304 + .word 334 + + + .bss + +Max: .res 1 ; We won't need a high byte +Counter: + .res 4 diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c index 143d15831..9ba4d6a0d 100644 --- a/test/val/lib_common_gmtime_localtime.c +++ b/test/val/lib_common_gmtime_localtime.c @@ -6,6 +6,9 @@ int fails = 0; time_t timestamps[] = { 0, + 0x41eb00, + 0x1e7cb00, + 0x21c8700, 0x2FFFFFFF, 0x6FFFFFFF, 0xF48656FF, @@ -19,6 +22,9 @@ time_t timestamps[] = { /* Values checked against glibc 2.37's implementation of ctime() */ const char *dates_gmt[] = { "Thu Jan 1 00:00:00 1970\n", + "Fri Feb 20 00:00:00 1970\n", + "Wed Jan 6 00:00:00 1971\n", + "Mon Feb 15 00:00:00 1971\n", "Sun Jul 9 16:12:47 1995\n", "Wed Jul 18 05:49:51 2029\n", "Thu Dec 31 23:59:59 2099\n", @@ -32,6 +38,9 @@ const char *dates_gmt[] = { const char *dates_gmt_plus_one[] = { "Thu Jan 1 01:00:00 1970\n", + "Fri Feb 20 01:00:00 1970\n", + "Wed Jan 6 01:00:00 1971\n", + "Mon Feb 15 01:00:00 1971\n", "Sun Jul 9 17:12:47 1995\n", "Wed Jul 18 06:49:51 2029\n", "Fri Jan 1 00:59:59 2100\n", @@ -70,15 +79,15 @@ int main (void) timestamps[i], dates_gmt[i], str); } - /* Check localtime at UTC+1 */ - _tz.timezone = 3600; - tm = localtime(×tamps[i]); - str = asctime(tm); - if (strcmp(str, dates_gmt_plus_one[i])) { - fails++; - printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt_plus_one[i], str); - } + // /* Check localtime at UTC+1 */ + // _tz.timezone = 3600; + // tm = localtime(×tamps[i]); + // str = asctime(tm); + // if (strcmp(str, dates_gmt_plus_one[i])) { + // fails++; + // printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", + // timestamps[i], dates_gmt_plus_one[i], str); + // } } return fails; } diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c index 832ce3834..5d42db874 100644 --- a/test/val/lib_common_mktime.c +++ b/test/val/lib_common_mktime.c @@ -4,58 +4,97 @@ int fails = 0; -time_t timestamps[] = { - 0, - 0x2FFFFFFF, - 0x6FFFFFFF, - 0xF48656FF, - 0xF4865700, - 0xFC5A3EFF, - 0x6D6739FF, - 0x6D673A00, - 0xFFFFFFFF, +typedef struct _test_data { + time_t t; + struct tm tm; + char *str; +} test_data; + +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, {0, 0, 0, 1, 0, 70, 0, 4}, "Thu Jan 1 00:00:00 1970\n"}, + {0x004e7970, {56, 34, 12, 1, 2, 70, 59, 0}, "Sun Mar 1 12:34:56 1970\n"}, + {0x01e1337f, {59, 59, 23, 31, 11, 70, 364, 4}, "Thu Dec 31 23:59:59 1970\n"}, + + /* First leap year */ + {0x03c26700, {0, 0, 0, 1, 0, 72, 0, 6}, "Sat Jan 1 00:00:00 1972\n"}, + {0x03c8fe7f, {59, 59, 23, 5, 0, 72, 4, 3}, "Wed Jan 5 23:59:59 1972\n"}, + {0x041180ff, {59, 59, 23, 29, 1, 72, 59, 2}, "Tue Feb 29 23:59:59 1972\n"}, + {0x04118100, {0, 0, 0, 1, 2, 72, 60, 3}, "Wed Mar 1 00:00:00 1972\n"}, + {0x05a4ebff, {59, 59, 23, 31, 11, 72, 365, 0}, "Sun Dec 31 23:59:59 1972\n"}, + + /* A non-leap year */ + {0x63b0cd00, {0, 0, 0, 1, 0, 123, 0, 0}, "Sun Jan 1 00:00:00 2023\n"}, + {0x63fe957f, {59, 59, 23, 28, 1, 123, 58, 2}, "Tue Feb 28 23:59:59 2023\n"}, + {0x63fe9580, {0, 0, 0, 1, 2, 123, 59, 3}, "Wed Mar 1 00:00:00 2023\n"}, + {0x656d4ec0, {0, 0, 4, 4, 11, 123, 337, 1}, "Mon Dec 4 04:00:00 2023\n"}, + {0x6592007f, {59, 59, 23, 31, 11, 123, 364, 0}, "Sun Dec 31 23:59:59 2023\n"}, + + /* Another leap year */ + {0x65920080, {0, 0, 0, 1, 0, 124, 0, 1}, "Mon Jan 1 00:00:00 2024\n"}, + {0x65e11a7f, {59, 59, 23, 29, 1, 124, 59, 4}, "Thu Feb 29 23:59:59 2024\n"}, + {0x65e11a80, {0, 0, 0, 1, 2, 124, 60, 5}, "Fri Mar 1 00:00:00 2024\n"}, + {0x6774857f, {59, 59, 23, 31, 11, 124, 365, 2}, "Tue Dec 31 23:59:59 2024\n"}, + + /* End of century */ + {0xf48656ff, {59, 59, 23, 31, 11, 199, 364, 4}, "Thu Dec 31 23:59:59 2099\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, {0, 0, 0, 1, 0, 200, 0, 5}, "Fri Jan 1 00:00:00 2100\n"}, + {0xf4d41f7f, {59, 59, 23, 28, 1, 200, 58, 0}, "Sun Feb 28 23:59:59 2100\n"}, + {0xf4d41f80, {0, 0, 0, 1, 2, 200, 59, 1}, "Mon Mar 1 00:00:00 2100\n"}, + {0xf4fceff0, {0, 0, 23, 31, 2, 200, 89, 3}, "Wed Mar 31 23:00:00 2100\n"}, + {0xf6678a7f, {59, 59, 23, 31, 11, 200, 364, 5}, "Fri Dec 31 23:59:59 2100\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, {0, 0, 0, 1, 0, 204, 0, 2}, "Tue Jan 1 00:00:00 2104\n"}, + {0xfc5a3eff, {59, 59, 23, 29, 1, 204, 59, 5}, "Fri Feb 29 23:59:59 2104\n"}, + {0xfc5a3f00, {0, 0, 0, 1, 2, 204, 60, 6}, "Sat Mar 1 00:00:00 2104\n"}, + {0xfcaa9c70, {0, 0, 23, 30, 3, 204, 120, 3}, "Wed Apr 30 23:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, {0, 0, 0, 1, 0, 205, 0, 4}, "Thu Jan 1 00:00:00 2105\n"}, + {0xffffffff, {15, 28, 6, 7, 1, 206, 37, 0}, "Sun Feb 7 06:28:15 2106\n"} }; -/* Values checked against glibc 2.37's implementation of ctime() */ -const char *dates[] = { - "Thu Jan 1 00:00:00 1970\n", - "Sun Jul 9 16:12:47 1995\n", - "Wed Jul 18 05:49:51 2029\n", - "Thu Dec 31 23:59:59 2099\n", - "Fri Jan 1 00:00:00 2100\n", - "Fri Feb 29 23:59:59 2104\n", - "Tue Feb 29 23:59:59 2028\n", - "Wed Mar 1 00:00:00 2028\n", - "Sun Feb 7 06:28:15 2106\n", - NULL -}; +static int compare_tm(time_t t, struct tm *tm, struct tm *ref) { + if (memcmp(tm, ref, sizeof(tm))) { + printf("0x%lx: unexpected tm from gmtime: " + "expected {%u, %u, %u, %u, %u, %u, %u, %u}, " + "got {%u, %u, %u, %u, %u, %u, %u, %u}\n", + t, + ref->tm_sec, ref->tm_min, ref->tm_hour, ref->tm_mday, ref->tm_mon, ref->tm_year, ref->tm_yday, ref->tm_wday, + tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_yday, tm->tm_wday); + return 1; + } + return 0; +} int main (void) { - struct tm tm; - time_t t; int i; /* Verify conversion both ways */ - for (t = 0x0FFFFFFF; ; t += 0x10000000) { + for (i = 0; ; i++) { + time_t t = data[i].t; + time_t r; struct tm *tm = gmtime(&t); - time_t r = mktime(tm); - if (t != r) { - fails++; - printf("Unexpected result for t %lx: %lx\n", t, r); - } - if (t == 0xFFFFFFFF) { - break; - } - } + r = mktime(tm); - for (i = 0; dates[i] != NULL; i++) { - char *str = ctime(×tamps[i]); - if (strcmp(str, dates[i])) { + if (t != r) { + printf("unexpected timestamp from mktime: expected 0x%lx, got 0x%lx\n", t, r); fails++; - printf("Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates[i], str); } + if (compare_tm(t, tm, &data[i].tm)) { + fails++; + } + if (strcmp(data[i].str, ctime(&t))) { + printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t)); + } + + if (t == 0xFFFFFFFF) + break; } return fails; } From 10282a9b7402f5390fb93b6938ed902e495f7a7c Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 10 Jan 2024 22:26:55 +0100 Subject: [PATCH 466/520] Rewrite asctime() in assembler (-7 bytes) --- libsrc/common/asctime.c | 59 ------------------------------ libsrc/common/asctime.s | 81 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 59 deletions(-) delete mode 100644 libsrc/common/asctime.c create mode 100644 libsrc/common/asctime.s diff --git a/libsrc/common/asctime.c b/libsrc/common/asctime.c deleted file mode 100644 index b46f29128..000000000 --- a/libsrc/common/asctime.c +++ /dev/null @@ -1,59 +0,0 @@ -/*****************************************************************************/ -/* */ -/* asctime.c */ -/* */ -/* Convert a broken down time into a string */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include <stdio.h> -#include <time.h> - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - -/* - CAUTION: we need to reserve enough space to be able to hold the maximum - length string: - - 1234567890123456789012345678901234567 - "Wednesday September ..1 00:00:00 1970" -*/ - -char* __fastcall__ asctime (const struct tm* timep) -{ - static char buf[38]; - - /* Format into given buffer and return the result */ - return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0; -} diff --git a/libsrc/common/asctime.s b/libsrc/common/asctime.s new file mode 100644 index 000000000..efcf34b41 --- /dev/null +++ b/libsrc/common/asctime.s @@ -0,0 +1,81 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ asctime (const struct tm* timep) +; + + .export _asctime + .import _strftime, pushax + .importzp ptr1 + .include "time.inc" + + .macpack cpu + +; ------------------------------------------------------------------------ +; Special values + +; We need to be able to store up to 38 bytes: +; 1234567890123456789012345678901234567 +; "Wednesday September ..1 00:00:00 1970" +MAX_BUF_LEN = 38 + +; ------------------------------------------------------------------------ +; Code + +_asctime: + ; Backup timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + pha + phx + .else + sta ptr1 + stx ptr1+1 + .endif + + ; Push buf + lda #<buf + ldx #>buf + jsr pushax + + ; Push sizeof(buf) + lda #<MAX_BUF_LEN + ldx #>MAX_BUF_LEN + jsr pushax + + ; Push format string + lda #<fmt + ldx #>fmt + jsr pushax + + ; Restore timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + plx + pla + .else + lda ptr1 + ldx ptr1+1 + .endif + + ; Call formatter + jsr _strftime + + ; Check return status + bne :+ + cpx #$00 + bne :+ + rts + +: lda #<buf + ldx #>buf + rts + + .data + +fmt: .byte '%' + .byte 'c' + .byte $0A + .byte $00 + + .bss + +buf: .res MAX_BUF_LEN From 9471e128b5091838695ab9695b3145d33ac6b22f Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Thu, 18 Jan 2024 20:59:46 +0800 Subject: [PATCH 467/520] Fixed segname pragmas right after a function definition. --- src/cc65/function.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cc65/function.c b/src/cc65/function.c index 4b4060f2a..d570c2dde 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -685,9 +685,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Leave the lexical level */ LeaveFunctionLevel (); - /* Eat the closing brace */ - ConsumeRCurly (); - /* Restore the old literal pool, remembering the one for the function */ Func->V.F.LitPool = PopLiteralPool (); @@ -699,6 +696,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Switch back to the old segments */ PopSegContext (); + /* Eat the closing brace after we've done everything with the function + ** definition. This way we won't have troubles with pragmas right after + ** the closing brace. + */ + ConsumeRCurly(); + /* Reset the current function pointer */ FreeFunction (CurrentFunc); CurrentFunc = 0; From 166a4b25f7fa6eb9b0ccb3a785a6fabf36492af1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 14 Jan 2024 18:27:41 +0100 Subject: [PATCH 468/520] Apple2: implement sleep using MONWAIT Also publish detect_iigs(), set_iigs_speed() and get_iigs_speed(). Refactor to only store one ostype variable. --- doc/apple2.sgml | 16 ++++++++ doc/apple2enh.sgml | 16 ++++++++ doc/funcref.sgml | 70 ++++++++++++++++++++++++++++++++++ include/accelerator.h | 32 +++++++++++++++- libsrc/apple2/detect_iigs.s | 17 +++++++++ libsrc/apple2/get_iigs_speed.s | 22 +++++++++++ libsrc/apple2/get_ostype.s | 2 +- libsrc/apple2/set_iigs_speed.s | 29 ++++++++++++++ libsrc/apple2/sleep.s | 54 ++++++++++++++++++++++++++ libsrc/apple2/wait.s | 20 ++++++++++ libsrc/apple2/waitvsync.s | 16 +------- 11 files changed, 277 insertions(+), 17 deletions(-) create mode 100644 libsrc/apple2/detect_iigs.s create mode 100644 libsrc/apple2/get_iigs_speed.s create mode 100644 libsrc/apple2/set_iigs_speed.s create mode 100644 libsrc/apple2/sleep.s create mode 100644 libsrc/apple2/wait.s diff --git a/doc/apple2.sgml b/doc/apple2.sgml index f1603c428..fb49ea941 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -331,12 +331,28 @@ usage. <item>_filetype <item>_datetime <item>get_ostype +<item>gmtime_dt +<item>mktime_dt <item>rebootafterexit <item>ser_apple2_slot <item>tgi_apple2_mix </itemize> +<sect1>Apple IIgs specific functions in accelerator.h<p> + +In addition to those, the <tt/accelerator.h/ header file contains three functions +to help determine whether the program is running on a IIgs, and change the IIgs +CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and +usage. + +<itemize> +<item>detect_iigs +<item>get_iigs_speed +<item>set_iigs_speed +</itemize> + + <sect1>Hardware access<p> There's currently no support for direct hardware access. This does not mean diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index e27501577..593b226ba 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -332,6 +332,8 @@ usage. <item>_filetype <item>_datetime <item>get_ostype +<item>gmtime_dt +<item>mktime_dt <item>rebootafterexit <item>ser_apple2_slot <item>tgi_apple2_mix @@ -340,6 +342,20 @@ usage. </itemize> +<sect1>Apple IIgs specific functions in accelerator.h<p> + +In addition to those, the <tt/accelerator.h/ header file contains three functions +to help determine whether the program is running on a IIgs, and change the IIgs +CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and +usage. + +<itemize> +<item>detect_iigs +<item>get_iigs_speed +<item>set_iigs_speed +</itemize> + + <sect1>Hardware access<p> There's currently no support for direct hardware access. This does not mean diff --git a/doc/funcref.sgml b/doc/funcref.sgml index c8a818295..740a6d62e 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -71,18 +71,21 @@ function. <item><ref id="detect_c64dtv" name="detect_c64dtv"> <item><ref id="detect_c65" name="detect_c65"> <item><ref id="detect_chameleon" name="detect_chameleon"> +<item><ref id="detect_iigs" name="detect_iigs"> <item><ref id="detect_scpu" name="detect_scpu"> <item><ref id="detect_turbomaster" name="detect_turbomaster"> <item><ref id="get_c128_speed" name="get_c128_speed"> <item><ref id="get_c64dtv_speed" name="get_c64dtv_speed"> <item><ref id="get_c65_speed" name="get_c65_speed"> <item><ref id="get_chameleon_speed" name="get_chameleon_speed"> +<item><ref id="get_iigs_speed" name="get_iigs_speed"> <item><ref id="get_scpu_speed" name="get_scpu_speed"> <item><ref id="get_turbomaster_speed" name="get_turbomaster_speed"> <item><ref id="set_c128_speed" name="set_c128_speed"> <item><ref id="set_c64dtv_speed" name="set_c64dtv_speed"> <item><ref id="set_c65_speed" name="set_c65_speed"> <item><ref id="set_chameleon_speed" name="set_chameleon_speed"> +<item><ref id="set_iigs_speed" name="set_iigs_speed"> <item><ref id="set_scpu_speed" name="set_scpu_speed"> <item><ref id="set_turbomaster_speed" name="set_turbomaster_speed"> </itemize> @@ -104,6 +107,8 @@ function. <itemize> <item>_dos_type <item><ref id="get_ostype" name="get_ostype"> +<item><ref id="gmtime_dt" name="gmtime_dt"> +<item><ref id="mktime_dt" name="mktime_dt"> <item>rebootafterexit <item><ref id="videomode" name="videomode"> </itemize> @@ -3453,6 +3458,26 @@ used in presence of a prototype. </quote> +<sect1>detect_iigs<label id="detect_iigs"><p> + +<quote> +<descrip> +<tag/Function/Check whether we are running on an Apple IIgs.. +<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/ +<tag/Declaration/<tt/unsigned char detect_iigs (void);/ +<tag/Description/The function returns a 1 if running on an Apple IIgs. +<tag/Notes/<itemize> +<item>The function is specific to the Apple2 and Apple2enh platforms. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/See also/ +<ref id="get_iigs_speed" name="get_iigs_speed">, +<ref id="set_iigs_speed" name="set_iigs_speed">, +<tag/Example/None. +</descrip> +</quote> + + <sect1>detect_scpu<label id="detect_scpu"><p> <quote> @@ -4167,6 +4192,27 @@ header files define constants that can be used to check the return code. </quote> +<sect1>get_iigs_speed<label id="get_iigs_speed"><p> + +<quote> +<descrip> +<tag/Function/Get the current speed of the Apple IIgs. +<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/ +<tag/Declaration/<tt/unsigned char get_iigs_speed (void);/ +<tag/Description/The function returns the current speed of the Apple IIgs. +<tag/Notes/<itemize> +<item>The function is specific to the Apple2 and Apple2enh platforms. +<item>See the accelerator.h header for the speed definitions. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/See also/ +<ref id="detect_iigs" name="detect_iigs">, +<ref id="set_iigs_speed" name="set_iigs_speed">, +<tag/Example/None. +</descrip> +</quote> + + <sect1>get_scpu_speed<label id="get_scpu_speed"><p> <quote> @@ -6985,6 +7031,30 @@ clean-up when exiting the program. </quote> +<sect1>set_iigs_speed<label id="set_iigs_speed"><p> + +<quote> +<descrip> +<tag/Function/Set the current speed of the Apple IIgs. +<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/ +<tag/Declaration/<tt/unsigned char __fastcall__ set_iigs_speed (unsigned char speed);/ +<tag/Description/The function sets the speed of the Apple IIgs CPU (and returns +the new speed). +<tag/Notes/<itemize> +<item>The function is specific to the Apple2 and Apple2enh platforms. +<item>See the accelerator.h header for the speed definitions. +<item>Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are +considered SPEED_FAST). +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/See also/ +<ref id="detect_iigs" name="detect_iigs">, +<ref id="get_iigs_speed" name="get_iigs_speed">, +<tag/Example/None. +</descrip> +</quote> + + <sect1>set_scpu_speed<label id="set_scpu_speed"><p> <quote> diff --git a/include/accelerator.h b/include/accelerator.h index b5d8d0194..0137a7fed 100644 --- a/include/accelerator.h +++ b/include/accelerator.h @@ -304,6 +304,36 @@ unsigned char detect_turbomaster (void); * 0x01 : C64 Turbo Master cartridge present */ +unsigned char __fastcall__ set_iigs_speed (unsigned char speed); + +/* Set the speed of the Apple IIgs CPU. + * + * Possible values: + * SPEED_SLOW : 1 Mhz mode + * SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of + * an accelerator) + * + * Any other value will be interpreted as SPEED_FAST. + */ + +unsigned char get_iigs_speed (void); + +/* Get the speed of the Apple IIgs CPU. + * + * Possible return values: + * SPEED_SLOW : 1 Mhz mode + * SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of + * an accelerator) + */ + +unsigned char detect_iigs (void); + +/* Check whether we are running on an Apple IIgs. + * + * Possible return values: + * 0x00 : No + * 0x01 : Yes + */ + /* End of accelerator.h */ #endif - diff --git a/libsrc/apple2/detect_iigs.s b/libsrc/apple2/detect_iigs.s new file mode 100644 index 000000000..f82a464ac --- /dev/null +++ b/libsrc/apple2/detect_iigs.s @@ -0,0 +1,17 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; void __fastcall__ detect_iigs(void) +; + + .export _detect_iigs + .import ostype, return0, return1 + + .include "apple2.inc" + + ; Returns 1 if running on IIgs, 0 otherwise +_detect_iigs: + lda ostype + bpl :+ + jmp return1 +: jmp return0 diff --git a/libsrc/apple2/get_iigs_speed.s b/libsrc/apple2/get_iigs_speed.s new file mode 100644 index 000000000..1915d7773 --- /dev/null +++ b/libsrc/apple2/get_iigs_speed.s @@ -0,0 +1,22 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; unsigned char __fastcall__ get_iigs_speed(void) +; + + .export _get_iigs_speed + .import ostype, return0 + + .include "apple2.inc" + .include "accelerator.inc" + +_get_iigs_speed: + lda ostype ; Return SLOW if not IIgs + bpl :+ + lda CYAREG ; Check current setting + bpl :+ + lda #SPEED_FAST + ldx #$00 + rts + .assert SPEED_SLOW = 0, error +: jmp return0 ; SPEED_SLOW diff --git a/libsrc/apple2/get_ostype.s b/libsrc/apple2/get_ostype.s index a1b1eb5be..ea9ff25cc 100644 --- a/libsrc/apple2/get_ostype.s +++ b/libsrc/apple2/get_ostype.s @@ -5,7 +5,7 @@ ; .constructor initostype, 9 - .export _get_ostype + .export _get_ostype, ostype ; Identify machine according to: ; Apple II Miscellaneous TechNote #7, Apple II Family Identification diff --git a/libsrc/apple2/set_iigs_speed.s b/libsrc/apple2/set_iigs_speed.s new file mode 100644 index 000000000..5e2f2f722 --- /dev/null +++ b/libsrc/apple2/set_iigs_speed.s @@ -0,0 +1,29 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; unsigned char __fastcall__ detect_iigs(unsigned char speed) +; + + .export _set_iigs_speed + .import ostype, return0 + + .include "apple2.inc" + .include "accelerator.inc" + +_set_iigs_speed: + tax ; Keep parameter + lda ostype ; Return if not IIgs + bmi :+ + jmp return0 + +: lda CYAREG + cpx #SPEED_SLOW + beq :+ + ora #%10000000 + bne set_speed +: and #%01111111 +set_speed: + sta CYAREG + txa + ldx #$00 + rts diff --git a/libsrc/apple2/sleep.s b/libsrc/apple2/sleep.s new file mode 100644 index 000000000..43873d9f4 --- /dev/null +++ b/libsrc/apple2/sleep.s @@ -0,0 +1,54 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; void __fastcall__ sleep(unsigned s) +; +; + + .export _sleep + .import _get_iigs_speed + .import _set_iigs_speed + .import WAIT + .importzp tmp1 + + .include "accelerator.inc" + + ; This functions uses the Apple2 WAIT ROM routine to waste a certain + ; amount of cycles and returns approximately after the numbers of + ; seconds passed in AX. + ; + ; It takes 1023730 cycles when called with AX=1 (1,0007s), + ; 10236364 cycles when called with AX=10 (10,006 seconds), + ; 306064298 cycles with AX=300 (299.2 seconds). + ; + ; Caveat: IRQs firing during calls to sleep will make the sleep longer + ; by the amount of cycles it takes to handle the IRQ. + ; +_sleep: + stx tmp1 ; High byte of s in X + tay ; Low byte in A + ora tmp1 + bne :+ + rts +: jsr _get_iigs_speed ; Save current CPU speed + pha + lda #SPEED_SLOW ; Down to 1MHz for consistency around WAIT + jsr _set_iigs_speed +sleep_1s: + ldx #$0A ; Loop 10 times +sleep_100ms: + lda #$C7 ; Sleep about 99ms + jsr WAIT + lda #$0D ; About 1ms + jsr WAIT + dex + bne sleep_100ms + dey + bne sleep_1s + dec tmp1 + bmi done + dey ; Down to #$FF + bne sleep_1s +done: + pla ; Restore CPU speed + jmp _set_iigs_speed diff --git a/libsrc/apple2/wait.s b/libsrc/apple2/wait.s new file mode 100644 index 000000000..3b569215b --- /dev/null +++ b/libsrc/apple2/wait.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; WAIT routine +; + + .export WAIT + + .include "apple2.inc" + + .segment "LOWCODE" + +WAIT: + ; Switch in ROM and call WAIT + bit $C082 + jsr $FCA8 ; Vector to WAIT routine + + ; Switch in LC bank 2 for R/O and return + bit $C080 + rts diff --git a/libsrc/apple2/waitvsync.s b/libsrc/apple2/waitvsync.s index a4ab5ebb3..1697622de 100644 --- a/libsrc/apple2/waitvsync.s +++ b/libsrc/apple2/waitvsync.s @@ -5,21 +5,11 @@ ; .ifdef __APPLE2ENH__ - .constructor initvsync .export _waitvsync - .import _get_ostype + .import ostype .include "apple2.inc" - .segment "ONCE" - -initvsync: - jsr _get_ostype - sta ostype - rts - - .code - _waitvsync: bit ostype bmi iigs ; $8x @@ -53,8 +43,4 @@ iic: sei cli rts - .segment "INIT" - -ostype: .res 1 - .endif ; __APPLE2ENH__ From d906748691d31dc8ec1dfd95d1314bec11a149a1 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Thu, 18 Jan 2024 17:37:09 +0100 Subject: [PATCH 469/520] Fix uploader implementation to reset IRQ bit for timer 4 (serial) interrupt --- libsrc/lynx/uploader.s | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libsrc/lynx/uploader.s b/libsrc/lynx/uploader.s index f16a1721a..df3e5df40 100644 --- a/libsrc/lynx/uploader.s +++ b/libsrc/lynx/uploader.s @@ -33,7 +33,7 @@ loop1: cont1: jsr read_byte sta (load_ptr2),y - sta PALETTE ; feedback ;-) + sta PALETTE + 1 ; feedback ;-) iny bne loop1 inc load_ptr2+1 @@ -69,6 +69,8 @@ again: ; last action : clear interrupt ; exit: + lda #$10 + sta INTRST clc rts From 93f9cb6e489e248a2f92c0b103d330b9e78ee220 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Thu, 18 Jan 2024 18:06:10 +0100 Subject: [PATCH 470/520] Adjusted uploader configuration. Split into two MEMORY areas, so it can be just below video memory. --- cfg/lynx-uploader.cfg | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cfg/lynx-uploader.cfg b/cfg/lynx-uploader.cfg index 476b3c5de..ea217626c 100644 --- a/cfg/lynx-uploader.cfg +++ b/cfg/lynx-uploader.cfg @@ -5,16 +5,17 @@ SYMBOLS { __BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size __EXEHDR__: type = import; __BOOTLDR__: type = import; - __DEFDIR__: type = import; __UPLOADER__: type = import; + __UPLOADERSIZE__: type = export, value = $61; + __HEADERSIZE__: type = export, value = 64; } MEMORY { ZP: file = "", define = yes, start = $0000, size = $0100; - HEADER: file = %O, start = $0000, size = $0040; + HEADER: file = %O, start = $0000, size = __HEADERSIZE__; BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__; - DIR: file = %O, start = $0000, size = 8; - MAIN: file = %O, define = yes, start = $0200, size = $BD38 - __STACKSIZE__; - UPLDR: file = %O, define = yes, start = $BFDC, size = $005C; + DIR: file = %O, start = $0000, size = 16; + MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__; + UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -30,8 +31,8 @@ SEGMENTS { RODATA: load = MAIN, type = ro, define = yes; DATA: load = MAIN, type = rw, define = yes; BSS: load = MAIN, type = bss, define = yes; - UPCODE: load = UPLDR, type = ro, define = yes; - UPDATA: load = UPLDR, type = rw, define = yes; + UPCODE: load = UPLOAD, type = ro, define = yes; + UPDATA: load = UPLOAD, type = rw, define = yes; } FEATURES { CONDES: type = constructor, From acce24fedcfa4d37dc5a28bf18129b68cb2194f9 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Thu, 18 Jan 2024 18:13:02 +0100 Subject: [PATCH 471/520] Switched to __BANK0BLOCKSIZE__ instead of __BLOCKSIZE__ to make current lynx config files work --- libsrc/lynx/lynx-cart.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/lynx/lynx-cart.s b/libsrc/lynx/lynx-cart.s index 94edff677..f9417aed3 100644 --- a/libsrc/lynx/lynx-cart.s +++ b/libsrc/lynx/lynx-cart.s @@ -88,7 +88,7 @@ lynxblock: lda __iodat sta IODAT stz _FileBlockByte - lda #<($100-(>__BLOCKSIZE__)) + lda #<($100-(>__BANK0BLOCKSIZE__)) sta _FileBlockByte+1 ply plx From 2e56dcc52196a8fdd65416adcc46c7e834db4615 Mon Sep 17 00:00:00 2001 From: Alex Thissen <athissen@killer-apps.nl> Date: Thu, 18 Jan 2024 18:13:39 +0100 Subject: [PATCH 472/520] Fix for mising import --- libsrc/lynx/lynx-cart.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/lynx/lynx-cart.s b/libsrc/lynx/lynx-cart.s index f9417aed3..d1f3e33eb 100644 --- a/libsrc/lynx/lynx-cart.s +++ b/libsrc/lynx/lynx-cart.s @@ -17,7 +17,7 @@ .include "extzp.inc" .export lynxskip0, lynxread0 .export lynxblock - .import __BLOCKSIZE__ + .import __BANK0BLOCKSIZE__ .code From ad90a3a421776d9c13756de5e5cdb53fa7b5a469 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Thu, 18 Jan 2024 18:57:57 +0000 Subject: [PATCH 473/520] Replaced references to __BLOCKSIZE__ with __BANK0BLOCKSIZE__ --- libsrc/lynx/lseek.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsrc/lynx/lseek.s b/libsrc/lynx/lseek.s index 4b4f94d7c..04d816945 100644 --- a/libsrc/lynx/lseek.s +++ b/libsrc/lynx/lseek.s @@ -18,7 +18,7 @@ .import ldeaxysp, decsp2, pushax, incsp8 .import tosandeax,decax1,tosdiveax,axlong,ldaxysp .import lynxskip0, lynxblock,tosasreax - .import __BLOCKSIZE__ + .import __BANK0BLOCKSIZE__ .importzp _FileCurrBlock .segment "CODE" @@ -32,15 +32,15 @@ jsr ldeaxysp jsr pusheax ldx #$00 - lda #<(__BLOCKSIZE__/1024 + 9) + lda #<(__BANK0BLOCKSIZE__/1024 + 9) jsr tosasreax sta _FileCurrBlock jsr lynxblock ldy #$05 jsr ldeaxysp jsr pusheax - lda #<(__BLOCKSIZE__-1) - ldx #>(__BLOCKSIZE__-1) + lda #<(__BANK0BLOCKSIZE__-1) + ldx #>(__BANK0BLOCKSIZE__-1) jsr axlong jsr tosandeax eor #$FF From 83691f30c1442aacb769d9eb3841802d2817beb8 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Fri, 19 Jan 2024 10:52:42 +0000 Subject: [PATCH 474/520] Missed a tab in config --- cfg/lynx-uploader.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/lynx-uploader.cfg b/cfg/lynx-uploader.cfg index ea217626c..62269de90 100644 --- a/cfg/lynx-uploader.cfg +++ b/cfg/lynx-uploader.cfg @@ -15,7 +15,7 @@ MEMORY { BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__; DIR: file = %O, start = $0000, size = 16; MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__; - UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061; + UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; From b23a7ec40746d68c90582dc4ec78ea822dd5d1b6 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 19 Jan 2024 21:14:47 +0100 Subject: [PATCH 475/520] Save two bytes in pushax and popptr1 It's not because Y must equal zero on rts that we should'nt spare one byte and one cycle. --- libsrc/runtime/popptr1.s | 8 +++++++- libsrc/runtime/pushax.s | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libsrc/runtime/popptr1.s b/libsrc/runtime/popptr1.s index 1d04330ab..b54bb9eb3 100644 --- a/libsrc/runtime/popptr1.s +++ b/libsrc/runtime/popptr1.s @@ -8,12 +8,18 @@ .import incsp2 .importzp sp, ptr1 + .macpack cpu + .proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved) ldy #1 lda (sp),y ; get hi byte sta ptr1+1 ; into ptr hi - dey ; no optimization for 65C02 here to have Y=0 at exit! + dey ; dey even for for 65C02 here to have Y=0 at exit! +.if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) ; get lo byte +.else lda (sp),y ; get lo byte +.endif sta ptr1 ; to ptr lo jmp incsp2 .endproc diff --git a/libsrc/runtime/pushax.s b/libsrc/runtime/pushax.s index ac181b994..27ddf641d 100644 --- a/libsrc/runtime/pushax.s +++ b/libsrc/runtime/pushax.s @@ -7,6 +7,8 @@ .export push0, pusha0, pushax .importzp sp + .macpack cpu + push0: lda #0 pusha0: ldx #0 @@ -29,7 +31,11 @@ pusha0: ldx #0 sta (sp),y ; (27) pla ; (31) dey ; (33) +.if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) ; (37) +.else sta (sp),y ; (38) - rts ; (44) +.endif + rts ; (44/43) .endproc From 01ee903cdfadf418e84c9e2243b652d64b9ca06f Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 10 Jan 2024 22:43:49 +0100 Subject: [PATCH 476/520] Fixup gmtime/localtime/mktime tests with all cases --- test/val/lib_common_gmtime_localtime.c | 151 ++++++++++++++----------- test/val/lib_common_mktime.c | 6 + 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c index 9ba4d6a0d..f0d9a8332 100644 --- a/test/val/lib_common_gmtime_localtime.c +++ b/test/val/lib_common_gmtime_localtime.c @@ -4,90 +4,107 @@ int fails = 0; -time_t timestamps[] = { - 0, - 0x41eb00, - 0x1e7cb00, - 0x21c8700, - 0x2FFFFFFF, - 0x6FFFFFFF, - 0xF48656FF, - 0xF4865700, - 0xFC5A3EFF, - 0x6D6739FF, - 0x6D673A00, - 0xFFFFFFFF, -}; +typedef struct _test_data { + time_t t; + char *gmt; + char *local; +} test_data; -/* Values checked against glibc 2.37's implementation of ctime() */ -const char *dates_gmt[] = { - "Thu Jan 1 00:00:00 1970\n", - "Fri Feb 20 00:00:00 1970\n", - "Wed Jan 6 00:00:00 1971\n", - "Mon Feb 15 00:00:00 1971\n", - "Sun Jul 9 16:12:47 1995\n", - "Wed Jul 18 05:49:51 2029\n", - "Thu Dec 31 23:59:59 2099\n", - "Fri Jan 1 00:00:00 2100\n", - "Fri Feb 29 23:59:59 2104\n", - "Tue Feb 29 23:59:59 2028\n", - "Wed Mar 1 00:00:00 2028\n", - "Sun Feb 7 06:28:15 2106\n", - NULL -}; +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, "Thu Jan 1 00:00:00 1970\n", "Thu Jan 1 01:00:00 1970\n"}, + {0x004e7970, "Sun Mar 1 12:34:56 1970\n", "Sun Mar 1 13:34:56 1970\n"}, + {0x01e1337f, "Thu Dec 31 23:59:59 1970\n", "Fri Jan 1 00:59:59 1971\n"}, -const char *dates_gmt_plus_one[] = { - "Thu Jan 1 01:00:00 1970\n", - "Fri Feb 20 01:00:00 1970\n", - "Wed Jan 6 01:00:00 1971\n", - "Mon Feb 15 01:00:00 1971\n", - "Sun Jul 9 17:12:47 1995\n", - "Wed Jul 18 06:49:51 2029\n", - "Fri Jan 1 00:59:59 2100\n", - "Fri Jan 1 01:00:00 2100\n", - "Sat Mar 1 00:59:59 2104\n", - "Wed Mar 1 00:59:59 2028\n", - "Wed Mar 1 01:00:00 2028\n", - "Thu Jan 1 00:59:59 1970\n", - NULL + /* First leap year */ + {0x03c26700, "Sat Jan 1 00:00:00 1972\n", "Sat Jan 1 01:00:00 1972\n"}, + {0x03c8fe7f, "Wed Jan 5 23:59:59 1972\n", "Thu Jan 6 00:59:59 1972\n"}, + {0x041180ff, "Tue Feb 29 23:59:59 1972\n", "Wed Mar 1 00:59:59 1972\n"}, + {0x04118100, "Wed Mar 1 00:00:00 1972\n", "Wed Mar 1 01:00:00 1972\n"}, + {0x05a4ebff, "Sun Dec 31 23:59:59 1972\n", "Mon Jan 1 00:59:59 1973\n"}, + + /* A non-leap year */ + {0x63b0cd00, "Sun Jan 1 00:00:00 2023\n", "Sun Jan 1 01:00:00 2023\n"}, + {0x63fe957f, "Tue Feb 28 23:59:59 2023\n", "Wed Mar 1 00:59:59 2023\n"}, + {0x63fe9580, "Wed Mar 1 00:00:00 2023\n", "Wed Mar 1 01:00:00 2023\n"}, + {0x656d4ec0, "Mon Dec 4 04:00:00 2023\n", "Mon Dec 4 05:00:00 2023\n"}, + {0x6592007f, "Sun Dec 31 23:59:59 2023\n", "Mon Jan 1 00:59:59 2024\n"}, + + /* Another leap year */ + {0x65920080, "Mon Jan 1 00:00:00 2024\n", "Mon Jan 1 01:00:00 2024\n"}, + {0x65e11a7f, "Thu Feb 29 23:59:59 2024\n", "Fri Mar 1 00:59:59 2024\n"}, + {0x65e11a80, "Fri Mar 1 00:00:00 2024\n", "Fri Mar 1 01:00:00 2024\n"}, + {0x6774857f, "Tue Dec 31 23:59:59 2024\n", "Wed Jan 1 00:59:59 2025\n"}, + + /* End of century */ + {0xf48656ff, "Thu Dec 31 23:59:59 2099\n", "Fri Jan 1 00:59:59 2100\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, "Fri Jan 1 00:00:00 2100\n", "Fri Jan 1 01:00:00 2100\n"}, + {0xf4d41f7f, "Sun Feb 28 23:59:59 2100\n", "Mon Mar 1 00:59:59 2100\n"}, + {0xf4d41f80, "Mon Mar 1 00:00:00 2100\n", "Mon Mar 1 01:00:00 2100\n"}, + {0xf4fceff0, "Wed Mar 31 23:00:00 2100\n", "Thu Apr 1 00:00:00 2100\n"}, + {0xf6678a7f, "Fri Dec 31 23:59:59 2100\n", "Sat Jan 1 00:59:59 2101\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, "Tue Jan 1 00:00:00 2104\n", "Tue Jan 1 01:00:00 2104\n"}, + {0xfc5a3eff, "Fri Feb 29 23:59:59 2104\n", "Sat Mar 1 00:59:59 2104\n"}, + {0xfc5a3f00, "Sat Mar 1 00:00:00 2104\n", "Sat Mar 1 01:00:00 2104\n"}, + {0xfcaa9c70, "Wed Apr 30 23:00:00 2104\n", "Thu May 1 00:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, "Thu Jan 1 00:00:00 2105\n", "Thu Jan 1 01:00:00 2105\n"}, + {0xffffffff, "Sun Feb 7 06:28:15 2106\n", "Thu Jan 1 00:59:59 1970\n"} }; int main (void) { int i; + struct tm *tm; + char *str; - for (i = 0; dates_gmt[i] != NULL; i++) { - struct tm *tm; - char *str; + tm = gmtime(NULL); + if (tm != NULL) { + printf("gmtime should return NULL with a NULL parameter\n"); + fails++; + } - /* Check gmtime */ - tm = gmtime(×tamps[i]); + tm = localtime(NULL); + if (tm != NULL) { + printf("localtime should return NULL with a NULL parameter\n"); + fails++; + } + + /* Verify conversion both ways */ + for (i = 0; ; i++) { + time_t t = data[i].t; + + tm = gmtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt[i])) { + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: gmtime: unexpected result: expected %s, got %s\n", t, data[i].gmt, str); fails++; - printf("gmtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt[i], str); } - - /* Check localtime with UTC timezone */ + _tz.timezone = 0; - tm = localtime(×tamps[i]); + tm = localtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt[i])) { + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: localtime (UTC+0): unexpected result: expected %s, got %s\n", t, data[i].gmt, str); fails++; - printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt[i], str); } - // /* Check localtime at UTC+1 */ - // _tz.timezone = 3600; - // tm = localtime(×tamps[i]); - // str = asctime(tm); - // if (strcmp(str, dates_gmt_plus_one[i])) { - // fails++; - // printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - // timestamps[i], dates_gmt_plus_one[i], str); - // } + _tz.timezone = 3600; + tm = localtime(&t); + str = asctime(tm); + if (strcmp(data[i].local, str)) { + printf("0x%lx: localtime (UTC+1): unexpected result: expected %s, got %s\n", t, data[i].local, str); + fails++; + } + + if (t == 0xFFFFFFFF) + break; } return fails; } diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c index 5d42db874..6cabef2be 100644 --- a/test/val/lib_common_mktime.c +++ b/test/val/lib_common_mktime.c @@ -75,6 +75,11 @@ int main (void) { int i; + if (mktime(NULL) != (time_t)-1) { + printf("mktime should return -1 with a NULL parameter\n"); + fails++; + } + /* Verify conversion both ways */ for (i = 0; ; i++) { time_t t = data[i].t; @@ -91,6 +96,7 @@ int main (void) } if (strcmp(data[i].str, ctime(&t))) { printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t)); + fails++; } if (t == 0xFFFFFFFF) From ba75a2ac267fee6adc7a581612ca878efec44e38 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Tue, 23 Jan 2024 14:33:05 +0800 Subject: [PATCH 477/520] Added missing checks for forward declarations of the main() function. More accurate diagnosis on implicit 'int' type specifiers. --- src/cc65/declare.c | 74 +++++++++++++----------- src/cc65/function.c | 31 +--------- test/ref/bug1889-missing-identifier.cref | 2 +- test/ref/inline-error.c | 2 + test/ref/inline-error.cref | 11 ++-- 5 files changed, 50 insertions(+), 70 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 29827d3aa..e1e66ab85 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2417,48 +2417,54 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Parse attributes for this declarator */ ParseAttribute (D); - /* 'inline' is only allowed on functions */ - if (Mode != DM_ACCEPT_PARAM_IDENT && - (D->StorageClass & SC_TYPEMASK) != SC_FUNC && - (D->StorageClass & SC_INLINE) == SC_INLINE) { - Error ("'inline' on non-function declaration"); - D->StorageClass &= ~SC_INLINE; - } - - /* Check a few pre-C99 things */ - if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { - /* Check and warn about an implicit int return in the function */ - if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) { - /* Function has an implicit int return. Output a warning if we don't - ** have the C89 standard enabled explicitly. + /* Check a few things for the instance (rather than the type) */ + if (D->Ident[0] != '\0') { + /* Check a few pre-C99 things */ + if ((Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE && IsRankInt (Spec->Type)) { + /* If the standard was not set explicitly to C89, print a warning + ** for typedefs with implicit int type specifier. */ if (IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' return type is an obsolete feature"); + if ((D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + Warning ("Implicit 'int' type specifier is an obsolete feature"); + } else { + Warning ("Type specifier defaults to 'int' in typedef of '%s'", + D->Ident); + Note ("Implicit 'int' type specifier is an obsolete feature"); + } } - GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - /* For anything that is not a function, check for an implicit int - ** declaration. - */ - if (!IsTypeFunc (D->Type) && IsRankInt (D->Type)) { - if ((D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - /* If the standard was not set explicitly to C89, print a warning - ** for variables with implicit int type. - */ - if (IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' is an obsolete feature"); + /* Check other things depending on the "kind" of the instance */ + if ((D->StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* Special handling for main() */ + if (strcmp (D->Ident, "main") == 0) { + /* main() cannot be a fastcall function */ + if (IsQualFastcall (D->Type)) { + Error ("'main' cannot be declared __fastcall__"); } - } else { - /* If the standard was not set explicitly to C89, print a warning - ** for typedefs with implicit int type. - */ - if (IS_Get (&Standard) >= STD_C99) { - Warning ("Type defaults to 'int' in typedef of '%s'", - D->Ident); - Note ("Implicit 'int' is an obsolete feature"); + + /* main() cannot be an inline function */ + if ((D->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'main' cannot be declared inline"); + D->StorageClass &= ~SC_INLINE; + } + + /* Check return type */ + if (GetUnqualRawTypeCode (GetFuncReturnType (D->Type)) != T_INT) { + /* If cc65 extensions aren't enabled, don't allow a main function + ** that doesn't return an int. + */ + if (IS_Get (&Standard) != STD_CC65) { + Error ("'main' must always return an int"); + } } } + } else if (Mode != DM_ACCEPT_PARAM_IDENT && + (D->StorageClass & SC_INLINE) == SC_INLINE) { + /* 'inline' is only allowed on functions */ + Error ("'inline' on non-function declaration"); + D->StorageClass &= ~SC_INLINE; } } diff --git a/src/cc65/function.c b/src/cc65/function.c index 596f9b617..a4b860251 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -450,7 +450,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Parse argument declarations and function body. */ { int ParamComplete; /* If all paramemters have complete types */ - int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; const Type* RType; /* Real type used for struct parameters */ const Type* ReturnType; /* Return type */ @@ -513,34 +512,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Mark this as the main function */ CurrentFunc->Flags |= FF_IS_MAIN; - /* Main cannot be a fastcall function */ - if (IsQualFastcall (Func->Type)) { - Error ("'main' cannot be declared as __fastcall__"); - } - - /* main() cannot be an inline function */ - if ((Func->Flags & SC_INLINE) == SC_INLINE) { - Error ("'main' cannot be declared inline"); - Func->Flags &= ~SC_INLINE; - } - - /* Check return type */ - if (GetUnqualRawTypeCode (ReturnType) == T_INT) { - /* Determine if this is a main function in a C99 environment that - ** returns an int. - */ - if (IS_Get (&Standard) >= STD_C99) { - C99MainFunc = 1; - } - } else { - /* If cc65 extensions aren't enabled, don't allow a main function - ** that doesn't return an int. - */ - if (IS_Get (&Standard) != STD_CC65) { - Error ("'main' must always return an int"); - } - } - /* Add a forced import of a symbol that is contained in the startup ** code. This will force the startup code to be linked in. */ @@ -665,7 +636,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* If this is the main function in a C99 environment returning an int, ** let it always return zero. Otherwise output a warning. */ - if (C99MainFunc) { + if (IS_Get (&Standard) >= STD_C99 && GetUnqualRawTypeCode (ReturnType) == T_INT) { g_getimmed (CF_INT | CF_CONST, 0, 0); } else if (IS_Get (&WarnReturnType)) { Warning ("Control reaches end of non-void function [-Wreturn-type]"); diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index e77c1a7a1..70c485fab 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,4 +1,4 @@ bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers -bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature +bug1889-missing-identifier.c:3: Warning: Implicit 'int' type specifier is an obsolete feature bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration bug1889-missing-identifier.c:6: Error: Expression expected diff --git a/test/ref/inline-error.c b/test/ref/inline-error.c index d8191025a..2dad41b6d 100644 --- a/test/ref/inline-error.c +++ b/test/ref/inline-error.c @@ -33,4 +33,6 @@ inline int main(void) /* Error */ f2b(); /* Still imported */ } +inline int main(void); /* Error */ + /* Warning: non-external inline functions declared but undefined in TU */ diff --git a/test/ref/inline-error.cref b/test/ref/inline-error.cref index abfdcdddd..4ce1d7073 100644 --- a/test/ref/inline-error.cref +++ b/test/ref/inline-error.cref @@ -13,8 +13,9 @@ inline-error.c:22: Error: 'inline' on non-function declaration inline-error.c:23: Error: 'inline' on non-function declaration inline-error.c:24: Error: 'inline' on non-function declaration inline-error.c:34: Warning: Variable 'fp2' is defined but never used -inline-error.c:37: Warning: Inline function 'f1a' used but never defined -inline-error.c:37: Warning: Inline function 'f1b' used but never defined -inline-error.c:37: Warning: Static function 'f1c' used but never defined -inline-error.c:37: Warning: Inline function 'f2a' used but never defined -inline-error.c:37: Warning: Inline function 'f2b' used but never defined +inline-error.c:36: Error: 'main' cannot be declared inline +inline-error.c:39: Warning: Inline function 'f1a' used but never defined +inline-error.c:39: Warning: Inline function 'f1b' used but never defined +inline-error.c:39: Warning: Static function 'f1c' used but never defined +inline-error.c:39: Warning: Inline function 'f2a' used but never defined +inline-error.c:39: Warning: Inline function 'f2b' used but never defined From 2ba176372e5b2643703e3c68e2f8e1cee54a4ec7 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 18 Jan 2024 13:55:18 +0100 Subject: [PATCH 478/520] Add beep for apple2 --- doc/apple2.sgml | 1 + doc/apple2enh.sgml | 1 + doc/funcref.sgml | 5 ++++- include/apple2.h | 3 +++ libsrc/apple2/beep.s | 20 ++++++++++++++++++++ libsrc/apple2/bell.s | 20 ++++++++++++++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 libsrc/apple2/beep.s create mode 100644 libsrc/apple2/bell.s diff --git a/doc/apple2.sgml b/doc/apple2.sgml index fb49ea941..3a3ec3666 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -330,6 +330,7 @@ usage. <item>_dos_type <item>_filetype <item>_datetime +<item>beep <item>get_ostype <item>gmtime_dt <item>mktime_dt diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 593b226ba..738e5b8af 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -331,6 +331,7 @@ usage. <item>_dos_type <item>_filetype <item>_datetime +<item>beep <item>get_ostype <item>gmtime_dt <item>mktime_dt diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 740a6d62e..81c63a38b 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -95,6 +95,7 @@ function. <itemize> <item>_dos_type +<item><ref id="beep" name="beep"> <item><ref id="get_ostype" name="get_ostype"> <item><ref id="gmtime_dt" name="gmtime_dt"> <item><ref id="mktime_dt" name="mktime_dt"> @@ -106,6 +107,7 @@ function. <itemize> <item>_dos_type +<item><ref id="beep" name="beep"> <item><ref id="get_ostype" name="get_ostype"> <item><ref id="gmtime_dt" name="gmtime_dt"> <item><ref id="mktime_dt" name="mktime_dt"> @@ -1771,10 +1773,11 @@ used in presence of a prototype. <descrip> <tag/Function/Beep sound. <tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/ +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ <tag/Declaration/<tt/void beep(void);/ <tag/Description/<tt/beep/ makes a brief tone. <tag/Notes/<itemize> -<item>The function is specific to the Sym-1. +<item>The function is specific to the Sym-1 and Apple2 platforms. </itemize> <tag/Availability/cc65 <tag/See also/ diff --git a/include/apple2.h b/include/apple2.h index 25995eec2..875c10661 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -197,6 +197,9 @@ extern void a2_lo_tgi[]; +void beep (void); +/* Beep beep. */ + unsigned char get_ostype (void); /* Get the machine type. Returns one of the APPLE_xxx codes. */ diff --git a/libsrc/apple2/beep.s b/libsrc/apple2/beep.s new file mode 100644 index 000000000..ebebb6e3f --- /dev/null +++ b/libsrc/apple2/beep.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; void beep(void) +; + + .export _beep + .import BELL + + .include "apple2.inc" + + .segment "LOWCODE" + +_beep: + lda CH ; Bell scrambles CH in 80col mode on IIgs, storing + pha ; it in OURCH and resetting CH to 0. Save it. + jsr BELL + pla + sta CH ; Restore CH + rts diff --git a/libsrc/apple2/bell.s b/libsrc/apple2/bell.s new file mode 100644 index 000000000..28e6d24f8 --- /dev/null +++ b/libsrc/apple2/bell.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; BELL routine +; + + .export BELL + + .include "apple2.inc" + + .segment "LOWCODE" + +BELL: + ; Switch in ROM and call BELL + bit $C082 + jsr $FF3A ; BELL + + ; Switch in LC bank 2 for R/O and return + bit $C080 + rts From 13ddd734db8b978fe6eaaa1cc1812a23b47a3544 Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:26:26 +0100 Subject: [PATCH 479/520] Add note about contacting the devs when starting to work on huge patches --- Contributing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index 25c6217aa..1fde873f2 100644 --- a/Contributing.md +++ b/Contributing.md @@ -1,4 +1,6 @@ -This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete. +This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete. + +Also, before you put a lot of work into implementing something you want to contribute, please get in touch with one of the developers and ask if what you are going to do is actually wanted and has a chance of being merged. Perhaps someone else is already working on it, or perhaps what you have in mind is not how we'd expect it to be - talking to us before you start might save you a lot of work in those cases. (''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.) From 3e01ac9b04f2bf7da1b1be46cc502b8f0423b08d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 15 Jan 2024 20:30:20 +0100 Subject: [PATCH 480/520] Fix malloc and realloc overflow If user requests a size >= 65532, adding the heap admin size overflows size. Fixes #2358. --- libsrc/common/malloc.s | 2 +- libsrc/common/realloc.c | 8 ++-- test/val/lib_common_malloc.c | 34 +++++++++++++++ test/val/lib_common_realloc.c | 81 +++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 test/val/lib_common_malloc.c create mode 100644 test/val/lib_common_realloc.c diff --git a/libsrc/common/malloc.s b/libsrc/common/malloc.s index 6872f1f2e..72c4aedaa 100644 --- a/libsrc/common/malloc.s +++ b/libsrc/common/malloc.s @@ -131,6 +131,7 @@ _malloc: sta ptr1 bcc @L1 inc ptr1+1 + beq OutOfHeapSpace ; if high byte's 0, we overflowed! @L1: ldx ptr1+1 bne @L2 cmp #HEAP_MIN_BLOCKSIZE+1 @@ -336,4 +337,3 @@ RetUserPtr: bcc @L9 inx @L9: rts - diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c index eeb1eeea5..b5429b3c2 100644 --- a/libsrc/common/realloc.c +++ b/libsrc/common/realloc.c @@ -59,6 +59,11 @@ void* __fastcall__ realloc (void* block, register size_t size) return 0; } + /* Don't overflow! */ + if (size > 0xFFFF - HEAP_ADMIN_SPACE) { + return 0; + } + /* Make the internal used size from the given size */ size += HEAP_ADMIN_SPACE; if (size < sizeof (struct freeblock)) { @@ -107,6 +112,3 @@ void* __fastcall__ realloc (void* block, register size_t size) } return newblock; } - - - diff --git a/test/val/lib_common_malloc.c b/test/val/lib_common_malloc.c new file mode 100644 index 000000000..5c68dc39a --- /dev/null +++ b/test/val/lib_common_malloc.c @@ -0,0 +1,34 @@ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "unittest.h" + +TEST +{ + char *buf, *buf2; + unsigned int i; + + buf = malloc(0); + ASSERT_IsTrue (buf == NULL, "malloc (0) returned something"); + + for (i = 1; i < 10; i++) { + buf = malloc(i); + ASSERT_IsTrue (buf != NULL, "small returned nothing"); + } + + buf = malloc(4096); + ASSERT_IsTrue (buf != NULL, "malloc (4096) returned nothing"); + + buf = malloc(61000UL); + ASSERT_IsTrue (buf == NULL, "malloc (61000) returned something"); + + for (i = 65535UL; i > _heapmaxavail(); i--) { + buf = malloc(i); + ASSERT_IsTrue (buf == NULL, "malloc returned something but shouldn't have"); + } + + buf = malloc(i); + ASSERT_IsTrue (buf != NULL, "malloc returned nothing but should have"); + ASSERT_IsTrue(_heapmaxavail() == 0, "heapmaxavail should be 0"); +} +ENDTEST diff --git a/test/val/lib_common_realloc.c b/test/val/lib_common_realloc.c new file mode 100644 index 000000000..d1e4fa3eb --- /dev/null +++ b/test/val/lib_common_realloc.c @@ -0,0 +1,81 @@ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "unittest.h" + +TEST +{ + char *buf, *buf2; + unsigned int i; + + buf = realloc(NULL, 0); + ASSERT_IsTrue (buf == NULL, "realloc (NULL, 0) returned something"); + + for (i = 1; i < 10; i++) { + buf2 = realloc(buf, i); + ASSERT_IsTrue (buf2 != NULL, "small realloc returned nothing"); + if (i > 1) { + ASSERT_IsTrue (buf2 == buf, "buf shouldn't have moved"); + } + buf = buf2; + } + + buf = realloc(NULL, 15); + ASSERT_IsTrue (buf != NULL, "realloc (NULL, 15) returned nothing"); + + buf = realloc(buf, 0); + ASSERT_IsTrue (buf == NULL, "realloc (buf, 0) returned something"); + + buf = realloc(buf, 32); + memset(buf, 'a', 32); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in buf"); + } + + /* Now realloc larger, while there's nothing else in the heap */ + buf = realloc(buf, 64); + memset(buf+32, 'b', 32); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 64; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + /* Now realloc smaller, while there's nothing else in the heap */ + buf = realloc(buf, 40); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 40; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + /* Allocate something else, so next realloc has to change block */ + malloc(50); + + /* Now realloc larger, with something else in the heap */ + buf = realloc(buf, 128); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 40; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + for (i = 129; i < 8192; i++) { + buf = realloc(buf, i); + ASSERT_IsTrue(buf != NULL, "realloc failed"); + } + + malloc(4096); + + buf2 = realloc(buf, 58000UL); + ASSERT_IsTrue (buf2 == NULL, "realloc (buf, 58000) returned something"); + + for (i = 65535UL; i > 65527UL; i--) { + buf2 = realloc(buf, i); + ASSERT_IsTrue (buf2 == NULL, "realloc returned something but shouldn't have"); + } +} +ENDTEST From 5d49fde788ba7adcee87d5eb050fe8269fa6103a Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 11 Jan 2024 18:19:13 +0100 Subject: [PATCH 481/520] add a return -1 helper --- libsrc/atari/open.s | 6 ++---- libsrc/cbm/cbm_read.s | 7 ++----- libsrc/cbm/cbm_write.s | 7 ++----- libsrc/common/fclose.s | 8 ++------ libsrc/common/fmisc.s | 9 ++------- libsrc/common/putenv.s | 9 ++------- libsrc/common/ungetc.s | 6 +----- libsrc/runtime/returnFFFF.s | 15 +++++++++++++++ 8 files changed, 28 insertions(+), 39 deletions(-) create mode 100644 libsrc/runtime/returnFFFF.s diff --git a/libsrc/atari/open.s b/libsrc/atari/open.s index ed3e40b2f..e7e55c54c 100644 --- a/libsrc/atari/open.s +++ b/libsrc/atari/open.s @@ -19,7 +19,7 @@ .import findfreeiocb .import incsp4 .import ldaxysp,addysp - .import ___oserror + .import ___oserror, returnFFFF .ifdef UCASE_FILENAME .import ucase_fn .endif @@ -39,9 +39,7 @@ parmok: jsr findfreeiocb lda #<EMFILE ; "too many open files" seterr: jsr ___directerrno jsr incsp4 ; clean up stack - lda #$FF - tax - rts ; return -1 + jmp returnFFFF ; process the mode argument diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s index 8a9939eca..98e3c25c9 100644 --- a/libsrc/cbm/cbm_read.s +++ b/libsrc/cbm/cbm_read.s @@ -40,7 +40,7 @@ .export _cbm_read .importzp ptr1, ptr2, ptr3, tmp1 - .import popax, popa + .import popax, popa, returnFFFF .import ___oserror @@ -107,7 +107,4 @@ _cbm_read: ; CHKIN failed @E1: sta ___oserror - lda #$FF - tax - rts ; return -1 - + jmp returnFFFF diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s index 18c6f4684..a4ecfbe3f 100644 --- a/libsrc/cbm/cbm_write.s +++ b/libsrc/cbm/cbm_write.s @@ -32,7 +32,7 @@ .export _cbm_write .importzp ptr1, ptr2, ptr3 - .import popax, popa + .import popax, popa, returnFFFF .import ___oserror @@ -88,7 +88,4 @@ _cbm_write: ; Error entry, error code is in A @E2: sta ___oserror - lda #$FF - tax - rts ; return -1 - + jmp returnFFFF diff --git a/libsrc/common/fclose.s b/libsrc/common/fclose.s index 2368bf9f6..f6c57841e 100644 --- a/libsrc/common/fclose.s +++ b/libsrc/common/fclose.s @@ -7,7 +7,7 @@ .export _fclose - .import _close + .import _close, ___directerrno .importzp ptr1 .include "errno.inc" @@ -31,10 +31,7 @@ ; File is not open lda #EINVAL - jsr ___seterrno - lda #$FF ; Return -1 - tax - rts + jmp ___directerrno ; File is open. Reset the flags and close the file. @@ -47,4 +44,3 @@ jmp _close ; Will set errno and return an error flag .endproc - diff --git a/libsrc/common/fmisc.s b/libsrc/common/fmisc.s index 90b48e8a6..d189db8a8 100644 --- a/libsrc/common/fmisc.s +++ b/libsrc/common/fmisc.s @@ -5,7 +5,7 @@ ; .export _clearerr, _feof, _ferror, _fileno, _fflush - .import return0 + .import return0, ___directerrno .importzp ptr1 .include "_file.inc" @@ -78,10 +78,7 @@ err: rts ; If the file is not valid, fileno must set errno and return -1 error: lda #<EBADF - jsr ___seterrno - lda #$FF - tax - rts + jmp ___directerrno .endproc ; @@ -89,5 +86,3 @@ error: lda #<EBADF ; _fflush = return0 - - diff --git a/libsrc/common/putenv.s b/libsrc/common/putenv.s index 5febcc71e..13f0e7dc4 100644 --- a/libsrc/common/putenv.s +++ b/libsrc/common/putenv.s @@ -10,7 +10,7 @@ .import _malloc, _free .import searchenv, copyenvptr .import __environ, __envcount, __envsize - .import return0 + .import return0, ___directerrno .import ptr1:zp, ptr2:zp, ptr3:zp, tmp1:zp .include "errno.inc" @@ -169,10 +169,7 @@ addentry: ; Error entries nomem: lda #ENOMEM -error: jsr ___seterrno - lda #$FF ; Return -1 - tax - rts +error: jmp ___directerrno .endproc @@ -184,5 +181,3 @@ error: jsr ___seterrno name: .addr 0 ; Pointer to name newsize: .byte 0 ; New environment size - - diff --git a/libsrc/common/ungetc.s b/libsrc/common/ungetc.s index 7e8c1f94f..c661600af 100644 --- a/libsrc/common/ungetc.s +++ b/libsrc/common/ungetc.s @@ -62,10 +62,6 @@ ; File is not open or the character is invalid error: lda #EINVAL - jsr ___seterrno - lda #$FF ; Return -1 - tax - rts + jmp ___directerrno .endproc - diff --git a/libsrc/runtime/returnFFFF.s b/libsrc/runtime/returnFFFF.s new file mode 100644 index 000000000..41180651b --- /dev/null +++ b/libsrc/runtime/returnFFFF.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 25.10.2000 +; +; CC65 runtime: Return -1 in a/x +; + + .export returnFFFF + +.proc returnFFFF + + lda #$FF + tax + rts + +.endproc From 1f820d0ae813694c40da9639aa7ec048b79fecbd Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 11 Jan 2024 19:31:04 +0100 Subject: [PATCH 482/520] Rewrite fgets in asm -104 bytes, -1% cycles --- libsrc/apple2/statvfs.s | 6 +- libsrc/common/fgets.c | 68 ---------------------- libsrc/common/fgets.s | 119 ++++++++++++++++++++++++++++++++++++++ libsrc/runtime/pushptr1.s | 14 +++++ 4 files changed, 135 insertions(+), 72 deletions(-) delete mode 100644 libsrc/common/fgets.c create mode 100644 libsrc/common/fgets.s create mode 100644 libsrc/runtime/pushptr1.s diff --git a/libsrc/apple2/statvfs.s b/libsrc/apple2/statvfs.s index 6274bb52b..8fcf46af8 100644 --- a/libsrc/apple2/statvfs.s +++ b/libsrc/apple2/statvfs.s @@ -6,7 +6,7 @@ .export _statvfs .import _dio_query_sectsize - .import mli_file_info, pushax, popax, popptr1 + .import mli_file_info, pushax, popax, popptr1, pushptr1 .include "zeropage.inc" .include "apple2.inc" .include "errno.inc" @@ -45,9 +45,7 @@ _statvfs: sty vol_sep ; Register '/' index lda #$00 sta (ptr1),y ; Cut pathname at first slash -: lda ptr1 - ldx ptr1+1 - jsr pushax +: jsr pushptr1 jsr mli_file_info diff --git a/libsrc/common/fgets.c b/libsrc/common/fgets.c deleted file mode 100644 index 21a991fd6..000000000 --- a/libsrc/common/fgets.c +++ /dev/null @@ -1,68 +0,0 @@ -/* -** Ullrich von Bassewitz, 11.08.1998 -** -** char* fgets (char* s, int size, FILE* f); -*/ - - - -#include <stdio.h> -#include <errno.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -char* __fastcall__ fgets (char* s, unsigned size, register FILE* f) -{ - register char* p = s; - unsigned i; - int c; - - if (size == 0) { - /* Invalid size */ - return (char*) _seterrno (EINVAL); - } - - /* Read input */ - i = 0; - while (--size) { - - /* Get next character */ - if ((c = fgetc (f)) == EOF) { - /* Error or EOF */ - if ((f->f_flags & _FERROR) != 0 || i == 0) { - /* ERROR or EOF on first char */ - *p = '\0'; - return 0; - } else { - /* EOF with data already read */ - break; - } - } - - /* One char more */ - *p = c; - ++p; - ++i; - - /* Stop at end of line */ - if ((char)c == '\n') { - break; - } - } - - /* Terminate the string */ - *p = '\0'; - - /* Done */ - return s; -} - - - diff --git a/libsrc/common/fgets.s b/libsrc/common/fgets.s new file mode 100644 index 000000000..465658191 --- /dev/null +++ b/libsrc/common/fgets.s @@ -0,0 +1,119 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ fgets (char* s, unsigned size, register FILE* f) +; + + .export _fgets + .import _fgetc, popptr1, pushptr1, popax, pushax, return0, ___errno + .importzp ptr1, ptr4 + + .include "errno.inc" + .include "stdio.inc" + .include "_file.inc" + + .macpack cpu + +terminate_ptr: + lda #$00 + tax + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr4) + .else + tay + sta (ptr4),y + .endif + rts + +_fgets: + sta ptr1 + stx ptr1+1 + + jsr popax + sta size + stx size+1 + + jsr popax + sta ptr4 + stx ptr4+1 + sta buf + stx buf+1 + + .if (.cpu .bitand ::CPU_ISET_65SC02) + stz didread + .else + lda #$00 ; We have read nothing yet + sta didread + .endif + + ; Check size + lda size + ora size+1 + bne read_loop + lda #EINVAL + sta ___errno + jmp return0 + +read_loop: + lda size ; Dec size + bne :+ + dec size+1 +: dec size + + bne :+ ; Check bound + ldx size+1 + beq done + +: jsr pushptr1 ; Push ptr1 for backup and load it to AX for fgetc + jsr _fgetc ; Read a char + + pha + jsr popptr1 ; Get ptr1 back + pla + + cpx #<EOF + beq got_eof + + ldy #$01 + sty didread ; We read at least one char + + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr4) + .else + dey + sta (ptr4),y + .endif + + inc ptr4 + bne :+ + inc ptr4+1 + +: cmp #$0A ; Stop at \n + beq done + + clc + bcc read_loop + +got_eof: + lda didread + beq stopped_at_first_char + ldy #_FILE::f_flags + lda (ptr1),y + and #_FERROR + bne stopped_at_first_char + +done: + jsr terminate_ptr + ldx #>buf + lda #<buf + rts + +stopped_at_first_char: + jmp terminate_ptr + + .bss + +c: .res 1 +buf: .res 2 +size: .res 2 +didread:.res 1 diff --git a/libsrc/runtime/pushptr1.s b/libsrc/runtime/pushptr1.s new file mode 100644 index 000000000..ca934d6c1 --- /dev/null +++ b/libsrc/runtime/pushptr1.s @@ -0,0 +1,14 @@ +; +; Colin Leroy-Mira, 2024 +; +; CC65 runtime: Push ptr1 to stack. +; A/X destroyed (set to ptr1) + + .export pushptr1 + .import pushax + .importzp ptr1 + +pushptr1: + lda ptr1 + ldx ptr1+1 + jmp pushax From b87df9e1c6326b4983a821d3035f8bafba221805 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 12 Jan 2024 23:04:14 +0100 Subject: [PATCH 483/520] Optimize -36 bytes out of posix_memalign And add a unit test --- libsrc/common/pmemalign.c | 22 ++++++++++++++-------- test/val/lib_common_pmemalign.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 test/val/lib_common_pmemalign.c diff --git a/libsrc/common/pmemalign.c b/libsrc/common/pmemalign.c index 52adb240d..4499084d1 100644 --- a/libsrc/common/pmemalign.c +++ b/libsrc/common/pmemalign.c @@ -50,7 +50,6 @@ */ - int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) /* Allocate a block of memory with the given "size", which is aligned to a ** memory address that is a multiple of "alignment". "alignment" MUST NOT be @@ -64,20 +63,27 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) size_t rawsize; size_t uppersize; size_t lowersize; + char err; register struct usedblock* b; /* points to raw Block */ register struct usedblock* u; /* points to User block */ register struct usedblock* p; /* Points to upper block */ /* Handle requests for zero-sized blocks */ if (size == 0) { +err_einval: + err = EINVAL; +err_out: *memptr = NULL; - return EINVAL; + return err; } - /* Test alignment: is it a power of two? There must be only one bit set. */ - if (alignment == 0 || (alignment & (alignment - 1)) != 0) { - *memptr = NULL; - return EINVAL; + /* Test alignment: is it a power of two? There must be one and only one bit set. */ + if (alignment == 0) { + goto err_einval; + } + + if (alignment & (alignment - 1)) { + goto err_einval; } /* Augment the block size up to the alignment, and allocate memory. @@ -90,8 +96,8 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) /* Handle out-of-memory */ if (b == NULL) { - *memptr = NULL; - return ENOMEM; + err = ENOMEM; + goto err_out; } /* Create (and return) a new pointer that points to the user-visible diff --git a/test/val/lib_common_pmemalign.c b/test/val/lib_common_pmemalign.c new file mode 100644 index 000000000..0e9e5f52f --- /dev/null +++ b/test/val/lib_common_pmemalign.c @@ -0,0 +1,31 @@ +#include <errno.h> +#include <stdlib.h> +#include "unittest.h" + +TEST +{ + void *buf; + int r; + + r = posix_memalign(&buf, 123, 1024); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with wrong alignment"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with wrong alignment"); + + r = posix_memalign(&buf, 0, 1024); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 alignment"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 alignment"); + + r = posix_memalign(&buf, 256, 0); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 size"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 size"); + + r = posix_memalign(&buf, 256, 32768U); + ASSERT_IsTrue(r == 0, "posix_memalign did not return 0 on correct call"); + ASSERT_IsTrue(buf != NULL, "posix_memalign left buf set to NULL on correct call"); + ASSERT_IsTrue(((unsigned int)buf & 0x00FF) == 0x00, "posix_memalign did not align memory"); + + r = posix_memalign(&buf, 256, 32768U); + ASSERT_IsTrue(r == ENOMEM, "posix_memalign did not return ENOMEM when no memory is available"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL when no memory is available"); +} +ENDTEST From f0b5b0296678dde02aa2c46096a7dee59cc4b9f4 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 21 Jan 2024 16:50:59 +0100 Subject: [PATCH 484/520] Swap in LC before destructors, ROM after --- doc/apple2.sgml | 2 +- doc/apple2enh.sgml | 2 +- libsrc/apple2/crt0.s | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 3a3ec3666..e6ec870ee 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -62,7 +62,7 @@ Special locations: </descrip><p> While running <tt/main()/ the Language Card bank 2 is enabled for read access. -However while running module constructors/destructors the Language Card is disabled. +However while running module constructors the Language Card is disabled. Enabling the Language Card allows to use it as additional memory for cc65 generated code. However code is never automatically placed there. Rather code diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 738e5b8af..d47820cc7 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -63,7 +63,7 @@ Special locations: </descrip><p> While running <tt/main()/ the Language Card bank 2 is enabled for read access. -However while running module constructors/destructors the Language Card is disabled. +However while running module constructors the Language Card is disabled. Enabling the Language Card allows to use it as additional memory for cc65 generated code. However code is never automatically placed there. Rather code diff --git a/libsrc/apple2/crt0.s b/libsrc/apple2/crt0.s index 60a8516d1..c129cdbf8 100644 --- a/libsrc/apple2/crt0.s +++ b/libsrc/apple2/crt0.s @@ -40,12 +40,15 @@ _exit: ldx #<exit lda #>exit jsr reset ; Setup RESET vector - ; Switch in ROM, in case it wasn't already switched in by a RESET. - bit $C082 + ; Switch in LC bank 2 for R/O in case it was switched out by a RESET. + bit $C080 ; Call the module destructors. jsr donelib + ; Switch in ROM. + bit $C082 + ; Restore the original RESET vector. exit: ldx #$02 : lda rvsave,x From 34f37c873ebe25e5973f0043b0a99a9867589cd9 Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Wed, 24 Jan 2024 16:32:45 +0100 Subject: [PATCH 485/520] Fixed comment --- libsrc/geos-common/graph/bitotherclip.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/geos-common/graph/bitotherclip.s b/libsrc/geos-common/graph/bitotherclip.s index 020139da8..fba00d966 100644 --- a/libsrc/geos-common/graph/bitotherclip.s +++ b/libsrc/geos-common/graph/bitotherclip.s @@ -6,7 +6,7 @@ ; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy, ; struct iconpic *myGfx); -; both proc1, proc2 should be: char __fastcall something (void); +; both proc1, proc2 should be: char foo (void); ; proc1 is called before reading a byte (.A returns next data) ; proc2 is called before reading each byte which is not pattern (code >219) From f7388cfb79def6c7fc9c737120553fea7b76b9e0 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 11 Jan 2024 18:30:13 +0100 Subject: [PATCH 486/520] add fgets/fgetc test --- include/stdio.h | 4 +++ test/ref/test_fgets.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/ref/test_fgets.c diff --git a/include/stdio.h b/include/stdio.h index 012b8e2ba..35ebd7784 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -86,6 +86,10 @@ extern FILE* stderr; # define FILENAME_MAX (80+1) #elif defined(__TELESTRAT__) # define FILENAME_MAX (50+1) +#elif defined(__SIM6502__) +# define FILENAME_MAX (1024+1) +#elif defined(__SIM65C02__) +# define FILENAME_MAX (1024+1) #else # define FILENAME_MAX (16+1) #endif diff --git a/test/ref/test_fgets.c b/test/ref/test_fgets.c new file mode 100644 index 000000000..70d30a066 --- /dev/null +++ b/test/ref/test_fgets.c @@ -0,0 +1,60 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fgets(buf, sizeof(buf), out) != NULL) { + printf("Error, could fgets with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fgets(NULL, 0, in) != NULL) { + printf("Error, could fgets with zero size\n"); + return 1; + } + + while (fgets(buf, sizeof(buf), in) != NULL) + { + printf("%s",buf); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + } + + fclose(in); + return 0; +} From 476591e8b7f28bd5145c4e81750a969ce83b97ff Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 11 Jan 2024 18:54:42 +0100 Subject: [PATCH 487/520] Rewrite fgetc in asm -82 bytes, -20% cycles --- libsrc/common/fgetc.c | 58 --------------------------- libsrc/common/fgetc.s | 92 +++++++++++++++++++++++++++++++++++++++++++ test/ref/test_fgets.c | 5 +++ 3 files changed, 97 insertions(+), 58 deletions(-) delete mode 100644 libsrc/common/fgetc.c create mode 100644 libsrc/common/fgetc.s diff --git a/libsrc/common/fgetc.c b/libsrc/common/fgetc.c deleted file mode 100644 index b4ba18d73..000000000 --- a/libsrc/common/fgetc.c +++ /dev/null @@ -1,58 +0,0 @@ -/* -** fgetc.c -** -** (C) Copyright 1998, 2002 Ullrich von Bassewitz (uz@cc65.org) -** -*/ - - - -#include <stdio.h> -#include <unistd.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fgetc (register FILE* f) -{ - unsigned char c; - - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* If we have a pushed back character, return it */ - if (f->f_flags & _FPUSHBACK) { - f->f_flags &= ~_FPUSHBACK; - return f->f_pushback; - } - - /* Read one byte */ - switch (read (f->f_fd, &c, 1)) { - - case -1: - /* Error */ - f->f_flags |= _FERROR; - return EOF; - - case 0: - /* EOF */ - f->f_flags |= _FEOF; - return EOF; - - default: - /* Char read */ - return c; - - } -} - - - diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s new file mode 100644 index 000000000..777696b7a --- /dev/null +++ b/libsrc/common/fgetc.s @@ -0,0 +1,92 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fgetc (register FILE* f) +; + + .export _fgetc + .import _read, pusha0, pushax, popptr1, incsp2, returnFFFF + .importzp ptr1 + + .include "stdio.inc" + .include "_file.inc" + +_fgetc: + sta ptr1 + stx ptr1+1 + jsr pushax ; Backup our ptr + + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #_FOPEN ; Check for file open + beq ret_eof + txa + and #(_FERROR|_FEOF); Check for error/eof + bne ret_eof + + txa + and #_FPUSHBACK ; Check for pushed back char + beq do_read + + txa + and #<(~_FPUSHBACK) ; Reset flag + sta (ptr1),y + + .assert _FILE::f_pushback = _FILE::f_flags+1, error + iny + jsr incsp2 ; Drop our ptr copy + lda (ptr1),y ; Return pushed back char + ldx #$00 + rts + +do_read: + ; Push _read parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #<c + ldx #>c + jsr pushax + + lda #$01 + ldx #$00 + + ; Read + jsr _read + + ; Check for errors + cmp #$00 + beq set_feof + + cmp #<(-1) + beq set_ferror + + jsr incsp2 + ; Return char + ldx #$00 + lda c + rts + +ret_eof: + jsr incsp2 + jmp returnFFFF + +set_ferror: + lda #_FERROR + bne set_err +set_feof: + lda #_FEOF +set_err: + pha + jsr popptr1 + pla + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 1 diff --git a/test/ref/test_fgets.c b/test/ref/test_fgets.c index 70d30a066..72ea308dd 100644 --- a/test/ref/test_fgets.c +++ b/test/ref/test_fgets.c @@ -45,6 +45,11 @@ int main(int argc,char **argv) return 1; } + /* Test ungetc while we're at it */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + while (fgets(buf, sizeof(buf), in) != NULL) { printf("%s",buf); From f1d95f1f07accf0e7912fd862738e4cd66794913 Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Thu, 25 Jan 2024 11:27:54 +0100 Subject: [PATCH 488/520] Added link for your interest --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11c3bb0ff..dce9a07bc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For details look at the [Website](https://cc65.github.io). Project founders: -* John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted +* John R. Dunning: [original implementation](https://public.websites.umich.edu/~archive/atari/8bit/Languages/Cc65/) of the C compiler and runtime library, Atari hosted * Ullrich von Bassewitz: * move the code to modern systems * rewrite most parts of the compiler From a8b870555e4dd371a718f0de506510b0ee202eff Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 22 Jan 2024 12:57:59 +0100 Subject: [PATCH 489/520] Rewrite realloc in asm -80 bytes, -39% cycles --- libsrc/common/realloc.c | 114 --------------------- libsrc/common/realloc.s | 213 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 114 deletions(-) delete mode 100644 libsrc/common/realloc.c create mode 100644 libsrc/common/realloc.s diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c deleted file mode 100644 index b5429b3c2..000000000 --- a/libsrc/common/realloc.c +++ /dev/null @@ -1,114 +0,0 @@ -/*****************************************************************************/ -/* */ -/* realloc.c */ -/* */ -/* Change the size of an allocated memory block */ -/* */ -/* */ -/* */ -/* (C) 1998-2004 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include <stdlib.h> -#include <string.h> -#include <_heap.h> - - - -void* __fastcall__ realloc (void* block, register size_t size) -{ - register struct usedblock* b; - struct usedblock* newblock; - unsigned oldsize; - unsigned newhptr; - - /* Check the block parameter */ - if (!block) { - /* Block is NULL, same as malloc */ - return malloc (size); - } - - /* Check the size parameter */ - if (size == 0) { - /* Block is not NULL, but size is: free the block */ - free (block); - return 0; - } - - /* Don't overflow! */ - if (size > 0xFFFF - HEAP_ADMIN_SPACE) { - return 0; - } - - /* Make the internal used size from the given size */ - size += HEAP_ADMIN_SPACE; - if (size < sizeof (struct freeblock)) { - size = sizeof (struct freeblock); - } - - /* The word below the user block contains a pointer to the start of the - ** raw memory block. The first word of this raw memory block is the full - ** size of the block. Get a pointer to the real block, get the old block - ** size. - */ - b = (((struct usedblock*) block) - 1)->start; - oldsize = b->size; - - /* Is the block at the current heap top? */ - if (((unsigned) b) + oldsize == ((unsigned) __heapptr)) { - /* Check if we've enough memory at the heap top */ - newhptr = ((unsigned) __heapptr) - oldsize + size; - if (newhptr <= ((unsigned) __heapend)) { - /* Ok, there's space enough */ - __heapptr = (unsigned*) newhptr; - b->size = size; - b->start = b; - return block; - } - } - - /* The given block was not located on top of the heap, or there's no - ** room left. Try to allocate a new block and copy the data. - */ - if (newblock = malloc (size)) { - - /* Adjust the old size to the user visible portion */ - oldsize -= HEAP_ADMIN_SPACE; - - /* If the new block is larger than the old one, copy the old - ** data only - */ - if (size > oldsize) { - size = oldsize; - } - - /* Copy the block data */ - memcpy (newblock, block, size); - free (block); - } - return newblock; -} diff --git a/libsrc/common/realloc.s b/libsrc/common/realloc.s new file mode 100644 index 000000000..925ac3d19 --- /dev/null +++ b/libsrc/common/realloc.s @@ -0,0 +1,213 @@ +; +; Colin Leroy-Mira, 2024 +; +; void* __fastcall__ realloc (void* block, register size_t size) +; + + .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4, sp + .import _malloc, _memcpy, _free + .import pushax, popptr1, return0 + .import incsp2, decsp2 + .export _realloc + + .include "_heap.inc" + + .macpack generic + +;---------------------------------------------------------------------------- +; Aliases for clarity + +block = ptr1 +size = ptr2 +ublock = ptr3 +oldsize = ptr4 +newblock = tmp1 ; (and tmp2) +orgblock = tmp3 ; (and tmp4) + +;---------------------------------------------------------------------------- +; Code + +_realloc: + sta size ; Store size + stx size+1 + + jsr popptr1 ; Pop block + + lda block+1 ; Is block null? + tax + ora block + bne :+ + + lda size ; Block is null, just malloc + ldx size+1 + jmp _malloc + +: lda size ; Is size 0? + ora size+1 + bne :+ + + lda block ; It is: free block (high byte already in X) + jsr _free + jmp return0 + +: clc ; Add internal used size + lda size + adc #HEAP_ADMIN_SPACE + sta size + bcc :+ + inc size+1 + bne :+ + + lda #$00 ; Size high byte now 0: We overflowed! + tax + rts + +: ldx size+1 ; Should we round size up? + bne :+ + cmp #.sizeof (freeblock) + bcs :+ + + lda #.sizeof (freeblock) + sta size ; (we presuppose that sizeof (freeblock) is < 256) + +: lda block ; Get pointer to raw memory block + sta orgblock ; Store original pointer + sec + sbc #.sizeof(usedblock) + sta ublock + lda block+1 + sta orgblock+1 ; Finish storing original pointer + sbc #0 + sta ublock+1 ; We have our usedblock struct + + ; Get block start + ldy #usedblock::start+1 + lda (ublock),y + tax ; Backup ublock high + dey + lda (ublock),y + + sta ublock ; Store ublock + stx ublock+1 + + ; Remember oldsize + ldy #usedblock::size+1 + lda (ublock),y + sta oldsize+1 + dey + lda (ublock),y + sta oldsize + + clc ; Is the block at heap top? + adc ublock + tay + lda ublock+1 + adc oldsize+1 + cmp ___heapptr+1 + bne must_malloc_new + cpy ___heapptr + bne must_malloc_new + + tya ; Put ___heapptr back in A + sec ; Check if we have enough memory at heap top + sbc oldsize ; Substract oldsize + sta newblock + lda ___heapptr+1 + sbc oldsize+1 + sta newblock+1 + clc + lda newblock ; And add size + adc size + sta newblock + lda newblock+1 + adc size+1 + sta newblock+1 + bcs must_malloc_new ; If we have a carry there we overflowed + + cmp ___heapend+1 + bne :+ + lda newblock + cmp ___heapend +: bcc :+ + bne must_malloc_new + +: lda newblock ; There is enough space + sta ___heapptr ; Update heapptr + lda newblock+1 + sta ___heapptr+1 + + ldy #usedblock::start+1 + lda ublock+1 + sta (ublock),y ; Update block start + dey + lda ublock + sta (ublock),y + dey + + .assert usedblock::size = usedblock::start-2, error + lda size+1 + sta (ublock),y ; Update block size + dey + lda size + sta (ublock),y + + lda orgblock ; Return original block + ldx orgblock+1 + rts + +must_malloc_new: ; The block is not at heap top, or too big + lda size+1 + pha ; Backup new size (at this point the only ptr + tax ; we'll need after malloc). tmp* are safe + lda size ; from malloc, memcpy and free. + pha + jsr _malloc + + cmp #$00 ; Did malloc succeed? + bne :+ + cpx #$00 + bne :+ + pla ; Pop size backup and return NULL + pla + txa ; X already 0 + rts ; No + +: sta newblock ; Yes, store newblock + stx newblock+1 + jsr pushax ; Push newblock for memcpy + + lda orgblock ; Push orgblock for memcpy + ldx orgblock+1 + jsr pushax + + sec ; Remove admin space from oldsize + lda oldsize + sbc #<HEAP_ADMIN_SPACE + sta oldsize + lda oldsize+1 + sbc #>HEAP_ADMIN_SPACE + sta oldsize+1 + + pla ; Restore new size to AX + tay + pla + tax + tya + + cmp oldsize ; Find the smallest size + bcc :+ + cpx oldsize+1 + bcc :+ + + lda oldsize + ldx oldsize+1 + +: jsr _memcpy ; And copy data + + lda orgblock ; Free old block + ldx orgblock+1 + jsr _free + + lda newblock ; Return new block + ldx newblock+1 + rts From aa6f850b8d2308b99bc13776e6e7937aacb5d711 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 11 Jan 2024 19:51:17 +0100 Subject: [PATCH 490/520] Rewrite gets in assembler +19 bytes if used alone, because it pulls in fgets, but as code is factorized, -128 bytes in programs using both fgets and gets. --- libsrc/common/gets.c | 63 -------------------------------------------- libsrc/common/gets.s | 47 +++++++++++++++++++++++++++++++++ test/ref/test_gets.c | 46 ++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 63 deletions(-) delete mode 100644 libsrc/common/gets.c create mode 100644 libsrc/common/gets.s create mode 100644 test/ref/test_gets.c diff --git a/libsrc/common/gets.c b/libsrc/common/gets.c deleted file mode 100644 index 2936c70de..000000000 --- a/libsrc/common/gets.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -** gets.c -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include <stdio.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -char* __fastcall__ gets (char* s) -{ - register char* p = s; - int c; - unsigned i = 0; - - while (1) { - - /* Get next character */ - if ((c = fgetc (stdin)) == EOF) { - /* Error or EOF */ - *p = '\0'; - if (stdin->f_flags & _FERROR) { - /* ERROR */ - return 0; - } else { - /* EOF */ - if (i) { - return s; - } else { - return 0; - } - } - } - - /* One char more. Newline ends the input */ - if ((char) c == '\n') { - *p = '\0'; - break; - } else { - *p = c; - ++p; - ++i; - } - - } - - /* Done */ - return s; -} - - - - diff --git a/libsrc/common/gets.s b/libsrc/common/gets.s new file mode 100644 index 000000000..dfaf2def3 --- /dev/null +++ b/libsrc/common/gets.s @@ -0,0 +1,47 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ gets (char* s) +; + + .export _gets + .import _fgets, _stdin, popax, pushax + .importzp ptr4 + +_gets: + ; Push buffer + sta ptr4 + stx ptr4+1 + jsr pushax + + ; Push size (there's no limit!) + lda #$FF + tax + jsr pushax + + lda _stdin + ldx _stdin+1 + + jsr _fgets + + ; Check return value + bne :+ + cpx #$00 + bne :+ + rts + +: ; At least one byte written. + jsr pushax ; Store returned pointer + + ; Remove \n if there is one. + lda ptr4 ; _fgets returns with ptr4 at + bne :+ ; end of buffer + dec ptr4+1 +: dec ptr4 + lda (ptr4),y ; _fgets returns with Y=0 + cmp #$0A + bne :+ + tya + sta (ptr4),y ; Set terminator over \n + +: jmp popax diff --git a/test/ref/test_gets.c b/test/ref/test_gets.c new file mode 100644 index 000000000..ee5b6fd58 --- /dev/null +++ b/test/ref/test_gets.c @@ -0,0 +1,46 @@ +/* + !!DESCRIPTION!! gets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> + +char buf[512]; + +#define INFILE "cf.in" + +#ifndef __CC65__ +/* Force declaration on host compiler, as gets() is deprecated for + * being dangerous as hell */ +char *gets (char *__s); +#endif + +#ifdef NO_OLD_FUNC_DECL +int main(int argc,char **argv) +#else +main(argc, argv) +int argc; +char *argv[]; +#endif +{ + /* Fake stdin with the reference file */ + fclose(stdin); + stdin = fopen(INFILE, "r"); + if (stdin == NULL) { + return EXIT_FAILURE; + } + + while (gets(buf) != NULL) + { + printf("%s",buf); + } + + fclose(stdin); + return 0; +} From 0dd7b0c3a5204ac96ae6067f3d48cbfc4f62ec05 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 25 Jan 2024 09:12:46 +0100 Subject: [PATCH 491/520] Implement __sysremove for sim65 This will allow using unlink()/remove() in sim65 programs Use it to unlink fgets' test output file --- libsrc/sim6502/paravirt.s | 5 ++++ src/sim65/paravirt.c | 43 ++++++++++++++++++++++++++++-- src/sim65/paravirt.h | 6 ++--- test/ref/test_fgets.c | 2 ++ test/val/remove.c | 55 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 test/val/remove.c diff --git a/libsrc/sim6502/paravirt.s b/libsrc/sim6502/paravirt.s index 0d8e528b1..3bd40fbe4 100644 --- a/libsrc/sim6502/paravirt.s +++ b/libsrc/sim6502/paravirt.s @@ -8,10 +8,15 @@ ; .export exit, args, _open, _close, _read, _write + .export __sysremove, ___osmaperrno +__sysremove := $FFF2 +___osmaperrno := $FFF3 _open := $FFF4 _close := $FFF5 _read := $FFF6 _write := $FFF7 args := $FFF8 exit := $FFF9 + + ; $FFFA-FFFF are hardware vectors, extend before not after! diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 2e52d6e7e..141bcd2bd 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -163,7 +163,7 @@ static void PVArgs (CPURegs* Regs) static void PVOpen (CPURegs* Regs) { - char Path[PVOPEN_PATH_SIZE]; + char Path[PV_PATH_SIZE]; int OFlag = O_INITIAL; int OMode = 0; unsigned RetVal, I = 0; @@ -184,7 +184,7 @@ static void PVOpen (CPURegs* Regs) break; } ++I; - if (I >= PVOPEN_PATH_SIZE) { + if (I >= PV_PATH_SIZE) { Error("PVOpen path too long at address $%04X",Name); } } @@ -253,6 +253,35 @@ static void PVClose (CPURegs* Regs) +static void PVSysRemove (CPURegs* Regs) +{ + char Path[PV_PATH_SIZE]; + unsigned RetVal, I = 0; + + unsigned Name = GetAX (Regs); + + Print (stderr, 2, "PVSysRemove ($%04X)\n", Name); + + do { + if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) { + break; + } + ++I; + if (I >= PV_PATH_SIZE) { + Error("PVSysRemove path too long at address $%04X", Name); + } + } + while (1); + + Print (stderr, 2, "PVSysRemove (\"%s\")\n", Path); + + RetVal = remove (Path); + + SetAX (Regs, RetVal); +} + + + static void PVRead (CPURegs* Regs) { unsigned char* Data; @@ -305,7 +334,17 @@ static void PVWrite (CPURegs* Regs) +static void PVOSMapErrno (CPURegs* Regs) +{ + unsigned err = GetAX(Regs); + SetAX (Regs, err != 0 ? -1 : 0); +} + + + static const PVFunc Hooks[] = { + PVSysRemove, + PVOSMapErrno, PVOpen, PVClose, PVRead, diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index 3badb50ea..f3281705e 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -44,11 +44,11 @@ -#define PARAVIRT_BASE 0xFFF4 +#define PARAVIRT_BASE 0xFFF2 /* Lowest address used by a paravirtualization hook */ -#define PVOPEN_PATH_SIZE 1024 -/* Maximum path size supported by PVOpen */ +#define PV_PATH_SIZE 1024 +/* Maximum path size supported by PVOpen/PVSysRemove */ diff --git a/test/ref/test_fgets.c b/test/ref/test_fgets.c index 72ea308dd..0529b1651 100644 --- a/test/ref/test_fgets.c +++ b/test/ref/test_fgets.c @@ -10,6 +10,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> FILE *in, *out; char buf[32]; @@ -34,6 +35,7 @@ int main(int argc,char **argv) printf("Error: file pointer should be in error state\n"); } fclose(out); + unlink(outfile_path); in = fopen(INFILE, "rb"); if (in == NULL) { diff --git a/test/val/remove.c b/test/val/remove.c new file mode 100644 index 000000000..eecf8be8f --- /dev/null +++ b/test/val/remove.c @@ -0,0 +1,55 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int fails = 0; + + +static void create_out_file(const char *outfile_path) { + FILE *out; + + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + printf("Could not create %s\n", outfile_path); + fails++; + return; + } + fclose(out); +} + +int main (int argc, char **argv) +{ + int r; + static char outfile_path[FILENAME_MAX+1]; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + create_out_file(outfile_path); + r = remove(outfile_path); + if (r != 0) { + printf("could not remove() %s\n", outfile_path); + fails++; + } + + create_out_file(outfile_path); + r = unlink(outfile_path); + if (r != 0) { + printf("could not unlink() %s\n", outfile_path); + fails++; + } + + r = remove("klsdfjqlsjdflkqjdsoizu"); + if (r == 0) { + printf("remove()ing non-existent file succeeded\n"); + fails++; + } + + r = unlink("klsdfjqlsjdflkqjdsoizu"); + if (r == 0) { + printf("unlink()ing non-existent file succeeded\n"); + fails++; + } + + return fails; +} From 7d7cf2d1e0f13dc095c4b085633772b2fab08eb1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 28 Jan 2024 21:33:12 +0100 Subject: [PATCH 492/520] Implement aslax7/shlax7/asrax7/shrax7 --- libsrc/runtime/aslax7.s | 21 +++++++++++++++++ libsrc/runtime/asrax7.s | 18 ++++++++++++++ libsrc/runtime/shrax7.s | 18 ++++++++++++++ src/cc65/codegen.c | 16 +++++++++++++ src/cc65/codeinfo.c | 4 ++++ test/val/lib_runtime_aslax7.c | 44 +++++++++++++++++++++++++++++++++++ test/val/lib_runtime_asrax7.c | 44 +++++++++++++++++++++++++++++++++++ test/val/lib_runtime_shlax7.c | 44 +++++++++++++++++++++++++++++++++++ test/val/lib_runtime_shrax7.c | 44 +++++++++++++++++++++++++++++++++++ 9 files changed, 253 insertions(+) create mode 100644 libsrc/runtime/aslax7.s create mode 100644 libsrc/runtime/asrax7.s create mode 100644 libsrc/runtime/shrax7.s create mode 100644 test/val/lib_runtime_aslax7.c create mode 100644 test/val/lib_runtime_asrax7.c create mode 100644 test/val/lib_runtime_shlax7.c create mode 100644 test/val/lib_runtime_shrax7.c diff --git a/libsrc/runtime/aslax7.s b/libsrc/runtime/aslax7.s new file mode 100644 index 000000000..533ee55e6 --- /dev/null +++ b/libsrc/runtime/aslax7.s @@ -0,0 +1,21 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shlax7, aslax7 + +aslax7: +shlax7: ; XXXXXXXL AAAAAAAl + tay + txa + lsr ; XXXXXXXL -> 0XXXXXXX, L->C + tya + ror ; AAAAAAAl -> LAAAAAAA, l->C + tax + lda #$00 ; LAAAAAAA 00000000 + ror ; LAAAAAAA l0000000 + rts + + ; 10 bytes, 16 cycles + rts diff --git a/libsrc/runtime/asrax7.s b/libsrc/runtime/asrax7.s new file mode 100644 index 000000000..3c9cce681 --- /dev/null +++ b/libsrc/runtime/asrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, signed +; + + .export asrax7 + +asrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + dex ; 11111111 XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/libsrc/runtime/shrax7.s b/libsrc/runtime/shrax7.s new file mode 100644 index 000000000..31712ca72 --- /dev/null +++ b/libsrc/runtime/shrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shrax7 + +shrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + inx ; 0000000H XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index c2bdfcd63..69dcc1c6c 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -3260,6 +3260,14 @@ void g_asr (unsigned flags, unsigned long val) } val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shrax7"); + } else { + AddCodeLine ("jsr asrax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shrax4"); @@ -3402,6 +3410,14 @@ void g_asl (unsigned flags, unsigned long val) AddCodeLine ("lda #$00"); val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shlax7"); + } else { + AddCodeLine ("jsr aslax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shlax4"); diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 88f8a5138..427bfc52b 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -99,6 +99,7 @@ static const FuncInfo FuncInfoTable[] = { { "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 }, + { "aslax7", REG_AX, PSTATE_ALL | REG_AXY }, { "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 }, @@ -108,6 +109,7 @@ static const FuncInfo FuncInfoTable[] = { { "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 }, + { "asrax7", REG_AX, PSTATE_ALL | REG_AX }, { "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 }, @@ -245,6 +247,7 @@ static const FuncInfo FuncInfoTable[] = { { "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 }, + { "shlax7", REG_AX, PSTATE_ALL | REG_AXY }, { "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 }, @@ -254,6 +257,7 @@ static const FuncInfo FuncInfoTable[] = { { "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 }, + { "shrax7", REG_AX, PSTATE_ALL | REG_AX }, { "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 }, diff --git a/test/val/lib_runtime_aslax7.c b/test/val/lib_runtime_aslax7.c new file mode 100644 index 000000000..ea8f0b375 --- /dev/null +++ b/test/val/lib_runtime_aslax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for aslax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %d << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != -32768); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %d << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_asrax7.c b/test/val/lib_runtime_asrax7.c new file mode 100644 index 000000000..942efea1d --- /dev/null +++ b/test/val/lib_runtime_asrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for asrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != -32768); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_shlax7.c b/test/val/lib_runtime_shlax7.c new file mode 100644 index 000000000..9a4869438 --- /dev/null +++ b/test/val/lib_runtime_shlax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shlax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %u << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != 0); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %u << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != 0); + + return 0; +} diff --git a/test/val/lib_runtime_shrax7.c b/test/val/lib_runtime_shrax7.c new file mode 100644 index 000000000..db7356d0e --- /dev/null +++ b/test/val/lib_runtime_shrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != 0); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != 0); + + return 0; +} From 7594af553ae3a189e7a7f9cf2f59c47af51441eb Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Sun, 28 Jan 2024 17:00:02 +0100 Subject: [PATCH 493/520] Fix #2388 Reopen stdin in binary mode instead of closing/opening --- test/ref/test_gets.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/ref/test_gets.c b/test/ref/test_gets.c index ee5b6fd58..003da5569 100644 --- a/test/ref/test_gets.c +++ b/test/ref/test_gets.c @@ -30,9 +30,7 @@ char *argv[]; #endif { /* Fake stdin with the reference file */ - fclose(stdin); - stdin = fopen(INFILE, "r"); - if (stdin == NULL) { + if (freopen(INFILE, "rb", stdin) == NULL) { return EXIT_FAILURE; } From ba3607102263c73af18105902a8e50063618bd96 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 30 Jan 2024 17:51:00 +0100 Subject: [PATCH 494/520] Rewrite fputs in assembly -28 bytes, -1% cycles --- libsrc/common/fputs.c | 28 ---------------------------- libsrc/common/fputs.s | 42 ++++++++++++++++++++++++++++++++++++++++++ test/ref/test_fputs.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 28 deletions(-) delete mode 100644 libsrc/common/fputs.c create mode 100644 libsrc/common/fputs.s create mode 100644 test/ref/test_fputs.c diff --git a/libsrc/common/fputs.c b/libsrc/common/fputs.c deleted file mode 100644 index be476a3f0..000000000 --- a/libsrc/common/fputs.c +++ /dev/null @@ -1,28 +0,0 @@ -/* -** int fputs (const char* s, FILE* f); -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include "_file.h" - - - -int __fastcall__ fputs (const char* s, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* Write the string */ - return write (f->f_fd, s, strlen (s)); -} - - - diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s new file mode 100644 index 000000000..e70374058 --- /dev/null +++ b/libsrc/common/fputs.s @@ -0,0 +1,42 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputs (const char* s, register FILE* f) +; + + .export _fputs + .importzp ptr1, ptr2 + .import _write, _strlen + .import swapstk, pushax, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputs: + sta ptr1 + stx ptr1+1 + + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #_FOPEN ; Check for file open + beq ret_eof + txa + and #(_FERROR|_FEOF); Check for error/eof + bne ret_eof + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + ldx #$00 + jsr swapstk ; Push fd, get s + + jsr pushax ; Push s + + jsr _strlen ; Get length + + ; Write + jmp _write + +ret_eof: + jmp returnFFFF diff --git a/test/ref/test_fputs.c b/test/ref/test_fputs.c new file mode 100644 index 000000000..ad0552317 --- /dev/null +++ b/test/ref/test_fputs.c @@ -0,0 +1,40 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +char buf[512], err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + strcpy(buf, "test"); + if (fputs(buf, in) != EOF) { + printf("Error: can fputs to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while (fgets(buf, 512, in) != NULL) { + fputs(buf, stdout); + } + + fclose(in); + return 0; +} From 1a5a7d67a7ebb4881a90234b596fdee4612b09ef Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 30 Jan 2024 17:59:33 +0100 Subject: [PATCH 495/520] Rewrite fputc in assembly -36 bytes, -12% cycles --- libsrc/common/fputc.c | 41 ------------------------ libsrc/common/fputc.s | 72 +++++++++++++++++++++++++++++++++++++++++++ libsrc/common/fputs.s | 4 +-- test/ref/test_fputc.c | 39 +++++++++++++++++++++++ 4 files changed, 113 insertions(+), 43 deletions(-) delete mode 100644 libsrc/common/fputc.c create mode 100644 libsrc/common/fputc.s create mode 100644 test/ref/test_fputc.c diff --git a/libsrc/common/fputc.c b/libsrc/common/fputc.c deleted file mode 100644 index b623949d3..000000000 --- a/libsrc/common/fputc.c +++ /dev/null @@ -1,41 +0,0 @@ -/* -** fputc.c -** -** Ullrich von Bassewitz, 02.06.1998 -*/ - - - -#include <stdio.h> -#include <unistd.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fputc (int c, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - goto ReturnEOF; - } - - /* Write the byte */ - if (write (f->f_fd, &c, 1) != 1) { - /* Error */ - f->f_flags |= _FERROR; -ReturnEOF: - return EOF; - } - - /* Return the byte written */ - return c & 0xFF; -} - - - diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s new file mode 100644 index 000000000..4633f24f1 --- /dev/null +++ b/libsrc/common/fputc.s @@ -0,0 +1,72 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputc (int c, FILE* f); +; + + .export _fputc + .importzp ptr1 + .import _write + .import pushax, pusha0, popax, incsp2 + .import pushptr1, popptr1, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputc: + sta ptr1 + stx ptr1+1 + + jsr popax ; Get char, as we'll have + sta c ; to return it anyway + stx c+1 + + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #_FOPEN ; Check for file open + beq ret_eof + txa + and #(_FERROR|_FEOF); Check for error/eof + bne ret_eof + + jsr pushptr1 ; Backup fp pointer + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #<c + ldx #>c + jsr pushax + + lda #$01 + ldx #$00 + + ; Write + jsr _write + + ; Check for errors + cmp #$01 + bne set_ferror + + ; Return char + lda c + ldx #$00 + jmp incsp2 ; Drop fp pointer copy + +ret_eof: + jmp returnFFFF + +set_ferror: + jsr popptr1 + lda #_FERROR + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 2 diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s index e70374058..00415aef1 100644 --- a/libsrc/common/fputs.s +++ b/libsrc/common/fputs.s @@ -32,9 +32,9 @@ _fputs: jsr swapstk ; Push fd, get s jsr pushax ; Push s - + jsr _strlen ; Get length - + ; Write jmp _write diff --git a/test/ref/test_fputc.c b/test/ref/test_fputc.c new file mode 100644 index 000000000..a19aeafaf --- /dev/null +++ b/test/ref/test_fputc.c @@ -0,0 +1,39 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +int c, err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fputc(c, in) != EOF) { + printf("Error: can fputc to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while ((c = fgetc(in)) != EOF) { + fputc(c, stdout); + } + + fclose(in); + return 0; +} From 2b97735d5d150f1ce9d568b10fd878fa503ca232 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 30 Jan 2024 18:11:10 +0100 Subject: [PATCH 496/520] Factorize file pointer check --- libsrc/common/checkferror.s | 24 ++++++++++++++++++++++++ libsrc/common/fgetc.s | 13 ++++--------- libsrc/common/fputc.s | 10 ++-------- libsrc/common/fputs.s | 10 ++-------- 4 files changed, 32 insertions(+), 25 deletions(-) create mode 100644 libsrc/common/checkferror.s diff --git a/libsrc/common/checkferror.s b/libsrc/common/checkferror.s new file mode 100644 index 000000000..736fa3ccd --- /dev/null +++ b/libsrc/common/checkferror.s @@ -0,0 +1,24 @@ +; +; Colin Leroy-Mira, 2024 +; +; Helper to check for file opened, not eof, not ferror +; Expects file pointer in ptr1, +; Returns with Z flag set if everything is OK, +; Destroys A, X, Y, +; Sets file flags in A +; + + .export checkferror + .importzp ptr1 + + .include "_file.inc" + +checkferror: + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #(_FOPEN|_FERROR|_FEOF); Check for file open, error/eof + tay + txa + cpy #_FOPEN + rts diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s index 777696b7a..98e6bb2f7 100644 --- a/libsrc/common/fgetc.s +++ b/libsrc/common/fgetc.s @@ -5,7 +5,8 @@ ; .export _fgetc - .import _read, pusha0, pushax, popptr1, incsp2, returnFFFF + .import _read, checkferror + .import pusha0, pushax, popptr1, incsp2, returnFFFF .importzp ptr1 .include "stdio.inc" @@ -16,16 +17,10 @@ _fgetc: stx ptr1+1 jsr pushax ; Backup our ptr - ldy #_FILE::f_flags - lda (ptr1),y - tax - and #_FOPEN ; Check for file open - beq ret_eof - txa - and #(_FERROR|_FEOF); Check for error/eof + jsr checkferror bne ret_eof - txa + tax and #_FPUSHBACK ; Check for pushed back char beq do_read diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s index 4633f24f1..358723538 100644 --- a/libsrc/common/fputc.s +++ b/libsrc/common/fputc.s @@ -6,7 +6,7 @@ .export _fputc .importzp ptr1 - .import _write + .import _write, checkferror .import pushax, pusha0, popax, incsp2 .import pushptr1, popptr1, returnFFFF @@ -21,13 +21,7 @@ _fputc: sta c ; to return it anyway stx c+1 - ldy #_FILE::f_flags - lda (ptr1),y - tax - and #_FOPEN ; Check for file open - beq ret_eof - txa - and #(_FERROR|_FEOF); Check for error/eof + jsr checkferror bne ret_eof jsr pushptr1 ; Backup fp pointer diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s index 00415aef1..b79a4707f 100644 --- a/libsrc/common/fputs.s +++ b/libsrc/common/fputs.s @@ -6,7 +6,7 @@ .export _fputs .importzp ptr1, ptr2 - .import _write, _strlen + .import _write, _strlen, checkferror .import swapstk, pushax, returnFFFF .include "stdio.inc" @@ -16,13 +16,7 @@ _fputs: sta ptr1 stx ptr1+1 - ldy #_FILE::f_flags - lda (ptr1),y - tax - and #_FOPEN ; Check for file open - beq ret_eof - txa - and #(_FERROR|_FEOF); Check for error/eof + jsr checkferror bne ret_eof ; Push _write parameters From afd8ee627e4e71900f9cbacda20e010fa529684d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 30 Jan 2024 19:52:48 +0100 Subject: [PATCH 497/520] Remove useless branching code in fgets --- libsrc/common/fgets.s | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libsrc/common/fgets.s b/libsrc/common/fgets.s index 465658191..172ca10dd 100644 --- a/libsrc/common/fgets.s +++ b/libsrc/common/fgets.s @@ -90,9 +90,7 @@ read_loop: : cmp #$0A ; Stop at \n beq done - - clc - bcc read_loop + bne read_loop got_eof: lda didread From ce606bb19e293339d31d82888fe7659d11b64b9d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Wed, 31 Jan 2024 08:11:46 +0100 Subject: [PATCH 498/520] Fix tests... --- test/val/lib_runtime_aslax7.c | 4 ++-- test/val/lib_runtime_asrax7.c | 4 ++-- test/val/lib_runtime_shlax7.c | 4 ++-- test/val/lib_runtime_shrax7.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/val/lib_runtime_aslax7.c b/test/val/lib_runtime_aslax7.c index ea8f0b375..7e3b4796a 100644 --- a/test/val/lib_runtime_aslax7.c +++ b/test/val/lib_runtime_aslax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %d << 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != -32768); + } while (++ai != -32768); do { refc = ac << 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %d << 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != -128); + } while (++ac != -128); return 0; } diff --git a/test/val/lib_runtime_asrax7.c b/test/val/lib_runtime_asrax7.c index 942efea1d..3cdf2aab8 100644 --- a/test/val/lib_runtime_asrax7.c +++ b/test/val/lib_runtime_asrax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != -32768); + } while (++ai != -32768); do { refc = ac >> 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != -128); + } while (++ac != -128); return 0; } diff --git a/test/val/lib_runtime_shlax7.c b/test/val/lib_runtime_shlax7.c index 9a4869438..d2073ca3a 100644 --- a/test/val/lib_runtime_shlax7.c +++ b/test/val/lib_runtime_shlax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %u << 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != 0); + } while (++ai != 0); do { refc = ac << 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %u << 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != 0); + } while (++ac != 0); return 0; } diff --git a/test/val/lib_runtime_shrax7.c b/test/val/lib_runtime_shrax7.c index db7356d0e..d3bc0db73 100644 --- a/test/val/lib_runtime_shrax7.c +++ b/test/val/lib_runtime_shrax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != 0); + } while (++ai != 0); do { refc = ac >> 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != 0); + } while (++ac != 0); return 0; } From 1e300bf768b949498895062b157d71aa3ac9bbc1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 1 Feb 2024 20:13:05 +0100 Subject: [PATCH 499/520] Add test case for issue #2395 --- test/todo/bug2395.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/todo/bug2395.c diff --git a/test/todo/bug2395.c b/test/todo/bug2395.c new file mode 100644 index 000000000..4f4d2a6d0 --- /dev/null +++ b/test/todo/bug2395.c @@ -0,0 +1,51 @@ + +/* bug #2395: Bitwise operators with a boolean expression fail when optimized */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +unsigned char a, b; +unsigned char c = 199; +unsigned char d = 100; + +int main(void) { + int fails = 0; + + a = c ^ (d != 0); + b = c ^ 1; + + printf("%u ^ (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("XOR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c | (d != 0); + b = c | 1; + + printf("%u | (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("OR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c & (d != 0); + b = c & 1; + + printf("%u & (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("AND error: a %d instead of %d\n", a, b); + fails++; + } + printf("%d errors\n", fails); + +#ifdef __OPT__ + return fails; +#else + /* Force exit failure on non-optimised version, which works, + * otherwise it breaks the build + */ + return 1; +#endif +} From 96d55e3703de9a9b079cc7f4da8e6b5ed82fb6e5 Mon Sep 17 00:00:00 2001 From: acqn <acqn163@outlook.com> Date: Fri, 2 Feb 2024 19:00:33 +0800 Subject: [PATCH 500/520] Fixed optimization for char-size bitwise XOR/OR/AND when the rhs operand is complicated. --- src/cc65/coptstop.c | 6 +++--- test/{todo => val}/bug2395.c | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename test/{todo => val}/bug2395.c (100%) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 7e024ae88..402f16b97 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1488,7 +1488,7 @@ static const OptFuncDesc FuncTable[] = { }; static const OptFuncDesc FuncRegATable[] = { - { "tosandax", Opt_a_tosand, REG_NONE, OP_NONE }, + { "tosandax", Opt_a_tosand, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, @@ -1496,13 +1496,13 @@ static const OptFuncDesc FuncRegATable[] = { { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, - { "tosorax", Opt_a_tosor, REG_NONE, OP_NONE }, + { "tosorax", Opt_a_tosor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, - { "tosxorax", Opt_a_tosxor, REG_NONE, OP_NONE }, + { "tosxorax", Opt_a_tosxor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, }; #define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) diff --git a/test/todo/bug2395.c b/test/val/bug2395.c similarity index 100% rename from test/todo/bug2395.c rename to test/val/bug2395.c From 54b423a99efbfec9634d68cbddc596f13da89f7f Mon Sep 17 00:00:00 2001 From: Bob Andrews <mrdudz@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:13:57 +0100 Subject: [PATCH 501/520] fix test --- test/val/bug2395.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/val/bug2395.c b/test/val/bug2395.c index 4f4d2a6d0..07c5cd7c5 100644 --- a/test/val/bug2395.c +++ b/test/val/bug2395.c @@ -40,12 +40,5 @@ int main(void) { } printf("%d errors\n", fails); -#ifdef __OPT__ return fails; -#else - /* Force exit failure on non-optimised version, which works, - * otherwise it breaks the build - */ - return 1; -#endif } From c4c6967e4a557915707f549e01cc24205b68265b Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Fri, 2 Feb 2024 19:34:45 +0100 Subject: [PATCH 502/520] Enable Windows tests on pull requests --- .github/workflows/build-on-pull-request.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 045bc048d..6217c42a2 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -54,7 +54,7 @@ jobs: make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=x86_64-w64-mingw32- build_windows: - name: Build (Windows) + name: Build and Test (Windows) runs-on: windows-latest steps: @@ -79,4 +79,14 @@ jobs: - name: Build app (x64 release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=x64 + - name: Build utils (MinGW) + shell: cmd + run: make -j2 util SHELL=cmd + - name: Build the platform libraries (make lib) + shell: cmd + run: make -j2 lib QUIET=1 SHELL=cmd + + - name: Run the regression tests (make test) + shell: cmd + run: make test QUIET=1 SHELL=cmd From 1fab179cb467e13c581ec65f635658cb9acf3264 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Thu, 1 Feb 2024 20:05:57 +0100 Subject: [PATCH 503/520] a BIT of 65C02 optimisations Use BIT immediate instead of AND when reloading A is required afterwards. Add an fread unit test as the optimisation touches fread. Sprinkle a few zero page indexed while we're at it. --- libsrc/common/_printf.s | 29 +++++++++++++++-- libsrc/common/fgetc.s | 9 +++++- libsrc/common/fread.s | 32 +++++++++++++++++-- libsrc/common/fwrite.s | 15 ++++++--- test/ref/test_fread.c | 70 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 test/ref/test_fread.c diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index a0074583e..d7eeb072d 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -13,6 +13,7 @@ .import _strlower, _strlen .macpack generic + .macpack cpu ; ---------------------------------------------------------------------------- ; We will store variables into the register bank in the zeropage. Define @@ -37,7 +38,11 @@ FCount = ptr2 GetFormatChar: ldy #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (Format) + .else lda (Format),y + .endif IncFormatPtr: inc Format bne @L1 @@ -110,7 +115,11 @@ GetIntArg: lda (ArgList),y tax dey + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (ArgList) + .else lda (ArgList),y + .endif rts ; ---------------------------------------------------------------------------- @@ -135,9 +144,9 @@ ReadInt: pha ; Save digit value lda ptr1 ldx ptr1+1 - asl ptr1 + asl a rol ptr1+1 ; * 2 - asl ptr1 + asl a rol ptr1+1 ; * 4, assume carry clear adc ptr1 sta ptr1 @@ -265,10 +274,16 @@ Save: lda regbank,y ; Initialize the output counter in the output descriptor to zero lda #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (OutData) + ldy #$01 + sta (OutData),y + .else tay sta (OutData),y iny sta (OutData),y + .endif ; Get the output function from the output descriptor and remember it @@ -338,7 +353,11 @@ MainLoop: sta (sp),y dey lda FCount + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) + .else sta (sp),y + .endif jsr CallOutFunc ; Call the output function ; We're back from out(), or we didn't call it. Check for end of string. @@ -551,10 +570,16 @@ CheckCount: jsr GetIntArg sta ptr1 stx ptr1+1 ; Get user supplied pointer + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (OutData) ; Low byte of OutData->ccount + sta (ptr1) + ldy #1 + .else ldy #0 lda (OutData),y ; Low byte of OutData->ccount sta (ptr1),y iny + .endif lda (OutData),y ; High byte of OutData->ccount sta (ptr1),y jmp MainLoop ; Done diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s index 98e6bb2f7..34d4df3aa 100644 --- a/libsrc/common/fgetc.s +++ b/libsrc/common/fgetc.s @@ -12,6 +12,8 @@ .include "stdio.inc" .include "_file.inc" + .macpack cpu + _fgetc: sta ptr1 stx ptr1+1 @@ -20,11 +22,16 @@ _fgetc: jsr checkferror bne ret_eof + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FPUSHBACK ; Check for pushed back char + beq do_read + .else tax and #_FPUSHBACK ; Check for pushed back char beq do_read - txa + .endif + and #<(~_FPUSHBACK) ; Reset flag sta (ptr1),y diff --git a/libsrc/common/fread.s b/libsrc/common/fread.s index 647a7f2c8..b39b9d748 100644 --- a/libsrc/common/fread.s +++ b/libsrc/common/fread.s @@ -20,6 +20,7 @@ .include "_file.inc" .macpack generic + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -47,13 +48,21 @@ ldy #_FILE::f_flags lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN ; Is the file open? + .else and #_FOPEN ; Is the file open? + .endif beq @L1 ; Branch if no ; Check if the stream is in an error state + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FERROR + .else lda (file),y ; get file->f_flags again and #_FERROR + .endif beq @L2 ; File not open or in error state @@ -65,11 +74,19 @@ ; Remember if we have a pushed back character and reset the flag. -@L2: tax ; X = 0 +@L2: .if (.cpu .bitand ::CPU_ISET_65SC02) + ldx #$00 + bit #_FPUSHBACK + .else + tax ; X = 0 lda (file),y and #_FPUSHBACK + .endif beq @L3 + + .if (.not .cpu .bitand ::CPU_ISET_65SC02) lda (file),y + .endif and #<~_FPUSHBACK sta (file),y ; file->f_flags &= ~_FPUSHBACK; inx ; X = 1 @@ -118,12 +135,20 @@ ; Copy the buffer pointer into ptr1, and increment the pointer value passed ; to read() by one, so read() starts to store data at buf+1. + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) + sta ptr1 + add #1 + sta (sp) + ldy #1 + .else ldy #0 lda (sp),y sta ptr1 add #1 sta (sp),y iny + .endif lda (sp),y sta ptr1+1 adc #0 @@ -134,8 +159,12 @@ ldy #_FILE::f_pushback lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr1) ; *buf = file->f_pushback; + .else ldy #0 sta (ptr1),y ; *buf = file->f_pushback; + .endif ; Restore the low byte of count and decrement count by one. This may result ; in count being zero, so check for that. @@ -210,4 +239,3 @@ .bss save: .res 2 pb: .res 1 - diff --git a/libsrc/common/fwrite.s b/libsrc/common/fwrite.s index 861feb120..e7151da95 100644 --- a/libsrc/common/fwrite.s +++ b/libsrc/common/fwrite.s @@ -8,7 +8,7 @@ .export _fwrite .import _write - .import pushax, incsp6, addysp, ldaxysp, pushwysp, return0 + .import pushax, pusha0, incsp6, addysp, ldaxysp, pushwysp, return0 .import tosumulax, tosudivax .importzp ptr1 @@ -16,6 +16,7 @@ .include "errno.inc" .include "_file.inc" + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -33,7 +34,11 @@ ldy #_FILE::f_flags lda (ptr1),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN + .else and #_FOPEN ; Is the file open? + .endif bne @L2 ; Branch if yes ; File not open @@ -45,7 +50,9 @@ ; Check if the stream is in an error state -@L2: lda (ptr1),y ; get file->f_flags again +@L2: .if (.not .cpu .bitand ::CPU_ISET_65SC02) + lda (ptr1),y ; get file->f_flags again + .endif and #_FERROR bne @L1 @@ -53,8 +60,7 @@ ldy #_FILE::f_fd lda (ptr1),y - ldx #$00 - jsr pushax ; file->f_fd + jsr pusha0 ; file->f_fd ldy #9 jsr pushwysp ; buf @@ -123,4 +129,3 @@ .bss file: .res 2 - diff --git a/test/ref/test_fread.c b/test/ref/test_fread.c new file mode 100644 index 000000000..5d180d723 --- /dev/null +++ b/test/ref/test_fread.c @@ -0,0 +1,70 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + static int r; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fread(buf, 1, sizeof(buf), out) != NULL) { + printf("Error, could fread with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + unlink(outfile_path); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + /* Test that ungetc doesn't break fread */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + r = fread(buf, 1, sizeof(buf), out); + + if (r == 0) { + printf("Error: could not start reading.\n"); + } + fwrite(buf, 1, r, stdout); + + /* Finish reading file. */ + while ((r = fread(buf, 1, sizeof(buf), out)) != 0) + { + fwrite(buf, 1, r, stdout); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + } + + fclose(in); + return 0; +} From 934de685bcc6501631c51ccb96e9f499ecb4709c Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 3 Feb 2024 01:34:21 +0100 Subject: [PATCH 504/520] this is pr #2194 - removed ramfont.o --- cfg/kim1-mtu60k.cfg | 41 ++ cfg/kim1-mtuE000.cfg | 41 ++ samples/kim1/Makefile | 61 +- samples/kim1/font.rom | 0 samples/kim1/kimGFX.c | 290 ++++++++++ samples/kim1/kimLife.c | 144 +++++ samples/kim1/kimTest.c | 262 +++++++++ samples/kim1/ramfont.asm | 272 +++++++++ samples/kim1/subs.asm | 1140 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 2248 insertions(+), 3 deletions(-) create mode 100644 cfg/kim1-mtu60k.cfg create mode 100644 cfg/kim1-mtuE000.cfg create mode 100644 samples/kim1/font.rom create mode 100644 samples/kim1/kimGFX.c create mode 100644 samples/kim1/kimLife.c create mode 100644 samples/kim1/kimTest.c create mode 100644 samples/kim1/ramfont.asm create mode 100644 samples/kim1/subs.asm diff --git a/cfg/kim1-mtu60k.cfg b/cfg/kim1-mtu60k.cfg new file mode 100644 index 000000000..4f24a4bf4 --- /dev/null +++ b/cfg/kim1-mtu60k.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o <prog>.bin <prog>.o + +FEATURES { + STARTADDRESS: default = $2000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $E000 - %S - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/cfg/kim1-mtuE000.cfg b/cfg/kim1-mtuE000.cfg new file mode 100644 index 000000000..5f93cc13f --- /dev/null +++ b/cfg/kim1-mtuE000.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o <prog>.bin <prog>.o + +FEATURES { + STARTADDRESS: default = $E000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = $2000, size = $E000 - $2000 - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile index 74c415fdc..08bb2a780 100644 --- a/samples/kim1/Makefile +++ b/samples/kim1/Makefile @@ -31,9 +31,12 @@ else LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) endif -EXELIST_kim1 = \ - kimHello.bin \ - kimSieve.bin +EXELIST_kim1 = \ + kimHello.bin \ + kimSieve.bin \ + kimLife.bin \ + kimTest.bin \ + kimGFX.bin ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) @@ -50,13 +53,65 @@ else @echo > $(NULLDEV) endif +subs.o: subs.asm + $(AS) subs.asm -o subs.o + +ramfont.o: ramfont.asm + $(AS) ramfont.asm -o ramfont.o + +kimLife.bin: kimLife.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimLife.bin kimLife.c + +kimTest.bin: kimTest.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimTest.bin kimTest.c + +kimGFX.bin: kimGFX.c subs.o ramfont.o + $(CL) -t kim1 --listing kimGFX.lst -C kim1-mtuE000.cfg -o kimGFX.bin kimGFX.c subs.o ramfont.o -Ln kimgfx.lbl + kimSieve.bin: kimSieve.c $(CL) -t kim1 -C kim1-60k.cfg -O -o kimSieve.bin kimSieve.c kimHello.bin: kimHello.c $(CL) -t kim1 -O -o kimHello.bin kimHello.c +# To build an intel-format file for the CORSHAM SD card reader + +kimLife.hex: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.hex -Intel -address-length=2 + +kimTest.hex: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.hex -Intel -address-length=2 + +kimGFX.hex: kimGFX.bin ramfont.o + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.hex -Intel -address-length=2 + +# To build a paper tape file for uploading to the KIM-1 via terminal + +kimLife.ptp: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.ptp -MOS_Technologies + +kimGFX.ptp: kimGFX.bin + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.ptp -MOS_Technologies + +kimTest.ptp: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.ptp -MOS_Technologies + clean: @$(DEL) kimSieve.bin 2>$(NULLDEV) @$(DEL) kimHello.bin 2>$(NULLDEV) + @$(DEL) kimLife.bin 2>$(NULLDEV) + @$(DEL) kimLife.ptp 2>$(NULLDEV) + @$(DEL) kimLife.hex 2>$(NULLDEV) + @$(DEL) kimTest.bin 2>$(NULLDEV) + @$(DEL) kimTest.ptp 2>$(NULLDEV) + @$(DEL) kimTest.hex 2>$(NULLDEV) + @$(DEL) kimGFX.bin 2>$(NULLDEV) + @$(DEL) kimGFX.ptp 2>$(NULLDEV) + @$(DEL) kimGFX.hex 2>$(NULLDEV) + @$(DEL) kimgfx.lbl 2>$(NULLDEV) + @$(DEL) kimGFX.lst 2>$(NULLDEV) + @$(DEL) subs.o 2>$(NULLDEV) + @$(DEL) ramfont.o 2>$(NULLDEV) + + diff --git a/samples/kim1/font.rom b/samples/kim1/font.rom new file mode 100644 index 000000000..e69de29bb diff --git a/samples/kim1/kimGFX.c b/samples/kim1/kimGFX.c new file mode 100644 index 000000000..45daafa1e --- /dev/null +++ b/samples/kim1/kimGFX.c @@ -0,0 +1,290 @@ +// -------------------------------------------------------------------------- +// Simple Graphics Test for KIM-1 with MTU Visible Memory Board +// +// Assumes the MTU Visible Memory Board mapped at 0xA000 for 8K of video RAM +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include <stdio.h> // For printf +#include <stdlib.h> // For rand, srand +#include <string.h> // For memcpy +#include <ctype.h> + +typedef unsigned char byte; + +extern void ClearScreen(void); // In subs.asm +extern void ScrollScreen(void); +extern void DrawCircle(void); +extern void SetPixel(void); +extern void ClearPixel(void); +extern void DrawChar(void); +extern void Demo(void); +extern void __fastcall__ Delay(byte loops); +extern void __fastcall__ DrawLine(byte bSet); +extern byte __fastcall__ AscToPet(byte in); +extern byte __fastcall__ PetToAsc(byte in); +extern byte __fastcall__ ReverseBits(byte in); +extern void __fastcall__ CharOut(byte asci_char); +extern byte __fastcall__ getch(); +extern unsigned char font8x8_basic[256][8]; + +extern int x1cord; +extern int y1cord; +extern int x2cord; +extern int y2cord; +extern int cursorX; +extern int cursorY; + +// If in zeropage: +// +// #pragma zpsym("x1cord") +// #pragma zpsym("x2cord") +// #pragma zpsym("y1cord") +// #pragma zpsym("y2cord") + +// Screen memory is placed at A000-BFFF, 320x200 pixels, mapped right to left within each horizontal byte + +byte * screen = (byte *) 0xA000; + +// Cursor position + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define CHARWIDTH 8 +#define CHARHEIGHT 8 +#define BYTESPERROW (SCREEN_WIDTH / 8) +#define BYTESPERCHARROW (BYTESPERROW * 8) +#define CHARSPERROW (SCREEN_WIDTH / CHARWIDTH) +#define ROWSPERCOLUMN (SCREEN_HEIGHT / CHARHEIGHT) + +// SETPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Draws a pixel on the screen in white or black at pixel pos x, y + +void SETPIXEL(int x, int y, byte b) +{ + x1cord = x; + y1cord = y; + + if (b) + SetPixel(); + else + ClearPixel(); +} + +// DRAWPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Turns on a screen pixel at pixel pos x,y + +void DRAWPIXEL(int x, int y) +{ + x1cord = x; + y1cord = y; + SetPixel(); +} + +int c; + +void DrawText(char * psz) +{ + while (*psz) + { + while (cursorX >= CHARSPERROW) + { + cursorX -= CHARSPERROW; + cursorY += 1; + } + + // If we've gone off the bottom of the screen, we scroll the screen and back up to the last line again + + if (cursorY >= ROWSPERCOLUMN) + { + cursorY = ROWSPERCOLUMN - 1; + ScrollScreen(); + } + + // If we output a newline we advanced the cursor down one line and reset it to the left + + if (*psz == 0x0A) + { + cursorX = 0; + cursorY++; + psz++; + } + else + { + c = *psz; + + __asm__ ("ldx %v", cursorX); + __asm__ ("ldy %v", cursorY); + __asm__ ("lda %v", c); + DrawChar(); + cursorX++; + psz++; + } + } +} + +void DrawTextAt(int x, int y, char * psz) +{ + cursorX = x; + cursorY = y; + DrawText(psz); +} + +// Something like Bresenham's algorithm for drawing a line +/* +void DrawLine(int x0, int y0, int x1, int y1, byte val) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + while (1) + { + SETPIXEL(x0, y0, val); + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 > -dx) + { + err -= dy; + x0 += sx; + } + if (e2 < dy) + { + err += dx; + y0 += sy; + } + } +} +*/ + +// DrawCircle +// +// Draw a circle without sin, cos, or floating point! + +void DrawCircleC(int x0, int y0, int radius, byte) +{ + x1cord = x0; + y1cord = y0; + y2cord = radius; + DrawCircle(); +} + +void DrawLineC(int x1, int y1, int x2, int y2, byte bSet) +{ + x1cord = x1; + y1cord = y1; + x2cord = x2; + y2cord = y2; + DrawLine(bSet); +} + +// MirrorFont +// +// RAM font is backwards left-right relative to the way memory is laid out on the KIM-1, so we swap all the +// bytes in place by reversing the order of the bits in every byte + +void MirrorFont() +{ + int c; + byte * pb = (byte *) font8x8_basic; + + for (c = 0; c < 128 * 8; c++) + pb[c] = ReverseBits(pb[c]); +} + +// DrawScreenMoire +// +// Draws a moire pattern on the screen without clearing it first + +void DrawMoire(int left, int top, int right, int bottom, byte pixel) +{ + int x, y; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, pixel); + + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, pixel); +} + +void DrawScreenMoire(int left, int top, int right, int bottom) +{ + int x, y; + + DrawLineC(left, top, right, top, 1); + DrawLineC(left, bottom, right, bottom, 1); + DrawLineC(left, top, left, bottom, 1); + DrawLineC(right, top, right, bottom, 1); + + left++; top++; right--; bottom--; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 1); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 1); + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 0); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 0); + +} + +int main (void) +{ + + int i; + int c = 0; + + Demo(); + + CharOut('R'); + CharOut('E'); + CharOut('A'); + CharOut('D'); + CharOut('Y'); + CharOut('.'); + CharOut('\n'); + + + while(1) + { + c = toupper(getch()); + if (c != EOF) + CharOut(c); + } + + // Clear the screen memory + while(1) + { + Demo(); + DrawScreenMoire(0,30, 319, 199); + Delay(10); + + Demo(); + for (i = 5; i < 80; i+=5) + { + DrawCircleC(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH/4, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH*3/4, SCREEN_HEIGHT/2 + 20, i, 1); + } + + Delay(10); + + } + + printf("Done, exiting...\r\n"); + return 0; +} diff --git a/samples/kim1/kimLife.c b/samples/kim1/kimLife.c new file mode 100644 index 000000000..fb1696b9e --- /dev/null +++ b/samples/kim1/kimLife.c @@ -0,0 +1,144 @@ +// -------------------------------------------------------------------------- +// Conway's Game of Life for KIM-1 +// +// Assumes the MTU Visible Memory Board mapped at 0x8000 for 8K of video RAM +// +// Dave Plummer on a rainy Thursday +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include <stdio.h> // For printf +#include <stdlib.h> // For rand, srand +#include <string.h> // For memcpy + +typedef unsigned char byte; + +// World size + +#define WIDTH 320 +#define HEIGHT 200 +#define NUMBITS 64000 +#define NUMBYTES 8000 +#define DENSITY 50 + +// Screen memory is placed at 8000, our world copy at A000, and they use the same layout so +// that we can memcpy from one to the other without translating + +byte * world = (byte *) 0x8000; +byte * new_world = (byte *) 0xA000; + +// BITARRAY +// +// Access individual bits in a block of memory + +// Access to the screen bitmap + +byte GETBIT(byte *p, int n) +{ + return (p[n >> 3] & (1 << (n & 7))) ? 1 : 0; +} + +void SETBIT(byte *p, int n) +{ + p[n >> 3] |= (1 << (n & 7)); +} + +void CLRBIT(byte *p, int n) +{ + p[n >> 3] &= ~(1 << (n & 7)); +} + +void SETPIXEL(byte * p, int x, int y, byte b) +{ + if (b) + SETBIT(p, y * WIDTH + x); + else + CLRBIT(p, y * WIDTH + x); +} + +byte GETPIXEL(byte *p, int x, int y) +{ + return GETBIT(p, y * WIDTH + x); +} + +// RandomFillWorld +// +// Populates the initial world with random cells + +void RandomFillWorld() +{ + int x, y; + + // I need a better way to see the RNG or it'll be the same game every time! + srand(0); + for (x = 0; x < WIDTH; x++) + { + for (y = 0; y < HEIGHT; y++) + { + byte b = ((rand() % 100) < DENSITY) ? 1 : 0; + SETPIXEL(world, x, y, b); + } + } +} + +// CountNeighbors +// +// Count the number of live cells around the given spot, excluding the actual spot specified + +int CountNeighbors(int x, int y) +{ + int i, j, nx, ny, count = 0; + + for (j = -1; j <= 1; j++) + { + for (i = -1; i <= 1; i++) + { + if (i != 0 || j != 0) + { + nx = (x + i + WIDTH) % WIDTH; + ny = (y + j + HEIGHT) % HEIGHT; + count += GETPIXEL(world, nx, ny) ? 1 : 0; + } + } + } + return count; +} + +// UpdateWorld +// +// Applies the rules of Conway's Game of Life to the cells + +void UpdateWorld() +{ + int x, y; + + for (y = 0; y < HEIGHT; y++) + { + for (x = 0; x < WIDTH; x++) + { + int neighbors = CountNeighbors(x, y); + if (GETPIXEL(world, x, y)) + SETPIXEL(new_world, x, y, (neighbors == 2 || neighbors == 3)); + else + SETPIXEL(new_world, x, y, (neighbors == 3)); + } + } +} + +int main (void) +{ + printf("\r\nStarting Conway's Game of Life: Randomizing World...\r\n"); + RandomFillWorld(); + printf("World Ready, Running!\r\n"); + + for (;;) + { + UpdateWorld(); + printf("["); + memcpy(world, new_world, NUMBYTES); + printf("]"); + } + + return 0; +} diff --git a/samples/kim1/kimTest.c b/samples/kim1/kimTest.c new file mode 100644 index 000000000..273aeb584 --- /dev/null +++ b/samples/kim1/kimTest.c @@ -0,0 +1,262 @@ +// -------------------------------------------------------------------------- +// Diagnostics Test for KIM-1 +// +// Dave Plummer +// davepl@davepl.com +// +// Memory test examples by Michael Barr +// +// -------------------------------------------------------------------------- + +#include <stdio.h> // For printf +#include <stdlib.h> // For rand, srand +#include <string.h> // For memcpy + +typedef unsigned char byte; + +// RepeatChar +// +// Outputs a given character N times + +void RepeatChar(char c, size_t count) +{ + while (count--) + putc(c, stdout); +} + +/********************************************************************** + * + * Function: memTestDataBus() + * + * Description: Test the data bus wiring in a memory region by + * performing a walking 1's test at a fixed address + * within that region. The address (and hence the + * memory region) is selected by the caller. + * + * Returns: 0 if the test succeeds. + * A non-zero result is the first pattern that failed. + * + **********************************************************************/ + +byte memTestDataBus(volatile byte * address) +{ + byte pattern; + + // Perform a walking 1's test at the given address. + + for (pattern = 1; pattern != 0; pattern <<= 1) + { + // Write the test pattern. + *address = pattern; + + // Read it back and check it + if (*address != pattern) + { + printf("\r\nmemTestDataBus: FAILED at %04x with pattern %02x\r\n", address, pattern); + return (pattern); + } + } + + return (0); +} + +/********************************************************************** + * + * Function: memTestAddressBus() + * + * Description: Test the address bus wiring in a memory region by + * performing a walking 1's test on the relevant bits + * of the address and checking for aliasing. This test + * will find single-bit address failures such as stuck + * -high, stuck-low, and shorted pins. The base address + * and size of the region are selected by the caller. + * + * Notes: For best results, the selected base address should + * have enough LSB 0's to guarantee single address bit + * changes. For example, to test a 64-Kbyte region, + * select a base address on a 64-Kbyte boundary. Also, + * select the region size as a power-of-two--if at all + * possible. + * + * Returns: NULL if the test succeeds. + * A non-zero result is the first address at which an + * aliasing problem was uncovered. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestAddressBus(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long addressMask = (nBytes/sizeof(byte) - 1); + unsigned long offset; + unsigned long testOffset; + + byte pattern = (byte) 0xAAAAAAAA; + byte antipattern = (byte) 0x55555555; + + + //Write the default pattern at each of the power-of-two offsets. + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + baseAddress[offset] = pattern; + } + + // Check for address bits stuck high. + + testOffset = 0; + baseAddress[testOffset] = antipattern; + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[offset]); + } + if (offset % 1024 == 0) + printf("."); + } + + baseAddress[testOffset] = pattern; + + + // Check for address bits stuck low or shorted. + + for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1) + { + baseAddress[testOffset] = antipattern; + + if (baseAddress[0] != pattern) + { + return ((byte *) &baseAddress[testOffset]); + } + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if ((baseAddress[offset] != pattern) && (offset != testOffset)) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[testOffset]); + } + } + baseAddress[testOffset] = pattern; + } + return (NULL); +} + +/********************************************************************** + * + * Function: memTestDevice() + * + * Description: Test the integrity of a physical memory device by + * performing an increment/decrement test over the + * entire region. In the process every storage bit + * in the device is tested as a zero and a one. The + * base address and the size of the region are + * selected by the caller. + * + * Returns: NULL if the test succeeds. + * + * A non-zero result is the first address at which an + * incorrect value was read back. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestDevice(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long offset; + unsigned long nWords = nBytes / sizeof(byte); + + byte pattern; + byte antipattern; + + + // Fill memory with a known pattern. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + baseAddress[offset] = pattern; + + // Check each location and invert it for the second pass. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with pattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + + antipattern = ~pattern; + baseAddress[offset] = antipattern; + + } + + // Check each location for the inverted pattern and zero it. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + antipattern = ~pattern; + if (baseAddress[offset] != antipattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with antipattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + } + + return (NULL); +} + +// TestMemory +// +// Run all three memory tests + +byte TestMemory(byte * startAddress, unsigned long size) +{ + if ((memTestDataBus(startAddress) != 0) || + (memTestAddressBus(startAddress, size) != NULL) || + (memTestDevice(startAddress, size) != NULL)) + { + return (-1); + } + else + { + return (0); + } +} + +int main (void) +{ + printf("\r\nTesting KIM-1...\r\n"); + RepeatChar('-', 39); + + printf("\r\nTesting RIOT RAM: 1780-17BF\r\n"); + if (TestMemory((byte *)0x1780, 0x17BF - 0x1780)) + return 0; + + printf("\r\nTesting RIOT RAM: 17C0-17E6\r\n"); + if (TestMemory((byte *)0x17C0, 0x17E6 - 0x17C0)) + return 0; + + printf("\r\nTesting Memory: 0400-13FF\r\n"); + if (TestMemory((byte *)0x0400, 0x13FF - 0x0400)) + return 0; + + printf("\r\nTesting Memory: 4000-DFFF\r\n"); + if (TestMemory((byte *)0x4000, 0xDFFF - 0x4000)) + return 0; + + printf("\r\nPASS!\r\n"); + return 1; +} + + diff --git a/samples/kim1/ramfont.asm b/samples/kim1/ramfont.asm new file mode 100644 index 000000000..ac0b2c9cd --- /dev/null +++ b/samples/kim1/ramfont.asm @@ -0,0 +1,272 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: ramfont.s +; Magnetic OCR (check number style) Font data +; +;----------------------------------------------------------------------------------- + +.segment "CODE" +.export _font8x8_basic + +_font8x8_basic: + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 0 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 1 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 2 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 3 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 4 + .byte $7e, $40, $40, $7c, $60, $60, $7e, $00 ; PETSCII code 5 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 6 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 7 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 8 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 9 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 10 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 11 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 12 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 13 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 14 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 15 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 16 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 17 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 18 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 19 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 20 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 21 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 22 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 23 + .byte $42, $42, $66, $18, $66, $62, $62, $00 ; PETSCII code 24 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 25 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 26 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 27 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 28 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 29 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 30 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 31 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 32 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 33 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 34 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 35 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 36 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 37 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 38 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 39 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 40 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 41 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 42 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 43 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 44 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 45 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 46 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 47 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 48 + .byte $18, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 49 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 50 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 51 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 52 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 53 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 54 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 55 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 56 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 57 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 58 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 59 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 60 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 61 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 62 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 63 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 64 + .byte $08, $1c, $3e, $7f, $7f, $1c, $3e, $00 ; PETSCII code 65 + .byte $10, $10, $10, $10, $10, $10, $10, $10 ; PETSCII code 66 + .byte $00, $00, $00, $ff, $00, $00, $00, $00 ; PETSCII code 67 + .byte $00, $00, $ff, $00, $00, $00, $00, $00 ; PETSCII code 68 + .byte $00, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 69 + .byte $00, $00, $00, $00, $00, $ff, $00, $00 ; PETSCII code 70 + .byte $20, $20, $20, $20, $20, $20, $20, $20 ; PETSCII code 71 + .byte $04, $04, $04, $04, $04, $04, $04, $04 ; PETSCII code 72 + .byte $00, $00, $00, $00, $e0, $10, $08, $08 ; PETSCII code 73 + .byte $08, $08, $08, $04, $03, $00, $00, $00 ; PETSCII code 74 + .byte $08, $08, $08, $10, $e0, $00, $00, $00 ; PETSCII code 75 + .byte $80, $80, $80, $80, $80, $80, $80, $ff ; PETSCII code 76 + .byte $80, $40, $20, $10, $08, $04, $02, $01 ; PETSCII code 77 + .byte $01, $02, $04, $08, $10, $20, $40, $80 ; PETSCII code 78 + .byte $ff, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 79 + .byte $ff, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 80 + .byte $00, $3c, $7e, $7e, $7e, $7e, $3c, $00 ; PETSCII code 81 + .byte $00, $00, $00, $00, $00, $00, $ff, $00 ; PETSCII code 82 + .byte $36, $7f, $7f, $7f, $3e, $1c, $08, $00 ; PETSCII code 83 + .byte $40, $40, $40, $40, $40, $40, $40, $40 ; PETSCII code 84 + .byte $00, $00, $00, $00, $03, $04, $08, $08 ; PETSCII code 85 + .byte $81, $42, $24, $18, $18, $24, $42, $81 ; PETSCII code 86 + .byte $00, $3c, $42, $42, $42, $42, $3c, $00 ; PETSCII code 87 + .byte $08, $1c, $2a, $77, $2a, $08, $08, $00 ; PETSCII code 88 + .byte $02, $02, $02, $02, $02, $02, $02, $02 ; PETSCII code 89 + .byte $08, $1c, $3e, $7f, $3e, $1c, $08, $00 ; PETSCII code 90 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 91 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 92 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 93 + .byte $00, $00, $01, $3e, $54, $14, $14, $00 ; PETSCII code 94 + .byte $ff, $7f, $3f, $1f, $0f, $07, $03, $01 ; PETSCII code 95 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 96 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 97 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 98 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 99 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 100 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 101 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 102 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 103 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 104 + .byte $ff, $fe, $fc, $f8, $f0, $e0, $c0, $80 ; PETSCII code 105 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 106 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 107 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 108 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 109 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 110 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 111 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 112 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 113 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 114 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 115 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 116 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 117 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 118 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 119 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 120 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 121 + .byte $01, $01, $01, $01, $01, $01, $01, $ff ; PETSCII code 122 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 123 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 124 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 125 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 126 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 127 + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 128 + .byte $00, $00, $3c, $04, $7c, $64, $7c, $00 ; PETSCII code 129 + .byte $40, $40, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 130 + .byte $00, $00, $7e, $42, $60, $62, $7e, $00 ; PETSCII code 131 + .byte $02, $02, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 132 + .byte $00, $00, $7e, $42, $7e, $60, $7e, $00 ; PETSCII code 133 + .byte $1e, $12, $10, $7c, $18, $18, $18, $00 ; PETSCII code 134 + .byte $00, $00, $7e, $42, $62, $7e, $02, $7e ; PETSCII code 135 + .byte $40, $40, $7e, $42, $62, $62, $62, $00 ; PETSCII code 136 + .byte $18, $00, $10, $10, $18, $18, $18, $00 ; PETSCII code 137 + .byte $0c, $00, $08, $0c, $0c, $0c, $44, $7c ; PETSCII code 138 + .byte $40, $40, $44, $48, $78, $64, $64, $00 ; PETSCII code 139 + .byte $10, $10, $10, $10, $18, $18, $18, $00 ; PETSCII code 140 + .byte $00, $00, $7f, $49, $6d, $6d, $6d, $00 ; PETSCII code 141 + .byte $00, $00, $7e, $42, $62, $62, $62, $00 ; PETSCII code 142 + .byte $00, $00, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 143 + .byte $00, $00, $7e, $42, $62, $7e, $40, $40 ; PETSCII code 144 + .byte $00, $00, $7e, $42, $46, $7e, $02, $02 ; PETSCII code 145 + .byte $00, $00, $7e, $40, $60, $60, $60, $00 ; PETSCII code 146 + .byte $00, $00, $7e, $40, $7e, $06, $7e, $00 ; PETSCII code 147 + .byte $10, $10, $7c, $10, $18, $18, $18, $00 ; PETSCII code 148 + .byte $00, $00, $42, $42, $62, $62, $7e, $00 ; PETSCII code 149 + .byte $00, $00, $62, $62, $66, $24, $3c, $00 ; PETSCII code 150 + .byte $00, $00, $49, $49, $6d, $6d, $7f, $00 ; PETSCII code 151 + .byte $00, $00, $42, $42, $3c, $62, $62, $00 ; PETSCII code 152 + .byte $00, $00, $62, $62, $42, $7e, $02, $7e ; PETSCII code 153 + .byte $00, $00, $7e, $06, $18, $60, $7e, $00 ; PETSCII code 154 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 155 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 156 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 157 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 158 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 159 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 160 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 161 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 162 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 163 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 164 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 165 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 166 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 167 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 168 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 169 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 170 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 171 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 172 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 173 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 174 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 175 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 176 + .byte $38, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 177 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 178 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 179 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 180 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 181 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 182 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 183 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 184 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 185 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 186 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 187 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 188 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 189 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 190 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 191 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 192 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 193 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 194 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 195 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 196 + .byte $7e, $40, $40, $78, $60, $60, $7e, $00 ; PETSCII code 197 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 198 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 199 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 200 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 201 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 202 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 203 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 204 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 205 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 206 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 207 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 208 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 209 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 210 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 211 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 212 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 213 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 214 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 215 + .byte $42, $42, $66, $3c, $66, $62, $62, $00 ; PETSCII code 216 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 217 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 218 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 219 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 220 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 221 + .byte $cc, $cc, $33, $33, $cc, $cc, $33, $33 ; PETSCII code 222 + .byte $cc, $66, $33, $99, $cc, $66, $33, $99 ; PETSCII code 223 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 224 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 225 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 226 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 227 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 228 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 229 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 230 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 231 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 232 + .byte $99, $33, $66, $cc, $99, $33, $66, $cc ; PETSCII code 233 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 234 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 235 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 236 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 237 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 238 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 239 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 240 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 241 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 242 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 243 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 244 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 245 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 246 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 247 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 248 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 249 + .byte $01, $02, $44, $48, $50, $60, $40, $00 ; PETSCII code 250 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 251 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 252 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 253 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 254 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 255 diff --git a/samples/kim1/subs.asm b/samples/kim1/subs.asm new file mode 100644 index 000000000..5b525749e --- /dev/null +++ b/samples/kim1/subs.asm @@ -0,0 +1,1140 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: subs.asm Assembly language subroutines for KIMGFX +; +;----------------------------------------------------------------------------------- + + +.SETCPU "6502" + +.export _ClearScreen +.export _ScrollScreen +.export _SetPixel +.export _ClearPixel +.export _DrawCircle +.export _DrawLine +.export _AscToPet +.export _ReverseBits +.export _DrawChar +.export _CharOut +.export _Demo +.export _Delay +.export _getch + + +.import _font8x8_basic + +; This is the assumed location of the MTU visible memory board's 8K of memory. You can adjust this +; constant to refelct other locations as needed. + +SCREEN = $A000 + +; Note that even though these constants are defined here and respected, there are still going to be +; logic assumptions in GetPixelAddress that assume a 320x200 screen. If you change these, you'll +; need to adjust GetPixelAddress to match. + +SCREEN_WIDTH = 320 +SCREEN_HEIGHT = 200 +SCREEN_BYTES = SCREEN_WIDTH * SCREEN_HEIGHT / 8 +CHARWIDTH = 8 +CHARHEIGHT = 8 +BYTESPERROW = (SCREEN_WIDTH / 8) +BYTESPERCHARROW = (BYTESPERROW * 8) +CHARSPERROW = (SCREEN_WIDTH / CHARWIDTH) +ROWSPERCOLUMN = (SCREEN_HEIGHT / CHARHEIGHT) +LASTROW = SCREEN + SCREEN_BYTES - BYTESPERCHARROW + +.segment "ZEROPAGE" + +btpt: .res 1 + +dest: +dest_lo: .res 1 +dest_hi: .res 1 + +src: +src_lo: .res 1 +src_hi: .res 1 + +adp1: +adp1_lo: .res 1 +adp1_hi: .res 1 + +adp2: +adp2_lo: .res 1 +adp2_hi: .res 1 + +scroll_src: +scroll_src_lo: .res 1 +scroll_src_hi: .res 1 + +scroll_dest: +scroll_dest_lo: .res 1 +scroll_dest_hi: .res 1 + + +.segment "DATA" + +; Arguments for graphics functions + +_x1cord: .res 2 +_x2cord: .res 2 +_y1cord: .res 2 +_y2cord: .res 2 +_cursorX: .res 1 +_cursorY: .res 1 + +; Linedraw + +dx: .res 2 +dy: .res 2 +e2: .res 2 +sx: .res 1 +sy: .res 1 +dltemp: .res 2 +pixel: .res 1 + +; DrawCircle + +xval: .res 2 ; These could move to zeropage for perf, but presume we +yval: .res 2 ; we want to minimize the amount we grow zero page use +err: .res 2 +temp: .res 2 +tempa: .res 1 +tempx: .res 1 +tempy: .res 1 +temp2: .res 2 +x0: .res 2 +y0: .res 2 + +; CharOut + +tempstr: .res 2 + +.export _x1cord ; Make sure these show up on the C side as zero page +.export _x2cord +.export _y1cord +.export _y2cord +.export _cursorX +.export _cursorY + +.segment "CODE" + +;----------------------------------------------------------------------------------- +; GetPixelAddress - Calculate the address of a pixel in the video memory +;----------------------------------------------------------------------------------- +; Based on MTU PIXADR code +;----------------------------------------------------------------------------------- +; In: _x1cord (16-bit) +; _y1cord (16-bit) +; Out: adp1 (16-bit) Address of pixel to set +;----------------------------------------------------------------------------------- + +_GetPixelAddress: + lda _x1cord ; compute bit address first + sta adp1 ; also transfer x1cord to adp1 + and #$07 ; + which is simply the low 3 bits of x + sta btpt + lda _x1cord+1 ; finish transferring x1cord to adp1 + sta adp1+1 + lsr adp1+1 ; double shift adp1 right 3 to get + ror adp1 ; int(xcord/8 ) + lsr adp1+1 + ror adp1 + lsr adp1+1 + ror adp1 + sec ; and temporary storage + lda _y1cord + sta adp2 + sta temp + lda #0 + sbc _y1cord+1 + sta adp2+1 + sta temp+1 + asl adp2 ; compute 40*(y1cord) + rol adp2+1 ; 2*(y1cord) + asl adp2 + rol adp2+1 ; 4*(y1cord) + lda adp2 ; add in temporary save of (y1cord) + clc ; to make 5*(y1cord) + adc temp + sta adp2 + lda adp2+1 + adc temp+1 + sta adp2+1 ; 5*(y1cord) + asl adp2 ; 10*(1cord) + rol adp2+1 + asl adp2 ; 20#(y1cord) + rol adp2+1 + asl adp2 ; 40*(y1cord) + rol adp2+1 + lda adp2 ; add in int(x1cord/8) computed earlier + clc + adc adp1 + sta adp1 + lda adp2+1 + adc adp1+1 + adc #>SCREEN ; add in vmorg*256 + sta adp1+1 ; final result + rts ; return + +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +; +; MSKTB1 is a table of 1 bits corresponding to bit numbers +; MSKTB2 is a table of 0 bits corresponding to bit numbers +;----------------------------------------------------------------------------------- + +msktb1: .byte $80,$40,$20,$10,$08,$04,$02,$01 +msktb2: .byte $7F,$BF,$DF,$EF,$F7,$FB,$FD,$FE + +_Delay: pha + sta temp + txa + pha + tya + pha + +@loopa: ldx #$ff +@loopx: ldy #$ff +@loopy: dey + bne @loopy + dex + bne @loopx + dec temp + bne @loopa + + pla + tay + pla + tax + pla + rts + +;----------------------------------------------------------------------------------- +; SetPixel - Set a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +;----------------------------------------------------------------------------------- + +_SetPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb1,y ; get a byte with that bit =1, others =0 + ldy #0 + ora (adp1),y ; combine the bit with the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearPixel - Clears a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- + +_ClearPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb2,y ; get a byte with that bit =0, others =1 + ldy #0 + and (adp1),y ; remove the bit from the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearScreen - Clears the entire video memory (and thus the screen) +;----------------------------------------------------------------------------------- + +_ClearScreen: + lda #$00 + + ldx #<SCREEN + stx dest_lo + ldx #>SCREEN + stx dest_hi + + ldy #0 +: sta (dest), y ; Loop unwound by a factor of 8, which means our iny before the branchh + iny ; will still work as it's on a page crossing boundary. + sta (dest), y ; This will avoid most of the overhead of the branch. + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + bne :- + + inc dest_hi + ldx dest_hi + cpx #>SCREEN + $20 + bne :- + + rts + +;----------------------------------------------------------------------------------- +; ScrollScreen - Scrolls the entire video memory (and thus the screen) up one row +;----------------------------------------------------------------------------------- + +BYTES_TO_MOVE = SCREEN_BYTES - BYTESPERCHARROW +PAGES_TO_MOVE = BYTES_TO_MOVE / 256 + +_ScrollScreen: + pha + tya + pha + txa + pha + + ; Load the source (A140) and destination (A000) addresses + lda #<(SCREEN+BYTESPERCHARROW) + sta scroll_src_lo + lda #>(SCREEN+BYTESPERCHARROW) + sta scroll_src_hi + lda #<SCREEN + sta scroll_dest_lo + lda #>SCREEN + sta scroll_dest_hi + + ldx #PAGES_TO_MOVE +@outerLoop: + ldy #0 +@innerLoop: ; + ; I could do this faster in self-modifying code (avoiding the zero page overhead) but then it + ; couldn't go into ROM + + lda (scroll_src),y ; I've unwound the loop to do 8 bytes at a time. Since we're doing full pages + sta (scroll_dest),y ; as long as we unwind the loop to do 8 bytes at a time, we know we'll still + iny ; do the final increment on a page boundary. + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + bne @innerLoop ; If Y overflows, it will be 0, so won't branch + inc scroll_src_hi + inc scroll_dest_hi + dex + bne @outerLoop + + ; Clear the last line + lda #<LASTROW + sta scroll_dest_lo + lda #>LASTROW + sta scroll_dest_hi + lda #$00 + ldy #0 +fullPageLoop: + sta (scroll_dest_lo),y + iny + bne fullPageLoop + inc scroll_dest_hi +partialPageLoop: + sta (scroll_dest_lo),y + iny + cpy #BYTESPERCHARROW - 256 ; Only clear up to the 64th byte (256 + 64 == 320) + bne partialPageLoop + + pla + tax + pla + tay + pla + rts + +;----------------------------------------------------------------------------------- +; DrawCircle - Draws a circle in video memory of a given radius at a given coord +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +; radius - _y2cord (16-bit) +;----------------------------------------------------------------------------------- +; Implements the midpoint circle algorithm without floating point or trig functions +;----------------------------------------------------------------------------------- +; int x = radius; +; int y = 0; +; int err = 0; +; +; while (x >= y) +; { +; SETPIXEL(x0 + x, y0 + y, val); +; SETPIXEL(x0 + y, y0 + x, val); +; SETPIXEL(x0 - y, y0 + x, val); +; SETPIXEL(x0 - x, y0 + y, val); +; SETPIXEL(x0 - x, y0 - y, val); +; SETPIXEL(x0 - y, y0 - x, val); +; SETPIXEL(x0 + y, y0 - x, val); +; SETPIXEL(x0 + x, y0 - y, val); +; +; y++; +; err += 1 + 2 * y; +; if (2 * (err - x) + 1 > 0) { +; x--; +; err += 1 - 2 * x; +; } +; } +;----------------------------------------------------------------------------------- + +_DrawCircle: lda _x1cord ; x0 = _x1cord + sta x0 + lda _x1cord+1 + sta x0+1 + lda _y1cord ; y0 = _y1cord + sta y0 + lda _y1cord+1 + sta y0+1 + + lda _y2cord ; x = radius + sta xval + lda _y2cord+1 + sta xval+1 + + lda #$0 ; yval = 0; + sta yval + sta yval+1 + sta err ; err = 0; + sta err+1 +circleloop: + lda xval+1 ; if (xval < yval) we're done; + sec + cmp yval+1 + bcc doneCircle ; if high byteof yval is greater, we can draw + bne doCircle ; it not greater and not equal, is less, so done + lda xval ; in other cases we need to compare the LSB next + cmp yval + bcs doCircle ; if it's less, but MSB was equal, we go draw + +doneCircle: rts + +doCircle: lda x0 ; Draw the first of 8 symmetric quadrant copies + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 - x, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 - x, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 - y, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 + y, val); + + lda x0 + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 + x, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 + y, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 - y, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 + x, val); + + inc yval ; yval++; + bne :+ + inc yval+1 + +: lda yval ; temp = 2 * yval + 1; + asl + sta temp + lda yval+1 + rol + sta temp+1 + inc temp + bne :+ + inc temp+1 +: + lda err ; err += temp + clc + adc temp + sta err + lda err+1 + adc temp+1 + sta err+1 + ; if (2 * (err - xval) + 1 > 0) then dec xval + lda err ; temp = err-xval + sec + sbc xval + sta temp + lda err+1 + sbc xval+1 + sta temp+1 + + asl temp ; temp = 2*(err-xval)+1 + rol temp+1 + inc temp + bne :+ + inc temp+1 +: + lda temp+1 ; if (temp > 0) we'll dec xval + bmi doneLoop ; less than zero, so no dec + bne decxval ; if not zero, go ahead and dec + + lda temp ; MSB is zero so now check the LSB + beq doneLoop ; both bytes are zero, so no dec + +decxval: lda xval ; xval-- + bne :+ + dec xval+1 +: dec xval + +updateerr: lda xval ; temp = xval * 2 + asl + sta temp + lda xval+1 + rol + sta temp+1 + + lda #1 ; temp2 == 1-temp == 1-(xval*2) + sec + sbc temp + sta temp2 + lda #0 + sbc temp+1 + sta temp2+1 + + lda err ; err += 1-(xval*2) + clc + adc temp2 + sta err + lda err+1 + adc temp2+1 + sta err+1 + +doneLoop: jmp circleloop + +;----------------------------------------------------------------------------------- +; Character set translation tables +;----------------------------------------------------------------------------------- + +ascToPetTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$20,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f + .byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f + .byte $e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef + .byte $f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff + +; PETSCI to Ascii lookup table - not current used, so commented out, but can be used to map fonts +; +; + petToAscTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$09,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f + .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + +;----------------------------------------------------------------------------------- +; PetToAsc - Convert a PETSCII character to ASCII +;----------------------------------------------------------------------------------- +; A - Character to convert +;----------------------------------------------------------------------------------- + +_AscToPet: tay + lda ascToPetTable, y + rts + +_PetToAsc: tay + lda petToAscTable, Y + rts + +;----------------------------------------------------------------------------------- +; ReverseBits - Reverse the bits in a byte +;----------------------------------------------------------------------------------- +; A = octet to be reversed +;----------------------------------------------------------------------------------- + +_ReverseBits: + ldx #8 ; set counter to 8 (for 8 bits) + lda #0 ; initialize A to 0 + sta temp ; clear result byte (accumulator for the reversed octet) +: asl ; shift leftmost bit of input into carry + lda temp ; load the temporary result byte into A + ror ; rotate carry into leftmost bit of result + sta temp ; store the updated result back into memory + dex ; decrement counter + bne :- ; repeat until all bits are processed + lda temp ; load the final reversed byte into A + rts ; return with result in A + +;----------------------------------------------------------------------------------- +; LoadFont - Makes sure the font data is ready to use. This usually requires +; reversing the bits so that they match the bit order of the screen +;----------------------------------------------------------------------------------- + +_LoadFont: ldx #3 + lda #<_font8x8_basic + sta adp1_lo + lda #>_font8x8_basic + sta adp1_hi + ldy #0 +@loop: lda (adp1), y + jsr _ReverseBits + sta (adp1), y + iny + bne @loop + + inc adp1_lo + bne :+ + inc adp1_hi +: dex + bne @loop + rts + +ScreenLineAddresses: + .word SCREEN + 0 * BYTESPERCHARROW, SCREEN + 1 * BYTESPERCHARROW + .word SCREEN + 2 * BYTESPERCHARROW, SCREEN + 3 * BYTESPERCHARROW + .word SCREEN + 4 * BYTESPERCHARROW, SCREEN + 5 * BYTESPERCHARROW + .word SCREEN + 6 * BYTESPERCHARROW, SCREEN + 7 * BYTESPERCHARROW + .word SCREEN + 8 * BYTESPERCHARROW, SCREEN + 9 * BYTESPERCHARROW + .word SCREEN + 10 * BYTESPERCHARROW, SCREEN + 11 * BYTESPERCHARROW + .word SCREEN + 12 * BYTESPERCHARROW, SCREEN + 13 * BYTESPERCHARROW + .word SCREEN + 14 * BYTESPERCHARROW, SCREEN + 15 * BYTESPERCHARROW + .word SCREEN + 16 * BYTESPERCHARROW, SCREEN + 17 * BYTESPERCHARROW + .word SCREEN + 18 * BYTESPERCHARROW, SCREEN + 19 * BYTESPERCHARROW + .word SCREEN + 20 * BYTESPERCHARROW, SCREEN + 21 * BYTESPERCHARROW + .word SCREEN + 22 * BYTESPERCHARROW, SCREEN + 23 * BYTESPERCHARROW + .word SCREEN + 24 * BYTESPERCHARROW + .assert( (* - ScreenLineAddresses) = ROWSPERCOLUMN * 2), error + +;----------------------------------------------------------------------------------- +; DrawChar - Draws an ASCII character at char location x, y +;----------------------------------------------------------------------------------- +; 0 <= x < 40 +; 0 <= y < 25 +; Preserves all registers, but its not very threadsafe or reentrant +;----------------------------------------------------------------------------------- + +_DrawChar: sty tempy + stx tempx + sta tempa + + tya ; Get the address in screen memory where this + asl ; character X/Y cursor pos should be drawn + tay + txa + clc + adc ScreenLineAddresses, y + sta dest_lo + lda ScreenLineAddresses+1, y + adc #0 + sta dest_hi + + lda #0 ; Get the address in font memory where this + sta src_hi ; Petscii chracter lives (after conversion from + lda tempa ; ascii) + + sty temp2 + jsr _AscToPet + ldy temp2 + + asl + rol src_hi + asl + rol src_hi + asl + rol src_hi + clc + adc #<_font8x8_basic ; Add the base address of the font table to the offset + sta src_lo + lda src_hi + adc #>_font8x8_basic + sta src_hi + + ldy #0 ; opy the character def to the screen, one byte at a time + ldx #0 +: lda (src), y ; Copy this byte from the character def to the screen target + sta (dest, x) + lda dest_lo ; Advance to the next "scanline", or pixel row, down + clc + adc #<BYTESPERROW + sta dest_lo + lda dest_hi + adc #>BYTESPERROW + sta dest_hi + + iny + cpy #8 + bne :- + + ldy tempy + ldx tempx + lda tempa + rts + +;----------------------------------------------------------------------------------- +; CursorOn - Turns on the text cursor and draws it at the current cursor pos +;----------------------------------------------------------------------------------- + +CursorOn: ldx _cursorX + ldy _cursorY + lda #'_' + jsr _DrawChar + rts + +CursorOff: ldx _cursorX + ldy _cursorY + lda #' ' + jsr _DrawChar + rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII char in A at the current cursor pos, saves all regs +;----------------------------------------------------------------------------------- + +_CharOut: sta temp + lda #0 + sta temp+1 + txa + pha + tya + pha + + ldx #<temp + ldy #>temp + jsr _DrawText + + pla + tay + pla + tax + rts + +;----------------------------------------------------------------------------------- +; Backspace - Erase the current character and move back one position. Does not +; move back up to previous line +;----------------------------------------------------------------------------------- + +Backspace: lda _cursorX + beq colzero + jsr CursorOff + dec _cursorX + jsr CursorOn +colzero: rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII string at the current cursor position +;----------------------------------------------------------------------------------- +; XY - Pointer to the string to draw, stops on NUL or 255 chars later +;----------------------------------------------------------------------------------- + +_DrawText: stx adp1_lo + sty adp1_hi + jsr CursorOff + + ldy #0 +checkHWrap: lda _cursorX + cmp #CHARSPERROW + bcc checkVWrap + lda #0 + sta _cursorX + inc _cursorY + +checkVWrap: lda _cursorY + cmp #ROWSPERCOLUMN + bcc loadChar + jsr _ScrollScreen + lda #ROWSPERCOLUMN-1 + sta _cursorY + +loadChar: lda (adp1), y + beq doneText + + cmp #$0a + bne :+ + + lda #0 ; Back to the left edge + sta _cursorX + inc _cursorY ; Advance to the next line + iny + bne checkHWrap + +: sty temp + ldx _cursorX + ldy _cursorY + jsr _DrawChar + ldy temp + inc _cursorX + iny + bne checkHWrap + +doneText: jsr CursorOn + rts + +demoText1: .byte " *** COMMODORE KIM-1 SHELL V0.1 ***", $0A, $0A + .byte " 60K RAM SYSTEM. 49152 BYTES FREE.", $0A, $0A, $00 +readyText: .byte $0A,"READY.", $0A, 00 + +_Demo: jsr _ClearScreen + lda #0 + sta _cursorX + sta _cursorY + ldx #<demoText1 + ldy #>demoText1 + jsr _DrawText + rts + +_Ready: ldx #<readyText + ldy #>readyText + jsr _DrawText + rts + + +;----------------------------------------------------------------------------------- +; DrawLine - Draws a line between two points +;----------------------------------------------------------------------------------- +; _x1cord (16-bit) +; _y1cord ( 8-bit) +; _x2cord (16-bit) +; _y2cord ( 8-bit) +;----------------------------------------------------------------------------------- +; Implements something like Bresenham's algorithm for drawing a line: +;----------------------------------------------------------------------------------- +; void DrawLine(int x0, int y0, int x1, int y1, byte val) +; { +; int dx = abs(_x2cord - _x1cord), sx = _x1cord < _x2cord ? 1 : -1; +; int dy = abs(_y2cord - _y1cord), sy = _y1cord < _y2cord ? 1 : -1; +; int err = (dx > dy ? dx : -dy) / 2, e2; +; +; while (1) +; { +; SETPIXEL(_x1cord, _y1cord, val); +; +; if (_x1cord == _x2cord && _y1cord == _y2cord) +; break; +; +; e2 = err; +; +; if (e2 > -dx) +; { +; err -= dy; +; _x1cord += sx; +; } +; if (e2 < dy) +; { +; err += dx; +; _y1cord += sy; +; } +; } +; } +;----------------------------------------------------------------------------------- + +_DrawLine: sta pixel + + ldx #$01 ; positive x-step for now + stx sx + + ; Calculate dx = (x2cord - X1cord) and see if its positive or not + + lda _x2cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x1cord + sta dx + lda _x2cord+1 + sbc _x1cord+1 + sta dx+1 + bpl calcdy ; dx is positive (dx >= 0), so we're good + + ; dx was negative (dx < 0), so we set sx to -1 and get the absolute + ; value by subtracting the other direction + + ldx #$FF ; negative x-step + stx sx + lda _x1cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x2cord + sta dx + lda _x1cord+1 + sbc _x2cord+1 + sta dx+1 + + ; Calculate dy = (y2cord - y1cord) and see if its positive or not + +calcdy: ldx #$01 ; positive y-step for now + stx sy + lda _y2cord + sec + sbc _y1cord + sta dy + bcs positivedy ; If y2cord > y1cord, then dy is positive and we're good + + ; dy was negative (dy < 0), so we set sy to -1 and get the absolute value + + ldx #$FF ; negative y-step + stx sy + lda _y1cord + sec + sbc _y2cord + sta dy + + ; Now we have dx and dy, so we can calculate err, but first we need + ; to see if dx > dy or not + +positivedy: lda dx+1 ; Check if dx > dy (both are always positive now) + bne dxgt ; If MSB of dx is greater than zero, then dx > dy since dy is 8-bits + lda dy + cmp dx + bcs dygte + +dxgt: lda dx ; We found dx>dy so set err = dx / 2 + sta err + lda dx+1 + lsr + sta err+1 ; err = dx/2 + ror err + jmp loop + +dygte: lda #0 ; we found dx <= dy so set err = -dy / 2 + sec + sbc dy ; else err = -dy / 2 + ror + ora #$80 + sta err + lda #$FF + sta err+1 + + ; Now we have dx, dy, and err, so we can start drawing pixels + +loop: lda pixel + beq clearpixel + jsr _SetPixel ; Plot the current _x1cord, _y1cord + jmp next +clearpixel: jsr _ClearPixel ; Clear the current _x1cord, _y1cord + +next: lda _x1cord ; if (_x1cord == _x2cord && _y1cord == _y2cord) then we rts + cmp _x2cord + bne noteq + lda _y1cord + cmp _y2cord + bne noteq + lda _x1cord+1 + cmp _x2cord+1 + bne noteq + + rts + +noteq: lda err ; e2 = err + sta e2 + lda err+1 + sta e2+1 + + ; Check the two update conditions for x and y, and update if needed + + lda e2 ; if (e2 > -dx) is the same as if (e2 + dx > 0), so we test that because its easier + clc ; If its true then we dec err and inc _x1cord + adc dx + sta temp + lda e2+1 + adc dx+1 + bmi doneupdatex ; If result is negative, then e2 + dx < 0, so we don't dec err or inc _x1cord + bne stepx ; If MSB is non-zero, then e2 + dx > 0, so we DO dec err and inc _x1cord + lda temp ; If result is zero in MSB, then we check the LSB here + beq doneupdatex ; If LSB is zero, then we don't dec err or inc _x1cord + ; We already know e2 + dx > 0, so LSB can't be negative +stepx: lda sx + bmi decx +incxval: inc _x1cord ; _x1cord += 1 because sx == 1 + bne updatexerr + inc _x1cord+1 + jmp updatexerr + +decx: lda _x1cord ; _x1cord += 1 because sx == 1 + sec + sbc #1 + sta _x1cord + lda _x1cord+1 + sbc #0 + sta _x1cord+1 + +updatexerr: lda err ; err -= dy + sec + sbc dy + sta err + lda err+1 + sbc #0 + sta err+1 + +doneupdatex: lda e2+1 ; if (e2 < dy) then we inc err and inc _y1cord + bmi updateerry ; If MSB is negative, then e2 < dy, so we inc err and inc _y1cord + bne noupdatey ; If the MSB of e2 is set and positive, then we know e2 > dy, so we don't inc err or inc _y1cord + lda e2 + sec + sbc dy + beq noupdatey ; e2 - dy == 0 so we don't inc err or inc _y1cord + bcs noupdatey ; if e2 was large enough that carry never cleared, then e2 > dy do no update + +updateerry: lda err ; err += dx + clc + adc dx + sta err + lda err+1 + adc dx+1 + sta err+1 + +stepy: lda _y1cord + clc + adc sy + sta _y1cord + +noupdatey: jmp loop + +_getch: jsr $1E5A ; Get character using Monitor ROM call + and #$7F ; Clear top bit + cmp #$0D ; Check for '\r' + bne gotch ; ...if CR character + lda #$0A ; Replace with '\n' +gotch: rts From 5acfb027941d3a8adac538078b7ccc587775af60 Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:20:17 +0100 Subject: [PATCH 505/520] update actions/checkout@v3 -> actions/checkout@v4 and microsoft/setup-msbuild@v1.1 -> microsoft/setup-msbuild@v2. lets see what happens :) --- .github/workflows/build-on-pull-request.yml | 6 +++--- .github/workflows/snapshot-on-push-master.yml | 8 ++++---- .github/workflows/windows-test-scheduled.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 6217c42a2..1064776cf 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -19,7 +19,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -62,10 +62,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (x86 debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=Win32 diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 571947c9b..f66b1a745 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -18,10 +18,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug @@ -44,7 +44,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -97,7 +97,7 @@ jobs: path: cc65-snapshot-win64.zip - name: Get the online documents repo. - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: cc65/doc # this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065 diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 451b37f79..25f0d3e50 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -43,11 +43,11 @@ jobs: - name: Checkout source if: steps.check-sha.outputs.cache-hit != 'true' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH if: steps.check-sha.outputs.cache-hit != 'true' - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (MSVC debug) if: steps.check-sha.outputs.cache-hit != 'true' From 3dfe0330003f19680f2afc9a607bea397b5c9fec Mon Sep 17 00:00:00 2001 From: mrdudz <mrdudz@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:02:08 +0100 Subject: [PATCH 506/520] update actions/upload-artifact@v3->actions/upload-artifact@v4, actions/cache@v3->actions/cache@v4 --- .github/workflows/build-on-pull-request.yml | 2 +- .github/workflows/snapshot-on-push-master.yml | 6 +++--- .github/workflows/windows-test-scheduled.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 1064776cf..146964a30 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -44,7 +44,7 @@ jobs: shell: bash run: make -j2 doc - name: Upload a documents snapshot. - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: ./html diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index f66b1a745..d58bff8ed 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -86,12 +86,12 @@ jobs: mv cc65.zip cc65-snapshot-win32.zip - name: Upload a 32-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win32 path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win64 path: cc65-snapshot-win64.zip @@ -120,7 +120,7 @@ jobs: - name: Package offline documents. run: 7z a cc65-snapshot-docs.zip ./html/*.* - name: Upload a Documents Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-docs path: cc65-snapshot-docs.zip diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 25f0d3e50..f72254273 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -30,7 +30,7 @@ jobs: run: mkdir ~/.cache-sha - name: Cache SHA - uses: actions/cache@v3 + uses: actions/cache@v4 id: check-sha with: path: ~/.cache-sha From 788ae82d30bcf29da0e160cd47532594780fe22b Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Fri, 9 Feb 2024 00:09:16 +0000 Subject: [PATCH 507/520] Fixes to serial driver implementation --- asminc/lynx.inc | 34 +++++++------- libsrc/lynx/crt0.s | 2 +- libsrc/lynx/ser/lynx-comlynx.s | 81 ++++++++++++++++++++-------------- 3 files changed, 67 insertions(+), 50 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index 5ae17f6ef..d65b7f8a9 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -267,22 +267,26 @@ MIKEYHREV = $FD88 MIKEYSREV = $FD89 IODIR = $FD8A IODAT = $FD8B -TxIntEnable = %10000000 -RxIntEnable = %01000000 -TxParEnable = %00010000 -ResetErr = %00001000 -TxOpenColl = %00000100 -TxBreak = %00000010 -ParEven = %00000001 -TxReady = %10000000 -RxReady = %01000000 -TxEmpty = %00100000 -RxParityErr = %00010000 -RxOverrun = %00001000 -RxFrameErr = %00000100 -RxBreak = %00000010 -ParityBit = %00000001 + SERCTL = $FD8C +; SERCTL bit definitions for write operations +TXINTEN = $80 +RXINTEN = $40 +PAREN = $10 +RESETERR = $08 +TXOPEN = $04 +TXBRK = $02 +PAREVEN = $01 +; SERCTL bit definitions for read operations +TXRDY = $80 +RXRDY = $40 +TXEMPTY = $20 +PARERR = $10 +OVERRUN = $08 +FRAMERR = $04 +RXBRK = $02 +PARBIT = $01 + SERDAT = $FD8D SDONEACK = $FD90 CPUSLEEP = $FD91 diff --git a/libsrc/lynx/crt0.s b/libsrc/lynx/crt0.s index 238a2c99d..030f523e9 100644 --- a/libsrc/lynx/crt0.s +++ b/libsrc/lynx/crt0.s @@ -68,7 +68,7 @@ MikeyInitData: .byte $9e,$18,$68,$1f,$00,$00,$00,$00,$00,$ff,$1a,$1b,$04,$0d,$2 ; Disable the TX/RX IRQ; set to 8E1. - lda #%00011101 + lda #PAREN|RESETERR|TXOPEN|PAREVEN ; #%00011101 sta SERCTL ; Clear all pending interrupts. diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 8aa3c838e..85703867b 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -73,7 +73,12 @@ SER_UNINSTALL: ; Must return an SER_ERR_xx code in a/x. SER_CLOSE: - ; Disable interrupts + ; Disable interrupts and stop timer 4 (serial) + lda #$0C ; TXOPEN|RESETERR + sta SERCTL + lda #$00 ; Disable count and no reload + sta TIM4CTLA + ; Done, return an error code lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -108,7 +113,7 @@ SER_OPEN: stz TxPtrIn stz TxPtrOut - ; clock = 8 * 15625 + ; source period is 1 us lda #%00011000 sta TIM4CTLA ldy #SER_PARAMS::BAUDRATE @@ -118,7 +123,7 @@ SER_OPEN: cmp #SER_BAUD_62500 beq setbaudrate - ldx #2 + ldx #3 cmp #SER_BAUD_31250 beq setbaudrate @@ -194,13 +199,14 @@ SER_OPEN: lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts + setprescaler: stx TIM4CTLA bra baudsuccess setbaudrate: stx TIM4BKUP baudsuccess: - ldx #TxOpenColl|ParEven + ldx #TXOPEN|PAREVEN stx contrl ldy #SER_PARAMS::DATABITS ; Databits lda (ptr1),y @@ -218,15 +224,15 @@ baudsuccess: beq checkhs cmp #SER_PAR_SPACE bne @L0 - ldx #TxOpenColl + ldx #TXOPEN stx contrl bra checkhs @L0: - ldx #TxParEnable|TxOpenColl|ParEven + ldx #PAREN|TXOPEN|PAREVEN stx contrl cmp #SER_PAR_EVEN beq checkhs - ldx #TxParEnable|TxOpenColl + ldx #PAREN|TXOPEN stx contrl checkhs: ldx contrl @@ -237,7 +243,7 @@ checkhs: bne invparameter lda SERDAT lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR ; Turn on interrupts for receive sta SERCTL lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -279,24 +285,26 @@ SER_PUT: ina cmp TxPtrOut bne PutByte + lda #SER_ERR_OVERFLOW ldx #0 ; return value is char rts + PutByte: ldy TxPtrIn txa sta TxBuffer,y inc TxPtrIn - bit TxDone - bmi @L1 + bit TxDone ; Check bit 7 of TxDone (TXINTEN) + bmi @L1 ; Was TXINTEN already set? php sei - lda contrl - ora #TxIntEnable|ResetErr - sta SERCTL ; Allow TX-IRQ to hang RX-IRQ + lda contrl ; contrl does not include RXINTEN setting + ora #TXINTEN|RESETERR + sta SERCTL ; Allow TX-IRQ to hang RX-IRQ (no receive while transmitting) sta TxDone - plp + plp ; Restore processor and interrupt enable @L1: lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -308,7 +316,7 @@ PutByte: ; Must return an SER_ERR_xx code in a/x. SER_STATUS: - ldy SerialStat + lda SerialStat ldx #$00 sta (ptr1,x) txa ; Return code = 0 @@ -342,27 +350,32 @@ SER_IRQ: @L0: bit TxDone bmi @tx_irq ; Transmit in progress - ldx SERDAT - lda SERCTL - and #RxParityErr|RxOverrun|RxFrameErr|RxBreak - beq @rx_irq + + ldx SERDAT ; Read received data + lda contrl + and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD + ora #OVERRUN|FRAMERR|RXBRK + bit SERCTL ; Compare with SERCTL + + beq @rx_irq ; No errors so far + tsb SerialStat ; Save error condition - bit #RxBreak + bit #RXBRK ; Check for break signal beq @noBreak + stz TxPtrIn ; Break received - drop buffers stz TxPtrOut stz RxPtrIn stz RxPtrOut @noBreak: lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR sta SERCTL - lda #$10 - sta INTRST bra @IRQexit + @rx_irq: lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR sta SERCTL txa ldx RxPtrIn @@ -370,20 +383,22 @@ SER_IRQ: txa inx -@cont0: cpx RxPtrOut beq @1 stx RxPtrIn - lda #SERIAL_INTERRUPT - sta INTRST bra @IRQexit @1: sta RxPtrIn lda #$80 tsb SerialStat + lda contrl + ora #RXINTEN|RESETERR + sta SERCTL + bra @IRQexit + @tx_irq: - ldx TxPtrOut ; Has all bytes been sent? + ldx TxPtrOut ; Have all bytes been sent? cpx TxPtrIn beq @allSent @@ -393,24 +408,22 @@ SER_IRQ: @exit1: lda contrl - ora #TxIntEnable|ResetErr + ora #TXINTEN|RESETERR sta SERCTL - lda #SERIAL_INTERRUPT - sta INTRST bra @IRQexit @allSent: lda SERCTL ; All bytes sent - bit #TxEmpty + bit #TXEMPTY beq @exit1 bvs @exit1 stz TxDone lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR ; Re-enable receive interrupt sta SERCTL +@IRQexit: lda #SERIAL_INTERRUPT sta INTRST -@IRQexit: clc rts From 014f85f226b38f434722ecc085d621e9d3d730d1 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Fri, 9 Feb 2024 10:42:52 +0000 Subject: [PATCH 508/520] Fixed baud rates --- libsrc/lynx/ser/lynx-comlynx.s | 86 +++++++++++++--------------------- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 85703867b..9564bcb4f 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -113,12 +113,12 @@ SER_OPEN: stz TxPtrIn stz TxPtrOut - ; source period is 1 us - lda #%00011000 - sta TIM4CTLA ldy #SER_PARAMS::BAUDRATE lda (ptr1),y + ; Source period is 1 us + ldy #%00011000 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_1 + ldx #1 cmp #SER_BAUD_62500 beq setbaudrate @@ -139,6 +139,10 @@ SER_OPEN: cmp #SER_BAUD_2400 beq setbaudrate + ldx #68 + cmp #SER_BAUD_1800 + beq setbaudrate + ldx #103 cmp #SER_BAUD_1200 beq setbaudrate @@ -147,65 +151,48 @@ SER_OPEN: cmp #SER_BAUD_600 beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011010 - stx TIM4CTLA + ; Source period is 4 us + ldy #%00011011 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_8 - ldx #12 - cmp #SER_BAUD_7200 - beq setbaudrate - - ldx #25 - cmp #SER_BAUD_3600 - beq setbaudrate - - ldx #207 - stx TIM4BKUP - - ; clock = 4 * 15625 - ldx #%00011100 + ldx #51 cmp #SER_BAUD_300 - beq setprescaler + beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011110 + ldx #103 cmp #SER_BAUD_150 - beq setprescaler + beq setbaudrate - ; clock = 1 * 15625 - ldx #%00011111 - stx TIM4CTLA - cmp #SER_BAUD_75 - beq baudsuccess + ldx #115 + cmp #SER_BAUD_134_5 + beq setbaudrate ldx #141 cmp #SER_BAUD_110 beq setbaudrate - ; clock = 2 * 15625 - ldx #%00011010 - stx TIM4CTLA - ldx #68 - cmp #SER_BAUD_1800 + ; Source period is 32 us + ldy #%00011101 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_32 + + ldx #51 + cmp #SER_BAUD_75 beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011110 - stx TIM4CTLA - ldx #231 - cmp #SER_BAUD_134_5 + ldx #68 + cmp #SER_BAUD_56_875 + beq setbaudrate + + ldx #77 + cmp #SER_BAUD_50 beq setbaudrate lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts -setprescaler: - stx TIM4CTLA - bra baudsuccess setbaudrate: + sty TIM4CTLA stx TIM4BKUP -baudsuccess: + ldx #TXOPEN|PAREVEN stx contrl ldy #SER_PARAMS::DATABITS ; Databits @@ -368,15 +355,9 @@ SER_IRQ: stz RxPtrIn stz RxPtrOut @noBreak: - lda contrl - ora #RXINTEN|RESETERR - sta SERCTL - bra @IRQexit + bra @exit0 @rx_irq: - lda contrl - ora #RXINTEN|RESETERR - sta SERCTL txa ldx RxPtrIn sta RxBuffer,x @@ -392,10 +373,7 @@ SER_IRQ: sta RxPtrIn lda #$80 tsb SerialStat - lda contrl - ora #RXINTEN|RESETERR - sta SERCTL - bra @IRQexit + bra @exit0 @tx_irq: ldx TxPtrOut ; Have all bytes been sent? @@ -418,6 +396,8 @@ SER_IRQ: beq @exit1 bvs @exit1 stz TxDone + +@exit0: lda contrl ora #RXINTEN|RESETERR ; Re-enable receive interrupt sta SERCTL From 65bce9ecdeda7a2214a27fafa4ccbfcd5daa5449 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Fri, 9 Feb 2024 12:54:00 +0000 Subject: [PATCH 509/520] Implemented mark and space checks. --- libsrc/lynx/ser/lynx-comlynx.s | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 9564bcb4f..3b6f18af6 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -151,7 +151,7 @@ SER_OPEN: cmp #SER_BAUD_600 beq setbaudrate - ; Source period is 4 us + ; Source period is 8 us ldy #%00011011 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_8 ldx #51 @@ -341,8 +341,9 @@ SER_IRQ: ldx SERDAT ; Read received data lda contrl and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD + tay ora #OVERRUN|FRAMERR|RXBRK - bit SERCTL ; Compare with SERCTL + bit SERCTL ; Check error flags in SERCTL beq @rx_irq ; No errors so far @@ -358,6 +359,15 @@ SER_IRQ: bra @exit0 @rx_irq: + tya + bne @2 ; Parity was enabled so no marker bit check needed + + lda contrl + eor SERCTL ; Should match current parity bit + and #PARBIT ; Check for mark or space value + bne @exit0 + +@2: txa ldx RxPtrIn sta RxBuffer,x From 6cf8ee8eb563f50ceef74605a95e202054e2991c Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Sat, 10 Feb 2024 21:15:05 +0000 Subject: [PATCH 510/520] Removed baud rates from 150 and lower. Fixed tab Replaced uploader references to SERIAL_INTERRUPT --- libsrc/lynx/ser/lynx-comlynx.s | 31 ++----------------------------- libsrc/lynx/uploader.s | 6 +++--- 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 3b6f18af6..486981184 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -158,33 +158,6 @@ SER_OPEN: cmp #SER_BAUD_300 beq setbaudrate - ldx #103 - cmp #SER_BAUD_150 - beq setbaudrate - - ldx #115 - cmp #SER_BAUD_134_5 - beq setbaudrate - - ldx #141 - cmp #SER_BAUD_110 - beq setbaudrate - - ; Source period is 32 us - ldy #%00011101 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_32 - - ldx #51 - cmp #SER_BAUD_75 - beq setbaudrate - - ldx #68 - cmp #SER_BAUD_56_875 - beq setbaudrate - - ldx #77 - cmp #SER_BAUD_50 - beq setbaudrate - lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts @@ -342,8 +315,8 @@ SER_IRQ: lda contrl and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD tay - ora #OVERRUN|FRAMERR|RXBRK - bit SERCTL ; Check error flags in SERCTL + ora #OVERRUN|FRAMERR|RXBRK + bit SERCTL ; Check presence of relevant error flags in SERCTL beq @rx_irq ; No errors so far diff --git a/libsrc/lynx/uploader.s b/libsrc/lynx/uploader.s index df3e5df40..5ce21b489 100644 --- a/libsrc/lynx/uploader.s +++ b/libsrc/lynx/uploader.s @@ -40,14 +40,14 @@ cont1: bra loop1 read_byte: - bit SERCTL + bit SERCTL ; Check for RXRDY ($40) bvc read_byte lda SERDAT rts _UpLoaderIRQ: lda INTSET - and #$10 + and #SERIAL_INTERRUPT bne @L0 clc rts @@ -69,7 +69,7 @@ again: ; last action : clear interrupt ; exit: - lda #$10 + lda #SERIAL_INTERRUPT sta INTRST clc rts From acff429eb8788ab6f1b1e40bb8cef0ac7de94930 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Sun, 11 Feb 2024 15:33:22 +0000 Subject: [PATCH 511/520] Added redeye check for SER_HS_SW handshake --- asminc/lynx.inc | 16 +++++++++++++--- libsrc/lynx/ser/lynx-comlynx.s | 12 ++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index d65b7f8a9..0d34e1c7c 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -259,16 +259,26 @@ SND_INTERRUPT = TIMER7_INTERRUPT INTRST = $FD80 INTSET = $FD81 + MAGRDY0 = $FD84 MAGRDY1 = $FD85 AUDIN = $FD86 SYSCTL1 = $FD87 MIKEYHREV = $FD88 MIKEYSREV = $FD89 -IODIR = $FD8A -IODAT = $FD8B -SERCTL = $FD8C +IODIR = $FD8A +IODAT = $FD8B +; IODIR and IODAT bit definitions +AUDIN_BIT = $10 ; Note that there is also the address AUDIN +READ_ENABLE = $10 ; Same bit for AUDIN_BIT +RESTLESS = $08 +NOEXP = $04 ; If set, redeye is not connected +CART_ADDR_DATA = $02 +CART_POWER_OFF = $02 ; Same bit for CART_ADDR_DATA +EXTERNAL_POWER = $01 + +SERCTL = $FD8C ; SERCTL bit definitions for write operations TXINTEN = $80 RXINTEN = $40 diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 486981184..7201264b7 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -200,7 +200,18 @@ checkhs: ldy #SER_PARAMS::HANDSHAKE ; Handshake lda (ptr1),y cmp #SER_HS_NONE + beq redeye_ok + cmp #SER_HS_SW ; Software handshake will check for connected redeye bne invparameter + + lda IODAT + and #NOEXP ; Check if redeye bit flag is unset + beq redeye_ok + lda #SER_ERR_NO_DEVICE ; ComLynx cable is not inserted + ldx #0 + rts + +redeye_ok: lda SERDAT lda contrl ora #RXINTEN|RESETERR ; Turn on interrupts for receive @@ -209,6 +220,7 @@ checkhs: .assert SER_ERR_OK = 0, error tax rts + invparameter: lda #SER_ERR_INIT_FAILED ldx #0 ; return value is char From 1deb9e52aec938ae338168035c743b04b91e20d0 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Sun, 11 Feb 2024 15:46:23 +0000 Subject: [PATCH 512/520] Replaced last literal value for SERCTL --- libsrc/lynx/ser/lynx-comlynx.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 7201264b7..9b007c6e0 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -74,7 +74,7 @@ SER_UNINSTALL: SER_CLOSE: ; Disable interrupts and stop timer 4 (serial) - lda #$0C ; TXOPEN|RESETERR + lda #TXOPEN|RESETERR sta SERCTL lda #$00 ; Disable count and no reload sta TIM4CTLA From 8b172e05bc4c39b53ee1dd05be082c01fea0d7e0 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Sun, 11 Feb 2024 20:59:08 +0000 Subject: [PATCH 513/520] Applied optimization as per review 42Bastian --- libsrc/lynx/ser/lynx-comlynx.s | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 9b007c6e0..aa4d71ad3 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -76,8 +76,7 @@ SER_CLOSE: ; Disable interrupts and stop timer 4 (serial) lda #TXOPEN|RESETERR sta SERCTL - lda #$00 ; Disable count and no reload - sta TIM4CTLA + stz TIM4CTLA ; Disable count and no reload ; Done, return an error code lda #SER_ERR_OK From 7d6f3d24d434953679c176708c67a0abc845eaa0 Mon Sep 17 00:00:00 2001 From: Alex Thissen <alex.thissen@xebia.com> Date: Sun, 11 Feb 2024 23:12:27 +0000 Subject: [PATCH 514/520] Changed sta (ptr1,x) to sta (ptr1) Reset serial status on ser_close Fixed error for saving serial state --- libsrc/lynx/ser/lynx-comlynx.s | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index aa4d71ad3..c4ae3d5b6 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -76,7 +76,8 @@ SER_CLOSE: ; Disable interrupts and stop timer 4 (serial) lda #TXOPEN|RESETERR sta SERCTL - stz TIM4CTLA ; Disable count and no reload + stz TIM4CTLA ; Disable count and no reload + stz SerialStat ; Reset status ; Done, return an error code lda #SER_ERR_OK @@ -241,8 +242,8 @@ GetByte: ldy RxPtrOut lda RxBuffer,y inc RxPtrOut + sta (ptr1) ldx #$00 - sta (ptr1,x) txa ; Return code = 0 rts @@ -288,8 +289,8 @@ PutByte: SER_STATUS: lda SerialStat + sta (ptr1) ldx #$00 - sta (ptr1,x) txa ; Return code = 0 rts @@ -327,7 +328,7 @@ SER_IRQ: and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD tay ora #OVERRUN|FRAMERR|RXBRK - bit SERCTL ; Check presence of relevant error flags in SERCTL + and SERCTL ; Check presence of relevant error flags in SERCTL beq @rx_irq ; No errors so far From 8d4946b3f451aec4547415ad09419ad90330b2bb Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Thu, 15 Feb 2024 07:52:42 +0100 Subject: [PATCH 515/520] Fixed segv touch /tmp/xx grc65 /tmp/xx --- src/grc65/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/grc65/main.c b/src/grc65/main.c index 7d31bfc52..6b3ca04de 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -231,10 +231,11 @@ static int findToken (const char * const *tokenTbl, const char *token) /* takes as input table of tokens and token, returns position in table or -1 if not found */ int i; - for (i = 0; tokenTbl[i][0]; i++) { - if (strcmp (tokenTbl[i], token) == 0) { - return i; - } + if (token != NULL) { + for (i = 0; tokenTbl[i][0]; i++) { + if (strcmp (tokenTbl[i], token) == 0) { + return i; + } } return -1; From ab0eb4fe58450649c698ebead914a97069dfbe7f Mon Sep 17 00:00:00 2001 From: Stefan <stefan.haubenthal@gmail.com> Date: Thu, 15 Feb 2024 09:03:46 +0100 Subject: [PATCH 516/520] oops --- src/grc65/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/grc65/main.c b/src/grc65/main.c index 6b3ca04de..5ef9e9645 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -236,6 +236,7 @@ static int findToken (const char * const *tokenTbl, const char *token) if (strcmp (tokenTbl[i], token) == 0) { return i; } + } } return -1; From 7a12399b39acd656da3d8802dca0ed1ef09d18c3 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 19 Feb 2024 13:27:42 +0100 Subject: [PATCH 517/520] Allow choosing 115200bps as the card allows it Of course, that won't work full speed with the standard IRQ-based RX. But that will allow users to setup the port at this speed without duplicating the setup part of the code. Up to them to add hooks to disable IRQs and read directly in a tight asm loop. --- libsrc/apple2/ser/a2.ssc.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index c8aa6e9a5..7053b7bb1 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -121,7 +121,7 @@ BaudTable: ; Table used to translate RS232 baudrate param .byte $0F ; SER_BAUD_19200 .byte $FF ; SER_BAUD_38400 .byte $FF ; SER_BAUD_57600 - .byte $FF ; SER_BAUD_115200 + .byte $00 ; SER_BAUD_115200 .byte $FF ; SER_BAUD_230400 BitTable: ; Table used to translate RS232 databits param From 3fd78208bab5cd7d8fa601a4a94af3f66ed39ec1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 19 Feb 2024 18:27:34 +0100 Subject: [PATCH 518/520] Disable IRQ if opening at 115200 bps --- doc/apple2.sgml | 5 +++++ doc/apple2enh.sgml | 5 +++++ libsrc/apple2/ser/a2.ssc.s | 10 ++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/apple2.sgml b/doc/apple2.sgml index e6ec870ee..99ff8139e 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -452,10 +452,15 @@ The names in the parentheses denote the symbols to be used for static linking of (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud aren't reachable because the ROM and ProDOS IRQ handlers are too slow. Software flow control (XON/XOFF) is not supported. + 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. + Note that using the driver at SER_BAUD_115200 will disable IRQs. It will be up + to the users to use the serial port, either by re-enabling IRQs themselves, + or by directly poll-reading the ACIA DATA register without the help of ser_get(). + The driver defaults to slot 2. Call <tt/ser_apple2_slot()/ prior to <tt/ser_open()/ in order to select a different slot. <tt/ser_apple2_slot()/ succeeds for all Apple II slots, but <tt/ser_open()/ fails with diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index d47820cc7..9c46bd4fb 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -453,10 +453,15 @@ The names in the parentheses denote the symbols to be used for static linking of (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud aren't reachable because the ROM and ProDOS IRQ handlers are too slow. Software flow control (XON/XOFF) is not supported. + 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. + Note that using the driver at SER_BAUD_115200 will disable IRQs. It will be up + to the users to use the serial port, either by re-enabling IRQs themselves, + or by directly poll-reading the ACIA DATA register without the help of ser_get(). + The driver defaults to slot 2. Call <tt/ser_apple2_slot()/ prior to <tt/ser_open()/ in order to select a different slot. <tt/ser_apple2_slot()/ succeeds for all Apple II slots, but <tt/ser_open()/ fails with diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 7053b7bb1..88dc4572c 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -302,6 +302,7 @@ HandshakeOK: lda (ptr1),y ; Baudrate index tay lda BaudTable,y ; Get 6551 value + sta tmp2 ; Backup for IRQ setting bpl BaudOK ; Check that baudrate is supported lda #SER_ERR_BAUD_UNAVAIL @@ -332,8 +333,13 @@ BaudOK: sta tmp1 ora #%00000001 ; Set DTR active sta RtsOff ; Store value to easily handle flow control later - ora #%00001000 ; Enable receive interrupts (RTS low) - sta ACIA_CMD,x + + ora #%00001010 ; Disable interrupts and set RTS low + + ldy tmp2 ; Don't enable IRQs if 115200bps + beq :+ + and #%11111101 ; Enable receive IRQs +: sta ACIA_CMD,x ; Done stx Index ; Mark port as open From 8b71fafb84f14763787a5fab5590c69cc7dccc9a Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Mon, 19 Feb 2024 21:30:26 +0100 Subject: [PATCH 519/520] IIgs SCC: Allow choosing 115200bps as the card allows it Of course, that won't work full speed with the standard IRQ-based RX. But that will allow users to setup the port at this speed without duplicating the setup part of the code. Up to them to add hooks to disable IRQs and read directly in a tight asm loop. --- libsrc/apple2/ser/a2.gs.s | 66 +++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index 3a2db1926..c53fe7ecb 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -66,6 +66,8 @@ HSType: .res 1 ; Flow-control type RecvBuf: .res 256 ; Receive buffers: 256 bytes SendBuf: .res 256 ; Send buffers: 256 bytes +ClockSource: .res 1 ; Whether to use BRG or XTAL for clock + .data Opened: .byte $00 ; 1 when opened @@ -106,6 +108,15 @@ TxBitTable: .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) .rodata +ClockMultiplier:.byte %01000000 ; Clock x16 (300-57600bps, ref page 5-8) + .byte %10000000 ; Clock x32 (115200bps, ref page 5-8) + +ClockSourceA: .byte %11010000 ; Use baud rate generator (page 5-17) + .byte %10000000 ; Use XTAL (115200bps) + +ClockSourceB: .byte %01010000 ; Use baud rate generator + .byte %00000000 ; Use XTAL (115200bps) + BaudTable: ; bit7 = 1 means setting is invalid ; Otherwise refers to the index in ; Baud(Low/High)Table @@ -127,7 +138,7 @@ BaudTable: ; bit7 = 1 means setting is invalid .byte $05 ; SER_BAUD_19200 .byte $06 ; SER_BAUD_38400 .byte $07 ; SER_BAUD_57600 - .byte $FF ; SER_BAUD_115200 + .byte $00 ; SER_BAUD_115200 .byte $FF ; SER_BAUD_230400 StopTable: .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) @@ -180,7 +191,6 @@ RX_CTRL_OFF = %11111110 ; ANDed,Rx disabled WR_TX_RX_CTRL = 4 RR_TX_RX_STATUS = 4 -TX_RX_CLOCK_MUL = %01000000 ; Clock x16 (Ref page 5-8) WR_TX_CTRL = 5 ; (Ref page 5-9) RR_TX_STATUS = 5 ; Corresponding status register @@ -197,15 +207,13 @@ MASTER_IRQ_MIE_RST = %00001010 ; STA'd MASTER_IRQ_SET = %00011001 ; STA'd WR_CLOCK_CTRL = 11 ; (Ref page 5-17) -CLOCK_CTRL_CH_A = %11010000 -CLOCK_CTRL_CH_B = %01010000 WR_BAUDL_CTRL = 12 ; (Ref page 5-18) WR_BAUDH_CTRL = 13 ; (Ref page 5-19) WR_MISC_CTRL = 14 ; (Ref page 5-19) -MISC_CTRL_RATE_GEN_ON = %00000001 ; ORed -MISC_CTRL_RATE_GEN_OFF = %11111110 ; ANDed +MISC_CTRL_RATE_GEN_ON = %00000001 ; STA'd +MISC_CTRL_RATE_GEN_OFF = %00000000 ; STA'd WR_IRQ_CTRL = 15 ; (Ref page 5-20) IRQ_CLEANUP_EIRQ = %00001000 @@ -329,6 +337,16 @@ IIgs: : txa ; Promote char return value rts +getClockSource: + ldy #SER_PARAMS::BAUDRATE + lda (ptr1),y ; Baudrate index - cc65 value + ldy #$01 + cmp #SER_BAUD_115200 + beq :+ + ldy #$00 +: sty ClockSource + rts + ;---------------------------------------------------------------------------- ; SER_OPEN: A pointer to a ser_params structure is passed in ptr1. ; Must return an SER_ERR_xx code in a/x. @@ -364,6 +382,8 @@ SER_OPEN: lda #$00 jsr writeSCCReg + jsr getClockSource ; Should we use BRG or XTAL? + ldy #SER_PARAMS::STOPBITS lda (ptr1),y ; Stop bits tay @@ -377,16 +397,18 @@ SER_OPEN: ora ParityTable,y ; Get value bmi InvParam - ora #TX_RX_CLOCK_MUL + ldy ClockSource ; Setup clock multiplier + ora ClockMultiplier,y ldy #WR_TX_RX_CTRL ; Setup stop & parity bits jsr writeSCCReg + ldy ClockSource cpx #CHANNEL_B bne ClockA ClockB: + lda ClockSourceB,y ldy #WR_CLOCK_CTRL - lda #CLOCK_CTRL_CH_B jsr writeSCCReg lda #INTR_PENDING_RX_EXT_B ; Store which IRQ bits we'll check @@ -394,8 +416,8 @@ ClockB: bra SetBaud ClockA: + lda ClockSourceA,y ldy #WR_CLOCK_CTRL - lda #CLOCK_CTRL_CH_A jsr writeSCCReg lda #INTR_PENDING_RX_EXT_A ; Store which IRQ bits we'll check @@ -411,11 +433,16 @@ SetBaud: InvParam: lda #SER_ERR_INIT_FAILED - ldy #$00 ; Mark port closed - bra SetupOut + ldx #$00 ; Promote char return value + stz Opened ; Mark port closed + cli + rts BaudOK: tay + cpy #SER_BAUD_115200 + beq :+ ; Skip baud rate generator setup: + ; For 115200bps, we use XTAL instead lda BaudLowTable,y ; Get low byte @@ -428,8 +455,13 @@ BaudOK: ldy #WR_BAUDH_CTRL jsr writeSCCReg - ldy #WR_MISC_CTRL ; Time to turn this thing on - lda #MISC_CTRL_RATE_GEN_ON +: lda #MISC_CTRL_RATE_GEN_ON ; Setup BRG according to selected rate + ldy ClockSource + cpy #$00 + beq :+ + lda #MISC_CTRL_RATE_GEN_OFF + +: ldy #WR_MISC_CTRL ; Time to turn this thing on jsr writeSCCReg ldy #SER_PARAMS::DATABITS @@ -486,11 +518,11 @@ StoreFlag: sta SER_FLAG ldy #$01 ; Mark port opened - lda #SER_ERR_OK - -SetupOut: - ldx #$00 ; Promote char return value sty Opened + + lda #SER_ERR_OK + ldx #$00 ; Promote char return value + cli rts From 86317711e0931af0dc735ca7a0715369d0d0310c Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira <colin@colino.net> Date: Tue, 20 Feb 2024 07:17:12 +0100 Subject: [PATCH 520/520] IIgs SCC: Rework branches to X-indexed variables and general cleanup/commenting --- libsrc/apple2/ser/a2.gs.s | 244 +++++++++++++++++--------------------- 1 file changed, 110 insertions(+), 134 deletions(-) diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s index c53fe7ecb..e35c6156b 100644 --- a/libsrc/apple2/ser/a2.gs.s +++ b/libsrc/apple2/ser/a2.gs.s @@ -66,36 +66,16 @@ HSType: .res 1 ; Flow-control type RecvBuf: .res 256 ; Receive buffers: 256 bytes SendBuf: .res 256 ; Send buffers: 256 bytes -ClockSource: .res 1 ; Whether to use BRG or XTAL for clock +CurClockSource: .res 1 ; Whether to use BRG or RTxC for clock .data Opened: .byte $00 ; 1 when opened Channel: .byte $00 ; Channel B by default -CurChanIrqFlags:.byte INTR_PENDING_RX_EXT_B +CurChanIrqFlags:.byte $00 SerFlagOrig: .byte $00 -; Tables used to translate cc65 RS232 params into register values -; (Ref page 5-18 and 5-19) -BaudLowTable: .byte $7E ; SER_BAUD_300 - .byte $5E ; SER_BAUD_1200 - .byte $2E ; SER_BAUD_2400 - .byte $16 ; SER_BAUD_4800 - .byte $0A ; SER_BAUD_9600 - .byte $04 ; SER_BAUD_19200 - .byte $01 ; SER_BAUD_38400 - .byte $00 ; SER_BAUD_57600 - -BaudHighTable: .byte $01 ; SER_BAUD_300 - .byte $00 ; SER_BAUD_1200 - .byte $00 ; SER_BAUD_2400 - .byte $00 ; SER_BAUD_4800 - .byte $00 ; SER_BAUD_9600 - .byte $00 ; SER_BAUD_19200 - .byte $00 ; SER_BAUD_38400 - .byte $00 ; SER_BAUD_57600 - RxBitTable: .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) .byte %10000000 ; SER_BITS_6 (Ref page 5-7) .byte %01000000 ; SER_BITS_7 @@ -108,38 +88,65 @@ TxBitTable: .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) .rodata -ClockMultiplier:.byte %01000000 ; Clock x16 (300-57600bps, ref page 5-8) +ClockMultiplier:.byte %01000000 ; Clock x16 (300-57600bps, WR4, ref page 5-8) .byte %10000000 ; Clock x32 (115200bps, ref page 5-8) -ClockSourceA: .byte %11010000 ; Use baud rate generator (page 5-17) - .byte %10000000 ; Use XTAL (115200bps) +ClockSource: .byte %01010000 ; Use baud rate generator (ch. B) (WR11, page 5-17) + .byte %00000000 ; Use RTxC (115200bps) (ch. B) + .byte %11010000 ; Use baud rate generator (ch. A) + .byte %10000000 ; Use RTxC (115200bps) (ch. A) -ClockSourceB: .byte %01010000 ; Use baud rate generator - .byte %00000000 ; Use XTAL (115200bps) +BrgEnabled: .byte %00000001 ; Baud rate generator on (WR14, page 5-19) + .byte %00000000 ; BRG Off + +ChanIrqFlags: .byte %00000101 ; ANDed (RX/special IRQ, ch. B) (page 5-25) + .byte %00101000 ; ANDed (RX/special IRQ, ch. A) + +ChanIrqMask: .byte %00000111 ; Ch. B IRQ flags mask + .byte %00111000 ; Ch. A IRQ flags mask BaudTable: ; bit7 = 1 means setting is invalid - ; Otherwise refers to the index in - ; Baud(Low/High)Table - .byte $FF ; SER_BAUD_45_5 - .byte $FF ; SER_BAUD_50 - .byte $FF ; SER_BAUD_75 - .byte $FF ; SER_BAUD_110 - .byte $FF ; SER_BAUD_134_5 - .byte $FF ; SER_BAUD_150 - .byte $00 ; SER_BAUD_300 - .byte $FF ; SER_BAUD_600 - .byte $01 ; SER_BAUD_1200 - .byte $FF ; SER_BAUD_1800 - .byte $02 ; SER_BAUD_2400 - .byte $FF ; SER_BAUD_3600 - .byte $03 ; SER_BAUD_4800 - .byte $FF ; SER_BAUD_7200 - .byte $04 ; SER_BAUD_9600 - .byte $05 ; SER_BAUD_19200 - .byte $06 ; SER_BAUD_38400 - .byte $07 ; SER_BAUD_57600 - .byte $00 ; SER_BAUD_115200 - .byte $FF ; SER_BAUD_230400 + ; Indexes cc65 RS232 SER_BAUD enum + ; into WR12/13 register values + ; (Ref page 5-18 and 5-19) + .word $FFFF ; SER_BAUD_45_5 + .word $FFFF ; SER_BAUD_50 + .word $FFFF ; SER_BAUD_75 + .word $FFFF ; SER_BAUD_110 + .word $FFFF ; SER_BAUD_134_5 + .word $FFFF ; SER_BAUD_150 + .word $017E ; SER_BAUD_300 + .word $FFFF ; SER_BAUD_600 + .word $005E ; SER_BAUD_1200 + .word $FFFF ; SER_BAUD_1800 + .word $002E ; SER_BAUD_2400 + .word $FFFF ; SER_BAUD_3600 + .word $0016 ; SER_BAUD_4800 + .word $FFFF ; SER_BAUD_7200 + .word $000A ; SER_BAUD_9600 + .word $0004 ; SER_BAUD_19200 + .word $0001 ; SER_BAUD_38400 + .word $0000 ; SER_BAUD_57600 + .word $0000 ; SER_BAUD_115200 (constant unused at that speed) + .word $FFFF ; SER_BAUD_230400 + +; About the speed selection: either we use the baud rate generator: +; - Load the time constants from BaudTable into WR12/WR13 +; - Setup the TX/RX clock source to BRG (ClockSource into WR11) +; - Setup the clock multiplier (WR4) +; - Enable the baud rate generator (WR14) +; In this case, the baud rate will be: +; rate = crystal_clock/(2+BRG_time_constant))/(2*clock_multiplier) +; Example: (3686400/(2+0x0004)) / (2*16) = 19200 bps +; +; Or we don't use the baud rate generator: +; - Setup the TX/RX clock source to RTxC +; - Setup the clock multiplier +; - Disable the baud rate generator +; - WR12 and 13 are ignored +; In this case, the baud rate will be: +; rate = crystal_clock/clock_multiplier +; Example: 3686400/32 = 115200 bps StopTable: .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) .byte %00001100 ; SER_STOP_2 (Ref page 5-8) @@ -167,6 +174,7 @@ SER_FLAG := $E10104 ; ------------------------------------------------------------------------ ; Channels + CHANNEL_B = 0 CHANNEL_A = 1 @@ -212,8 +220,6 @@ WR_BAUDL_CTRL = 12 ; (Ref page 5-18) WR_BAUDH_CTRL = 13 ; (Ref page 5-19) WR_MISC_CTRL = 14 ; (Ref page 5-19) -MISC_CTRL_RATE_GEN_ON = %00000001 ; STA'd -MISC_CTRL_RATE_GEN_OFF = %00000000 ; STA'd WR_IRQ_CTRL = 15 ; (Ref page 5-20) IRQ_CLEANUP_EIRQ = %00001000 @@ -228,13 +234,8 @@ IRQ_RX = %00100000 IRQ_SPECIAL = %01100000 RR_INTR_PENDING_STATUS = 3 ; (Ref page 5-25) -INTR_PENDING_RX_EXT_A = %00101000 ; ANDed (RX or special IRQ) -INTR_PENDING_RX_EXT_B = %00000101 ; ANDed (RX or special IRQ) INTR_IS_RX = %00100100 ; ANDed (RX IRQ, channel A or B) -SER_FLAG_CH_A = %00111000 -SER_FLAG_CH_B = %00000111 - .code ; Read register value to A. @@ -338,13 +339,12 @@ IIgs: rts getClockSource: - ldy #SER_PARAMS::BAUDRATE - lda (ptr1),y ; Baudrate index - cc65 value - ldy #$01 + .assert SER_PARAMS::BAUDRATE = 0, error + lda (ptr1) ; Baudrate index - cc65 value cmp #SER_BAUD_115200 - beq :+ - ldy #$00 -: sty ClockSource + lda #$00 + adc #$00 + sta CurClockSource ; 0 = BRG, 1 = RTxC rts ;---------------------------------------------------------------------------- @@ -378,13 +378,13 @@ SER_OPEN: ldy #RR_INIT_STATUS ; Hit rr0 once to sync up jsr readSSCReg - ldy #WR_MISC_CTRL ; Turn everything off + ldy #WR_MISC_CTRL ; WR14: Turn everything off lda #$00 jsr writeSCCReg - jsr getClockSource ; Should we use BRG or XTAL? + jsr getClockSource ; Should we use BRG or RTxC? - ldy #SER_PARAMS::STOPBITS + ldy #SER_PARAMS::STOPBITS ; WR4 setup: clock mult., stop & parity lda (ptr1),y ; Stop bits tay lda StopTable,y ; Get value @@ -397,109 +397,92 @@ SER_OPEN: ora ParityTable,y ; Get value bmi InvParam - ldy ClockSource ; Setup clock multiplier + ldy CurClockSource ; Clock multiplier ora ClockMultiplier,y - ldy #WR_TX_RX_CTRL ; Setup stop & parity bits - jsr writeSCCReg + ldy #WR_TX_RX_CTRL + jsr writeSCCReg ; End of WR4 setup - ldy ClockSource + ldy CurClockSource ; WR11 setup: clock source cpx #CHANNEL_B - bne ClockA -ClockB: - lda ClockSourceB,y + beq SetClock + iny ; Shift to get correct ClockSource val + iny ; depending on our channel + +SetClock: + lda ClockSource,y ldy #WR_CLOCK_CTRL - jsr writeSCCReg + jsr writeSCCReg ; End of WR11 setup - lda #INTR_PENDING_RX_EXT_B ; Store which IRQ bits we'll check - sta CurChanIrqFlags - - bra SetBaud -ClockA: - lda ClockSourceA,y - ldy #WR_CLOCK_CTRL - jsr writeSCCReg - - lda #INTR_PENDING_RX_EXT_A ; Store which IRQ bits we'll check + lda ChanIrqFlags,x ; Store which IRQ bits we'll check sta CurChanIrqFlags SetBaud: - ldy #SER_PARAMS::BAUDRATE - lda (ptr1),y ; Baudrate index - cc65 value + .assert SER_PARAMS::BAUDRATE = 0, error + lda (ptr1) ; Baudrate index - cc65 value + asl tay - lda BaudTable,y ; Get chip value from Low/High tables + lda BaudTable,y ; Get low byte of register value bpl BaudOK ; Verify baudrate is supported InvParam: lda #SER_ERR_INIT_FAILED - ldx #$00 ; Promote char return value - stz Opened ; Mark port closed - cli - rts + ldy #$00 ; Mark port closed + bra SetupOut BaudOK: - tay - cpy #SER_BAUD_115200 - beq :+ ; Skip baud rate generator setup: - ; For 115200bps, we use XTAL instead - - lda BaudLowTable,y ; Get low byte - - phy - ldy #WR_BAUDL_CTRL - jsr writeSCCReg + phy ; WR12 setup: BRG time constant, low byte + ldy #WR_BAUDL_CTRL ; Setting WR12 & 13 is useless if we're using + jsr writeSCCReg ; RTxC, but doing it anyway makes code smaller ply - lda BaudHighTable,y ; Get high byte + iny + lda BaudTable,y ; WR13 setup: BRG time constant, high byte ldy #WR_BAUDH_CTRL jsr writeSCCReg -: lda #MISC_CTRL_RATE_GEN_ON ; Setup BRG according to selected rate - ldy ClockSource - cpy #$00 - beq :+ - lda #MISC_CTRL_RATE_GEN_OFF - -: ldy #WR_MISC_CTRL ; Time to turn this thing on + ldy CurClockSource ; WR14 setup: BRG enabling + lda BrgEnabled,y + ldy #WR_MISC_CTRL ; Time to turn this thing on jsr writeSCCReg - ldy #SER_PARAMS::DATABITS - lda (ptr1),y ; Data bits + ldy #SER_PARAMS::DATABITS ; WR3 setup: RX data bits + lda (ptr1),y tay - lda RxBitTable,y ; Data bits for RX - ora #RX_CTRL_ON ; and turn RX on + lda RxBitTable,y + ora #RX_CTRL_ON ; and turn receiver on phy ldy #WR_RX_CTRL - jsr writeSCCReg + jsr writeSCCReg ; End of WR3 setup ply - lda TxBitTable,y ; Data bits for TX - ora #TX_CTRL_ON ; and turn TX on - and #TX_DTR_ON + lda TxBitTable,y ; WR5 setup: TX data bits + ora #TX_CTRL_ON ; and turn transmitter on + and #TX_DTR_ON ; and turn DTR on sta RtsOff ; Save value for flow control - ora #TX_RTS_ON + ora #TX_RTS_ON ; and turn RTS on ldy #WR_TX_CTRL - jsr writeSCCReg + jsr writeSCCReg ; End of WR5 setup - ldy #WR_IRQ_CTRL + ldy #WR_IRQ_CTRL ; WR15 setup: IRQ lda #IRQ_CLEANUP_EIRQ jsr writeSCCReg - ldy #WR_INIT_CTRL ; Clear ext status (write twice) + ldy #WR_INIT_CTRL ; WR0 setup: clear existing IRQs lda #INIT_CTRL_CLEAR_EIRQ - jsr writeSCCReg + jsr writeSCCReg ; Clear (write twice) jsr writeSCCReg - ldy #WR_TX_RX_MODE_CTRL ; Activate RX IRQ + ldy #WR_TX_RX_MODE_CTRL ; WR1 setup: Activate RX IRQ lda #TX_RX_MODE_RXIRQ jsr writeSCCReg - lda SCCBREG ; Activate master IRQ + lda SCCBREG ; WR9 setup: Activate master IRQ ldy #WR_MASTER_IRQ_RST lda #MASTER_IRQ_SET jsr writeSCCReg @@ -507,22 +490,15 @@ BaudOK: lda SER_FLAG ; Get SerFlag's current value sta SerFlagOrig ; and save it - cpx #CHANNEL_B - bne IntA -IntB: - ora #SER_FLAG_CH_B ; Inform firmware we want channel B IRQs - bra StoreFlag -IntA: - ora #SER_FLAG_CH_A ; Inform firmware we want channel A IRQs -StoreFlag: + ora ChanIrqMask,x ; Tell firmware which channel IRQs we want sta SER_FLAG ldy #$01 ; Mark port opened - sty Opened - lda #SER_ERR_OK - ldx #$00 ; Promote char return value +SetupOut: + ldx #$00 ; Promote char return value + sty Opened cli rts