cc65/src/ca65/pseudo.c

2262 lines
55 KiB
C

/*****************************************************************************/
/* */
/* pseudo.c */
/* */
/* Pseudo instructions for the ca65 macroassembler */
/* */
/* */
/* */
/* (C) 1998-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. */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
/* common */
#include "alignment.h"
#include "assertion.h"
#include "bitops.h"
#include "cddefs.h"
#include "coll.h"
#include "filestat.h"
#include "gentype.h"
#include "intstack.h"
#include "scopedefs.h"
#include "symdefs.h"
#include "tgttrans.h"
#include "xmalloc.h"
/* ca65 */
#include "anonname.h"
#include "asserts.h"
#include "condasm.h"
#include "dbginfo.h"
#include "enum.h"
#include "error.h"
#include "expr.h"
#include "feature.h"
#include "filetab.h"
#include "global.h"
#include "incpath.h"
#include "instr.h"
#include "listing.h"
#include "macro.h"
#include "nexttok.h"
#include "objcode.h"
#include "options.h"
#include "pseudo.h"
#include "repeat.h"
#include "segment.h"
#include "sizeof.h"
#include "span.h"
#include "spool.h"
#include "struct.h"
#include "symbol.h"
#include "symtab.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Keyword we're about to handle */
static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
/* CPU stack */
static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
/* Segment stack */
#define MAX_PUSHED_SEGMENTS 16
static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
static void DoUnexpected (void);
/* Got an unexpected keyword */
static void DoInvalid (void);
/* Handle a token that is invalid here, since it should have been handled on
** a much lower level of the expression hierarchy. Getting this sort of token
** means that the lower level code has bugs.
** This function differs to DoUnexpected in that the latter may be triggered
** by the user by using keywords in the wrong location. DoUnexpected is not
** an error in the assembler itself, while DoInvalid is.
*/
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static unsigned char OptionalAddrSize (void)
/* If a colon follows, parse an optional address size spec and return it.
** Otherwise return ADDR_SIZE_DEFAULT.
*/
{
unsigned AddrSize = ADDR_SIZE_DEFAULT;
if (CurTok.Tok == TOK_COLON) {
NextTok ();
AddrSize = ParseAddrSize ();
if (!ValidAddrSizeForCPU (AddrSize)) {
/* Print an error and reset to default */
Error ("Invalid address size specification for current CPU");
AddrSize = ADDR_SIZE_DEFAULT;
}
NextTok ();
}
return AddrSize;
}
static void SetBoolOption (unsigned char* Flag)
/* Read a on/off/+/- option and set flag accordingly */
{
static const char* const Keys[] = {
"OFF",
"ON",
};
if (CurTok.Tok == TOK_PLUS) {
*Flag = 1;
NextTok ();
} else if (CurTok.Tok == TOK_MINUS) {
*Flag = 0;
NextTok ();
} else if (CurTok.Tok == TOK_IDENT) {
/* Map the keyword to a number */
switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
case 0: *Flag = 0; NextTok (); break;
case 1: *Flag = 1; NextTok (); break;
default: ErrorSkip ("'on' or 'off' expected"); break;
}
} else if (TokIsSep (CurTok.Tok)) {
/* Without anything assume switch on */
*Flag = 1;
} else {
ErrorSkip ("'on' or 'off' expected");
}
}
static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
/* Allow to assign the value of an export in an .export statement */
{
/* The name and optional address size spec may be followed by an assignment
** or equal token.
*/
if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
/* Assignment means the symbol is a label */
if (CurTok.Tok == TOK_ASSIGN) {
Flags |= SF_LABEL;
}
/* Skip the assignment token */
NextTok ();
/* Define the symbol with the expression following the '=' */
SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
}
/* Now export the symbol */
SymExport (Sym, AddrSize, Flags);
}
static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
unsigned char DefAddrSize, unsigned Flags)
/* Export or import symbols */
{
SymEntry* Sym;
unsigned char AddrSize;
while (1) {
/* We need an identifier here */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
/* Find the symbol table entry, allocate a new one if necessary */
Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
/* Skip the name */
NextTok ();
/* Get an optional address size */
AddrSize = OptionalAddrSize ();
if (AddrSize == ADDR_SIZE_DEFAULT) {
AddrSize = DefAddrSize;
}
/* Call the actual import/export function */
Func (Sym, AddrSize, Flags);
/* More symbols? */
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
} else {
break;
}
}
}
static long IntArg (long Min, long Max)
/* Read an integer argument and check a range. Accept the token "unlimited"
** and return -1 in this case.
*/
{
if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
NextTok ();
return -1;
} else {
long Val = ConstExpression ();
if (Val < Min || Val > Max) {
Error ("Range error");
Val = Min;
}
return Val;
}
}
static void ConDes (const StrBuf* Name, unsigned Type)
/* Parse remaining line for constructor/destructor of the remaining type */
{
long Prio;
/* Find the symbol table entry, allocate a new one if necessary */
SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
/* Optional constructor priority */
if (CurTok.Tok == TOK_COMMA) {
/* Priority value follows */
NextTok ();
Prio = ConstExpression ();
if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
/* Value out of range */
Error ("Range error");
return;
}
} else {
/* Use the default priority value */
Prio = CD_PRIO_DEF;
}
/* Define the symbol */
SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
}
static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize,
const char* ElementType,
unsigned ElementTypeLen)
/* Create an array (or single data) of the given type. SpanSize is the size
** of the span, ElementType is a string that encodes the element data type.
** The function returns Type.
*/
{
/* Get the size of the element type */
unsigned ElementSize = GT_GET_SIZE (ElementType[0]);
/* Get the number of array elements */
unsigned ElementCount = SpanSize / ElementSize;
/* The span size must be divideable by the element size */
CHECK ((SpanSize % ElementSize) == 0);
/* Encode the array */
GT_AddArray (Type, ElementCount);
SB_AppendBuf (Type, ElementType, ElementTypeLen);
/* Return the pointer to the created array type */
return Type;
}
/*****************************************************************************/
/* Handler functions */
/*****************************************************************************/
static void DoA16 (void)
/* Switch the accu to 16 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has two extension bytes */
ExtBytes [AM65I_IMM_ACCU] = 2;
}
}
static void DoA8 (void)
/* Switch the accu to 8 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has one extension byte */
ExtBytes [AM65I_IMM_ACCU] = 1;
}
}
static void DoAddr (void)
/* Define addresses */
{
/* Element type for the generated array */
static const char EType[2] = { GT_PTR, GT_VOID };
/* Record type information */
Span* S = OpenSpan ();
StrBuf Type = STATIC_STRBUF_INITIALIZER;
/* Parse arguments */
while (1) {
ExprNode* Expr = Expression ();
if (GetCPU () == CPU_65816 || ForceRange) {
/* Do a range check */
Expr = GenWordExpr (Expr);
}
EmitWord (Expr);
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
/* Close the span, then add type information to it */
S = CloseSpan (S);
SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
/* Free the strings */
SB_Done (&Type);
}
static void DoAlign (void)
/* Align the PC to some boundary */
{
long FillVal;
long Alignment;
/* Read the alignment value */
Alignment = ConstExpression ();
if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
ErrorSkip ("Range error");
return;
}
/* Optional value follows */
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
FillVal = ConstExpression ();
/* We need a byte value here */
if (!IsByteRange (FillVal)) {
ErrorSkip ("Range error");
return;
}
} else {
FillVal = -1;
}
/* Generate the alignment */
SegAlign (Alignment, (int) FillVal);
}
static void DoASCIIZ (void)
/* Define text with a zero terminator */
{
while (1) {
/* Must have a string constant */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
/* Translate into target charset and emit */
TgtTranslateStrBuf (&CurTok.SVal);
EmitStrBuf (&CurTok.SVal);
NextTok ();
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
} else {
break;
}
}
Emit0 (0);
}
static void DoAssert (void)
/* Add an assertion */
{
static const char* const ActionTab [] = {
"WARN", "WARNING",
"ERROR",
"LDWARN", "LDWARNING",
"LDERROR",
};
AssertAction Action;
unsigned Msg;
/* First we have the expression that has to evaluated */
ExprNode* Expr = Expression ();
ConsumeComma ();
/* Action follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
case 0:
case 1:
/* Warning */
Action = ASSERT_ACT_WARN;
break;
case 2:
/* Error */
Action = ASSERT_ACT_ERROR;
break;
case 3:
case 4:
/* Linker warning */
Action = ASSERT_ACT_LDWARN;
break;
case 5:
/* Linker error */
Action = ASSERT_ACT_LDERROR;
break;
default:
Error ("Illegal assert action specifier");
/* Use lderror - there won't be an .o file anyway */
Action = ASSERT_ACT_LDERROR;
break;
}
NextTok ();
/* We can have an optional message. If no message is present, use
** "Assertion failed".
*/
if (CurTok.Tok == TOK_COMMA) {
/* Skip the comma */
NextTok ();
/* Read the message */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
/* Translate the message into a string id. We can then skip the input
** string.
*/
Msg = GetStrBufId (&CurTok.SVal);
NextTok ();
} else {
/* Use "Assertion failed" */
Msg = GetStringId ("Assertion failed");
}
/* Remember the assertion */
AddAssertion (Expr, (AssertAction) Action, Msg);
}
static void DoAutoImport (void)
/* Mark unresolved symbols as imported */
{
SetBoolOption (&AutoImport);
}
static void DoBankBytes (void)
/* Define bytes, extracting the bank byte from each expression in the list */
{
while (1) {
EmitByte (FuncBankByte ());
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
}
static void DoBss (void)
/* Switch to the BSS segment */
{
UseSeg (&BssSegDef);
}
static void DoByteBase (int EnableTranslation)
/* Define bytes or literals */
{
/* Element type for the generated array */
static const char EType[1] = { GT_BYTE };
/* Record type information */
Span* S = OpenSpan ();
StrBuf Type = AUTO_STRBUF_INITIALIZER;
/* Parse arguments */
while (1) {
if (CurTok.Tok == TOK_STRCON) {
/* A string, translate into target charset
if appropriate */
if (EnableTranslation) {
TgtTranslateStrBuf (&CurTok.SVal);
}
/* Emit */
EmitStrBuf (&CurTok.SVal);
NextTok ();
} else {
EmitByte (BoundedExpr (Expression, 1));
}
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
/* Do smart handling of dangling comma */
if (CurTok.Tok == TOK_SEP) {
Error ("Unexpected end of line");
break;
}
}
}
/* Close the span, then add type information to it.
** Note: empty string operands emit nothing;
** so, add a type only if there's a span.
*/
S = CloseSpan (S);
if (S != 0) {
SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
}
/* Free the type string */
SB_Done (&Type);
}
static void DoByte (void)
/* Define bytes with translation */
{
DoByteBase (1);
}
static void DoCase (void)
/* Switch the IgnoreCase option */
{
SetBoolOption (&IgnoreCase);
IgnoreCase = !IgnoreCase;
}
static void DoCharMap (void)
/* Allow custom character mappings */
{
long Index;
long Code;
/* Read the index as numerical value */
Index = ConstExpression ();
if (Index < 0 || Index > 255) {
/* Value out of range */
ErrorSkip ("Index range error");
return;
}
/* Comma follows */
ConsumeComma ();
/* Read the character code */
Code = ConstExpression ();
if (Code < 0 || Code > 255) {
/* Value out of range */
ErrorSkip ("Code range error");
return;
}
/* Set the character translation */
TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
}
static void DoCode (void)
/* Switch to the code segment */
{
UseSeg (&CodeSegDef);
}
static void DoConDes (void)
/* Export a symbol as constructor/destructor */
{
static const char* const Keys[] = {
"CONSTRUCTOR",
"DESTRUCTOR",
"INTERRUPTOR",
};
StrBuf Name = STATIC_STRBUF_INITIALIZER;
long Type;
/* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
/* Type follows. May be encoded as identifier or numerical */
ConsumeComma ();
if (CurTok.Tok == TOK_IDENT) {
/* Map the following keyword to a number, then skip it */
Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
NextTok ();
/* Check if we got a valid keyword */
if (Type < 0) {
ErrorSkip ("Syntax error");
goto ExitPoint;
}
} else {
/* Read the type as numerical value */
Type = ConstExpression ();
if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
/* Value out of range */
ErrorSkip ("Range error");
goto ExitPoint;
}
}
/* Parse the remainder of the line and export the symbol */
ConDes (&Name, (unsigned) Type);
ExitPoint:
/* Free string memory */
SB_Done (&Name);
}
static void DoConstructor (void)
/* Export a symbol as constructor */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
/* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
/* Parse the remainder of the line and export the symbol */
ConDes (&Name, CD_TYPE_CON);
/* Free string memory */
SB_Done (&Name);
}
static void DoData (void)
/* Switch to the data segment */
{
UseSeg (&DataSegDef);
}
static void DoDbg (void)
/* Add debug information from high level code */
{
static const char* const Keys[] = {
"FILE",
"FUNC",
"LINE",
"SYM",
};
int Key;
/* We expect a subkey */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
/* Map the following keyword to a number */
Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
/* Skip the subkey */
NextTok ();
/* Check the key and dispatch to a handler */
switch (Key) {
case 0: DbgInfoFile (); break;
case 1: DbgInfoFunc (); break;
case 2: DbgInfoLine (); break;
case 3: DbgInfoSym (); break;
default: ErrorSkip ("Syntax error"); break;
}
}
static void DoDByt (void)
/* Output double bytes */
{
/* Element type for the generated array */
static const char EType[1] = { GT_DBYTE };
/* Record type information */
Span* S = OpenSpan ();
StrBuf Type = STATIC_STRBUF_INITIALIZER;
/* Parse arguments */
while (1) {
EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
/* Close the span, then add type information to it */
S = CloseSpan (S);
SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
/* Free the type string */
SB_Done (&Type);
}
static void DoDebugInfo (void)
/* Switch debug info on or off */
{
SetBoolOption (&DbgSyms);
}
static void DoDefine (void)
/* Define a one-line macro */
{
/* The function is called with the .DEFINE token in place, because we need
** to disable .define macro expansions before reading the next token.
** Otherwise, the name of the macro might be expanded; therefore,
** we never would see it.
*/
DisableDefineStyleMacros ();
NextTok ();
EnableDefineStyleMacros ();
MacDef (MAC_STYLE_DEFINE);
}
static void DoDelMac (void)
/* Delete a classic macro */
{
/* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
} else {
MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
NextTok ();
}
}
static void DoDestructor (void)
/* Export a symbol as destructor */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
/* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
/* Parse the remainder of the line and export the symbol */
ConDes (&Name, CD_TYPE_DES);
/* Free string memory */
SB_Done (&Name);
}
static void DoDWord (void)
/* Define dwords */
{
while (1) {
EmitDWord (BoundedExpr (Expression, 4));
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
}
static void DoEnd (void)
/* End of assembly */
{
ForcedEnd = 1;
NextTok ();
}
static void DoEndProc (void)
/* Leave a lexical level */
{
if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
/* No local scope */
ErrorSkip ("No open .PROC");
} else {
SymLeaveLevel ();
}
}
static void DoEndScope (void)
/* Leave a lexical level */
{
if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
/* No local scope */
ErrorSkip ("No open .SCOPE");
} else {
SymLeaveLevel ();
}
}
static void DoError (void)
/* User error */
{
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
Error ("User error: %m%p", &CurTok.SVal);
SkipUntilSep ();
}
}
static void DoExitMacro (void)
/* Exit a macro expansion */
{
if (!InMacExpansion ()) {
/* We aren't expanding a macro currently */
DoUnexpected ();
} else {
MacAbort ();
}
}
static void DoExport (void)
/* Export a symbol */
{
ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
}
static void DoExportZP (void)
/* Export a zeropage symbol */
{
ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
}
static void DoFarAddr (void)
/* Define far addresses (24 bit) */
{
/* Element type for the generated array */
static const char EType[2] = { GT_FAR_PTR, GT_VOID };
/* Record type information */
Span* S = OpenSpan ();
StrBuf Type = STATIC_STRBUF_INITIALIZER;
/* Parse arguments */
while (1) {
EmitFarAddr (BoundedExpr (Expression, 3));
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
/* Close the span, then add type information to it */
S = CloseSpan (S);
SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
/* Free the type string */
SB_Done (&Type);
}
static void DoFatal (void)
/* Fatal user error */
{
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
Fatal ("User error: %m%p", &CurTok.SVal);
SkipUntilSep ();
}
}
static void DoFeature (void)
/* Switch the Feature option */
{
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 */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
/* Make the string attribute lower case */
LocaseSVal ();
Feature = FindFeature(&CurTok.SVal);
if (Feature == FEAT_UNKNOWN) {
/* Not found */
ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal);
return;
}
if (Feature == FEAT_ADDRSIZE) {
Warning (1, "Deprecated feature: '.feature addrsize'. Pseudo function .addrsize is always available.");
}
NextTok ();
/* Optional +/- or ON/OFF */
On = 1;
if (CurTok.Tok != TOK_COMMA && !TokIsSep (CurTok.Tok)) {
SetBoolOption(&On);
}
/* Apply feature setting. */
SetFeature (Feature, On);
/* Allow more than one feature separated by commas. */
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
} else {
break;
}
}
}
static void DoFileOpt (void)
/* Insert a file option */
{
long OptNum;
/* The option type may be given as a keyword or as a number. */
if (CurTok.Tok == TOK_IDENT) {
/* Option given as keyword */
static const char* const Keys [] = {
"AUTHOR", "COMMENT", "COMPILER"
};
/* Map the option to a number */
OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
if (OptNum < 0) {
/* Not found */
ErrorSkip ("File option keyword expected");
return;
}
/* Skip the keyword */
NextTok ();
/* Must be followed by a comma */
ConsumeComma ();
/* We accept only string options for now */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
/* Insert the option */
switch (OptNum) {
case 0:
/* Author */
OptAuthor (&CurTok.SVal);
break;
case 1:
/* Comment */
OptComment (&CurTok.SVal);
break;
case 2:
/* Compiler */
OptCompiler (&CurTok.SVal);
break;
default:
Internal ("Invalid OptNum: %ld", OptNum);
}
/* Done */
NextTok ();
} else {
/* Option given as number */
OptNum = ConstExpression ();
if (!IsByteRange (OptNum)) {
ErrorSkip ("Range error");
return;
}
/* Must be followed by a comma */
ConsumeComma ();
/* We accept only string options for now */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
/* Insert the option */
OptStr ((unsigned char) OptNum, &CurTok.SVal);
/* Done */
NextTok ();
}
}
static void DoForceImport (void)
/* Do a forced import on a symbol */
{
ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
}
static void DoGlobal (void)
/* Declare a global symbol */
{
ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
}
static void DoGlobalZP (void)
/* Declare a global zeropage symbol */
{
ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
}
static void DoHiBytes (void)
/* Define bytes, extracting the hi byte from each expression in the list */
{
while (1) {
EmitByte (FuncHiByte ());
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
}
static void DoI16 (void)
/* Switch the index registers to 16 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has two extension bytes */
ExtBytes [AM65I_IMM_INDEX] = 2;
}
}
static void DoI8 (void)
/* Switch the index registers to 16 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has one extension byte */
ExtBytes [AM65I_IMM_INDEX] = 1;
}
}
static void DoImport (void)
/* Import a symbol */
{
ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
}
static void DoImportZP (void)
/* Import a zero page symbol */
{
ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
}
static void DoIncBin (void)
/* Include a binary file */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
struct stat StatBuf;
long Start = 0L;
long Count = -1L;
long Size;
FILE* F;
/* Name must follow */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
SB_Copy (&Name, &CurTok.SVal);
SB_Terminate (&Name);
NextTok ();
/* A starting offset may follow */
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
Start = ConstExpression ();
/* And a length may follow */
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
Count = ConstExpression ();
}
}
/* Try to open the file */
F = fopen (SB_GetConstBuf (&Name), "rb");
if (F == 0) {
/* Search for the file in the binary include directory */
char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
/* Not found or cannot open, print an error and bail out */
ErrorSkip ("Cannot open include file '%m%p': %s", &Name, strerror (errno));
xfree (PathName);
goto ExitPoint;
}
/* Remember the new file name */
SB_CopyStr (&Name, PathName);
/* Free the allocated memory */
xfree (PathName);
}
/* Get the size of the file */
fseek (F, 0, SEEK_END);
Size = ftell (F);
/* Stat the file and remember the values. There's a race condition here,
** since we cannot use fileno() (non-standard identifier in standard
** header file), and therefore not fstat. When using stat with the
** file name, there's a risk that the file was deleted and recreated
** while it was open. Since mtime and size are only used to check
** if a file has changed in the debugger, we will ignore this problem
** here.
*/
SB_Terminate (&Name);
if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
Fatal ("Cannot stat input file '%m%p': %s", &Name, strerror (errno));
}
/* Add the file to the input file table */
AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime);
/* If a count was not given, calculate it now */
if (Count < 0) {
Count = Size - Start;
if (Count < 0) {
/* Nothing to read - flag this as a range error */
ErrorSkip ("Range error");
goto Done;
}
} else {
/* Count was given, check if it is valid */
if (Start + Count > Size) {
ErrorSkip ("Range error");
goto Done;
}
}
/* Seek to the start position */
fseek (F, Start, SEEK_SET);
/* Read chunks and insert them into the output */
while (Count > 0) {
unsigned char Buf [1024];
/* Calculate the number of bytes to read */
size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
/* Read chunk */
size_t BytesRead = fread (Buf, 1, BytesToRead, F);
if (BytesToRead != BytesRead) {
/* Some sort of error */
ErrorSkip ("Cannot read from include file '%m%p': %s",
&Name, strerror (errno));
break;
}
/* Insert it into the output */
EmitData (Buf, BytesRead);
/* Keep the counters current */
Count -= BytesRead;
}
Done:
/* Close the file, ignore errors since it's r/o */
(void) fclose (F);
ExitPoint:
/* Free string memory */
SB_Done (&Name);
}
static void DoInclude (void)
/* Include another file */
{
/* Name must follow */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
SB_Terminate (&CurTok.SVal);
if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
/* Error opening the file, skip remainder of line */
SkipUntilSep ();
}
}
}
static void DoInterruptor (void)
/* Export a symbol as interruptor */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
/* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
return;
}
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
/* Parse the remainder of the line and export the symbol */
ConDes (&Name, CD_TYPE_INT);
/* Free string memory */
SB_Done (&Name);
}
static void DoInvalid (void)
/* Handle a token that is invalid here, since it should have been handled on
** a much lower level of the expression hierarchy. Getting this sort of token
** means that the lower level code has bugs.
** This function differs to DoUnexpected in that the latter may be triggered
** by the user by using keywords in the wrong location. DoUnexpected is not
** an error in the assembler itself, while DoInvalid is.
*/
{
Internal ("Unexpected token: %m%p", &Keyword);
}
static void DoLineCont (void)
/* Switch the use of line continuations */
{
SetBoolOption (&LineCont);
}
static void DoList (void)
/* Enable/disable the listing */
{
/* Get the setting */
unsigned char List = 0;
SetBoolOption (&List);
/* Manage the counter */
if (List) {
EnableListing ();
} else {
DisableListing ();
}
}
static void DoLiteral (void)
/* Define bytes without translation */
{
DoByteBase (0);
}
static void DoLoBytes (void)
/* Define bytes, extracting the lo byte from each expression in the list */
{
while (1) {
EmitByte (FuncLoByte ());
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
}
static void DoListBytes (void)
/* Set maximum number of bytes to list for one line */
{
SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
}
static void DoLocalChar (void)
/* Define the character that starts local labels */
{
if (CurTok.Tok != TOK_CHARCON) {
ErrorSkip ("Character constant expected");
} else {
if (CurTok.IVal != '@' && CurTok.IVal != '?') {
Error ("Invalid start character for locals");
} else {
LocalStart = (char) CurTok.IVal;
}
NextTok ();
}
}
static void DoMacPack (void)
/* Insert a macro package */
{
/* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
} else {
SB_AppendStr (&CurTok.SVal, ".mac");
SB_Terminate (&CurTok.SVal);
if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
/* Error opening the file, skip remainder of line */
SkipUntilSep ();
}
}
}
static void DoMacro (void)
/* Start a macro definition */
{
MacDef (MAC_STYLE_CLASSIC);
}
static void DoNull (void)
/* Switch to the NULL segment */
{
UseSeg (&NullSegDef);
}
static void DoOrg (void)
/* Start absolute code */
{
long PC = ConstExpression ();
if (PC < 0 || PC > 0xFFFFFF) {
Error ("Range error");
return;
}
EnterAbsoluteMode (PC);
}
static void DoOut (void)
/* Output a string */
{
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
/* Output the string and be sure to flush the output to keep it in
** sync with any error messages if the output is redirected to a file.
*/
printf ("%.*s\n",
(int) SB_GetLen (&CurTok.SVal),
SB_GetConstBuf (&CurTok.SVal));
fflush (stdout);
NextTok ();
}
}
static void DoP02 (void)
/* Switch to 6502 CPU */
{
SetCPU (CPU_6502);
}
static void DoPC02 (void)
/* Switch to 65C02 CPU */
{
SetCPU (CPU_65C02);
}
static void DoP816 (void)
/* Switch to 65816 CPU */
{
SetCPU (CPU_65816);
}
static void DoP4510 (void)
/* Switch to 4510 CPU */
{
SetCPU (CPU_4510);
}
static void DoPDTV (void)
/* Switch to C64DTV CPU */
{
SetCPU (CPU_6502DTV);
}
static void DoPageLength (void)
/* Set the page length for the listing */
{
PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
}
static void DoPopCharmap (void)
/* Restore a charmap */
{
if (TgtTranslateStackIsEmpty ()) {
ErrorSkip ("Charmap stack is empty");
return;
}
TgtTranslatePop ();
}
static void DoPopCPU (void)
/* Pop an old CPU setting from the CPU stack */
{
/* Must have a CPU on the stack */
if (IS_IsEmpty (&CPUStack)) {
ErrorSkip ("CPU stack is empty");
return;
}
/* Set the CPU to the value popped from stack */
SetCPU (IS_Pop (&CPUStack));
}
static void DoPopSeg (void)
/* Pop an old segment from the segment stack */
{
SegDef* Def;
/* Must have a segment on the stack */
if (CollCount (&SegStack) == 0) {
ErrorSkip ("Segment stack is empty");
return;
}
/* Pop the last element */
Def = CollPop (&SegStack);
/* Restore this segment */
UseSeg (Def);
/* Delete the segment definition */
FreeSegDef (Def);
}
static void DoProc (void)
/* Start a new lexical scope */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
unsigned char AddrSize;
SymEntry* Sym = 0;
if (CurTok.Tok == TOK_IDENT) {
/* The new scope has a name. Remember it. */
SB_Copy (&Name, &CurTok.SVal);
/* Search for the symbol, generate a new one if needed */
Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
/* Skip the scope name */
NextTok ();
/* Read an optional address size specifier */
AddrSize = OptionalAddrSize ();
/* Mark the symbol as defined */
SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
} else {
/* A .PROC statement without a name */
Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
AnonName (&Name, "PROC");
AddrSize = ADDR_SIZE_DEFAULT;
}
/* Enter a new scope */
SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
/* Free memory for Name */
SB_Done (&Name);
}
static void DoPSC02 (void)
/* Switch to 65SC02 CPU */
{
SetCPU (CPU_65SC02);
}
static void DoPushCharmap (void)
/* Save the current charmap */
{
if (!TgtTranslatePush ()) {
ErrorSkip ("Charmap stack overflow");
}
}
static void DoPushCPU (void)
/* Push the current CPU setting onto the CPU stack */
{
/* Can only push a limited size of segments */
if (IS_IsFull (&CPUStack)) {
ErrorSkip ("CPU stack overflow");
return;
}
/* Get the current segment and push it */
IS_Push (&CPUStack, GetCPU ());
}
static void DoPushSeg (void)
/* Push the current segment onto the segment stack */
{
/* Can only push a limited size of segments */
if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
ErrorSkip ("Segment stack overflow");
return;
}
/* Get the current segment and push it */
CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
}
static void DoReferTo (void)
/* Mark given symbol as referenced */
{
SymEntry* Sym = ParseAnySymName (SYM_ALLOC_NEW);
if (Sym) {
SymRef (Sym);
}
}
static void DoReloc (void)
/* Enter relocatable mode */
{
EnterRelocMode ();
}
static void DoRepeat (void)
/* Repeat some instruction block */
{
ParseRepeat ();
}
static void DoRes (void)
/* Reserve some number of storage bytes */
{
long Count;
long Val;
Count = ConstExpression ();
if (Count > 0xFFFF || Count < 0) {
ErrorSkip ("Range error");
return;
}
if (CurTok.Tok == TOK_COMMA) {
NextTok ();
Val = ConstExpression ();
/* We need a byte value here */
if (!IsByteRange (Val)) {
ErrorSkip ("Range error");
return;
}
/* Emit constant values */
while (Count--) {
Emit0 ((unsigned char) Val);
}
} else {
/* Emit fill fragments */
EmitFill (Count);
}
}
static void DoROData (void)
/* Switch to the r/o data segment */
{
UseSeg (&RODataSegDef);
}
static void DoScope (void)
/* Start a local scope */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
unsigned char AddrSize;
if (CurTok.Tok == TOK_IDENT) {
/* The new scope has a name. Remember and skip it. */
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
} else {
/* An unnamed scope */
AnonName (&Name, "SCOPE");
}
/* Read an optional address size specifier */
AddrSize = OptionalAddrSize ();
/* Enter the new scope */
SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
/* Free memory for Name */
SB_Done (&Name);
}
static void DoSegment (void)
/* Switch to another segment */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
SegDef Def;
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
/* Save the name of the segment and skip it */
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
/* Use the name for the segment definition */
SB_Terminate (&Name);
Def.Name = SB_GetBuf (&Name);
/* Check for an optional address size modifier */
Def.AddrSize = OptionalAddrSize ();
/* Set the segment */
UseSeg (&Def);
}
/* Free memory for Name */
SB_Done (&Name);
}
static void DoSetCPU (void)
/* Switch the CPU instruction set */
{
/* We expect an identifier */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
cpu_t CPU;
/* Try to find the CPU */
SB_Terminate (&CurTok.SVal);
CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
/* Switch to the new CPU */
SetCPU (CPU);
/* Skip the identifier. If the CPU switch was successful, the scanner
** will treat the input now correctly for the new CPU.
*/
NextTok ();
}
}
static void DoSmart (void)
/* Smart mode on/off */
{
SetBoolOption (&SmartMode);
}
static void DoTag (void)
/* Allocate space for a struct */
{
SymEntry* SizeSym;
long Size;
/* Read the struct name */
SymTable* Struct = ParseScopedSymTable ();
/* Check the supposed struct */
if (Struct == 0) {
ErrorSkip ("Unknown struct");
return;
}
if (GetSymTabType (Struct) != SCOPE_STRUCT) {
ErrorSkip ("Not a struct");
return;
}
/* Get the symbol that defines the size of the struct */
SizeSym = GetSizeOfScope (Struct);
/* Check if it does exist and if its value is known */
if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
ErrorSkip ("Size of struct/union is unknown");
return;
}
/* Optional multiplicator may follow */
if (CurTok.Tok == TOK_COMMA) {
long Multiplicator;
NextTok ();
Multiplicator = ConstExpression ();
/* Multiplicator must make sense */
if (Multiplicator <= 0) {
ErrorSkip ("Range error");
return;
}
Size *= Multiplicator;
}
/* Emit fill fragments */
EmitFill (Size);
}
static void DoUnDef (void)
/* Undefine a define-style macro */
{
/* The function is called with the .UNDEF token in place, because we need
** to disable .define macro expansions before reading the next token.
** Otherwise, the name of the macro would be expanded; therefore,
** we never would see it.
*/
DisableDefineStyleMacros ();
NextTok ();
EnableDefineStyleMacros ();
/* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Identifier expected");
} else {
MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
NextTok ();
}
}
static void DoUnexpected (void)
/* Got an unexpected keyword */
{
Error ("Unexpected '%m%p'", &Keyword);
SkipUntilSep ();
}
static void DoWarning (void)
/* User warning */
{
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
Warning (0, "User warning: %m%p", &CurTok.SVal);
SkipUntilSep ();
}
}
static void DoWord (void)
/* Define words */
{
/* Element type for the generated array */
static const char EType[1] = { GT_WORD };
/* Record type information */
Span* S = OpenSpan ();
StrBuf Type = STATIC_STRBUF_INITIALIZER;
/* Parse arguments */
while (1) {
EmitWord (BoundedExpr (Expression, 2));
if (CurTok.Tok != TOK_COMMA) {
break;
} else {
NextTok ();
}
}
/* Close the span, then add type information to it */
S = CloseSpan (S);
SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
/* Free the type string */
SB_Done (&Type);
}
static void DoZeropage (void)
/* Switch to the zeropage segment */
{
UseSeg (&ZeropageSegDef);
}
/*****************************************************************************/
/* Table data */
/*****************************************************************************/
/* Control commands flags */
enum {
ccNone = 0x0000, /* No special flags */
ccKeepToken = 0x0001 /* Do not skip the control token */
};
/* Control command table */
typedef struct CtrlDesc CtrlDesc;
struct CtrlDesc {
unsigned Flags; /* Flags for this directive */
void (*Handler) (void); /* Command handler */
};
#define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
static CtrlDesc CtrlCmdTab [] = {
{ ccNone, DoA16 },
{ ccNone, DoA8 },
{ ccNone, DoAddr }, /* .ADDR */
{ ccNone, DoUnexpected }, /* .ADDRSIZE */
{ ccNone, DoAlign },
{ ccNone, DoASCIIZ },
{ ccNone, DoUnexpected }, /* .ASIZE */
{ ccNone, DoAssert },
{ ccNone, DoAutoImport },
{ ccNone, DoUnexpected }, /* .BANK */
{ ccNone, DoUnexpected }, /* .BANKBYTE */
{ ccNone, DoBankBytes },
{ ccNone, DoUnexpected }, /* .BLANK */
{ ccNone, DoBss },
{ ccNone, DoByte },
{ ccNone, DoCase },
{ ccNone, DoCharMap },
{ ccNone, DoCode },
{ ccNone, DoUnexpected, }, /* .CONCAT */
{ ccNone, DoConDes },
{ ccNone, DoUnexpected }, /* .CONST */
{ ccNone, DoConstructor },
{ ccNone, DoUnexpected }, /* .CPU */
{ ccNone, DoData },
{ ccNone, DoDbg, },
{ ccNone, DoDByt },
{ ccNone, DoDebugInfo },
{ ccKeepToken, DoDefine },
{ ccNone, DoUnexpected }, /* .DEFINED */
{ ccNone, DoUnexpected }, /* .DEFINEDMACRO */
{ ccNone, DoDelMac },
{ ccNone, DoDestructor },
{ ccNone, DoDWord },
{ ccKeepToken, DoConditionals }, /* .ELSE */
{ ccKeepToken, DoConditionals }, /* .ELSEIF */
{ ccKeepToken, DoEnd },
{ ccNone, DoUnexpected }, /* .ENDENUM */
{ ccKeepToken, DoConditionals }, /* .ENDIF */
{ ccNone, DoUnexpected }, /* .ENDMACRO */
{ ccNone, DoEndProc },
{ ccNone, DoUnexpected }, /* .ENDREPEAT */
{ ccNone, DoEndScope },
{ ccNone, DoUnexpected }, /* .ENDSTRUCT */
{ ccNone, DoUnexpected }, /* .ENDUNION */
{ ccNone, DoEnum },
{ ccNone, DoError },
{ ccNone, DoExitMacro },
{ ccNone, DoExport },
{ ccNone, DoExportZP },
{ ccNone, DoFarAddr },
{ ccNone, DoFatal },
{ ccNone, DoFeature },
{ ccNone, DoFileOpt },
{ ccNone, DoForceImport },
{ ccNone, DoUnexpected }, /* .FORCEWORD */
{ ccNone, DoGlobal },
{ ccNone, DoGlobalZP },
{ ccNone, DoUnexpected }, /* .HIBYTE */
{ ccNone, DoHiBytes },
{ ccNone, DoUnexpected }, /* .HIWORD */
{ ccNone, DoI16 },
{ ccNone, DoI8 },
{ ccNone, DoUnexpected }, /* .IDENT */
{ ccKeepToken, DoConditionals }, /* .IF */
{ ccKeepToken, DoConditionals }, /* .IFBLANK */
{ ccKeepToken, DoConditionals }, /* .IFCONST */
{ ccKeepToken, DoConditionals }, /* .IFDEF */
{ ccKeepToken, DoConditionals }, /* .IFNBLANK */
{ ccKeepToken, DoConditionals }, /* .IFNCONST */
{ ccKeepToken, DoConditionals }, /* .IFNDEF */
{ ccKeepToken, DoConditionals }, /* .IFNREF */
{ ccKeepToken, DoConditionals }, /* .IFP02 */
{ ccKeepToken, DoConditionals }, /* .IFP4510 */
{ ccKeepToken, DoConditionals }, /* .IFP816 */
{ ccKeepToken, DoConditionals }, /* .IFPC02 */
{ ccKeepToken, DoConditionals }, /* .IFPDTV */
{ ccKeepToken, DoConditionals }, /* .IFPSC02 */
{ ccKeepToken, DoConditionals }, /* .IFREF */
{ ccNone, DoImport },
{ ccNone, DoImportZP },
{ ccNone, DoIncBin },
{ ccNone, DoInclude },
{ ccNone, DoInterruptor },
{ ccNone, DoUnexpected }, /* .ISIZE */
{ ccNone, DoUnexpected }, /* .ISMNEMONIC */
{ ccNone, DoInvalid }, /* .LEFT */
{ ccNone, DoLineCont },
{ ccNone, DoList },
{ ccNone, DoListBytes },
{ ccNone, DoLiteral },
{ ccNone, DoUnexpected }, /* .LOBYTE */
{ ccNone, DoLoBytes },
{ ccNone, DoUnexpected }, /* .LOCAL */
{ ccNone, DoLocalChar },
{ ccNone, DoUnexpected }, /* .LOWORD */
{ ccNone, DoMacPack },
{ ccNone, DoMacro },
{ ccNone, DoUnexpected }, /* .MATCH */
{ ccNone, DoUnexpected }, /* .MAX */
{ ccNone, DoInvalid }, /* .MID */
{ ccNone, DoUnexpected }, /* .MIN */
{ ccNone, DoNull },
{ ccNone, DoOrg },
{ ccNone, DoOut },
{ ccNone, DoP02 },
{ ccNone, DoP4510 },
{ ccNone, DoP816 },
{ ccNone, DoPageLength },
{ ccNone, DoUnexpected }, /* .PARAMCOUNT */
{ ccNone, DoPC02 },
{ ccNone, DoPDTV },
{ ccNone, DoPopCharmap },
{ ccNone, DoPopCPU },
{ ccNone, DoPopSeg },
{ ccNone, DoProc },
{ ccNone, DoPSC02 },
{ ccNone, DoPushCharmap },
{ ccNone, DoPushCPU },
{ ccNone, DoPushSeg },
{ ccNone, DoUnexpected }, /* .REFERENCED */
{ ccNone, DoReferTo }, /* .REFERTO */
{ ccNone, DoReloc },
{ ccNone, DoRepeat },
{ ccNone, DoRes },
{ ccNone, DoInvalid }, /* .RIGHT */
{ ccNone, DoROData },
{ ccNone, DoScope },
{ ccNone, DoSegment },
{ ccNone, DoUnexpected }, /* .SET */
{ ccNone, DoSetCPU },
{ ccNone, DoUnexpected }, /* .SIZEOF */
{ ccNone, DoSmart },
{ ccNone, DoUnexpected }, /* .SPRINTF */
{ ccNone, DoUnexpected }, /* .STRAT */
{ ccNone, DoUnexpected }, /* .STRING */
{ ccNone, DoUnexpected }, /* .STRLEN */
{ ccNone, DoStruct },
{ ccNone, DoTag },
{ ccNone, DoUnexpected }, /* .TCOUNT */
{ ccNone, DoUnexpected }, /* .TIME */
{ ccKeepToken, DoUnDef },
{ ccNone, DoUnion },
{ ccNone, DoUnexpected }, /* .VERSION */
{ ccNone, DoWarning },
{ ccNone, DoWord },
{ ccNone, DoUnexpected }, /* .XMATCH */
{ ccNone, DoZeropage },
};
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void HandlePseudo (void)
/* Handle a pseudo instruction */
{
CtrlDesc* D;
/* Calculate the index into the table */
unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
/* Safety check */
if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
(unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
}
CHECK (Index < PSEUDO_COUNT);
/* Get the pseudo intruction descriptor */
D = &CtrlCmdTab [Index];
/* Remember the instruction, then skip it if needed */
if ((D->Flags & ccKeepToken) == 0) {
SB_Copy (&Keyword, &CurTok.SVal);
NextTok ();
}
/* Call the handler */
D->Handler ();
}
void CheckPseudo (void)
/* Check if the stacks are empty at end of assembly */
{
if (CollCount (&SegStack) != 0) {
Warning (1, "Segment stack is not empty");
}
if (!IS_IsEmpty (&CPUStack)) {
Warning (1, "CPU stack is not empty");
}
if (!TgtTranslateStackIsEmpty ()) {
Warning (1, "Charmap stack is not empty");
}
}