/*
* Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
*
* Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
* Copyright (c) 2001, 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_gram.y,v 1.23 2003/01/20 18:02:11 gibbs Exp $
*/
program:
include
| program include
| prefix
| program prefix
| patch_arg_list
| program patch_arg_list
| version
| program version
| register
| program register
| constant
| program constant
| macrodefn
| program macrodefn
| scratch_ram
| program scratch_ram
| scb
| program scb
| label
| program label
| set_src_mode
| program set_src_mode
| set_dst_mode
| program set_dst_mode
| critical_section_start
| program critical_section_start
| critical_section_end
| program critical_section_end
| conditional
| program conditional
| code
| program code
;
symbol = $1;
switch (symbol->type) {
case ALIAS:
symbol = $1->info.ainfo->parent;
case REGISTER:
case SCBLOC:
case SRAMLOC:
$$.value = symbol->info.rinfo->address;
break;
case MASK:
case FIELD:
case ENUM:
case ENUM_ENTRY:
$$.value = symbol->info.finfo->value;
break;
case DOWNLOAD_CONST:
case CONST:
$$.value = symbol->info.cinfo->value;
break;
case UNINITIALIZED:
default:
{
snprintf(errbuf, sizeof(errbuf),
"Undefined symbol %s referenced",
symbol->name);
stop(errbuf, EX_DATAERR);
/* NOTREACHED */
break;
}
}
SLIST_INIT(&$$.referenced_syms);
symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
}
;
constant:
T_CONST T_SYMBOL expression
{
if ($2->type != UNINITIALIZED) {
stop("Re-definition of symbol as a constant",
EX_DATAERR);
/* NOTREACHED */
}
$2->type = CONST;
initialize_symbol($2);
$2->info.cinfo->value = $3.value;
}
| T_CONST T_SYMBOL T_DOWNLOAD
{
if ($1) {
stop("Invalid downloaded constant declaration",
EX_DATAERR);
/* NOTREACHED */
}
if ($2->type != UNINITIALIZED) {
stop("Re-definition of symbol as a downloaded constant",
EX_DATAERR);
/* NOTREACHED */
}
$2->type = DOWNLOAD_CONST;
initialize_symbol($2);
$2->info.cinfo->value = download_constant_count++;
}
;
macrodefn_prologue:
T_DEFINE T_SYMBOL
{
if ($2->type != UNINITIALIZED) {
stop("Re-definition of symbol as a macro",
EX_DATAERR);
/* NOTREACHED */
}
cur_symbol = $2;
cur_symbol->type = MACRO;
initialize_symbol(cur_symbol);
}
;
/*
* Ensure that the previous scope is either an
* if or and else if.
*/
scope_context = SLIST_FIRST(&scope_stack);
last_scope = TAILQ_LAST(&scope_context->inner_scope,
scope_tailq);
if (last_scope == NULL
|| last_scope->type == T_ELSE) {
/*
* Ensure that the previous scope is either an
* if or and else if.
*/
scope_context = SLIST_FIRST(&scope_stack);
last_scope = TAILQ_LAST(&scope_context->inner_scope,
scope_tailq);
if (last_scope == NULL
|| last_scope->type == SCOPE_ELSE) {
stop("'else' without leading 'if'", EX_DATAERR);
/* NOTREACHED */
}
new_scope = scope_alloc();
new_scope->type = SCOPE_ELSE;
new_scope->begin_addr = instruction_ptr;
}
;
/*
* This grammer differs from the one in the aic7xxx
* reference manual since the grammer listed there is
* ambiguous and causes a shift/reduce conflict.
* It also seems more logical as the "immediate"
* argument is listed as the second arg like the
* other formats.
*/
static void
process_field(int field_type, symbol_t *sym, int value)
{
/*
* Add the current register to its
* symbol list, if it already exists,
* warn if we are setting it to a
* different value, or in the bit to
* the "allowed bits" of this register.
*/
if (sym->type == UNINITIALIZED) {
sym->type = field_type;
initialize_symbol(sym);
sym->info.finfo->value = value;
if (field_type != ENUM_ENTRY) {
if (field_type != MASK && value == 0) {
stop("Empty Field, or Enum", EX_DATAERR);
/* NOTREACHED */
}
sym->info.finfo->value = value;
sym->info.finfo->mask = value;
} else if (field_symbol != NULL) {
sym->info.finfo->mask = field_symbol->info.finfo->value;
} else {
sym->info.finfo->mask = 0xFF;
}
} else if (sym->type != field_type) {
stop("Field definition mirrors a definition of the same "
" name, but a different type", EX_DATAERR);
/* NOTREACHED */
} else if (value != sym->info.finfo->value) {
stop("Field redefined with a conflicting value", EX_DATAERR);
/* NOTREACHED */
}
/* Fail if this symbol is already listed */
if (symlist_search(&(sym->info.finfo->symrefs),
cur_symbol->name) != NULL) {
stop("Field defined multiple times for register", EX_DATAERR);
/* NOTREACHED */
}
symlist_add(&(sym->info.finfo->symrefs), cur_symbol,
SYMLIST_INSERT_HEAD);
cur_symbol->info.rinfo->valid_bitmask |= sym->info.finfo->mask;
cur_symbol->info.rinfo->typecheck_masks = TRUE;
symlist_add(&(cur_symbol->info.rinfo->fields), sym, SYMLIST_SORT);
}
static void
initialize_symbol(symbol_t *symbol)
{
switch (symbol->type) {
case UNINITIALIZED:
stop("Call to initialize_symbol with type field unset",
EX_SOFTWARE);
/* NOTREACHED */
break;
case REGISTER:
case SRAMLOC:
case SCBLOC:
symbol->info.rinfo =
(struct reg_info *)malloc(sizeof(struct reg_info));
if (symbol->info.rinfo == NULL) {
stop("Can't create register info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.rinfo, 0,
sizeof(struct reg_info));
SLIST_INIT(&(symbol->info.rinfo->fields));
/*
* Default to allowing access in all register modes
* or to the mode specified by the SCB or SRAM space
* we are in.
*/
if (scb_or_sram_symbol != NULL)
symbol->info.rinfo->modes =
scb_or_sram_symbol->info.rinfo->modes;
else
symbol->info.rinfo->modes = ~0;
break;
case ALIAS:
symbol->info.ainfo =
(struct alias_info *)malloc(sizeof(struct alias_info));
if (symbol->info.ainfo == NULL) {
stop("Can't create alias info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.ainfo, 0,
sizeof(struct alias_info));
break;
case MASK:
case FIELD:
case ENUM:
case ENUM_ENTRY:
symbol->info.finfo =
(struct field_info *)malloc(sizeof(struct field_info));
if (symbol->info.finfo == NULL) {
stop("Can't create field info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.finfo, 0, sizeof(struct field_info));
SLIST_INIT(&(symbol->info.finfo->symrefs));
break;
case CONST:
case DOWNLOAD_CONST:
symbol->info.cinfo =
(struct const_info *)malloc(sizeof(struct const_info));
if (symbol->info.cinfo == NULL) {
stop("Can't create alias info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.cinfo, 0,
sizeof(struct const_info));
break;
case LABEL:
symbol->info.linfo =
(struct label_info *)malloc(sizeof(struct label_info));
if (symbol->info.linfo == NULL) {
stop("Can't create label info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.linfo, 0,
sizeof(struct label_info));
break;
case CONDITIONAL:
symbol->info.condinfo =
(struct cond_info *)malloc(sizeof(struct cond_info));
if (symbol->info.condinfo == NULL) {
stop("Can't create conditional info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.condinfo, 0,
sizeof(struct cond_info));
break;
case MACRO:
symbol->info.macroinfo =
(struct macro_info *)malloc(sizeof(struct macro_info));
if (symbol->info.macroinfo == NULL) {
stop("Can't create macro info", EX_SOFTWARE);
/* NOTREACHED */
}
memset(symbol->info.macroinfo, 0,
sizeof(struct macro_info));
STAILQ_INIT(&symbol->info.macroinfo->args);
break;
default:
stop("Call to initialize_symbol with invalid symbol type",
EX_SOFTWARE);
/* NOTREACHED */
break;
}
}
static void
add_macro_arg(const char *argtext, int argnum)
{
struct macro_arg *marg;
int i;
int retval;
if (cur_symbol == NULL || cur_symbol->type != MACRO) {
stop("Invalid current symbol for adding macro arg",
EX_SOFTWARE);
/* NOTREACHED */
}
marg = (struct macro_arg *)malloc(sizeof(*marg));
if (marg == NULL) {
stop("Can't create macro_arg structure", EX_SOFTWARE);
/* NOTREACHED */
}
marg->replacement_text = NULL;
retval = snprintf(regex_pattern, sizeof(regex_pattern),
"[^-/A-Za-z0-9_](%s)([^-/A-Za-z0-9_]|$)",
argtext);
if (retval >= sizeof(regex_pattern)) {
stop("Regex text buffer too small for arg",
EX_SOFTWARE);
/* NOTREACHED */
}
retval = regcomp(&marg->arg_regex, regex_pattern, REG_EXTENDED);
if (retval != 0) {
stop("Regex compilation failed", EX_SOFTWARE);
/* NOTREACHED */
}
STAILQ_INSERT_TAIL(&cur_symbol->info.macroinfo->args, marg, links);
}
static void
add_macro_body(const char *bodytext)
{
if (cur_symbol == NULL || cur_symbol->type != MACRO) {
stop("Invalid current symbol for adding macro arg",
EX_SOFTWARE);
/* NOTREACHED */
}
cur_symbol->info.macroinfo->body = strdup(bodytext);
if (cur_symbol->info.macroinfo->body == NULL) {
stop("Can't duplicate macro body text", EX_SOFTWARE);
/* NOTREACHED */
}
}
/* Test register permissions */
test_writable_symbol(dest->symbol);
test_readable_symbol(src->symbol);
/* Ensure that immediate makes sense for this destination */
type_check(dest->symbol, immed, opcode);
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f1_instr = &instr->format.format1;
f1_instr->ret = ret ? 1 : 0;
f1_instr->opcode = opcode;
f1_instr->destination = dest->symbol->info.rinfo->address
+ dest->offset;
f1_instr->source = src->symbol->info.rinfo->address
+ src->offset;
f1_instr->immediate = immed->value;
if (is_download_const(immed))
f1_instr->parity = 1;
else if (dest->symbol == mode_ptr.symbol) {
u_int src_value;
u_int dst_value;
/*
* Attempt to update mode information if
* we are operating on the mode register.
*/
if (src->symbol == allones.symbol)
src_value = 0xFF;
else if (src->symbol == allzeros.symbol)
src_value = 0;
else if (src->symbol == mode_ptr.symbol)
src_value = (dst_mode << 4) | src_mode;
else
goto cant_update;
/*
* Make sure that we aren't attempting to write something
* that hasn't been defined. If this is an and operation,
* this is a mask, so "undefined" bits are okay.
*/
if (and_op == FALSE
&& (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
snprintf(errbuf, sizeof(errbuf),
"Invalid bit(s) 0x%x in immediate written to %s",
expression->value & ~symbol->info.rinfo->valid_bitmask,
symbol->name);
stop(errbuf, EX_DATAERR);
/* NOTREACHED */
}
/*
* Now make sure that all of the symbols referenced by the
* expression are defined for this register.
*/
if (symbol->info.rinfo->typecheck_masks != FALSE) {
for(node = expression->referenced_syms.slh_first;
node != NULL;
node = node->links.sle_next) {
if ((node->symbol->type == MASK
|| node->symbol->type == FIELD
|| node->symbol->type == ENUM
|| node->symbol->type == ENUM_ENTRY)
&& symlist_search(&node->symbol->info.finfo->symrefs,
symbol->name) == NULL) {
snprintf(errbuf, sizeof(errbuf),
"Invalid field or mask %s "
"for register %s",
node->symbol->name, symbol->name);
stop(errbuf, EX_DATAERR);
/* NOTREACHED */
}
}
}
}
static void
make_expression(expression_t *immed, int value)
{
SLIST_INIT(&immed->referenced_syms);
immed->value = value & 0xff;
}
static void
add_conditional(symbol_t *symbol)
{
static int numfuncs;
if (numfuncs == 0) {
/* add a special conditional, "0" */
symbol_t *false_func;