--- /dev/null
+/*****************************************************************************/
+/* */
+/* asserts.c */
+/* */
+/* Linker assertions for the ca65 crossassembler */
+/* */
+/* */
+/* */
+/* (C) 2003 Ullrich von Bassewitz */
+/* Römerstrasse 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. */
+/* */
+/*****************************************************************************/
+
+
+
+/* common */
+#include "coll.h"
+#include "xmalloc.h"
+
+/* ca65 */
+#include "asserts.h"
+#include "expr.h"
+#include "objfile.h"
+#include "scanner.h"
+
+
+
+/*****************************************************************************/
+/* Data */
+/*****************************************************************************/
+
+
+
+/* An assertion entry */
+typedef struct Assertion Assertion;
+struct Assertion {
+ ExprNode* Expr; /* Expression to evaluate */
+ unsigned Action; /* Action to take */
+ unsigned Msg; /* Message to print (if any) */
+ FilePos Pos; /* File position of assertion */
+};
+
+/* Collection with all assertions for a module */
+static Collection Assertions = STATIC_COLLECTION_INITIALIZER;
+
+
+
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+static Assertion* NewAssertion (ExprNode* Expr, unsigned Action, unsigned Msg)
+/* Create a new Assertion struct and return it */
+{
+ /* Allocate memory */
+ Assertion* A = xmalloc (sizeof (Assertion));
+
+ /* Initialize the fields */
+ A->Expr = Expr;
+ A->Action = Action;
+ A->Msg = Msg;
+ A->Pos = CurPos;
+
+ /* Return the new struct */
+ return A;
+}
+
+
+
+void AddAssertion (ExprNode* Expr, unsigned Action, unsigned Msg)
+/* Add an assertion to the assertion table */
+{
+ /* Add an assertion object to the table */
+ CollAppend (&Assertions, NewAssertion (Expr, Action, Msg));
+}
+
+
+
+void WriteAssertions (void)
+/* Write the assertion table to the object file */
+{
+ unsigned I;
+
+ /* Get the number of strings in the string pool */
+ unsigned Count = CollCount (&Assertions);
+
+ /* Tell the object file module that we're about to start the assertions */
+ ObjStartAssertions ();
+
+ /* Write the string count to the list */
+ ObjWriteVar (Count);
+
+ /* Write the assertions */
+ for (I = 0; I < Count; ++I) {
+
+ /* Get the next assertion */
+ Assertion* A = CollAtUnchecked (&Assertions, I);
+
+ /* Finalize the expression */
+ A->Expr = FinalizeExpr (A->Expr);
+
+ /* Write it to the file */
+ WriteExpr (A->Expr);
+ ObjWriteVar (A->Action);
+ ObjWriteVar (A->Msg);
+ ObjWritePos (&A->Pos);
+ }
+
+ /* Done writing the assertions */
+ ObjEndAssertions ();
+}
+
+
+
+
/* */
/* */
/* (C) 1998-2003 Ullrich von Bassewitz */
-/* Wacholderweg 14 */
-/* D-70597 Stuttgart */
-/* EMail: uz@musoftware.de */
+/* Römerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
+/* ld65 */
#include "error.h"
#include "expr.h"
#include "instr.h"
#include "nexttok.h"
#include "ea.h"
-
+
/*****************************************************************************/
-/* Code */
+/* Code */
/*****************************************************************************/
-void GetEA (unsigned long* AddrMode, ExprNode** Expr, ExprNode** Bank)
-/* Parse an effective address, return the possible modes in AddrMode, and the
- * expression involved (if any) in Expr.
- */
+void GetEA (EffAddr* A)
+/* Parse an effective address, return the result in A */
{
- /* Clear the expressions */
- *Bank = *Expr = 0;
+ /* Clear the output struct */
+ A->AddrModeSet = 0;
+ A->Bank = 0;
+ A->Expr = 0;
if (TokIsSep (Tok)) {
- *AddrMode = AM_IMPLICIT;
+ A->AddrModeSet = AM_IMPLICIT;
} else if (Tok == TOK_HASH) {
/* #val */
NextTok ();
- *Expr = Expression ();
- *AddrMode = AM_IMM;
+ A->Expr = Expression ();
+ A->AddrModeSet = AM_IMM;
} else if (Tok == TOK_A) {
NextTok ();
- *AddrMode = AM_ACCU;
+ A->AddrModeSet = AM_ACCU;
} else if (Tok == TOK_LBRACK) {
/* [dir] or [dir],y */
NextTok ();
- *Expr = Expression ();
+ A->Expr = Expression ();
Consume (TOK_RBRACK, ERR_RBRACK_EXPECTED);
if (Tok == TOK_COMMA) {
/* [dir],y */
NextTok ();
Consume (TOK_Y, ERR_Y_EXPECTED);
- *AddrMode = AM_DIR_IND_LONG_Y;
+ A->AddrModeSet = AM_DIR_IND_LONG_Y;
} else {
/* [dir] */
- *AddrMode = AM_DIR_IND_LONG;
+ A->AddrModeSet = AM_DIR_IND_LONG;
}
} else if (Tok == TOK_LPAREN) {
/* One of the indirect modes */
NextTok ();
- *Expr = Expression ();
+ A->Expr = Expression ();
if (Tok == TOK_COMMA) {
/* (expr,X) or (rel,S),y */
NextTok ();
if (Tok == TOK_X) {
- /* (adr,x) */
- NextTok ();
- *AddrMode = AM_ABS_X_IND | AM_DIR_X_IND;
- ConsumeRParen ();
+ /* (adr,x) */
+ NextTok ();
+ A->AddrModeSet = AM_ABS_X_IND | AM_DIR_X_IND;
+ ConsumeRParen ();
} else if (Tok == TOK_S) {
- /* (rel,s),y */
+ /* (rel,s),y */
NextTok ();
- *AddrMode = AM_STACK_REL_IND_Y;
+ A->AddrModeSet = AM_STACK_REL_IND_Y;
ConsumeRParen ();
ConsumeComma ();
Consume (TOK_Y, ERR_Y_EXPECTED);
/* (adr),y */
NextTok ();
Consume (TOK_Y, ERR_Y_EXPECTED);
- *AddrMode = AM_DIR_IND_Y;
+ A->AddrModeSet = AM_DIR_IND_Y;
} else {
/* (adr) */
- *AddrMode = AM_ABS_IND | AM_DIR_IND;
+ A->AddrModeSet = AM_ABS_IND | AM_DIR_IND;
}
}
* adr,y
* adr,s
*/
- *Expr = Expression ();
+ A->Expr = Expression ();
if (Tok == TOK_DOT) {
/* Expr was a bank specification: bank.adr or bank.adr,x */
- *Bank = *Expr;
+ A->Bank = A->Expr;
NextTok ();
- *Expr = Expression ();
+ A->Expr = Expression ();
if (Tok == TOK_COMMA) {
- /* bank.adr,x */
- NextTok ();
- Consume (TOK_X, ERR_X_EXPECTED);
- *AddrMode = AM_ABS_LONG_X;
+ /* bank.adr,x */
+ NextTok ();
+ Consume (TOK_X, ERR_X_EXPECTED);
+ A->AddrModeSet = AM_ABS_LONG_X;
} else {
/* bank.adr */
- *AddrMode = AM_ABS_LONG;
+ A->AddrModeSet = AM_ABS_LONG;
}
} else {
switch (Tok) {
case TOK_X:
- *AddrMode = AM_ABS_X | AM_DIR_X;
+ A->AddrModeSet = AM_ABS_X | AM_DIR_X;
NextTok ();
break;
case TOK_Y:
- *AddrMode = AM_ABS_Y | AM_DIR_Y;
+ A->AddrModeSet = AM_ABS_Y | AM_DIR_Y;
NextTok ();
break;
case TOK_S:
- *AddrMode = AM_STACK_REL;
+ A->AddrModeSet = AM_STACK_REL;
NextTok ();
break;
}
- } else {
+ } else {
- *AddrMode = AM_ABS | AM_DIR;
+ A->AddrModeSet = AM_ABS | AM_DIR;
}
}
#include <ctype.h>
/* common */
+#include "assertdefs.h"
#include "bitops.h"
#include "check.h"
/* ca65 */
+#include "asserts.h"
#include "ea.h"
#include "error.h"
#include "expr.h"
#include "global.h"
+#include "instr.h"
#include "nexttok.h"
#include "objcode.h"
+#include "spool.h"
#include "symtab.h"
-#include "instr.h"
static void PutBlockMove (const InsDesc* Ins);
static void PutREP (const InsDesc* Ins);
static void PutSEP (const InsDesc* Ins);
+static void PutJmp (const InsDesc* Ins);
static void PutAll (const InsDesc* Ins);
{ "BPL", 0x0020000, 0x10, 0, PutPCRel8 },
{ "BRK", 0x0000001, 0x00, 0, PutAll },
{ "BVC", 0x0020000, 0x50, 0, PutPCRel8 },
- { "BVS", 0x0020000, 0x70, 0, PutPCRel8 },
+ { "BVS", 0x0020000, 0x70, 0, PutPCRel8 },
{ "CLC", 0x0000001, 0x18, 0, PutAll },
{ "CLD", 0x0000001, 0xd8, 0, PutAll },
{ "CLI", 0x0000001, 0x58, 0, PutAll },
{ "INC", 0x000006c, 0x00, 4, PutAll },
{ "INX", 0x0000001, 0xe8, 0, PutAll },
{ "INY", 0x0000001, 0xc8, 0, PutAll },
- { "JMP", 0x0000808, 0x4c, 6, PutAll },
+ { "JMP", 0x0000808, 0x4c, 6, PutJmp },
{ "JSR", 0x0000008, 0x20, 7, PutAll },
{ "LDA", 0x080A26C, 0xa0, 0, PutAll },
{ "LDX", 0x080030C, 0xa2, 1, PutAll },
{ "TSX", 0x0000001, 0xba, 0, PutAll },
{ "TXA", 0x0000001, 0x8a, 0, PutAll },
{ "TXS", 0x0000001, 0x9a, 0, PutAll },
- { "TYA", 0x0000001, 0x98, 0, PutAll }
+ { "TYA", 0x0000001, 0x98, 0, PutAll }
}
};
{ "INX", 0x0000001, 0xe8, 0, PutAll },
{ "INY", 0x0000001, 0xc8, 0, PutAll },
{ "JMP", 0x0010808, 0x4c, 6, PutAll },
- { "JSR", 0x0000008, 0x20, 7, PutAll },
+ { "JSR", 0x0000008, 0x20, 7, PutAll },
{ "LDA", 0x080A66C, 0xa0, 0, PutAll },
{ "LDX", 0x080030C, 0xa2, 1, PutAll },
{ "LDY", 0x080006C, 0xa0, 1, PutAll },
{ "JSL", 0x0000010, 0x20, 7, PutAll },
{ "JSR", 0x0010018, 0x20, 7, PutAll },
{ "LDA", 0x0b8f6fc, 0xa0, 0, PutAll },
- { "LDX", 0x0c0030c, 0xa2, 1, PutAll },
+ { "LDX", 0x0c0030c, 0xa2, 1, PutAll },
{ "LDY", 0x0c0006c, 0xa0, 1, PutAll },
{ "LSR", 0x000006F, 0x42, 1, PutAll },
{ "MVN", 0x1000000, 0x54, 0, PutBlockMove },
{ "TAD", 0x0000001, 0x5b, 0, PutAll }, /* == TCD */
{ "TAS", 0x0000001, 0x1b, 0, PutAll }, /* == TCS */
{ "TAX", 0x0000001, 0xaa, 0, PutAll },
- { "TAY", 0x0000001, 0xa8, 0, PutAll },
+ { "TAY", 0x0000001, 0xa8, 0, PutAll },
{ "TCD", 0x0000001, 0x5b, 0, PutAll },
{ "TCS", 0x0000001, 0x1b, 0, PutAll },
{ "TDA", 0x0000001, 0x7b, 0, PutAll }, /* == TDC */
/*****************************************************************************/
-/* Handler functions */
+/* Handler functions */
/*****************************************************************************/
-static long PutImmed8 (const InsDesc* Ins)
-/* Parse and emit an immediate 8 bit instruction. Return the value of the
- * operand if it's available and const.
+static int EvalEA (const InsDesc* Ins, EffAddr* A)
+/* Evaluate the effective address. All fields in A will be valid after calling
+ * this function. The function returns true on success and false on errors.
*/
{
- ExprNode* Expr;
- ExprNode* Bank;
- unsigned long AddrMode;
- unsigned char OpCode;
- long Val = -1;
-
- /* Get the addressing mode used */
- GetEA (&AddrMode, &Expr, &Bank);
+ /* Get the set of possible addressing modes */
+ GetEA (A);
/* From the possible addressing modes, remove the ones that are invalid
* for this instruction or CPU.
*/
- AddrMode &= Ins->AddrMode;
+ A->AddrModeSet &= Ins->AddrMode;
/* If we have possible zero page addressing modes, and the expression
* involved (if any) is not in byte range, remove the zero page addressing
* modes.
*/
- if (Expr && (AddrMode & AM_ZP) && !IsByteExpr (Expr)) {
- AddrMode &= ~AM_ZP;
+ if (A->Expr && (A->AddrModeSet & AM_ZP) && !IsByteExpr (A->Expr)) {
+ A->AddrModeSet &= ~AM_ZP;
}
/* Check if we have any adressing modes left */
- if (AddrMode == 0) {
- Error (ERR_ILLEGAL_ADDR_MODE);
- return -1;
+ if (A->AddrModeSet == 0) {
+ Error (ERR_ILLEGAL_ADDR_MODE);
+ return 0;
+ }
+ A->AddrMode = BitFind (A->AddrModeSet);
+ A->AddrModeBit = (0x01UL << A->AddrMode);
+
+ /* If the instruction has a one byte operand and immediate addressing is
+ * allowed but not used, check for an operand expression in the form
+ * <label or >label, where label is a far or absolute label. If found,
+ * emit a warning. This warning protects against a typo, where the '#'
+ * for the immediate operand is omitted.
+ */
+ if (A->Expr && (Ins->AddrMode & AM_IMM) &&
+ (A->AddrModeSet & (AM_DIR | AM_ABS | AM_ABS_LONG)) &&
+ ExtBytes[A->AddrMode] == 1) {
+
+ /* Found, check the expression */
+ ExprNode* Left = A->Expr->Left;
+ if ((A->Expr->Op == EXPR_BYTE0 || A->Expr->Op == EXPR_BYTE1) &&
+ Left->Op == EXPR_SYMBOL &&
+ !SymIsZP (Left->V.Sym)) {
+
+ /* Output a warning */
+ Warning (WARN_SUSPICIOUS_ADDREXPR);
+ }
}
- AddrMode = BitFind (AddrMode);
/* Build the opcode */
- OpCode = Ins->BaseCode | EATab [Ins->ExtCode][AddrMode];
+ A->Opcode = Ins->BaseCode | EATab[Ins->ExtCode][A->AddrMode];
+
+ /* Success */
+ return 1;
+}
+
+
+
+static void EmitCode (EffAddr* A)
+/* Output code for the data in A */
+{
+ /* Check how many extension bytes are needed and output the instruction */
+ switch (ExtBytes[A->AddrMode]) {
+
+ case 0:
+ Emit0 (A->Opcode);
+ break;
+
+ case 1:
+ Emit1 (A->Opcode, A->Expr);
+ break;
+
+ case 2:
+ if (CPU == CPU_65816 && (A->AddrModeBit & (AM_ABS | AM_ABS_X | AM_ABS_Y))) {
+ /* This is a 16 bit mode that uses an address. If in 65816,
+ * mode, force this address into 16 bit range to allow
+ * addressing inside a 64K segment.
+ */
+ Emit2 (A->Opcode, ForceWordExpr (A->Expr));
+ } else {
+ Emit2 (A->Opcode, A->Expr);
+ }
+ break;
+
+ case 3:
+ if (A->Bank) {
+ /* Separate bank given */
+ Emit3b (A->Opcode, A->Expr, A->Bank);
+ } else {
+ /* One far argument */
+ Emit3 (A->Opcode, A->Expr);
+ }
+ break;
+
+ default:
+ Internal ("Invalid operand byte count: %u", ExtBytes[A->AddrMode]);
+
+ }
+}
+
+
+
+static long PutImmed8 (const InsDesc* Ins)
+/* Parse and emit an immediate 8 bit instruction. Return the value of the
+ * operand if it's available and const.
+ */
+{
+ EffAddr A;
+ long Val = -1;
+
+ /* Evaluate the addressing mode */
+ if (EvalEA (Ins, &A) == 0) {
+ /* An error occurred */
+ return -1L;
+ }
/* If we have an expression and it's const, get it's value */
- if (Expr && IsConstExpr (Expr)) {
- Val = GetExprVal (Expr);
+ if (A.Expr && IsConstExpr (A.Expr)) {
+ Val = GetExprVal (A.Expr);
}
/* Check how many extension bytes are needed and output the instruction */
- switch (ExtBytes [AddrMode]) {
+ switch (ExtBytes[A.AddrMode]) {
- case 1:
- Emit1 (OpCode, Expr);
+ case 1:
+ Emit1 (A.Opcode, A.Expr);
break;
default:
- Internal ("Invalid operand byte count: %u", ExtBytes [AddrMode]);
+ Internal ("Invalid operand byte count: %u", ExtBytes[A.AddrMode]);
}
/* Return the expression value */
Warning (WARN_CANNOT_TRACK_STATUS);
} else {
if (Val & 0x10) {
- /* Index registers to 16 bit */
- ExtBytes [AMI_IMM_INDEX] = 2;
+ /* Index registers to 16 bit */
+ ExtBytes[AMI_IMM_INDEX] = 2;
}
if (Val & 0x20) {
- /* Accu to 16 bit */
- ExtBytes [AMI_IMM_ACCU] = 2;
+ /* Accu to 16 bit */
+ ExtBytes[AMI_IMM_ACCU] = 2;
}
}
}
-static void PutAll (const InsDesc* Ins)
-/* Handle all other instructions */
+static void PutJmp (const InsDesc* Ins)
+/* Handle the jump instruction for the 6502. Problem is that these chips have
+ * a bug: If the address crosses a page, the upper byte gets not corrected and
+ * the instruction will fail. The PutJmp function will add a linker assertion
+ * to check for this case and is otherwise identical to PutAll.
+ */
{
- ExprNode* Expr;
- ExprNode* Bank;
- unsigned long AddrModeSet;
- unsigned char OpCode;
- unsigned AddrMode;
- unsigned long AddrModeBit;
-
- /* Get the addressing mode used */
- GetEA (&AddrModeSet, &Expr, &Bank);
-
- /* From the possible addressing modes, remove the ones that are invalid
- * for this instruction or CPU.
- */
- AddrModeSet &= Ins->AddrMode;
+ EffAddr A;
- /* If we have possible zero page addressing modes, and the expression
- * involved (if any) is not in byte range, remove the zero page addressing
- * modes.
- */
- if (Expr && (AddrModeSet & AM_ZP) && !IsByteExpr (Expr)) {
- AddrModeSet &= ~AM_ZP;
- }
+ /* Evaluate the addressing mode used */
+ if (EvalEA (Ins, &A)) {
- /* Check if we have any adressing modes left */
- if (AddrModeSet == 0) {
- Error (ERR_ILLEGAL_ADDR_MODE);
- return;
- }
- AddrMode = BitFind (AddrModeSet);
+ /* Check for indirect addressing */
+ if (A.AddrModeBit & AM_ABS_IND) {
- /* If the instruction has a one byte operand and immediate addressing is
- * allowed but not used, check for an operand expression in the form
- * <label or >label, where label is a far or absolute label. If found,
- * emit a warning. This warning protects against a typo, where the '#'
- * for the immediate operand is omitted.
- */
- if (Expr && (Ins->AddrMode & AM_IMM) &&
- (AddrModeSet & (AM_DIR | AM_ABS | AM_ABS_LONG)) &&
- ExtBytes[AddrMode] == 1) {
+ /* 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.
+ */
+ ExprNode* E = CompareExpr (ForceByteExpr (CloneExpr (A.Expr)), 0xFF);
- /* Found, check the expression */
- ExprNode* Left = Expr->Left;
- if ((Expr->Op == EXPR_BYTE0 || Expr->Op == EXPR_BYTE1) &&
- Left->Op == EXPR_SYMBOL &&
- !SymIsZP (Left->V.Sym)) {
+ /* Generate the message */
+ unsigned Msg = GetStringId ("\"jmp (abs)\" across page border");
- /* Output a warning */
- Warning (WARN_SUSPICIOUS_ADDREXPR);
+ /* Generate the assertion */
+ AddAssertion (E, ASSERT_ACT_WARN, Msg);
}
- }
-
- /* Build the opcode */
- OpCode = Ins->BaseCode | EATab [Ins->ExtCode][AddrMode];
- /* Check how many extension bytes are needed and output the instruction */
- switch (ExtBytes[AddrMode]) {
+ /* No error, output code */
+ EmitCode (&A);
+ }
+}
- case 0:
- Emit0 (OpCode);
- break;
-
- case 1:
- Emit1 (OpCode, Expr);
- break;
-
- case 2:
- AddrModeBit = (1L << AddrMode);
- if (CPU == CPU_65816 && (AddrModeBit & (AM_ABS | AM_ABS_X | AM_ABS_Y))) {
- /* This is a 16 bit mode that uses an address. If in 65816,
- * mode, force this address into 16 bit range to allow
- * addressing inside a 64K segment.
- */
- Emit2 (OpCode, ForceWordExpr (Expr));
- } else {
- Emit2 (OpCode, Expr);
- }
- break;
- case 3:
- if (Bank) {
- /* Separate bank given */
- Emit3b (OpCode, Expr, Bank);
- } else {
- /* One far argument */
- Emit3 (OpCode, Expr);
- }
- break;
- default:
- Internal ("Invalid operand byte count: %u", ExtBytes [AddrMode]);
+static void PutAll (const InsDesc* Ins)
+/* Handle all other instructions */
+{
+ EffAddr A;
+ /* Evaluate the addressing mode used */
+ if (EvalEA (Ins, &A)) {
+ /* No error, output code */
+ EmitCode (&A);
}
}