freebsd-dev/sys/dev/aic7xxx/aicasm/aicasm_gram.y
Justin T. Gibbs b744190698 Correct logic bug in aicasm's undefined register bit access detection code.
The code in question verifies that all register write operations only change
bits that are defined (in the register definition file) for that effected
register.  The bug effectively disabled this checking.

o Fix the check by testing the opcode against all supported read ("and" based)
  operands.

o Add missing bit definitions to the aic7xxx and aic79xx register definition
  files so that the warning (treated as a fatal error) does not spuriously
  fire.

Reported by:	Pawel Worach <pawel.worach@gmail.com>
MFC after:	1 week
2010-07-14 14:31:18 +00:00

1953 lines
40 KiB
Plaintext

%{
/*-
* 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.
*
* $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_gram.y#29 $
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <inttypes.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#ifdef __linux__
#include "../queue.h"
#else
#include <sys/queue.h>
#endif
#include "aicasm.h"
#include "aicasm_symbol.h"
#include "aicasm_insformat.h"
int yylineno;
char *yyfilename;
char stock_prefix[] = "aic_";
char *prefix = stock_prefix;
char *patch_arg_list;
char *versions;
static char errbuf[255];
static char regex_pattern[255];
static symbol_t *cur_symbol;
static symbol_t *field_symbol;
static symbol_t *scb_or_sram_symbol;
static symtype cur_symtype;
static symbol_ref_t accumulator;
static symbol_ref_t mode_ptr;
static symbol_ref_t allones;
static symbol_ref_t allzeros;
static symbol_ref_t none;
static symbol_ref_t sindex;
static int instruction_ptr;
static int num_srams;
static int sram_or_scb_offset;
static int download_constant_count;
static int in_critical_section;
static u_int enum_increment;
static u_int enum_next_value;
static void process_field(unsigned int field_type, symbol_t *sym, int mask);
static void initialize_symbol(symbol_t *symbol);
static void add_macro_arg(const char *argtext, int position);
static void add_macro_body(const char *bodytext);
static void process_register(symbol_t **p_symbol);
static void format_1_instr(int opcode, symbol_ref_t *dest,
expression_t *immed, symbol_ref_t *src, int ret);
static void format_2_instr(int opcode, symbol_ref_t *dest,
expression_t *places, symbol_ref_t *src, int ret);
static void format_3_instr(int opcode, symbol_ref_t *src,
expression_t *immed, symbol_ref_t *address);
static void test_readable_symbol(symbol_t *symbol);
static void test_writable_symbol(symbol_t *symbol);
static void type_check(symbol_t *symbol, expression_t *expression, int and_op);
static void make_expression(expression_t *immed, int value);
static void add_conditional(symbol_t *symbol);
static void add_version(const char *verstring);
static int is_download_const(expression_t *immed);
extern int yylex (void);
extern int yyparse (void);
#define SRAM_SYMNAME "SRAM_BASE"
#define SCB_SYMNAME "SCB_BASE"
%}
%union {
u_int value;
char *str;
symbol_t *sym;
symbol_ref_t sym_ref;
expression_t expression;
}
%token T_REGISTER
%token <value> T_CONST
%token T_EXPORT
%token T_DOWNLOAD
%token T_SCB
%token T_SRAM
%token T_ALIAS
%token T_SIZE
%token T_EXPR_LSHIFT
%token T_EXPR_RSHIFT
%token <value> T_ADDRESS
%token T_ACCESS_MODE
%token T_MODES
%token T_DEFINE
%token T_SET_SRC_MODE
%token T_SET_DST_MODE
%token <value> T_MODE
%token T_BEGIN_CS
%token T_END_CS
%token T_FIELD
%token T_ENUM
%token T_MASK
%token <value> T_NUMBER
%token <str> T_PATH T_STRING T_ARG T_MACROBODY
%token <sym> T_CEXPR
%token T_EOF T_INCLUDE T_VERSION T_PREFIX T_PATCH_ARG_LIST
%token <value> T_SHR T_SHL T_ROR T_ROL
%token <value> T_MVI T_MOV T_CLR T_BMOV
%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
%token <value> T_ADD T_ADC
%token <value> T_INC T_DEC
%token <value> T_STC T_CLC
%token <value> T_CMP T_NOT T_XOR
%token <value> T_TEST T_AND
%token <value> T_OR
%token T_RET
%token T_NOP
%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX T_MODE_PTR
%token T_A
%token <sym> T_SYMBOL
%token T_NL
%token T_IF T_ELSE T_ELSE_IF T_ENDIF
%type <sym_ref> reg_symbol address destination source opt_source
%type <expression> expression immediate immediate_or_a
%type <value> export ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
%type <value> mode_value mode_list macro_arglist
%left '|'
%left '&'
%left T_EXPR_LSHIFT T_EXPR_RSHIFT
%left '+' '-'
%left '*' '/'
%right '~'
%nonassoc UMINUS
%%
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
;
include:
T_INCLUDE '<' T_PATH '>'
{
include_file($3, BRACKETED_INCLUDE);
}
| T_INCLUDE '"' T_PATH '"'
{
include_file($3, QUOTED_INCLUDE);
}
;
prefix:
T_PREFIX '=' T_STRING
{
if (prefix != stock_prefix)
stop("Prefix multiply defined",
EX_DATAERR);
prefix = strdup($3);
if (prefix == NULL)
stop("Unable to record prefix", EX_SOFTWARE);
}
;
patch_arg_list:
T_PATCH_ARG_LIST '=' T_STRING
{
if (patch_arg_list != NULL)
stop("Patch argument list multiply defined",
EX_DATAERR);
patch_arg_list = strdup($3);
if (patch_arg_list == NULL)
stop("Unable to record patch arg list", EX_SOFTWARE);
}
;
version:
T_VERSION '=' T_STRING
{ add_version($3); }
;
register:
T_REGISTER { cur_symtype = REGISTER; } reg_definition
;
reg_definition:
T_SYMBOL '{'
{
if ($1->type != UNINITIALIZED) {
stop("Register multiply defined", EX_DATAERR);
/* NOTREACHED */
}
cur_symbol = $1;
cur_symbol->type = cur_symtype;
initialize_symbol(cur_symbol);
}
reg_attribute_list
'}'
{
/*
* Default to allowing everything in for registers
* with no bit or mask definitions.
*/
if (cur_symbol->info.rinfo->valid_bitmask == 0)
cur_symbol->info.rinfo->valid_bitmask = 0xFF;
if (cur_symbol->info.rinfo->size == 0)
cur_symbol->info.rinfo->size = 1;
/*
* This might be useful for registers too.
*/
if (cur_symbol->type != REGISTER) {
if (cur_symbol->info.rinfo->address == 0)
cur_symbol->info.rinfo->address =
sram_or_scb_offset;
sram_or_scb_offset +=
cur_symbol->info.rinfo->size;
}
cur_symbol = NULL;
}
;
reg_attribute_list:
reg_attribute
| reg_attribute_list reg_attribute
;
reg_attribute:
reg_address
| size
| access_mode
| modes
| field_defn
| enum_defn
| mask_defn
| alias
| accumulator
| mode_pointer
| allones
| allzeros
| none
| sindex
;
reg_address:
T_ADDRESS T_NUMBER
{
cur_symbol->info.rinfo->address = $2;
}
;
size:
T_SIZE T_NUMBER
{
cur_symbol->info.rinfo->size = $2;
if (scb_or_sram_symbol != NULL) {
u_int max_addr;
u_int sym_max_addr;
max_addr = scb_or_sram_symbol->info.rinfo->address
+ scb_or_sram_symbol->info.rinfo->size;
sym_max_addr = cur_symbol->info.rinfo->address
+ cur_symbol->info.rinfo->size;
if (sym_max_addr > max_addr)
stop("SCB or SRAM space exhausted", EX_DATAERR);
}
}
;
access_mode:
T_ACCESS_MODE T_MODE
{
cur_symbol->info.rinfo->mode = $2;
}
;
modes:
T_MODES mode_list
{
cur_symbol->info.rinfo->modes = $2;
}
;
mode_list:
mode_value
{
$$ = $1;
}
| mode_list ',' mode_value
{
$$ = $1 | $3;
}
;
mode_value:
T_NUMBER
{
if ($1 > 4) {
stop("Valid register modes range between 0 and 4.",
EX_DATAERR);
/* NOTREACHED */
}
$$ = (0x1 << $1);
}
| T_SYMBOL
{
symbol_t *symbol;
symbol = $1;
if (symbol->type != CONST) {
stop("Only \"const\" symbols allowed in "
"mode definitions.", EX_DATAERR);
/* NOTREACHED */
}
if (symbol->info.cinfo->value > 4) {
stop("Valid register modes range between 0 and 4.",
EX_DATAERR);
/* NOTREACHED */
}
$$ = (0x1 << symbol->info.cinfo->value);
}
;
field_defn:
T_FIELD
{
field_symbol = NULL;
enum_next_value = 0;
enum_increment = 1;
}
'{' enum_entry_list '}'
| T_FIELD T_SYMBOL expression
{
process_field(FIELD, $2, $3.value);
field_symbol = $2;
enum_next_value = 0;
enum_increment = 0x01 << (ffs($3.value) - 1);
}
'{' enum_entry_list '}'
| T_FIELD T_SYMBOL expression
{
process_field(FIELD, $2, $3.value);
}
;
enum_defn:
T_ENUM
{
field_symbol = NULL;
enum_next_value = 0;
enum_increment = 1;
}
'{' enum_entry_list '}'
| T_ENUM T_SYMBOL expression
{
process_field(ENUM, $2, $3.value);
field_symbol = $2;
enum_next_value = 0;
enum_increment = 0x01 << (ffs($3.value) - 1);
}
'{' enum_entry_list '}'
;
enum_entry_list:
enum_entry
| enum_entry_list ',' enum_entry
;
enum_entry:
T_SYMBOL
{
process_field(ENUM_ENTRY, $1, enum_next_value);
enum_next_value += enum_increment;
}
| T_SYMBOL expression
{
process_field(ENUM_ENTRY, $1, $2.value);
enum_next_value = $2.value + enum_increment;
}
;
mask_defn:
T_MASK T_SYMBOL expression
{
process_field(MASK, $2, $3.value);
}
;
alias:
T_ALIAS T_SYMBOL
{
if ($2->type != UNINITIALIZED) {
stop("Re-definition of register alias",
EX_DATAERR);
/* NOTREACHED */
}
$2->type = ALIAS;
initialize_symbol($2);
$2->info.ainfo->parent = cur_symbol;
}
;
accumulator:
T_ACCUM
{
if (accumulator.symbol != NULL) {
stop("Only one accumulator definition allowed",
EX_DATAERR);
/* NOTREACHED */
}
accumulator.symbol = cur_symbol;
}
;
mode_pointer:
T_MODE_PTR
{
if (mode_ptr.symbol != NULL) {
stop("Only one mode pointer definition allowed",
EX_DATAERR);
/* NOTREACHED */
}
mode_ptr.symbol = cur_symbol;
}
;
allones:
T_ALLONES
{
if (allones.symbol != NULL) {
stop("Only one definition of allones allowed",
EX_DATAERR);
/* NOTREACHED */
}
allones.symbol = cur_symbol;
}
;
allzeros:
T_ALLZEROS
{
if (allzeros.symbol != NULL) {
stop("Only one definition of allzeros allowed",
EX_DATAERR);
/* NOTREACHED */
}
allzeros.symbol = cur_symbol;
}
;
none:
T_NONE
{
if (none.symbol != NULL) {
stop("Only one definition of none allowed",
EX_DATAERR);
/* NOTREACHED */
}
none.symbol = cur_symbol;
}
;
sindex:
T_SINDEX
{
if (sindex.symbol != NULL) {
stop("Only one definition of sindex allowed",
EX_DATAERR);
/* NOTREACHED */
}
sindex.symbol = cur_symbol;
}
;
expression:
expression '|' expression
{
$$.value = $1.value | $3.value;
symlist_merge(&$$.referenced_syms,
&$1.referenced_syms,
&$3.referenced_syms);
}
| expression '&' expression
{
$$.value = $1.value & $3.value;
symlist_merge(&$$.referenced_syms,
&$1.referenced_syms,
&$3.referenced_syms);
}
| expression '+' expression
{
$$.value = $1.value + $3.value;
symlist_merge(&$$.referenced_syms,
&$1.referenced_syms,
&$3.referenced_syms);
}
| expression '-' expression
{
$$.value = $1.value - $3.value;
symlist_merge(&($$.referenced_syms),
&($1.referenced_syms),
&($3.referenced_syms));
}
| expression '*' expression
{
$$.value = $1.value * $3.value;
symlist_merge(&($$.referenced_syms),
&($1.referenced_syms),
&($3.referenced_syms));
}
| expression '/' expression
{
$$.value = $1.value / $3.value;
symlist_merge(&($$.referenced_syms),
&($1.referenced_syms),
&($3.referenced_syms));
}
| expression T_EXPR_LSHIFT expression
{
$$.value = $1.value << $3.value;
symlist_merge(&$$.referenced_syms,
&$1.referenced_syms,
&$3.referenced_syms);
}
| expression T_EXPR_RSHIFT expression
{
$$.value = $1.value >> $3.value;
symlist_merge(&$$.referenced_syms,
&$1.referenced_syms,
&$3.referenced_syms);
}
| '(' expression ')'
{
$$ = $2;
}
| '~' expression
{
$$ = $2;
$$.value = (~$$.value) & 0xFF;
}
| '-' expression %prec UMINUS
{
$$ = $2;
$$.value = -$$.value;
}
| T_NUMBER
{
$$.value = $1;
SLIST_INIT(&$$.referenced_syms);
}
| T_SYMBOL
{
symbol_t *symbol;
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);
}
;
macrodefn:
macrodefn_prologue T_MACROBODY
{
add_macro_body($2);
}
| macrodefn_prologue '(' macro_arglist ')' T_MACROBODY
{
add_macro_body($5);
cur_symbol->info.macroinfo->narg = $3;
}
;
macro_arglist:
{
/* Macros can take no arguments */
$$ = 0;
}
| T_ARG
{
$$ = 1;
add_macro_arg($1, 0);
}
| macro_arglist ',' T_ARG
{
if ($1 == 0) {
stop("Comma without preceding argument in arg list",
EX_DATAERR);
/* NOTREACHED */
}
$$ = $1 + 1;
add_macro_arg($3, $1);
}
;
scratch_ram:
T_SRAM '{'
{
snprintf(errbuf, sizeof(errbuf), "%s%d", SRAM_SYMNAME,
num_srams);
cur_symbol = symtable_get(SRAM_SYMNAME);
cur_symtype = SRAMLOC;
cur_symbol->type = SRAMLOC;
initialize_symbol(cur_symbol);
}
reg_address
{
sram_or_scb_offset = cur_symbol->info.rinfo->address;
}
size
{
scb_or_sram_symbol = cur_symbol;
}
scb_or_sram_attributes
'}'
{
cur_symbol = NULL;
scb_or_sram_symbol = NULL;
}
;
scb:
T_SCB '{'
{
cur_symbol = symtable_get(SCB_SYMNAME);
cur_symtype = SCBLOC;
if (cur_symbol->type != UNINITIALIZED) {
stop("Only one SRAM definition allowed",
EX_SOFTWARE);
/* NOTREACHED */
}
cur_symbol->type = SCBLOC;
initialize_symbol(cur_symbol);
/* 64 bytes of SCB space */
cur_symbol->info.rinfo->size = 64;
}
reg_address
{
sram_or_scb_offset = cur_symbol->info.rinfo->address;
}
size
{
scb_or_sram_symbol = cur_symbol;
}
scb_or_sram_attributes
'}'
{
cur_symbol = NULL;
scb_or_sram_symbol = NULL;
}
;
scb_or_sram_attributes:
/* NULL definition is okay */
| modes
| scb_or_sram_reg_list
| modes scb_or_sram_reg_list
;
scb_or_sram_reg_list:
reg_definition
| scb_or_sram_reg_list reg_definition
;
reg_symbol:
T_SYMBOL
{
process_register(&$1);
$$.symbol = $1;
$$.offset = 0;
}
| T_SYMBOL '[' T_SYMBOL ']'
{
process_register(&$1);
if ($3->type != CONST) {
stop("register offset must be a constant", EX_DATAERR);
/* NOTREACHED */
}
if (($3->info.cinfo->value + 1) > (unsigned)$1->info.rinfo->size) {
stop("Accessing offset beyond range of register",
EX_DATAERR);
/* NOTREACHED */
}
$$.symbol = $1;
$$.offset = $3->info.cinfo->value;
}
| T_SYMBOL '[' T_NUMBER ']'
{
process_register(&$1);
if (($3 + 1) > (unsigned)$1->info.rinfo->size) {
stop("Accessing offset beyond range of register",
EX_DATAERR);
/* NOTREACHED */
}
$$.symbol = $1;
$$.offset = $3;
}
| T_A
{
if (accumulator.symbol == NULL) {
stop("No accumulator has been defined", EX_DATAERR);
/* NOTREACHED */
}
$$.symbol = accumulator.symbol;
$$.offset = 0;
}
;
destination:
reg_symbol
{
test_writable_symbol($1.symbol);
$$ = $1;
}
;
immediate:
expression
{ $$ = $1; }
;
immediate_or_a:
expression
{
if ($1.value == 0 && is_download_const(&$1) == 0) {
snprintf(errbuf, sizeof(errbuf),
"\nExpression evaluates to 0 and thus "
"references the accumulator.\n "
"If this is the desired effect, use 'A' "
"instead.\n");
stop(errbuf, EX_DATAERR);
}
$$ = $1;
}
| T_A
{
SLIST_INIT(&$$.referenced_syms);
symlist_add(&$$.referenced_syms, accumulator.symbol,
SYMLIST_INSERT_HEAD);
$$.value = 0;
}
;
source:
reg_symbol
{
test_readable_symbol($1.symbol);
$$ = $1;
}
;
opt_source:
{
$$.symbol = NULL;
$$.offset = 0;
}
| ',' source
{ $$ = $2; }
;
ret:
{ $$ = 0; }
| T_RET
{ $$ = 1; }
;
set_src_mode:
T_SET_SRC_MODE T_NUMBER ';'
{
src_mode = $2;
}
;
set_dst_mode:
T_SET_DST_MODE T_NUMBER ';'
{
dst_mode = $2;
}
;
critical_section_start:
T_BEGIN_CS ';'
{
critical_section_t *cs;
if (in_critical_section != FALSE) {
stop("Critical Section within Critical Section",
EX_DATAERR);
/* NOTREACHED */
}
cs = cs_alloc();
cs->begin_addr = instruction_ptr;
in_critical_section = TRUE;
}
;
critical_section_end:
T_END_CS ';'
{
critical_section_t *cs;
if (in_critical_section == FALSE) {
stop("Unballanced 'end_cs'", EX_DATAERR);
/* NOTREACHED */
}
cs = TAILQ_LAST(&cs_tailq, cs_tailq);
cs->end_addr = instruction_ptr;
in_critical_section = FALSE;
}
;
export:
{ $$ = 0; }
| T_EXPORT
{ $$ = 1; }
;
label:
export T_SYMBOL ':'
{
if ($2->type != UNINITIALIZED) {
stop("Program label multiply defined", EX_DATAERR);
/* NOTREACHED */
}
$2->type = LABEL;
initialize_symbol($2);
$2->info.linfo->address = instruction_ptr;
$2->info.linfo->exported = $1;
}
;
address:
T_SYMBOL
{
$$.symbol = $1;
$$.offset = 0;
}
| T_SYMBOL '+' T_NUMBER
{
$$.symbol = $1;
$$.offset = $3;
}
| T_SYMBOL '-' T_NUMBER
{
$$.symbol = $1;
$$.offset = -$3;
}
| '.'
{
$$.symbol = NULL;
$$.offset = 0;
}
| '.' '+' T_NUMBER
{
$$.symbol = NULL;
$$.offset = $3;
}
| '.' '-' T_NUMBER
{
$$.symbol = NULL;
$$.offset = -$3;
}
;
conditional:
T_IF T_CEXPR '{'
{
scope_t *new_scope;
add_conditional($2);
new_scope = scope_alloc();
new_scope->type = SCOPE_IF;
new_scope->begin_addr = instruction_ptr;
new_scope->func_num = $2->info.condinfo->func_num;
}
| T_ELSE T_IF T_CEXPR '{'
{
scope_t *new_scope;
scope_t *scope_context;
scope_t *last_scope;
/*
* 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) {
stop("'else if' without leading 'if'", EX_DATAERR);
/* NOTREACHED */
}
add_conditional($3);
new_scope = scope_alloc();
new_scope->type = SCOPE_ELSE_IF;
new_scope->begin_addr = instruction_ptr;
new_scope->func_num = $3->info.condinfo->func_num;
}
| T_ELSE '{'
{
scope_t *new_scope;
scope_t *scope_context;
scope_t *last_scope;
/*
* 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;
}
;
conditional:
'}'
{
scope_t *scope_context;
scope_context = SLIST_FIRST(&scope_stack);
if (scope_context->type == SCOPE_ROOT) {
stop("Unexpected '}' encountered", EX_DATAERR);
/* NOTREACHED */
}
scope_context->end_addr = instruction_ptr;
/* Pop the scope */
SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
process_scope(scope_context);
if (SLIST_FIRST(&scope_stack) == NULL) {
stop("Unexpected '}' encountered", EX_DATAERR);
/* NOTREACHED */
}
}
;
f1_opcode:
T_AND { $$ = AIC_OP_AND; }
| T_XOR { $$ = AIC_OP_XOR; }
| T_ADD { $$ = AIC_OP_ADD; }
| T_ADC { $$ = AIC_OP_ADC; }
;
code:
f1_opcode destination ',' immediate_or_a opt_source ret ';'
{
format_1_instr($1, &$2, &$4, &$5, $6);
}
;
code:
T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
{
format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
}
;
code:
T_INC destination opt_source ret ';'
{
expression_t immed;
make_expression(&immed, 1);
format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
}
;
code:
T_DEC destination opt_source ret ';'
{
expression_t immed;
make_expression(&immed, -1);
format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
}
;
code:
T_CLC ret ';'
{
expression_t immed;
make_expression(&immed, -1);
format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
}
| T_CLC T_MVI destination ',' immediate_or_a ret ';'
{
format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
}
;
code:
T_STC ret ';'
{
expression_t immed;
make_expression(&immed, 1);
format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
}
| T_STC destination ret ';'
{
expression_t immed;
make_expression(&immed, 1);
format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
}
;
code:
T_BMOV destination ',' source ',' immediate ret ';'
{
format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
}
;
code:
T_MOV destination ',' source ret ';'
{
expression_t immed;
make_expression(&immed, 1);
format_1_instr(AIC_OP_BMOV, &$2, &immed, &$4, $5);
}
;
code:
T_MVI destination ',' immediate ret ';'
{
if ($4.value == 0
&& is_download_const(&$4) == 0) {
expression_t immed;
/*
* Allow move immediates of 0 so that macros,
* that can't know the immediate's value and
* otherwise compensate, still work.
*/
make_expression(&immed, 1);
format_1_instr(AIC_OP_BMOV, &$2, &immed, &allzeros, $5);
} else {
format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
}
}
;
code:
T_NOT destination opt_source ret ';'
{
expression_t immed;
make_expression(&immed, 0xff);
format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
}
;
code:
T_CLR destination ret ';'
{
expression_t immed;
make_expression(&immed, 0xff);
format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
}
;
code:
T_NOP ret ';'
{
expression_t immed;
make_expression(&immed, 0xff);
format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
}
;
code:
T_RET ';'
{
expression_t immed;
make_expression(&immed, 0xff);
format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
}
;
/*
* 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.
*/
f2_opcode:
T_SHL { $$ = AIC_OP_SHL; }
| T_SHR { $$ = AIC_OP_SHR; }
| T_ROL { $$ = AIC_OP_ROL; }
| T_ROR { $$ = AIC_OP_ROR; }
;
code:
f2_opcode destination ',' expression opt_source ret ';'
{
format_2_instr($1, &$2, &$4, &$5, $6);
}
;
jmp_jc_jnc_call:
T_JMP { $$ = AIC_OP_JMP; }
| T_JC { $$ = AIC_OP_JC; }
| T_JNC { $$ = AIC_OP_JNC; }
| T_CALL { $$ = AIC_OP_CALL; }
;
jz_jnz:
T_JZ { $$ = AIC_OP_JZ; }
| T_JNZ { $$ = AIC_OP_JNZ; }
;
je_jne:
T_JE { $$ = AIC_OP_JE; }
| T_JNE { $$ = AIC_OP_JNE; }
;
code:
jmp_jc_jnc_call address ';'
{
expression_t immed;
make_expression(&immed, 0);
format_3_instr($1, &sindex, &immed, &$2);
}
;
code:
T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
{
format_3_instr($5, &$2, &$4, &$6);
}
;
code:
T_TEST source ',' immediate_or_a jz_jnz address ';'
{
format_3_instr($5, &$2, &$4, &$6);
}
;
code:
T_CMP source ',' immediate_or_a je_jne address ';'
{
format_3_instr($5, &$2, &$4, &$6);
}
;
code:
T_MOV source jmp_jc_jnc_call address ';'
{
expression_t immed;
make_expression(&immed, 0);
format_3_instr($3, &$2, &immed, &$4);
}
;
code:
T_MVI immediate jmp_jc_jnc_call address ';'
{
format_3_instr($3, &allzeros, &$2, &$4);
}
;
%%
static void
process_field(unsigned 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 __unused)
{
struct macro_arg *marg;
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 >= (int)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 */
}
}
static void
process_register(symbol_t **p_symbol)
{
symbol_t *symbol = *p_symbol;
if (symbol->type == UNINITIALIZED) {
snprintf(errbuf, sizeof(errbuf), "Undefined register %s",
symbol->name);
stop(errbuf, EX_DATAERR);
/* NOTREACHED */
} else if (symbol->type == ALIAS) {
*p_symbol = symbol->info.ainfo->parent;
} else if ((symbol->type != REGISTER)
&& (symbol->type != SCBLOC)
&& (symbol->type != SRAMLOC)) {
snprintf(errbuf, sizeof(errbuf),
"Specified symbol %s is not a register",
symbol->name);
stop(errbuf, EX_DATAERR);
}
}
static void
format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
symbol_ref_t *src, int ret)
{
struct instruction *instr;
struct ins_format1 *f1_instr;
if (src->symbol == NULL)
src = dest;
/* 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;
switch (opcode) {
case AIC_OP_AND:
dst_value = src_value & immed->value;
break;
case AIC_OP_XOR:
dst_value = src_value ^ immed->value;
break;
case AIC_OP_ADD:
dst_value = (src_value + immed->value) & 0xFF;
break;
case AIC_OP_OR:
dst_value = src_value | immed->value;
break;
case AIC_OP_BMOV:
dst_value = src_value;
break;
default:
goto cant_update;
}
src_mode = dst_value & 0xF;
dst_mode = (dst_value >> 4) & 0xF;
}
cant_update:
symlist_free(&immed->referenced_syms);
instruction_ptr++;
}
static void
format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
symbol_ref_t *src, int ret)
{
struct instruction *instr;
struct ins_format2 *f2_instr;
uint8_t shift_control;
if (src->symbol == NULL)
src = dest;
/* Test register permissions */
test_writable_symbol(dest->symbol);
test_readable_symbol(src->symbol);
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f2_instr = &instr->format.format2;
f2_instr->ret = ret ? 1 : 0;
f2_instr->opcode = AIC_OP_ROL;
f2_instr->destination = dest->symbol->info.rinfo->address
+ dest->offset;
f2_instr->source = src->symbol->info.rinfo->address
+ src->offset;
if (places->value > 8 || places->value <= 0) {
stop("illegal shift value", EX_DATAERR);
/* NOTREACHED */
}
switch (opcode) {
case AIC_OP_SHL:
if (places->value == 8)
shift_control = 0xf0;
else
shift_control = (places->value << 4) | places->value;
break;
case AIC_OP_SHR:
if (places->value == 8) {
shift_control = 0xf8;
} else {
shift_control = (places->value << 4)
| (8 - places->value)
| 0x08;
}
break;
case AIC_OP_ROL:
shift_control = places->value & 0x7;
break;
case AIC_OP_ROR:
shift_control = (8 - places->value) | 0x08;
break;
default:
shift_control = 0; /* Quiet Compiler */
stop("Invalid shift operation specified", EX_SOFTWARE);
/* NOTREACHED */
break;
};
f2_instr->shift_control = shift_control;
symlist_free(&places->referenced_syms);
instruction_ptr++;
}
static void
format_3_instr(int opcode, symbol_ref_t *src,
expression_t *immed, symbol_ref_t *address)
{
struct instruction *instr;
struct ins_format3 *f3_instr;
int addr;
/* Test register permissions */
test_readable_symbol(src->symbol);
/* Ensure that immediate makes sense for this source */
type_check(src->symbol, immed, opcode);
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f3_instr = &instr->format.format3;
if (address->symbol == NULL) {
/* 'dot' referrence. Use the current instruction pointer */
addr = instruction_ptr + address->offset;
} else if (address->symbol->type == UNINITIALIZED) {
/* forward reference */
addr = address->offset;
instr->patch_label = address->symbol;
} else
addr = address->symbol->info.linfo->address + address->offset;
f3_instr->opcode = opcode;
f3_instr->address = addr;
f3_instr->source = src->symbol->info.rinfo->address
+ src->offset;
f3_instr->immediate = immed->value;
if (is_download_const(immed))
f3_instr->parity = 1;
symlist_free(&immed->referenced_syms);
instruction_ptr++;
}
static void
test_readable_symbol(symbol_t *symbol)
{
if ((symbol->info.rinfo->modes & (0x1 << src_mode)) == 0) {
snprintf(errbuf, sizeof(errbuf),
"Register %s unavailable in source reg mode %d",
symbol->name, src_mode);
stop(errbuf, EX_DATAERR);
}
if (symbol->info.rinfo->mode == WO) {
stop("Write Only register specified as source",
EX_DATAERR);
/* NOTREACHED */
}
}
static void
test_writable_symbol(symbol_t *symbol)
{
if ((symbol->info.rinfo->modes & (0x1 << dst_mode)) == 0) {
snprintf(errbuf, sizeof(errbuf),
"Register %s unavailable in destination reg mode %d",
symbol->name, dst_mode);
stop(errbuf, EX_DATAERR);
}
if (symbol->info.rinfo->mode == RO) {
stop("Read Only register specified as destination",
EX_DATAERR);
/* NOTREACHED */
}
}
static void
type_check(symbol_t *symbol, expression_t *expression, int opcode)
{
symbol_node_t *node;
int and_op;
uint8_t invalid_bits;
and_op = FALSE;
if (opcode == AIC_OP_AND
|| opcode == AIC_OP_BMOV
|| opcode == AIC_OP_JE
|| opcode == AIC_OP_JNE
|| opcode == AIC_OP_JNZ
|| opcode == AIC_OP_JZ)
and_op = TRUE;
/*
* 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.
*/
invalid_bits = expression->value & ~symbol->info.rinfo->valid_bitmask;
if (and_op == FALSE && invalid_bits != 0) {
snprintf(errbuf, sizeof(errbuf),
"Invalid bit(s) 0x%x in immediate written to %s",
invalid_bits, 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;
false_func = symtable_get("0");
if (false_func->type != UNINITIALIZED) {
stop("Conditional expression '0' "
"conflicts with a symbol", EX_DATAERR);
/* NOTREACHED */
}
false_func->type = CONDITIONAL;
initialize_symbol(false_func);
false_func->info.condinfo->func_num = numfuncs++;
symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
}
/* This condition has occurred before */
if (symbol->type == CONDITIONAL)
return;
if (symbol->type != UNINITIALIZED) {
stop("Conditional expression conflicts with a symbol",
EX_DATAERR);
/* NOTREACHED */
}
symbol->type = CONDITIONAL;
initialize_symbol(symbol);
symbol->info.condinfo->func_num = numfuncs++;
symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
}
static void
add_version(const char *verstring)
{
const char verprefix[] = " * ";
int newlen;
int oldlen;
newlen = strlen(verstring) + strlen(verprefix);
oldlen = 0;
if (versions != NULL)
oldlen = strlen(versions);
versions = realloc(versions, newlen + oldlen + 2);
if (versions == NULL)
stop("Can't allocate version string", EX_SOFTWARE);
strcpy(&versions[oldlen], verprefix);
strcpy(&versions[oldlen + strlen(verprefix)], verstring);
versions[newlen + oldlen] = '\n';
versions[newlen + oldlen + 1] = '\0';
}
static void
yyerror(const char *string)
{
stop(string, EX_DATAERR);
}
static int
is_download_const(expression_t *immed)
{
if ((immed->referenced_syms.slh_first != NULL)
&& (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
return (TRUE);
return (FALSE);
}