refactored symbol assignment (unfinished)

git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@214 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye 2020-06-05 01:11:51 +00:00
parent 72fc28e84c
commit 00d0462f74
8 changed files with 114 additions and 98 deletions

View File

@ -351,7 +351,14 @@ static void get_symbol_value(scope_t scope, char optional_prefix_char, size_t na
struct object *arg; struct object *arg;
// if the symbol gets created now, mark it as unsure // if the symbol gets created now, mark it as unsure
symbol = symbol_find(scope, NUMBER_EVER_UNDEFINED); // TODO - split into "find" and "if NULL object, make into undefined int" symbol = symbol_find(scope);
if (symbol->object.type == NULL) {
// finish symbol item by making it an undefined int
symbol->object.type = &type_int;
symbol->object.u.number.flags = NUMBER_EVER_UNDEFINED;
symbol->object.u.number.addr_refs = 0;
symbol->object.u.number.val.intval = 0;
}
// first push on arg stack, so we have a local copy we can "unpseudopc" // first push on arg stack, so we have a local copy we can "unpseudopc"
arg = &arg_stack[arg_sp++]; arg = &arg_stack[arg_sp++];
*arg = symbol->object; *arg = symbol->object;

View File

@ -168,16 +168,17 @@ static void set_label(scope_t scope, int stat_flags, int force_bit, boolean chan
struct object result; struct object result;
struct symbol *symbol; struct symbol *symbol;
symbol = symbol_find(scope, force_bit); // TODO - split into "find", "forcebit handling", "if NULL object, make int" and "if not int, complain"
// label definition
if ((stat_flags & SF_FOUND_BLANK) && config.warn_on_indented_labels) if ((stat_flags & SF_FOUND_BLANK) && config.warn_on_indented_labels)
Throw_first_pass_warning("Label name not in leftmost column."); Throw_first_pass_warning("Label name not in leftmost column.");
symbol = symbol_find(scope);
// label definition
vcpu_read_pc(&pc); vcpu_read_pc(&pc);
// FIXME - if undefined, check pass.complain_about_undefined and maybe throw "value not defined"! // FIXME - if undefined, check pass.complain_about_undefined and maybe throw "value not defined"!
result.type = &type_int; result.type = &type_int;
result.u.number.flags = pc.flags & NUMBER_IS_DEFINED; result.u.number.flags = pc.flags & NUMBER_IS_DEFINED;
result.u.number.val.intval = pc.val.intval; result.u.number.val.intval = pc.val.intval;
result.u.number.addr_refs = pc.addr_refs; result.u.number.addr_refs = pc.addr_refs;
symbol_forcebit(symbol, force_bit); // TODO - "if NULL object, make int" and "if not int, complain"
symbol_set_object(symbol, &result, change_allowed); // FIXME - "backward anon allows number redef" is different from "!set allows object redef"! symbol_set_object(symbol, &result, change_allowed); // FIXME - "backward anon allows number redef" is different from "!set allows object redef"!
symbol->pseudopc = pseudopc_get_context(); symbol->pseudopc = pseudopc_get_context();
// global labels must open new scope for cheap locals // global labels must open new scope for cheap locals
@ -186,29 +187,55 @@ static void set_label(scope_t scope, int stat_flags, int force_bit, boolean chan
} }
// call with symbol name in GlobalDynaBuf and GotByte == '='
// "po_set" is for "!set" pseudo opcode, so changes are allowed
void parse_assignment(scope_t scope, int force_bit, boolean po_set)
{
struct symbol *symbol;
struct object result;
GetByte(); // eat '='
symbol = symbol_find(scope);
ALU_any_result(&result);
// if wanted, mark as address reference
if (typesystem_says_address()) {
// FIXME - checking types explicitly is ugly...
if ((result.type == &type_int)
|| (result.type == &type_float))
result.u.number.addr_refs = 1;
}
// FIXME - force bit can only be used if result is number! check!
symbol_forcebit(symbol, force_bit);
// if this was called by !set, new force bit replaces old one:
if (po_set) {
// clear symbol's force bits and set new ones
// (but only do this for numbers!)
if (((symbol->object.type == &type_int) || (symbol->object.type == &type_float))
&& ((result.type == &type_int) || (result.type == &type_float))) {
symbol->object.u.number.flags &= ~(NUMBER_FORCEBITS | NUMBER_FITS_BYTE);
if (force_bit) {
symbol->object.u.number.flags |= force_bit;
result.u.number.flags &= ~(NUMBER_FORCEBITS | NUMBER_FITS_BYTE);
}
}
// FIXME - take a good look at the flags handling above and in the fn called below and clean this up!
}
symbol_set_object(symbol, &result, po_set);
}
// parse symbol definition (can be either global or local, may turn out to be a label). // parse symbol definition (can be either global or local, may turn out to be a label).
// name must be held in GlobalDynaBuf. // name must be held in GlobalDynaBuf.
static void parse_symbol_definition(scope_t scope, int stat_flags) static void parse_symbol_definition(scope_t scope, int stat_flags)
{ {
struct object result; struct object result;
struct symbol *symbol; struct symbol *symbol;
int force_bit = Input_get_force_bit(); // skips spaces after int force_bit;
// FIXME - force bit is allowed for label definitions?!
force_bit = Input_get_force_bit(); // skips spaces after (yes, force bit is allowed for label definitions)
if (GotByte == '=') { if (GotByte == '=') {
// explicit symbol definition (symbol = <something>) // explicit symbol definition (symbol = <something>)
symbol = symbol_find(scope, force_bit); // FIXME - split into "find", "forcebit handling", "if not NULL object, types must be equal"... parse_assignment(scope, force_bit, FALSE);
// symbol = parsed value
GetByte(); // skip '='
ALU_any_result(&result);
// if wanted, mark as address reference
if (typesystem_says_address()) {
// FIXME - checking types explicitly is ugly...
if ((result.type == &type_int)
|| (result.type == &type_float))
result.u.number.addr_refs = 1;
}
symbol_set_object(symbol, &result, FALSE);
Input_ensure_EOS(); Input_ensure_EOS();
} else { } else {
// implicit symbol definition (label) // implicit symbol definition (label)
@ -220,8 +247,11 @@ static void parse_symbol_definition(scope_t scope, int stat_flags)
// Parse global symbol definition or assembler mnemonic // Parse global symbol definition or assembler mnemonic
static void parse_mnemo_or_global_symbol_def(int *statement_flags) static void parse_mnemo_or_global_symbol_def(int *statement_flags)
{ {
boolean is_mnemonic;
is_mnemonic = CPU_state.type->keyword_is_mnemonic(Input_read_keyword());
// It is only a label if it isn't a mnemonic // It is only a label if it isn't a mnemonic
if ((CPU_state.type->keyword_is_mnemonic(Input_read_keyword()) == FALSE) if ((!is_mnemonic)
&& first_label_of_statement(statement_flags)) { && first_label_of_statement(statement_flags)) {
// Now GotByte = illegal char // Now GotByte = illegal char
// 04 Jun 2005: this fix should help to explain "strange" error messages. // 04 Jun 2005: this fix should help to explain "strange" error messages.
@ -297,7 +327,7 @@ void Parse_until_eob_or_eof(void)
statement_flags = 0; // no "label = pc" definition yet statement_flags = 0; // no "label = pc" definition yet
typesystem_force_address_statement(FALSE); typesystem_force_address_statement(FALSE);
// Parse until end of statement. Only loops if statement // Parse until end of statement. Only loops if statement
// contains "label = pc" definition and something else; or // contains implicit label definition (=pc) and something else; or
// if "!ifdef/ifndef" is true/false, or if "!addr" is used without block. // if "!ifdef/ifndef" is true/false, or if "!addr" is used without block.
do { do {
// check for pseudo opcodes was moved out of switch, // check for pseudo opcodes was moved out of switch,

View File

@ -134,6 +134,9 @@ do { \
extern void config_default(struct config *conf); extern void config_default(struct config *conf);
// allocate memory and die if not available // allocate memory and die if not available
extern void *safe_malloc(size_t amount); extern void *safe_malloc(size_t amount);
// call with symbol name in GlobalDynaBuf and GotByte == '='
// "po_set" is for "!set" pseudo opcode, so changes are allowed
extern void parse_assignment(scope_t scope, int force_bit, boolean po_set);
// Parse block, beginning with next byte. // Parse block, beginning with next byte.
// End reason (either CHAR_EOB or CHAR_EOF) can be found in GotByte afterwards // End reason (either CHAR_EOB or CHAR_EOF) can be found in GotByte afterwards
// Has to be re-entrant. // Has to be re-entrant.

View File

@ -250,8 +250,8 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// internal_name = MacroTitle ARG_SEPARATOR (grows to signature) // internal_name = MacroTitle ARG_SEPARATOR (grows to signature)
// Accept n>=0 comma-separated arguments before CHAR_EOS. // Accept n>=0 comma-separated arguments before CHAR_EOS.
// Valid argument formats are: // Valid argument formats are:
// EXPRESSION (everything that does NOT start with '~' // ~SYMBOL call by ref
// ~SYMBOL // EXPRESSION call by value (everything that does NOT start with '~')
// now GotByte = non-space // now GotByte = non-space
if (GotByte != CHAR_EOS) { // any at all? if (GotByte != CHAR_EOS) { // any at all?
do { do {
@ -263,10 +263,10 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
if (GotByte == REFERENCE_CHAR) { if (GotByte == REFERENCE_CHAR) {
// read call-by-reference arg // read call-by-reference arg
DynaBuf_append(internal_name, ARGTYPE_REF); DynaBuf_append(internal_name, ARGTYPE_REF);
GetByte(); // skip '~' character GetByte(); // eat '~'
Input_read_scope_and_keyword(&symbol_scope); Input_read_scope_and_keyword(&symbol_scope);
// GotByte = illegal char // GotByte = illegal char
arg_table[arg_count].symbol = symbol_find(symbol_scope, 0); // FIXME - do not default to undefined int! arg_table[arg_count].symbol = symbol_find(symbol_scope); // CAUTION, object type may be NULL!
} else { } else {
// read call-by-value arg // read call-by-value arg
DynaBuf_append(internal_name, ARGTYPE_VALUE); DynaBuf_append(internal_name, ARGTYPE_VALUE);
@ -317,22 +317,21 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// In both cases, GlobalDynaBuf may be used. // In both cases, GlobalDynaBuf may be used.
if (GotByte == REFERENCE_CHAR) { if (GotByte == REFERENCE_CHAR) {
// assign call-by-reference arg // assign call-by-reference arg
GetByte(); // skip '~' character GetByte(); // eat '~'
Input_read_scope_and_keyword(&symbol_scope); Input_read_scope_and_keyword(&symbol_scope);
// create new tree node and link existing symbol struct from arg list to it // create new tree node and link existing symbol struct from arg list to it
if ((Tree_hard_scan(&symbol_node, symbols_forest, symbol_scope, TRUE) == FALSE) if ((Tree_hard_scan(&symbol_node, symbols_forest, symbol_scope, TRUE) == FALSE)
&& (FIRST_PASS)) && (FIRST_PASS))
Throw_error("Macro parameter twice."); Throw_error("Macro parameter twice.");
symbol_node->body = arg_table[arg_count].symbol; symbol_node->body = arg_table[arg_count].symbol; // CAUTION, object type may be NULL
} else { } else {
// assign call-by-value arg // assign call-by-value arg
Input_read_scope_and_keyword(&symbol_scope); Input_read_scope_and_keyword(&symbol_scope);
symbol = symbol_find(symbol_scope, 0); // FIXME - split into "find", "ensure freshly created" symbol = symbol_find(symbol_scope);
// FIXME - add a possibility to symbol_find to make it possible to find out // FIXME - find out if symbol was just created.
// whether symbol was just created. Then check for the same error message here // Then check for the same error message here as above ("Macro parameter twice.").
// as above ("Macro parameter twice.").
// TODO - on the other hand, this would rule out globals as args (stupid anyway, but not illegal yet!) // TODO - on the other hand, this would rule out globals as args (stupid anyway, but not illegal yet!)
symbol->object = arg_table[arg_count].result; // FIXME - this assignment redefines globals without throwing errors! symbol->object = arg_table[arg_count].result; // FIXME - this assignment redefines globals/whatever without throwing errors!
} }
++arg_count; ++arg_count;
} while (Input_accept_comma()); } while (Input_accept_comma());

View File

@ -739,37 +739,19 @@ Throw_serious_error("Not yet"); // FIXME
// (re)set symbol // (re)set symbol
static enum eos po_set(void) // now GotByte = illegal char static enum eos po_set(void) // now GotByte = illegal char
{ {
struct object result; scope_t scope;
int force_bit; int force_bit;
struct symbol *symbol;
scope_t scope;
if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before
// now GotByte = illegal char return SKIP_REMAINDER; // zero length
return SKIP_REMAINDER;
force_bit = Input_get_force_bit(); // skips spaces after force_bit = Input_get_force_bit(); // skips spaces after
symbol = symbol_find(scope, force_bit); // FIXME - split into "find" and "handle forcebits", remove the "default to undefined int" part!
if (GotByte != '=') { if (GotByte != '=') {
Throw_error(exception_syntax); Throw_error(exception_syntax);
return SKIP_REMAINDER; return SKIP_REMAINDER;
} }
// symbol = parsed value parse_assignment(scope, force_bit, TRUE);
GetByte(); // proceed with next char
ALU_any_result(&result);
// clear symbol's force bits and set new ones
// (but only do this for numbers!)
if (((symbol->object.type == &type_int) || (symbol->object.type == &type_float))
&& ((result.type == &type_int) || (result.type == &type_float))) {
symbol->object.u.number.flags &= ~(NUMBER_FORCEBITS | NUMBER_FITS_BYTE);
if (force_bit) {
symbol->object.u.number.flags |= force_bit;
result.u.number.flags &= ~(NUMBER_FORCEBITS | NUMBER_FITS_BYTE);
}
}
// FIXME - take a good look at the flags handling above and in the fn called below and clean this up!
symbol_set_object(symbol, &result, TRUE);
return ENSURE_EOS; return ENSURE_EOS;
} }
@ -1053,11 +1035,12 @@ static enum eos po_for(void) // now GotByte = illegal char
struct for_loop loop; struct for_loop loop;
if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before
return SKIP_REMAINDER; return SKIP_REMAINDER; // zero length
// now GotByte = illegal char // now GotByte = illegal char
force_bit = Input_get_force_bit(); // skips spaces after force_bit = Input_get_force_bit(); // skips spaces after
loop.symbol = symbol_find(scope, force_bit); // FIXME - split into "find" and "handle force bit". if type is not NULL, complain if not number! loop.symbol = symbol_find(scope); // FIXME - if type is not NULL, complain if not number!
symbol_forcebit(loop.symbol, force_bit);
if (!Input_accept_comma()) { if (!Input_accept_comma()) {
Throw_error(exception_syntax); Throw_error(exception_syntax);
return SKIP_REMAINDER; return SKIP_REMAINDER;
@ -1066,7 +1049,8 @@ static enum eos po_for(void) // now GotByte = illegal char
ALU_defined_int(&intresult); // read first argument ALU_defined_int(&intresult); // read first argument
loop.counter.addr_refs = intresult.addr_refs; loop.counter.addr_refs = intresult.addr_refs;
if (Input_accept_comma()) { if (Input_accept_comma()) {
loop.use_old_algo = FALSE; // new format - yay! // new format - yay!
loop.use_old_algo = FALSE;
if (config.wanted_version < VER_NEWFORSYNTAX) if (config.wanted_version < VER_NEWFORSYNTAX)
Throw_first_pass_warning("Found new \"!for\" syntax."); Throw_first_pass_warning("Found new \"!for\" syntax.");
loop.counter.first = intresult.val.intval; // use first argument loop.counter.first = intresult.val.intval; // use first argument
@ -1079,7 +1063,8 @@ static enum eos po_for(void) // now GotByte = illegal char
} }
loop.counter.increment = (loop.counter.last < loop.counter.first) ? -1 : 1; loop.counter.increment = (loop.counter.last < loop.counter.first) ? -1 : 1;
} else { } else {
loop.use_old_algo = TRUE; // old format - booo! // old format - booo!
loop.use_old_algo = TRUE;
if (config.wanted_version >= VER_NEWFORSYNTAX) if (config.wanted_version >= VER_NEWFORSYNTAX)
Throw_first_pass_warning("Found old \"!for\" syntax."); Throw_first_pass_warning("Found old \"!for\" syntax.");
if (intresult.val.intval < 0) if (intresult.val.intval < 0)

View File

@ -107,8 +107,9 @@ static void dump_vice_unusednonaddress(struct rwnode *node, FILE *fd)
} }
// temporary helper function to properly refactor this mess // search for symbol. if it does not exist, create with NULL object (CAUTION!).
static struct symbol *temp_find(scope_t scope) // the symbol name must be held in GlobalDynaBuf.
struct symbol *symbol_find(scope_t scope)
{ {
struct rwnode *node; struct rwnode *node;
struct symbol *symbol; struct symbol *symbol;
@ -132,51 +133,27 @@ static struct symbol *temp_find(scope_t scope)
return symbol; // now symbol->object.type can be tested to see if this was freshly created. return symbol; // now symbol->object.type can be tested to see if this was freshly created.
// CAUTION: this only works if caller always sets a type pointer after checking! if NULL is kept, the struct still looks new later on... // CAUTION: this only works if caller always sets a type pointer after checking! if NULL is kept, the struct still looks new later on...
} }
// search for symbol. create if nonexistant. if created, give it flags "flags".
// the symbol name must be held in GlobalDynaBuf.
/*
FIXME - to get lists/strings to work, this can no longer create an int by default!
maybe get rid of "int flags" and use some "struct object *default" instead?
called by;
alu.c
get_symbol_value (here it's okay to create an undefined int)
global.c
set_label implicit symbol definition (gets assigned pc, so int is ok)
parse_symbol_definition explicit symbol definition
macro.c
Macro_parse_call early to build array of outer refs in case of call-by-ref
Macro_parse_call later to lookup inner symbols in case of call-by-value
symbol.c
symbol_define
symbol_fix_forward_anon_name
*/
struct symbol *symbol_find(scope_t scope, int flags) // FIXME - "flags" is either 0 or UNDEFINED or a forcebit, right? move UNDEFINED and forcebit stuff elsewhere!
{
struct symbol *symbol;
int new_force_bits;
symbol = temp_find(scope);
// FIXME - merge with function below!
void symbol_forcebit(struct symbol *symbol, int force_bit)
{
// if symbol has no object assigned to it, make it an int // if symbol has no object assigned to it, make it an int
if (symbol->object.type == NULL) { if (symbol->object.type == NULL) {
// finish empty symbol item // finish empty symbol item
symbol->object.type = &type_int; symbol->object.type = &type_int;
symbol->object.u.number.flags = flags; symbol->object.u.number.flags = force_bit;
symbol->object.u.number.addr_refs = 0; symbol->object.u.number.addr_refs = 0;
symbol->object.u.number.val.intval = 0; symbol->object.u.number.val.intval = 0;
} else { } else {
// make sure the force bits don't clash // make sure the force bits don't clash
if ((symbol->object.type == &type_int) if ((symbol->object.type == &type_int)
|| (symbol->object.type == &type_float)) { || (symbol->object.type == &type_float)) {
new_force_bits = flags & NUMBER_FORCEBITS; if ((symbol->object.u.number.flags & NUMBER_FORCEBITS) != force_bit)
if (new_force_bits) Throw_error("Too late for postfix.");
if ((symbol->object.u.number.flags & NUMBER_FORCEBITS) != new_force_bits)
Throw_error("Too late for postfix.");
} }
} }
return symbol;
} }
// assign value to symbol. the function acts upon the symbol's flag bits and // assign value to symbol. the function acts upon the symbol's flag bits and
// produces an error if needed. // produces an error if needed.
// TODO - split checks into two parts: first deal with object type. in case of number, then check value/flags/whatever // TODO - split checks into two parts: first deal with object type. in case of number, then check value/flags/whatever
@ -237,8 +214,8 @@ void symbol_define(intval_t value)
result.type = &type_int; result.type = &type_int;
result.u.number.flags = NUMBER_IS_DEFINED; result.u.number.flags = NUMBER_IS_DEFINED;
result.u.number.val.intval = value; result.u.number.val.intval = value;
symbol = symbol_find(SCOPE_GLOBAL, 0); symbol = symbol_find(SCOPE_GLOBAL);
symbol_set_object(symbol, &result, TRUE); symbol->object = result;
} }
@ -268,6 +245,12 @@ void symbols_vicelabels(FILE *fd)
// references the *next* anonymous forward label definition. The tricky bit is, // references the *next* anonymous forward label definition. The tricky bit is,
// each name length would need its own counter. But hey, ACME's real quick in // each name length would need its own counter. But hey, ACME's real quick in
// finding symbols, so I'll just abuse the symbol system to store those counters. // finding symbols, so I'll just abuse the symbol system to store those counters.
// example:
// forward anon name is "+++"
// we look up that symbol's value in the current local scope -> $12
// we attach hex digits to name -> "+++21"
// that's the name of the symbol that _actually_ contains the address
// caller sets "increment" to TRUE for writing, FALSE for reading
void symbol_fix_forward_anon_name(boolean increment) void symbol_fix_forward_anon_name(boolean increment)
{ {
struct symbol *counter_symbol; struct symbol *counter_symbol;
@ -275,10 +258,17 @@ void symbol_fix_forward_anon_name(boolean increment)
// terminate name, find "counter" symbol and read value // terminate name, find "counter" symbol and read value
DynaBuf_append(GlobalDynaBuf, '\0'); DynaBuf_append(GlobalDynaBuf, '\0');
counter_symbol = symbol_find(section_now->local_scope, 0); counter_symbol = symbol_find(section_now->local_scope);
// sanity check: it must be an int! if (counter_symbol->object.type == NULL) {
if (counter_symbol->object.type != &type_int) // finish freshly created symbol item
counter_symbol->object.type = &type_int;
counter_symbol->object.u.number.flags = NUMBER_IS_DEFINED;
counter_symbol->object.u.number.addr_refs = 0;
counter_symbol->object.u.number.val.intval = 0;
} else if (counter_symbol->object.type != &type_int) {
// sanity check: it must be an int!
Bug_found("ForwardAnonCounterNotInt", 0); Bug_found("ForwardAnonCounterNotInt", 0);
}
// make sure it gets reset to zero in each new pass // make sure it gets reset to zero in each new pass
if (counter_symbol->pass != pass.number) { if (counter_symbol->pass != pass.number) {
counter_symbol->pass = pass.number; counter_symbol->pass = pass.number;

View File

@ -31,9 +31,11 @@ extern struct rwnode *symbols_forest[]; // trees (because of 8-bit hash)
// function acts upon the symbol's flag bits and produces an error if needed. // function acts upon the symbol's flag bits and produces an error if needed.
extern void symbol_set_object(struct symbol *symbol, struct object *new_obj, boolean change_allowed); extern void symbol_set_object(struct symbol *symbol, struct object *new_obj, boolean change_allowed);
// search for symbol. create if nonexistant. if created, assign flags. // search for symbol. if it does not exist, create with NULL object (CAUTION!).
// name must be held in GlobalDynaBuf. // the symbol name must be held in GlobalDynaBuf.
extern struct symbol *symbol_find(scope_t, int flags); extern struct symbol *symbol_find(scope_t scope);
// FIXME
extern void symbol_forcebit(struct symbol *symbol, int force_bit);
// set global symbol to value, no questions asked (for "-D" switch) // set global symbol to value, no questions asked (for "-D" switch)
// name must be held in GlobalDynaBuf. // name must be held in GlobalDynaBuf.
extern void symbol_define(intval_t value); extern void symbol_define(intval_t value);

View File

@ -9,7 +9,7 @@
#define RELEASE "0.96.5" // update before release FIXME #define RELEASE "0.96.5" // update before release FIXME
#define CODENAME "Fenchurch" // update before release #define CODENAME "Fenchurch" // update before release
#define CHANGE_DATE "3 June" // update before release FIXME #define CHANGE_DATE "5 June" // update before release FIXME
#define CHANGE_YEAR "2020" // update before release #define CHANGE_YEAR "2020" // update before release
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME