/* Some helpful utility functions for gpasm.
Copyright 1998-2005 James Bowman, Craig Franklin
Copyright 2015-2016 Molnár Károly
*/
#include "stdhdr.h"
#include "libgputils.h"
#include "gpasm.h"
#include "gpmsg.h"
#include "directive.h"
#include "coff.h"
#define STR_INHX8M "inhx8m"
#define STR_INHX8S "inhx8s"
#define STR_INHX16 "inhx16"
#define STR_INHX32 "inhx32"
#define HVM_NONE 0
#define HVM_BEGIN 1
#define HVM_NAME 2
/*------------------------------------------------------------------------------------------------*/
// convert character to numerical code for conversion.
// Returns a value greater than base for illegal character.
// On Z80, it was tricky "add 0x90, daa, adc 0x40, daa" for [0-9A-F]
static unsigned _toN(int c) {
if (c<='9') c-='0';
else if ((c&=~0x20)>='A') c-='A'-10;
return (unsigned)c;
}
/*------------------------------------------------------------------------------------------------*/
/* MPASM compatible stripped version of strtoul:
* The _strtoi returns low sizeof(int) * 8 bits of the value:
* value & ((2 ^ (sizeof(int) * 8)) - 1);
* The strtoul returns ULONG_MAX if the correct value is outside the range
* of representable values (see man strtoul). The behavior is also
* platform dependent: (int)strtoul("123456789", 16) returns -1 on 32 bit
* platforms and 0x23456789 on 64 bit platforms. */
static int _strtoi(const char *s, char **endptr, unsigned radix) {
unsigned value = 0;
int sign = 1;
if (*s == '-') {
sign = -1;
++s;
}
for(;;++s) {
unsigned digit = _toN(*s);
if (digit >= radix) break;
value = value * radix + digit;
}
if (endptr) *endptr = (char *)s;
return (int)value * sign;
}
/*------------------------------------------------------------------------------------------------*/
void _error_illegal_char(char c) {
char buf[80];
snprintf(buf, sizeof(buf),
isprint((unsigned char)c)
? "Illegal character '%c' in numeric constant."
: "Illegal character %#x in numeric constant.",c);
gpmsg0(GPE_UNKNOWN, buf);
}
int string_to_int(const char *String, int Radix) {
char *endptr;
char ch;
int value = _strtoi(String, &endptr, Radix);
if (endptr && !!(ch = *endptr)) _error_illegal_char(ch);
return value;
}
/*------------------------------------------------------------------------------------------------*/
/*
String: An text which is probably represents a number.
Type : An address of type of number: bin, dec, oct or hex
*/
long gp_strtol(const char *String, numstring_t *Type) {
assert(String);
assert(Type);
*Type = NUM_STR_UNKNOWN;
int length = strlen(String);
if (!length) return 0;
char ch0 = String[0];
char ch1 = String[1];
const char*strlast = String+length-1;
char ch_last = *strlast;
int base = 0;
if (ch1 == '\'' && ch_last==ch1 && length>3) {
switch (ch0|0x20) {
case 'b': base = 2; break; // b'___' OR B'___'
case 'o':
case 'q': base = 8; break; // q'___' OR Q'___'
case 'd': base = 10; break; // d'___' OR D'___'
case 'h': base = 16; break; // h'___' OR H'___'
}
if (base) {String += 2; goto conv;}
}
if (ch0 == '.' && length>1) { // .___
++String; ++strlast;
base = 10; goto conv;
}
if (ch0 == '0' && length>2) {
switch (ch1|0x20) {
case 'x': base = 16; break; // 0x___ OR 0X___
case 'b': base = 2; break;
case 'o':
case 'q': base = 8; break;
case 'd': base = 10; break;
}
if (base) {
String += 2;
++strlast;
goto conv;
}
}
if (length>1) switch (ch_last|0x20) {
case 'b': base = 2; break; // ___b OR ___B
case 'o': // ___o OR ___O
case 'q': base = 8; break; // ___q OR ___Q
case 'd': base = 10; break; // ___d OR ___D (overrides hexadecimal radix setting!! A bug?)
case 'h': base = 16; break; // ___h OR ___H
}
if (base) goto conv; // no change of String an strend
base = state.radix; // ___
++strlast;
conv:
char*end;
long val = strtol(String, &end, base);
if (end==strlast) *Type = (numstring_types)base; // report success
return val;
}
/*------------------------------------------------------------------------------------------------*/
static bool _find_hv_macro_start(const char *String, const char **Start, const char **Body) {
const char *ptr;
char ch;
int _state;
if ((String == NULL) || (*String == '\0')) {
return false;
}
ptr = String;
do {
*Start = NULL;
*Body = NULL;
_state = HVM_NONE;
do {
/* Find the beginner '#' character. */
while (true) {
if ((ch = *ptr) == '\0') {
/* Too soon it's over the string. */
return false;
}
if (ch == '#') {
/* This is the beginner '#' character. */
*Start = ptr;
++ptr;
_state = HVM_BEGIN;
break;
}
++ptr;
}
/* Skip the ' ' characters. */
while (true) {
if ((ch = *ptr) == '\0') {
/* Too soon it's over the string. */
return false;
}
if (ch != ' ') {
break;
}
++ptr;
}
/* Next if this a '#' character. */
} while (ch == '#');
/* Find the 'v' or 'V' character. */
do {
if ((ch = *ptr) == '\0') {
/* Too soon it's over the string. */
return false;
}
++ptr;
if ((ch == 'v') || (ch == 'V')) {
/* This a "v" macro. */
_state = HVM_NAME;
break;
}
} while (ch == ' ');
} while (_state != HVM_NAME);
/* Find the '(' character. */
while (true) {
ch = *ptr;
if (ch == '(') {
/* This a body of "v" macro. */
*Body = ptr;
return true;
}
if ((ch == '\0') || (ch != ' ')) {
/* Too soon it's over the string or this no space character. */
return false;
}
++ptr;
}
}
/*------------------------------------------------------------------------------------------------*/
bool find_hv_macro(const char *String, const char **Start, const char **End) {
const char *ptr;
int bracket_count;
char ch;
const char *body;
if (!String || !*String) return false;
if (!_find_hv_macro_start(String, Start, &body)) return false;
bracket_count = 1;
ptr = body;
while (true) {
++ptr;
if ((ch = *ptr) == '\0') {
break;
}
if (ch == '(') {
++bracket_count;
}
else if (ch == ')') {
--bracket_count;
}
if (bracket_count == 0) {
/* Found the ending bracket, finish. */
++ptr;
*End = ptr;
return true;
}
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
static const char escapes[]="abtnvfr"; // well-known codes 7 .. 13
char gpasm_escape(char c) {
if (!c) return c;
if (c=='e') return 0x27;
{
char*p=strchr(escapes,c);
if (p) return '\a'+(p-escapes);
}
return c;
}
/*------------------------------------------------------------------------------------------------*/
/*
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: "
This function performs in-place modification.
*/
char* convert_escaped_char(char *Str, char Ch) {
const char *s=Str;
char *d=Str;
if (!Str) return Str;
while (*s) {
if (*s == '\\' && s[1] == Ch) ++s;
*d++ = *s++;
}
*d = 0;
return Str;
}
static bool isutf8trail(uint8_t c) {
return 0x80<=c && c<0xC0;
}
/* Get the next character code of C-style-escaped string.
With <utf>, <Ps> is expected to be an UTF-8 string,
and the function returns true (21-bit) characters.
Use case 1: UTF8 input is converted to ISO-Latin1 for character displays.
Use case 2: USB where "du" statement generates UTF-16 string descriptors.
Otherwise, encoding is out of concern, and bytes are returned as-is.
Return a pointer to the next character. */
const char* convert_escape_chars(const char *Ps, int *Value, bool utf) {
uint8_t ch = *Ps;
if (ch=='\\') { // escape char, convert its value and write to the pnew string
ch = *++Ps;
*Value = 0;
if (!ch) gpmsg0(GPE_UNKNOWN, "Missing value in \\ escape character.");
else if ('0'<=ch && ch<='7') { // octal number as character code
unsigned count = ch<'4' ? 3 : 2; // allow maximum of 255 by lenght-limit
do{
*Value = *Value<<3 | ch - '0';
ch = *++Ps; // get next character
}while (--count && '0'<=ch && ch<='7');
}else if (ch=='x' || utf && ch=='u') { // two-digit hex number / unicode constant
int count = ch=='u' ? 6 : 2; // "\x" allow maximum of 255 by length-limit
ch = *++Ps;
if (!ch) gpmsg0(GPE_UNKNOWN, "Missing hex value in \\x escape character.");
else{
unsigned n = _toN(ch);
if (n>=16) _error_illegal_char(ch);
else do{
*Value = *Value<<4 | n;
ch = *++Ps; // get next character
}while (--count && (n=_toN(ch))<16);
}
}else{
*Value = gpasm_escape(ch);
++Ps; // point to next character to parse
}
}else{
int u;
// check input string for valid UTF-8 entity by lookahead, and return character if so
if (utf && ch>=0xC2) {
if (ch<0xE0
&& isutf8trail(Ps[1])) {
*Value=ch<<6&0x7C0|Ps[1]&0x3F;
++Ps;
}else if (0xE0<=ch && ch<0xF0
&& isutf8trail(Ps[1])
&& isutf8trail(Ps[2])
&& (u=ch<<12&0xF000|Ps[1]<<6&0xFC0|Ps[2]&0x3F)>=0x800) {
*Value=u;
Ps+=2;
}else if (0xF0<=ch && ch<0xF8
&& isutf8trail(Ps[1])
&& isutf8trail(Ps[2])
&& isutf8trail(Ps[3])
&& (u=ch<<18&0x1C0000|Ps[1]<<12&0x3F000|Ps[2]<<6&0xFC0|Ps[3]&0x3F)>=0x10000
&& u<0x110000) {
*Value=u;
Ps+=3;
}else *Value = ch;
}else *Value = ch;
if (ch) ++Ps;
}
return Ps;
}
/*------------------------------------------------------------------------------------------------*/
/* In some contexts, such as in the operand to a literal instruction, a
* single-character string literal in an expression can be coerced to a
* character literal. coerce_str1 converts a string-type pnode to a
* constant-type pnode in-place. */
void coerce_str1(pnode_t *Exp) {
int value;
const char *pc;
if ((Exp) && PnIsString(Exp)) {
pc = convert_escape_chars(PnString(Exp), &value,true);
if (!*pc) {
/* castable string, make the conversion */
Exp->tag = PTAG_CONSTANT;
PnConstant(Exp) = value;
}
}
}
/*------------------------------------------------------------------------------------------------*/
bool
set_symbol_attr(int *Section_number, unsigned *Class, enum gpasmValTypes Type)
{
int n_section;
unsigned cl;
bool valid;
switch (Type) {
case VAL_EXTERNAL:
n_section = N_UNDEF;
cl = C_EXT;
valid = true;
break;
case VAL_GLOBAL:
n_section = state.obj.section_num;
cl = C_EXT;
valid = true;
break;
case VAL_STATIC:
n_section = state.obj.section_num;
cl = C_STAT;
valid = true;
break;
case VAL_ADDRESS:
n_section = state.obj.section_num;
cl = C_LABEL;
valid = true;
break;
case VAL_ABSOLUTE:
n_section = N_ABS;
cl = C_NULL;
valid = true;
break;
case VAL_DEBUG:
n_section = N_DEBUG;
cl = C_NULL;
valid = true;
break;
default:
valid = false;
}
if (valid) {
if (Section_number) {
*Section_number = n_section;
}
if (Class) {
*Class = cl;
}
}
return valid;
}
/*------------------------------------------------------------------------------------------------*/
void set_global(const char *Name, gpasmVal Value, enum gpasmValTypes Type,
bool Proc_dependent, bool Has_no_value) {
symbol_t* sym;
variable_t* var;
unsigned flags;
int section_number;
unsigned pclass;
char* coff_name;
/* 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 = gp_sym_get_symbol(state.stTop, Name);
if (!sym) {
sym = gp_sym_add_symbol(state.stGlobal, Name);
}
var = (variable_t *)gp_sym_get_symbol_annotation(sym);
if (!var) {
/* pnew symbol */
flags = (Proc_dependent) ? VATRR_PROC_DEPENDENT : 0;
flags |= (Has_no_value) ? VATRR_HAS_NO_VALUE : 0;
var = new variable_t;
var->value = Value;
var->coff_symbol_num = state.obj.symbol_num;
var->coff_section_num = state.obj.section_num;
var->coff_section_flags = state.obj.new_sect_flags;
var->type = Type;
var->previous_type = Type;
var->flags = flags;
gp_sym_annotate_symbol(sym, var);
if (set_symbol_attr(§ion_number, &pclass, Type)) {
/* Increment the index into the coff symbol table for the relocations. */
state.obj.symbol_num++;
}
}
else if (Type == VAL_VARIABLE) {
/*
* 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 statement
* 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;
FlagClr(var->flags, VATRR_HAS_NO_VALUE);
}
else if (state.pass == 2) {
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
msg_has_no_value(NULL, Name);
}
else if (var->value != Value) {
gpmsg(GPE_DIFFLAB, NULL, Name);
}
coff_name = coff_local_name(Name);
coff_add_sym(coff_name, Value, var->type, state.obj.section_num);
if (coff_name) {
free(coff_name);
}
}
}
/*------------------------------------------------------------------------------------------------*/
/* Returns descriptor of a constant or variable. */
variable_t *
get_global_constant(const char *Name)
{
const symbol_t *sym;
variable_t *var;
if (((sym = gp_sym_get_symbol(state.stGlobal, Name))) &&
((var = (variable_t*)gp_sym_get_symbol_annotation(sym))) &&
((var->type == VAL_CONSTANT) || (var->type == VAL_VARIABLE))) {
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
gpmsg(GPW_SYM_NO_VALUE, NULL, Name);
}
return var;
}
return NULL;
}
/*------------------------------------------------------------------------------------------------*/
/* Clean out the variables. */
void
delete_variable_symbols(symbol_table_t *Table)
{
size_t i;
symbol_t *sym;
const variable_t *var;
if (Table == NULL) {
return;
}
for (i = 0; i < gp_sym_get_symbol_count(Table); ) {
sym = gp_sym_get_symbol_with_index(Table, i);
assert(sym);
var = (const variable_t *)gp_sym_get_symbol_annotation(sym);
if ((var) && (var->type == VAL_VARIABLE)) {
gp_sym_remove_symbol_with_index(Table, i);
}
else {
++i;
}
}
delete_variable_symbols(gp_sym_get_guest_table(Table));
}
/*------------------------------------------------------------------------------------------------*/
/* Clean out the processor dependent variables. */
void
delete_processor_variable_symbols(symbol_table_t *Table)
{
size_t i;
symbol_t *sym;
const variable_t *var;
if (Table == NULL) {
return;
}
for (i = 0; i < gp_sym_get_symbol_count(Table); ) {
sym = gp_sym_get_symbol_with_index(Table, i);
assert(sym);
var = (const variable_t *)gp_sym_get_symbol_annotation(sym);
if ((var) && (var->type == VAL_VARIABLE) && FlagIsSet(var->flags, VATRR_PROC_DEPENDENT)) {
gp_sym_remove_symbol_with_index(Table, i);
}
else {
++i;
}
}
delete_processor_variable_symbols(gp_sym_get_guest_table(Table));
}
/*------------------------------------------------------------------------------------------------*/
void
select_error_level(int Level)
{
if (state.cmd_line.error_level) {
gpmsg(GPM_SUPVAL, NULL);
}
else {
if ((Level >= 0) && (Level <= 2)) {
if (state.cmd_line.strict_level && (state.strict_level > 0)) {
/*
The "strict messages" more important than the other messages, therefore
enable each messages.
*/
state.error_level = 0;
}
else {
state.error_level = Level;
}
}
else {
if (state.pass == 0) {
fprintf(stderr, "Error: Invalid warning level \"%i\".\n", Level);
}
else {
gpmsg0(GPE_ILLEGAL_ARGU, "Expected w= 0, 1, 2");
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
void
select_strict_level(int Level)
{
if (state.cmd_line.strict_level) {
gpmsg(GPM_SUPVAL, NULL);
}
else {
if ((Level >= 0) && (Level <= 2)) {
state.strict_level = Level;
if ((Level > 0) && state.cmd_line.error_level) {
/* Enable each messages. */
state.error_level = 0;
}
}
else {
if (state.pass == 0) {
fprintf(stderr, "Error: Invalid strict level \"%i\".\n", Level);
}
else {
gpmsg0(GPE_ILLEGAL_ARGU, "Expected S= 0, 1, 2");
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
void
select_expand(const char *Expand)
{
if (state.cmd_line.macro_expand) {
gpmsg(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 {
gpmsg0(GPE_ILLEGAL_ARGU, "Expected ON or OFF.");
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
void
select_hex_format(const char *Format_name)
{
if (state.cmd_line.hex_format) {
gpmsg(GPW_CMDLINE_HEXFMT, NULL);
}
else {
if (strcasecmp(Format_name, STR_INHX8M) == 0) {
state.hex_format = INHX8M;
}
else if (strcasecmp(Format_name, STR_INHX8S) == 0) {
state.hex_format = INHX8S;
}
else if (strcasecmp(Format_name, STR_INHX16) == 0) {
state.hex_format = INHX16;
}
else if (strcasecmp(Format_name, STR_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 {
gpmsg0(GPE_ILLEGAL_ARGU, "Expected " STR_INHX8M ", " STR_INHX8S ", " STR_INHX16 ", or " STR_INHX32 ".");
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
void
select_radix(const char *Radix_name)
{
if (state.cmd_line.radix) {
gpmsg(GPW_CMDLINE_RADIX, NULL);
}
else {
if ((strcasecmp(Radix_name, "h") == 0) ||
(strcasecmp(Radix_name, "hex") == 0) ||
(strcasecmp(Radix_name, "hexadecimal") == 0)) {
state.radix = 16;
}
else if ((strcasecmp(Radix_name, "d") == 0) ||
(strcasecmp(Radix_name, "dec") == 0) ||
(strcasecmp(Radix_name, "decimal") == 0)) {
state.radix = 10;
}
else if ((strcasecmp(Radix_name, "o") == 0) ||
(strcasecmp(Radix_name, "oct") == 0) ||
(strcasecmp(Radix_name, "octal") == 0)) {
state.radix = 8;
}
else {
state.radix = 16;
if (state.pass == 0) {
fprintf(stderr, "Error: Invalid radix \"%s\", will use hex.\n", Radix_name);
}
else {
gpmsg(GPW_RADIX, NULL);
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
gpasmVal
do_or_append_insn(const char *Op, pnode_t *Parms)
{
if (IN_MACRO_WHILE_DEFINITION) {
if (strcasecmp(Op, "endm") == 0) {
return do_insn(Op, Parms);
}
else if (IN_WHILE_DEFINITION) {
if (strcasecmp(Op, "while") == 0) {
assert(state.while_depth != 0);
++state.while_depth;
}
else if ((state.while_head) && (strcasecmp(Op, "endw") == 0)) {
assert(state.while_depth != 0);
if (--state.while_depth == 0) {
return do_insn(Op, Parms);
}
}
}
macro_append();
return 0;
}
else {
return do_insn(Op, Parms);
}
}
/*------------------------------------------------------------------------------------------------*/
/* Build string from a macro parameter list.*/
char *
macro_params_to_string(char *String, size_t String_max_length, size_t *Length, const pnode_t *Macro_params)
{
size_t l;
size_t size;
if (Macro_params == NULL) {
return String;
}
assert(String_max_length > 0);
switch (Macro_params->tag) {
case PTAG_LIST:
String = macro_params_to_string(String, String_max_length, Length, PnListHead(Macro_params));
String = macro_params_to_string(String, String_max_length, Length, PnListTail(Macro_params));
break;
case PTAG_SYMBOL: {
if (!String) {
String = new char[String_max_length + 1];
String[0] = '\0';
*Length = 0;
}
l = *Length;
if (!l) {
/* This is the first parameter. */
l = snprintf(String, String_max_length, "%s", PnSymbol(Macro_params));
}else{
size = String_max_length > l ? String_max_length - l : 0;
if (size > 0) {
l += snprintf(String + l, size, ", %s", PnSymbol(Macro_params));
}
}
*Length = l;
break;
}
case PTAG_CONSTANT:
case PTAG_STRING:
case PTAG_OFFSET:
case PTAG_BINOP:
case PTAG_UNOP:
default:
break;
}
return String;
}
/*------------------------------------------------------------------------------------------------*/
const char *
variable_type_to_str(enum gpasmValTypes Type)
{
switch (Type) {
case VAL_CONSTANT: return "CONSTANT";
case VAL_VARIABLE: return "VARIABLE";
case VAL_ADDRESS: return "ADDRESS";
case VAL_CBLOCK: return "CBLOCK";
case VAL_EXTERNAL: return "EXTERNAL";
case VAL_GLOBAL: return "GLOBAL";
case VAL_STATIC: return "STATIC";
case VAL_ABSOLUTE: return "ABSOLUTE";
case VAL_DEBUG: return "DEBUG";
default: return "UNKNOWN";
}
}
/*------------------------------------------------------------------------------------------------*/
const char *
value_type_to_str(const variable_t *Variable, bool Previous)
{
enum gpasmValTypes type;
if (Variable == NULL) {
return NULL;
}
type = (Previous) ? Variable->previous_type : Variable->type;
return variable_type_to_str(type);
}
/*------------------------------------------------------------------------------------------------*/
const char *
pnode_symbol_name(const pnode_t *Pnode)
{
return (PnIsSymbol(Pnode) ? PnSymbol(Pnode) : NULL);
}
/*------------------------------------------------------------------------------------------------*/
gpasmVal pnode_symbol_value(const pnode_t* Pnode) {
if (!PnIsSymbol(Pnode)) return 0;
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(PnSymbol(Pnode), "$")) {
*/
const char* name = PnSymbol(Pnode);
if ((name[0] == '$') && (name[1] == '\0')) {
int addr = (IS_RAM_ORG ? state.byte_addr :
gp_processor_insn_from_byte_p(state.processor, state.byte_addr));
return addr;
}
const symbol_t* sym = gp_sym_get_symbol(state.stTop, PnSymbol(Pnode));
assert(sym);
const variable_t* var = (const variable_t*)gp_sym_get_symbol_annotation(sym);
assert(var);
return var->value;
}
/*------------------------------------------------------------------------------------------------*/
const char* pnode_string(const pnode_t *Pnode) {
return (PnIsString(Pnode) ? PnString(Pnode) : NULL);
}
/*------------------------------------------------------------------------------------------------*/
void msg_has_no_value(const char *Optional_text, const char *Symbol_name) {
if (state.mpasm_compatible) return;
switch (state.strict_level) {
case 1:
gpmsg(GPW_SYM_NO_VALUE, Optional_text, Symbol_name);
break;
case 2:
gpmsg(GPE_SYM_NO_VALUE, Optional_text, Symbol_name);
break;
}
}
/*------------------------------------------------------------------------------------------------*/
/*
static void
_print_macro_line(const macro_body_t *Mac)
{
if (Mac->src_line) {
printf(" src_line = %s\n", Mac->src_line);
}
}
static void
_print_macro_body(const macro_body_t *Mac)
{
const macro_body_t *mb = Mac;
printf("{\n");
while(mb) {
_print_macro_line(mb);
mb = mb->next;
}
printf("}\n");
}*/
/*------------------------------------------------------------------------------------------------*/
/* Function to append a line to an ongoing macro definition. */
void macro_append() {
macro_body_t* body = new macro_body_t;
body->src_line = NULL;
body->next = NULL; /* make sure it's terminated */
*state.mac_prev = body; /* append this to the chain */
state.mac_prev = &body->next; /* this is the pnew end of the chain */
state.mac_body = body;
}
/*------------------------------------------------------------------------------------------------*/
void hex_create() {
if (state.hex_file == OUT_SUPPRESS) {
/* Must delete hex file when suppressed. */
gp_writehex(state.base_file_name, state.i_memory, state.hex_format, 1, state.dos_newlines, 1);
return;
}
if (!gp_writehex_check(state.i_memory, state.hex_format)) {
gpmsg(GPE_IHEX, NULL);
gp_writehex(state.base_file_name, state.i_memory, state.hex_format, 1, state.dos_newlines, 1);
}else if (state.device.pclass) {
if (!gp_writehex(state.base_file_name, state.i_memory, state.hex_format, state.num.errors,
state.dos_newlines, state.device.pclass->core_mask)) {
gpmsg0(GPE_UNKNOWN, "Error generating hex file.");
exit(1);
}
}else{
/* Won't have anything to write, just remove any old files. */
gp_writehex(state.base_file_name, state.i_memory, state.hex_format, 1, state.dos_newlines, 1);
}
}
Detected encoding: UTF-8 | 0
|