mirror of http://shamusworld.gotdns.org/git/rmac
878 lines
20 KiB
C
878 lines
20 KiB
C
//
|
|
// RMAC - Renamed Macro Assembler for all Atari computers
|
|
// PROCLN.C - Line Processing
|
|
// Copyright (C) 199x Landon Dyer, 2011-2022 Reboot and Friends
|
|
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
|
|
// Source utilised with the kind permission of Landon Dyer
|
|
//
|
|
|
|
#include "procln.h"
|
|
#include "6502.h"
|
|
#include "amode.h"
|
|
#include "direct.h"
|
|
#include "dsp56k_amode.h"
|
|
#include "dsp56k_mach.h"
|
|
#include "error.h"
|
|
#include "expr.h"
|
|
#include "listing.h"
|
|
#include "mach.h"
|
|
#include "macro.h"
|
|
#include "op.h"
|
|
#include "riscasm.h"
|
|
#include "sect.h"
|
|
#include "symbol.h"
|
|
|
|
#define DEF_KW // Declare keyword values
|
|
#include "kwtab.h" // Incl generated keyword tables & defs
|
|
|
|
#define DEF_MN // Incl 68k keyword definitions
|
|
#define DECL_MN // Incl 68k keyword state machine tables
|
|
#include "mntab.h"
|
|
#define DEF_REG68 // Incl 68k register definitions
|
|
#include "68kregs.h"
|
|
|
|
#define DEF_MR
|
|
#define DECL_MR
|
|
#include "risckw.h"
|
|
|
|
#define DEF_MP // Include 6502 keyword definitions
|
|
#define DECL_MP // Include 6502 keyword state machine tables
|
|
#include "6502kw.h"
|
|
|
|
#define DEF_MO // Include OP keyword definitions
|
|
#define DECL_MO // Include OP keyword state machine tables
|
|
#include "opkw.h"
|
|
|
|
#define DEF_DSP // Include DSP56K keywords definitions
|
|
#define DECL_DSP // Include DSP56K keyword state machine tables
|
|
#include "dsp56kkw.h"
|
|
#define DEF_REG56 // Include DSP56K register definitions
|
|
#include "56kregs.h"
|
|
|
|
IFENT * ifent; // Current ifent
|
|
static IFENT ifent0; // Root ifent
|
|
IFENT * f_ifent; // Freelist of ifents
|
|
int disabled; // Assembly conditionally disabled
|
|
int just_bss; // 1, ds.b in microprocessor mode
|
|
uint32_t pcloc; // Value of "PC" at beginning of line
|
|
SYM * lab_sym; // Label on line (or NULL)
|
|
char * label_defined; // The name of the last label defined in current line (if any)
|
|
|
|
const char extra_stuff[] = "extra (unexpected) text found after addressing mode";
|
|
const char comma_error[] = "missing comma";
|
|
const char syntax_error[] = "syntax error";
|
|
const char locgl_error[] = "cannot GLOBL local symbol";
|
|
const char lab_ignored[] = "label ignored";
|
|
|
|
// Table to convert an addressing-mode number to a bitmask.
|
|
LONG amsktab[0124] = {
|
|
M_DREG, M_DREG, M_DREG, M_DREG,
|
|
M_DREG, M_DREG, M_DREG, M_DREG,
|
|
|
|
M_AREG, M_AREG, M_AREG, M_AREG,
|
|
M_AREG, M_AREG, M_AREG, M_AREG,
|
|
|
|
M_AIND, M_AIND, M_AIND, M_AIND,
|
|
M_AIND, M_AIND, M_AIND, M_AIND,
|
|
|
|
M_APOSTINC, M_APOSTINC, M_APOSTINC, M_APOSTINC,
|
|
M_APOSTINC, M_APOSTINC, M_APOSTINC, M_APOSTINC,
|
|
|
|
M_APREDEC, M_APREDEC, M_APREDEC, M_APREDEC,
|
|
M_APREDEC, M_APREDEC, M_APREDEC, M_APREDEC,
|
|
|
|
M_ADISP, M_ADISP, M_ADISP, M_ADISP,
|
|
M_ADISP, M_ADISP, M_ADISP, M_ADISP,
|
|
|
|
M_AINDEXED, M_AINDEXED, M_AINDEXED, M_AINDEXED,
|
|
M_AINDEXED, M_AINDEXED, M_AINDEXED, M_AINDEXED,
|
|
|
|
M_ABSW, // 070
|
|
M_ABSL, // 071
|
|
M_PCDISP, // 072
|
|
M_PCINDEXED, // 073
|
|
M_IMMED, // 074
|
|
0L, // 075
|
|
0L, // 076
|
|
0L, // 077
|
|
M_ABASE, // 0100
|
|
M_MEMPOST, // 0101
|
|
M_MEMPRE, // 0102
|
|
M_PCBASE, // 0103
|
|
M_PCMPOST, // 0104
|
|
M_PCMPRE, // 0105
|
|
M_AM_USP, // 0106
|
|
M_AM_SR, // 0107
|
|
M_AM_CCR, // 0110
|
|
M_AM_NONE, // 0111
|
|
0x30, // 0112
|
|
0x30, // 0113
|
|
0L, // 0114
|
|
0L, // 0115
|
|
0L, // 0116
|
|
0L, // 0117
|
|
M_CACHE40, // 0120
|
|
M_CREG, // 0121
|
|
M_FREG, // 0122
|
|
M_FPSCR // 0123
|
|
}; // 0123 length
|
|
|
|
// Function prototypes
|
|
int HandleLabel(char *, int);
|
|
|
|
//
|
|
// Initialize line processor
|
|
//
|
|
void InitLineProcessor(void)
|
|
{
|
|
disabled = 0;
|
|
ifent = &ifent0;
|
|
f_ifent = ifent0.if_prev = NULL;
|
|
ifent0.if_state = 0;
|
|
}
|
|
|
|
//
|
|
// Line processor
|
|
//
|
|
void Assemble(void)
|
|
{
|
|
int state; // Keyword machine state (output)
|
|
int j; // Random int, must be fast
|
|
char * p; // Random char ptr, must be fast
|
|
TOKEN * tk; // First token in line
|
|
char * label; // Symbol (or NULL)
|
|
char * equate; // Symbol (or NULL)
|
|
int labtyp = 0; // Label type (':', DCOLON)
|
|
int equtyp = 0; // Equ type ('=', DEQUALS)
|
|
uint64_t eval; // Expression value
|
|
WORD eattr; // Expression attributes
|
|
SYM * esym; // External symbol involved in expr.
|
|
WORD siz = 0; // Size suffix to mnem/diretve/macro
|
|
LONG amsk0, amsk1; // Address-type masks for ea0, ea1
|
|
MNTAB * m; // Code generation table pointer
|
|
SYM * sy, * sy2; // Symbol (temp usage)
|
|
char * opname = NULL; // Name of dirctve/mnemonic/macro
|
|
int listflag; // 0: Don't call listeol()
|
|
WORD rmask; // Register list, for REG
|
|
int equreg; // RISC register
|
|
listflag = 0; // Initialise listing flag
|
|
|
|
loop: // Line processing loop label
|
|
|
|
// Get another line of tokens
|
|
if (TokenizeLine() == TKEOF)
|
|
{
|
|
DEBUG { printf("Assemble: Found TKEOF flag...\n"); }
|
|
if (list_flag && listflag) // Flush last line of source
|
|
listeol();
|
|
|
|
if (ifent->if_prev != NULL) // Check conditional token
|
|
error("hit EOF without finding matching .endif");
|
|
|
|
return;
|
|
}
|
|
|
|
DEBUG { DumpTokenBuffer(); }
|
|
|
|
if (list_flag)
|
|
{
|
|
if (listflag && listing > 0)
|
|
listeol(); // Tell listing generator about EOL
|
|
|
|
lstout((char)(disabled ? '-' : lntag)); // Prepare new line for listing
|
|
listflag = 1; // OK to call `listeol' now
|
|
just_bss = 0; // Reset just_bss mode
|
|
}
|
|
|
|
state = -3; // No keyword (just EOL)
|
|
label = NULL; // No label
|
|
label_defined = NULL; // No label defined yet
|
|
lab_sym = NULL; // No (exported) label
|
|
equate = NULL; // No equate
|
|
tk = tok; // Save first token in line
|
|
pcloc = (uint32_t)sloc; // Set beginning-of-line PC
|
|
|
|
loop1: // Internal line processing loop
|
|
|
|
if (*tok == EOL) // Restart loop if end-of-line
|
|
goto loop;
|
|
|
|
// First token MUST be a symbol (Shamus: not sure why :-/)
|
|
if (*tok != SYMBOL)
|
|
{
|
|
error("syntax error; expected symbol");
|
|
|
|
goto loop;
|
|
}
|
|
|
|
j = (int)tok[2]; // Skip equates (normal statements)
|
|
|
|
if (j == '=' || j == DEQUALS || j == SET || j == REG || j == EQUREG || j == CCDEF)
|
|
{
|
|
equate = string[tok[1]];
|
|
equtyp = j;
|
|
tok += 3;
|
|
goto normal;
|
|
}
|
|
|
|
// Skip past label (but record it)
|
|
if (j == ':' || j == DCOLON)
|
|
{
|
|
label = string[tok[1]]; // Get label name
|
|
labtyp = tok[2]; // Get label type
|
|
tok += 3; // Go to next line token
|
|
}
|
|
|
|
// EOL is legal here...
|
|
if (*tok == EOL)
|
|
goto normal;
|
|
|
|
// First token MUST be a symbol (if we get here, tok didn't advance)
|
|
if (*tok++ != SYMBOL)
|
|
{
|
|
error("syntax error; expected symbol");
|
|
goto loop;
|
|
}
|
|
|
|
opname = p = string[*tok++];
|
|
|
|
// Check to see if the SYMBOL is a keyword (a mnemonic or directive).
|
|
// On output, `state' will have one of the values:
|
|
// -3 there was no symbol (EOL)
|
|
// -2..-1 the symbol didn't match any keyword
|
|
// 0..499 vanilla directives (dc, ds, etc.)
|
|
// 500..999 electric directives (macro, rept, etc.)
|
|
// 1000..+ mnemonics (move, lsr, etc.)
|
|
for(state=0; state>=0;)
|
|
{
|
|
j = mnbase[state] + (int)tolowertab[*p];
|
|
|
|
// Reject, character doesn't match
|
|
if (mncheck[j] != state)
|
|
{
|
|
state = -1; // No match
|
|
break;
|
|
}
|
|
|
|
// Must accept or reject at EOS
|
|
if (!*++p)
|
|
{
|
|
state = mnaccept[j]; // (-1 on no terminal match)
|
|
break;
|
|
}
|
|
|
|
state = mntab[j];
|
|
}
|
|
|
|
// Check for ".b" ".w" ".l" after directive, macro or mnemonic.
|
|
siz = SIZN;
|
|
|
|
switch (*tok)
|
|
{
|
|
case DOTW: siz = SIZW, tok++; break;
|
|
case DOTL: siz = SIZL, tok++; break;
|
|
case DOTB: siz = SIZB, tok++; break;
|
|
case DOTD: siz = SIZD, tok++; break;
|
|
case DOTP: siz = SIZP, tok++; break;
|
|
case DOTQ: siz = SIZQ, tok++; break;
|
|
case DOTS: siz = SIZS, tok++; break;
|
|
case DOTX: siz = SIZX, tok++; break;
|
|
}
|
|
|
|
// Do special directives (500..999) (These must be handled in "real time")
|
|
if (state >= 500 && state < 1000)
|
|
{
|
|
switch (state)
|
|
{
|
|
case MN_IF:
|
|
d_if();
|
|
goto loop;
|
|
|
|
case MN_ELSE:
|
|
d_else();
|
|
goto loop;
|
|
|
|
case MN_ENDIF:
|
|
d_endif();
|
|
goto loop;
|
|
|
|
case MN_IIF: // .iif --- immediate if
|
|
if (disabled || expr(exprbuf, &eval, &eattr, &esym) != OK)
|
|
goto loop;
|
|
|
|
if (!(eattr & DEFINED))
|
|
{
|
|
error(undef_error);
|
|
goto loop;
|
|
}
|
|
|
|
if (*tok++ != ',')
|
|
{
|
|
error(comma_error);
|
|
goto loop;
|
|
}
|
|
|
|
if (eval == 0)
|
|
goto loop;
|
|
|
|
goto loop1;
|
|
|
|
case MN_MACRO: // .macro --- macro definition
|
|
if (!disabled)
|
|
{
|
|
// Label on a macro definition is bad mojo... Warn the user
|
|
if (label != NULL)
|
|
warn(lab_ignored);
|
|
|
|
DefineMacro();
|
|
}
|
|
|
|
goto loop;
|
|
|
|
case MN_EXITM: // .exitm --- exit macro
|
|
case MN_ENDM: // .endm --- same as .exitm
|
|
if (!disabled)
|
|
{
|
|
if (label != NULL)
|
|
warn(lab_ignored);
|
|
|
|
ExitMacro();
|
|
}
|
|
|
|
goto loop;
|
|
|
|
case MN_REPT:
|
|
if (!disabled)
|
|
{
|
|
// Handle labels on REPT directive lines...
|
|
if (label)
|
|
{
|
|
if (HandleLabel(label, labtyp) != 0)
|
|
goto loop;
|
|
|
|
label_defined = label;
|
|
}
|
|
|
|
HandleRept();
|
|
}
|
|
|
|
goto loop;
|
|
|
|
case MN_ENDR:
|
|
if (!disabled)
|
|
error("mis-nested .endr");
|
|
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
normal:
|
|
if (disabled) // Conditionally disabled code
|
|
goto loop;
|
|
|
|
// Do equates
|
|
if (equate != NULL)
|
|
{
|
|
// Pick global or local symbol enviroment
|
|
j = (*equate == '.' ? curenv : 0);
|
|
sy = lookup(equate, LABEL, j);
|
|
|
|
if (sy == NULL)
|
|
{
|
|
sy = NewSymbol(equate, LABEL, j);
|
|
sy->sattr = 0;
|
|
|
|
if (equtyp == DEQUALS)
|
|
{
|
|
// Can't GLOBAL a local symbol
|
|
if (j)
|
|
{
|
|
error(locgl_error);
|
|
goto loop;
|
|
}
|
|
|
|
sy->sattr = GLOBAL;
|
|
}
|
|
}
|
|
else if ((sy->sattr & DEFINED) && equtyp != SET)
|
|
{
|
|
if ((equtyp == EQUREG) && (sy->sattre & UNDEF_EQUR))
|
|
{
|
|
sy->sattre &= ~UNDEF_EQUR;
|
|
sy->svalue = 0;
|
|
}
|
|
else if ((equtyp == CCDEF) && (sy->sattre & UNDEF_CC))
|
|
{
|
|
sy->sattre &= ~UNDEF_CC;
|
|
sy->svalue = 0;
|
|
}
|
|
else
|
|
{
|
|
error("multiple equate to '%s'", sy->sname);
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
// Put symbol in "order of definition" list if it's not already there
|
|
AddToSymbolDeclarationList(sy);
|
|
|
|
// Parse value to equate symbol to;
|
|
// o .equr
|
|
// o .reg
|
|
// o everything else
|
|
if (equtyp == EQUREG)
|
|
{
|
|
//Linko's request to issue a warning on labels that equated to the same
|
|
//register would go here. Not sure how to implement it though. :-/
|
|
/*
|
|
Maybe like this way:
|
|
have an array of bools with 64 entries. Whenever a register is equated, set the
|
|
corresponding register bool to true. Whenever it's undef'ed, set it to false.
|
|
When checking to see if it's already been equated, issue a warning.
|
|
*/
|
|
|
|
// Check for register to equate to
|
|
// This check will change once we split the registers per architecture into their own tables
|
|
// and out of kw.tab. But for now it'll do...
|
|
if ((*tok >= REG68_D0) && (*tok <= REG56_BA))
|
|
{
|
|
sy->sattre = EQUATEDREG; // Mark as equated register
|
|
equreg = *tok;
|
|
// Check for ",<bank #>" override notation and skip past it.
|
|
// It is ignored now. Was that ever useful anyway?
|
|
if ((rgpu ||rdsp) && (tok[1] == ',') && (tok[2] == CONST))
|
|
{
|
|
// Advance token pointer and skip everything
|
|
tok += 4;
|
|
}
|
|
|
|
eattr = ABS | DEFINED | GLOBAL;
|
|
eval = equreg;
|
|
tok++;
|
|
}
|
|
// Checking for a register symbol
|
|
else if (tok[0] == SYMBOL)
|
|
{
|
|
sy2 = lookup(string[tok[1]], LABEL, j);
|
|
|
|
// Make sure symbol is a valid equreg
|
|
if (!sy2 || !(sy2->sattre & EQUATEDREG))
|
|
{
|
|
error("invalid .equr/.regequ definition");
|
|
goto loop;
|
|
}
|
|
else
|
|
{
|
|
eattr = ABS | DEFINED | GLOBAL; // Copy symbol's attributes
|
|
sy->sattre = sy2->sattre;
|
|
eval = (sy2->svalue & 0xFFFFF0FF);
|
|
tok += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("invalid .equr/.regequ definition");
|
|
goto loop;
|
|
}
|
|
}
|
|
else if (equtyp == REG)
|
|
{
|
|
if (reglist(&rmask) < 0)
|
|
goto loop;
|
|
|
|
eval = (uint32_t)rmask;
|
|
eattr = ABS | DEFINED;
|
|
}
|
|
else if (equtyp == CCDEF)
|
|
{
|
|
sy->sattre |= EQUATEDCC;
|
|
eattr = ABS | DEFINED | GLOBAL;
|
|
|
|
if (tok[0] == SYMBOL)
|
|
{
|
|
sy2 = lookup(string[tok[1]], LABEL, j);
|
|
|
|
if (!sy2 || !(sy2->sattre & EQUATEDCC))
|
|
{
|
|
error("invalid gpu/dsp .ccdef definition");
|
|
goto loop;
|
|
}
|
|
else
|
|
{
|
|
eattr = ABS | DEFINED | GLOBAL;
|
|
sy->sattre = sy2->sattre;
|
|
eval = sy2->svalue;
|
|
tok += 2;
|
|
}
|
|
}
|
|
else if (expr(exprbuf, &eval, &eattr, &esym) != OK)
|
|
goto loop;
|
|
}
|
|
// equ an equr
|
|
else if (*tok == SYMBOL)
|
|
{
|
|
sy2 = lookup(string[tok[1]], LABEL, j);
|
|
|
|
if (sy2 && (sy2->sattre & EQUATEDREG))
|
|
{
|
|
sy->stype = sy2->stype;
|
|
sy->sattr = sy2->sattr;
|
|
sy->sattre = sy2->sattre;
|
|
sy->svalue = sy2->svalue;
|
|
goto loop;
|
|
}
|
|
else if (expr(exprbuf, &eval, &eattr, &esym) != OK)
|
|
goto loop;
|
|
}
|
|
else if (expr(exprbuf, &eval, &eattr, &esym) != OK)
|
|
goto loop;
|
|
|
|
if (!(eattr & DEFINED))
|
|
{
|
|
error(undef_error);
|
|
goto loop;
|
|
}
|
|
|
|
sy->sattr |= eattr | EQUATED; // Symbol inherits value and attributes
|
|
sy->svalue = eval;
|
|
|
|
if (list_flag) // Put value in listing
|
|
listvalue((uint32_t)eval);
|
|
|
|
ErrorIfNotAtEOL(); // Must be at EOL now
|
|
goto loop;
|
|
}
|
|
|
|
// Do labels
|
|
if (label != NULL)
|
|
{
|
|
// Non-zero == error occurred
|
|
if (HandleLabel(label, labtyp) != 0)
|
|
goto loop;
|
|
|
|
label_defined = label;
|
|
}
|
|
|
|
// Punt on EOL
|
|
if (state == -3)
|
|
goto loop;
|
|
|
|
// If we're in 6502 mode and are still in need of a mnemonic, then search
|
|
// for valid 6502 mnemonic.
|
|
if (m6502 && (state < 0 || state >= 1000))
|
|
{
|
|
#ifdef ST
|
|
state = kmatch(opname, mpbase, mpcheck, mptab, mpaccept);
|
|
#else
|
|
for(state=0, p=opname; state>= 0; )
|
|
{
|
|
j = mpbase[state] + tolowertab[*p];
|
|
|
|
if (mpcheck[j] != state) // Reject, character doesn't match
|
|
{
|
|
state = -1; // No match
|
|
break;
|
|
}
|
|
|
|
if (!*++p)
|
|
{ // Must accept or reject at EOS
|
|
state = mpaccept[j]; // (-1 on no terminal match)
|
|
break;
|
|
}
|
|
|
|
state = mptab[j];
|
|
}
|
|
#endif
|
|
|
|
// Call 6502 code generator if we found a mnemonic
|
|
if (state >= 2000)
|
|
{
|
|
m6502cg(state - 2000);
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
// If we are in GPU or DSP mode and still in need of a mnemonic then search
|
|
// for one
|
|
if ((rgpu || rdsp) && (state < 0 || state >= 1000))
|
|
{
|
|
for(state=0, p=opname; state>=0;)
|
|
{
|
|
j = mrbase[state] + (int)tolowertab[*p];
|
|
|
|
// Reject, character doesn't match
|
|
if (mrcheck[j] != state)
|
|
{
|
|
state = -1; // No match
|
|
break;
|
|
}
|
|
|
|
// Must accept or reject at EOS
|
|
if (!*++p)
|
|
{
|
|
state = mraccept[j]; // (-1 on no terminal match)
|
|
break;
|
|
}
|
|
|
|
state = mrtab[j];
|
|
}
|
|
|
|
// Call RISC code generator if we found a mnemonic
|
|
if (state >= 3000)
|
|
{
|
|
GenerateRISCCode(state);
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
// If we are in OP mode and still in need of a mnemonic then search for one
|
|
if (robjproc && ((state < 0) || (state >= 1000)))
|
|
{
|
|
for(state=0, p=opname; state>=0;)
|
|
{
|
|
j = mobase[state] + (int)tolowertab[*p];
|
|
|
|
// Reject, character doesn't match
|
|
if (mocheck[j] != state)
|
|
{
|
|
state = -1; // No match
|
|
break;
|
|
}
|
|
|
|
// Must accept or reject at EOS
|
|
if (!*++p)
|
|
{
|
|
state = moaccept[j]; // (-1 on no terminal match)
|
|
break;
|
|
}
|
|
|
|
state = motab[j];
|
|
}
|
|
|
|
// Call OP code generator if we found a mnemonic
|
|
if (state >= 3100)
|
|
{
|
|
GenerateOPCode(state);
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
// If we are in 56K mode and still in need of a mnemonic then search for one
|
|
if (dsp56001 && ((state < 0) || (state >= 1000)))
|
|
{
|
|
for(state=0, p=opname; state>=0;)
|
|
{
|
|
j = dspbase[state] + (int)tolowertab[*p];
|
|
|
|
// Reject, character doesn't match
|
|
if (dspcheck[j] != state)
|
|
{
|
|
state = -1; // No match
|
|
break;
|
|
}
|
|
|
|
// Must accept or reject at EOS
|
|
if (!*++p)
|
|
{
|
|
state = dspaccept[j]; // (-1 on no terminal match)
|
|
break;
|
|
}
|
|
|
|
state = dsptab[j];
|
|
}
|
|
|
|
// Call DSP code generator if we found a mnemonic
|
|
if (state >= 2000)
|
|
{
|
|
LONG parcode;
|
|
int operands;
|
|
MNTABDSP * md = &dsp56k_machtab[state - 2000];
|
|
deposit_extra_ea = 0; // Assume no extra word needed
|
|
|
|
if (md->mnfunc == dsp_mult)
|
|
{
|
|
// Special case for multiplication instructions: they require
|
|
// 3 operands
|
|
if ((operands = dsp_amode(3)) == ERROR)
|
|
goto loop;
|
|
}
|
|
else if ((md->mnattr & PARMOVE) && md->mn0 != M_AM_NONE)
|
|
{
|
|
if (dsp_amode(2) == ERROR)
|
|
goto loop;
|
|
}
|
|
else if ((md->mnattr & PARMOVE) && md->mn0 == M_AM_NONE)
|
|
{
|
|
// Instructions that have parallel moves but use no operands
|
|
// (probably only move). In this case, don't parse addressing
|
|
// modes--just go straight to parallel parse
|
|
dsp_am0 = dsp_am1 = M_AM_NONE;
|
|
}
|
|
else
|
|
{
|
|
// Non parallel move instructions can have up to 4 parameters
|
|
// (well, only tcc instructions really)
|
|
if ((operands = dsp_amode(4)) == ERROR)
|
|
goto loop;
|
|
|
|
if (operands == 4)
|
|
{
|
|
dsp_tcc4(md->mninst);
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
if (md->mnattr & PARMOVE)
|
|
{
|
|
// Check for parallel moves
|
|
if ((parcode = parmoves(dsp_a1reg)) == ERROR)
|
|
goto loop;
|
|
}
|
|
else
|
|
{
|
|
if (*tok != EOL)
|
|
error("parallel moves not allowed with this instruction");
|
|
|
|
parcode = 0;
|
|
}
|
|
|
|
while ((dsp_am0 & md->mn0) == 0 || (dsp_am1 & md->mn1) == 0)
|
|
md = &dsp56k_machtab[md->mncont];
|
|
|
|
GENLINENOSYM();
|
|
(*md->mnfunc)(md->mninst | (parcode << 8));
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
// Invoke macro or complain about bad mnemonic
|
|
if (state < 0)
|
|
{
|
|
if ((sy = lookup(opname, MACRO, 0)) != NULL)
|
|
InvokeMacro(sy, siz);
|
|
else
|
|
error("unknown op '%s'", opname);
|
|
|
|
goto loop;
|
|
}
|
|
|
|
// Call directive handlers
|
|
if (state < 500)
|
|
{
|
|
(*dirtab[state])(siz);
|
|
goto loop;
|
|
}
|
|
|
|
// Do mnemonics
|
|
// o can't deposit instrs in BSS or ABS
|
|
// o do automatic .EVEN for instrs
|
|
// o allocate space for largest possible instr
|
|
// o can't do ".b" operations with an address register
|
|
if (scattr & SBSS)
|
|
{
|
|
error("cannot initialize non-storage (BSS) section");
|
|
goto loop;
|
|
}
|
|
|
|
if (sloc & 1) // Automatic .even
|
|
auto_even();
|
|
|
|
if (challoc - ch_size < 18) // Make sure have space in current chunk
|
|
chcheck(0);
|
|
|
|
m = &machtab[state - 1000];
|
|
|
|
// Call special-mode handler
|
|
if (m->mnattr & CGSPECIAL)
|
|
{
|
|
GENLINENOSYM();
|
|
(*m->mnfunc)(m->mninst, siz);
|
|
goto loop;
|
|
}
|
|
|
|
if (amode(1) < 0) // Parse 0, 1 or 2 addr modes
|
|
goto loop;
|
|
|
|
// Check that we're at EOL
|
|
// The only exception is ptestr/ptestw instructions
|
|
// that have 3 or 4 operands and are not handled by
|
|
// amode(). (yes, we're taking a performance hit here sadly)
|
|
if (m->mnfunc != m_ptestr && m->mnfunc != m_ptestw)
|
|
if (*tok != EOL)
|
|
error(extra_stuff);
|
|
|
|
amsk0 = amsktab[am0];
|
|
amsk1 = amsktab[am1];
|
|
|
|
// Catch attempts to use ".B" with an address register (yes, this check
|
|
// does work at this level)
|
|
if (siz == SIZB && (am0 == AREG || am1 == AREG))
|
|
{
|
|
error("cannot use '.b' with an address register");
|
|
goto loop;
|
|
}
|
|
|
|
// Keep a backup of chptr (used for optimisations during codegen)
|
|
chptr_opcode = chptr;
|
|
|
|
while (!(m->mnattr & siz) || (amsk0 & m->mn0) == 0 || (amsk1 & m->mn1) == 0)
|
|
m = &machtab[m->mncont];
|
|
|
|
DEBUG { printf(" 68K: mninst=$%X, siz=$%X, mnattr=$%X, amsk0=$%X, mn0=$%X, amsk1=$%X, mn1=$%X\n", m->mninst, siz, m->mnattr, amsk0, m->mn0, amsk1, m->mn1); }
|
|
|
|
GENLINENOSYM();
|
|
(*m->mnfunc)(m->mninst, siz);
|
|
goto loop;
|
|
}
|
|
|
|
//
|
|
// Handle the creation of labels
|
|
//
|
|
int HandleLabel(char * label, int labelType)
|
|
{
|
|
// Check for dot in front of label; means this is a local label if present
|
|
int environment = (*label == '.' ? curenv : 0);
|
|
SYM * symbol = lookup(label, LABEL, environment);
|
|
|
|
if (symbol == NULL)
|
|
{
|
|
symbol = NewSymbol(label, LABEL, environment);
|
|
symbol->sattr = 0;
|
|
symbol->sattre = 0;
|
|
}
|
|
else if (symbol->sattr & DEFINED)
|
|
return error("multiply-defined label '%s'", label);
|
|
|
|
// Put symbol in "order of definition" list if it's not already in it
|
|
AddToSymbolDeclarationList(symbol);
|
|
|
|
if (orgactive)
|
|
{
|
|
symbol->svalue = orgaddr;
|
|
symbol->sattr |= ABS | DEFINED | EQUATED;
|
|
}
|
|
else
|
|
{
|
|
symbol->svalue = sloc;
|
|
symbol->sattr |= DEFINED | cursect;
|
|
}
|
|
|
|
lab_sym = symbol;
|
|
|
|
// Yes, our CS professors told us to write checks for equality this way,
|
|
// but damn, it hurts my brain every time I look at it. :-/
|
|
if (0 == environment)
|
|
curenv++;
|
|
|
|
// Make label global if it has a double colon
|
|
if (labelType == DCOLON)
|
|
{
|
|
if (environment != 0)
|
|
return error(locgl_error);
|
|
|
|
symbol->sattr |= GLOBAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|