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