/* Some helpful utility functions for gpasm
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
James Bowman, Craig Franklin
This file is part of gputils.
gputils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
gputils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gputils; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "stdhdr.h"
#include "libgputils.h"
#include "gpasm.h"
#include "gperror.h"
#include "directive.h"
#include "coff.h"
static struct file_context *last = NULL;
int
stringtolong(char *string, int radix)
{
char *endptr;
int value;
value = strtoul(string, &endptr, radix);
if ((endptr == NULL) || (*endptr != '\0')) {
char complaint[80];
snprintf(complaint, sizeof(complaint),
isprint(*endptr) ?
"Illegal character '%c' in numeric constant " :
"Illegal character %#x in numeric constant" ,
*endptr);
gperror(GPE_UNKNOWN, complaint);
}
return value;
}
int gpasm_magic(char *c)
{
if (c[0] == '\\') {
switch (c[1]) {
case 'a':
return '\a';
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
default:
return c[1];
}
}
return c[0];
}
/*
convert_escaped_char(char *src,char c)
Input: pointer to a string
Output returns the input string with escaped char converted to a regular char
For example if escaped character is a double quote then:
This is a escaped quote: \"
is translated to:
This is a escaped quote: "
*/
char *
convert_escaped_char(char *str, char c)
{
char *src = str;
char *dest = str;
if (!str)
return str;
while (*src) {
if (*src =='\\' && src[1] == c)
src++;
*dest++ = *src++;
}
*dest=0;
return str;
}
/* Determine the value of the escape char pointed to by ps. Return a pointer
to the next character. */
char *
convert_escape_chars(char *ps, int *value)
{
int count;
if (*ps != '\\') {
*value = *ps++;
} else {
/* escape char, convert its value and write to the new string */
switch (ps[1]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
/* octal number */
count = 0;
*value = 0;
ps++;
while (count < 3) {
if (*ps < '0' || *ps > '7')
break;
*value = (*value << 3) + *ps - '0';
ps++;
count++;
}
break;
case 'x':
/* hex number */
if ((ps[2] == '\0') || (ps[3] == '\0')) {
gperror(GPE_UNKNOWN, "missing hex value in \\x escape character");
*value = 0;
/* return a NULL character */
ps[2] = '\0';
ps += 2;
} else {
char buffer[3];
buffer[0] = ps[2];
buffer[1] = ps[3];
buffer[2] = 0;
*value = stringtolong(buffer, 16);
ps += 4;
}
break;
default:
if (ps[1] == '\0') {
gperror(GPE_UNKNOWN, "missing value in \\ escape character");
*value = 0;
/* return a NULL character */
ps++;
} else {
*value = gpasm_magic(ps);
ps += 2;
}
}
}
return ps;
}
void set_global(char *name,
gpasmVal value,
enum globalLife lifetime,
enum gpasmValTypes type)
{
struct symbol *sym;
struct variable *var;
/* Search the entire stack (i.e. include macro's local symbol
tables) for the symbol. If not found, then add it to the global
symbol table. */
sym = get_symbol(state.stTop, name);
if (sym == NULL)
sym = add_symbol(state.stGlobal, name);
var = get_symbol_annotation(sym);
if (var == NULL) {
/* new symbol */
var = malloc(sizeof(*var));
annotate_symbol(sym, var);
var->value = value;
var->coff_num = state.obj.symbol_num;
var->coff_section_num = state.obj.section_num;
var->type = type;
var->previous_type = type; /* coff symbols can be changed to global */
} else if (lifetime == TEMPORARY) {
/*
* TSD - the following embarrassing piece of code is a hack
* to fix a problem when global variables are changed
* during the expansion of a macro. Macros are expanded
* by running through them twice. if you have a stetement
* like:
* some_var set some_var + 1
* then this is incremented twice! So the if statement
* makes sure that the value is assigned on the second
* pass only in the macro. Jeez this really sucks....
*/
var->value = value;
} else if (state.pass == 2) {
char *coff_name;
if (var->value != value) {
char message[BUFSIZ];
snprintf(message, sizeof(message),
"Value of symbol \"%s\" differs on second pass\n pass 1=%d, pass 2=%d",
name,var->value,value);
gperror(GPE_DIFFLAB, message);
}
coff_name = coff_local_name(name);
coff_add_sym(coff_name, value, var->type);
if (coff_name != NULL)
free(coff_name);
}
/* increment the index into the coff symbol table for the relocations */
switch(type) {
case gvt_extern:
case gvt_global:
case gvt_static:
case gvt_address:
case gvt_debug:
case gvt_absolute:
state.obj.symbol_num++;
break;
default:
break;
}
}
void select_errorlevel(int level)
{
if (state.cmd_line.error_level) {
gpmessage(GPM_SUPVAL, NULL);
} else {
if (level == 0) {
state.error_level = 0;
} else if (level == 1) {
state.error_level = 1;
} else if (level == 2) {
state.error_level = 2;
} else {
if (state.pass == 0) {
fprintf(stderr,
"Error: invalid warning level \"%i\"\n",
level);
} else {
gperror(GPE_ILLEGAL_ARGU, "Expected w= 0, 1, 2");
}
}
}
}
void select_expand(char *expand)
{
if (state.cmd_line.macro_expand) {
gpmessage(GPM_SUPLIN, NULL);
} else {
if (strcasecmp(expand, "ON") == 0) {
state.lst.expand = true;
} else if (strcasecmp(expand, "OFF") == 0) {
state.lst.expand = false;
} else {
state.lst.expand = true;
if (state.pass == 0) {
fprintf(stderr,
"Error: invalid option \"%s\"\n",
expand);
} else {
gpwarning(GPE_ILLEGAL_ARGU, "Expected ON or OFF");
}
}
}
}
void select_hexformat(char *format_name)
{
if (state.cmd_line.hex_format) {
gpwarning(GPW_CMDLINE_HEXFMT, NULL);
} else {
if (strcasecmp(format_name, "inhx8m") == 0) {
state.hex_format = inhx8m;
} else if (strcasecmp(format_name, "inhx8s") == 0) {
state.hex_format = inhx8s;
} else if (strcasecmp(format_name, "inhx16") == 0) {
state.hex_format = inhx16;
} else if (strcasecmp(format_name, "inhx32") == 0) {
state.hex_format = inhx32;
} else {
state.hex_format = inhx8m;
if (state.pass == 0) {
fprintf(stderr,
"Error: invalid format \"%s\"\n",
format_name);
} else {
gperror(GPE_ILLEGAL_ARGU, "Expected inhx8m, inhx8s, inhx16, or inhx32");
}
}
}
}
void select_radix(char *radix_name)
{
if (state.cmd_line.radix) {
gpwarning(GPW_CMDLINE_RADIX, NULL);
} else {
if (strcasecmp(radix_name, "hex") == 0) {
state.radix = 16;
} else if (strcasecmp(radix_name, "dec") == 0) {
state.radix = 10;
} else if (strcasecmp(radix_name, "decimal") == 0) {
state.radix = 10;
} else if (strcasecmp(radix_name, "oct") == 0) {
state.radix = 8;
} else if (strcasecmp(radix_name, "octal") == 0) {
state.radix = 8;
} else {
state.radix = 16;
if (state.pass == 0) {
fprintf(stderr,
"invalid radix \"%s\", will use hex.\n",
radix_name);
} else {
gpwarning(GPW_RADIX, NULL);
}
}
}
}
/************************************************************************/
/* Function to append a line to an ongoing macro definition */
void macro_append(void)
{
struct macro_body *body = malloc(sizeof(*body));
body->src_line = NULL;
*state.mac_prev = body; /* append this to the chain */
state.mac_prev = &body->next; /* this is the new end of the chain */
state.mac_body = body;
body->next = NULL; /* make sure it's terminated */
}
gpasmVal do_or_append_insn(char *op, struct pnode *parms)
{
gpasmVal r;
if (!state.mac_prev ||
(strcasecmp(op, "endm") == 0) ||
(state.while_head && (strcasecmp(op, "endw") == 0))) {
r = do_insn(op, parms);
} else {
macro_append();
r = 0;
}
return r;
}
void print_pnode(struct pnode *p)
{
if(!p) {
printf("Null\n");
return;
}
switch(p->tag) {
case constant:
printf(" constant: %d\n",p->value.constant);
break;
case symbol:
printf(" symbol: %s\n",p->value.symbol);
break;
case unop:
printf(" unop: %d\n",p->value.unop.op);
break;
case binop:
printf(" binop: %d\n",p->value.binop.op);
break;
case string:
printf(" string: %s\n",p->value.string);
break;
case list:
printf(" list:\n");
break;
default:
printf("unknown type\n");
}
}
void print_macro_node(struct macro_body *mac)
{
if(mac->src_line)
printf(" src_line = %s\n",mac->src_line);
}
void print_macro_body(struct macro_body *mac)
{
struct macro_body *mb = mac;
printf("{\n");
while(mb) {
print_macro_node(mb);
mb = mb->next;
}
printf("}\n");
}
/************************************************************************/
/* add_file: add a file of type 'type' to the file_context stack.
*/
struct file_context * add_file(unsigned int type, char *name)
{
static unsigned int file_id = 0;
struct file_context *new;
/* First check to make sure this file is not already in the list */
if(last) {
new = last;
do {
if(strcmp(new->name, name) == 0)
return(new);
new = new->prev;
} while(new != NULL);
}
new = malloc(sizeof(*new));
new->name = strdup(name);
new->ft = type;
new->prev = last;
new->id = file_id++;
new->next = NULL;
if(last)
last->next = new;
last = new;
state.files = new;
return(new);
}
/* free_files: free memory allocated to the file_context stack
*/
void free_files(void)
{
struct file_context *old;
while(last != NULL) {
old = last;
last = old->prev;
free(old->name);
free(old);
}
}
void hex_init(void)
{
if (state.hexfile == suppress) {
/* Must delete hex file when suppressed. */
writehex(state.basefilename,
state.i_memory,
state.hex_format,
1,
0,
state.dos_newlines);
return;
}
if (check_writehex(state.i_memory, state.hex_format)) {
gperror(GPE_IHEX,NULL);
} else {
int byte_words;
if (state.device.core_size > 0xff) {
byte_words = 0;
} else {
byte_words = 1;
if (state.hex_format != inhx8m) {
gpwarning(GPW_UNKNOWN,"Must use inhx8m format for EEPROM8");
state.hex_format = inhx8m;
}
}
if (writehex(state.basefilename, state.i_memory,
state.hex_format, state.num.errors,
byte_words, state.dos_newlines)) {
gperror(GPE_UNKNOWN,"Error generating hex file");
}
}
return;
}
syntax highlighted by Code2HTML, v. 0.9.1