From dddf3f3d10fd125f9db743669046cc30889b23ca Mon Sep 17 00:00:00 2001 From: marcobaye Date: Thu, 28 May 2020 18:40:40 +0000 Subject: [PATCH] '&' operator (for un-pseudopc-ing) seems to be finished git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@196 4df02467-bbd4-4a76-a152-e7ce94205b78 --- src/alu.c | 92 +++++++++++++++++++++++++++++++++------------ src/output.c | 60 ++++++++++++++++++++++++++--- src/output.h | 17 +++++---- src/pseudoopcodes.c | 6 +-- src/symbol.c | 4 ++ src/symbol.h | 1 + src/version.h | 2 +- 7 files changed, 140 insertions(+), 42 deletions(-) diff --git a/src/alu.c b/src/alu.c index 2412046..d9d169c 100644 --- a/src/alu.c +++ b/src/alu.c @@ -345,23 +345,33 @@ static void is_not_defined(struct symbol *optional_symbol, char optional_prefix_ // their internal name is different (longer) than their displayed name. // This function is not allowed to change DynaBuf because that's where the // symbol name is stored! -static void get_symbol_value(scope_t scope, char optional_prefix_char, size_t name_length, int derefs) +static void get_symbol_value(scope_t scope, char optional_prefix_char, size_t name_length, unsigned int unpseudo_count) { struct symbol *symbol; + struct object *arg; - if (derefs) - Bug_found("DerefsNotDoneYet", derefs); // TODO - support! // if the symbol gets created now, mark it as unsure symbol = symbol_find(scope, NUMBER_EVER_UNDEFINED); + // first push on arg stack, so we have a local copy we can "unpseudopc" + arg = &arg_stack[arg_sp++]; + *arg = symbol->result; + if (unpseudo_count) { + if (arg->type == &type_int) { + pseudopc_unpseudo(&arg->u.number, symbol->pseudopc, unpseudo_count); + // TODO - check return value and enter error state if nonzero? + } else { + Throw_error("Monadic '&' operator can only be applied to labels."); // TODO - add to docs + // TODO - enter error state? + } + } // if needed, output "value not defined" error - if (!(symbol->result.type->is_defined(&symbol->result))) + // FIXME - in case of unpseudopc, error message should include the correct amount of '&' characters + if (!(arg->type->is_defined(arg))) is_not_defined(symbol, optional_prefix_char, GLOBALDYNABUF_CURRENT, name_length); // in first pass, count usage if (FIRST_PASS) symbol->usage++; - // push argument, regardless of whether int or float // FIXME - if arg is list, increment ref count! - arg_stack[arg_sp++] = symbol->result; } @@ -651,6 +661,42 @@ static void list_init_list(struct object *self) // expression parser +// helper function for "monadic &" (either octal value or "unpseudo" operator) +// returns nonzero on error +static int parse_octal_or_unpseudo(void) // now GotByte = '&' +{ + unsigned int unpseudo_count = 1; + + while (GetByte() == '&') + ++unpseudo_count; + if ((unpseudo_count == 1) && (GotByte >= '0') & (GotByte <= '7')) { + parse_octal_literal(); // now GotByte = non-octal char + return 0; // ok + } + + // TODO - support anonymous labels as well? + if (GotByte == '.') { + GetByte(); + if (Input_read_keyword() == 0) // now GotByte = illegal char + return 1; // error (no string given) + + get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1, unpseudo_count); // -1 to not count terminator + } else if (GotByte == CHEAP_PREFIX) { + GetByte(); + if (Input_read_keyword() == 0) // now GotByte = illegal char + return 1; // error (no string given) + + get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1, unpseudo_count); // -1 to not count terminator + } else if (BYTE_STARTS_KEYWORD(GotByte)) { + Input_read_keyword(); // now GotByte = illegal char + get_symbol_value(SCOPE_GLOBAL, '\0', GlobalDynaBuf->size - 1, unpseudo_count); // no prefix, -1 to not count terminator + } else { + Throw_error(exception_missing_string); + return 1; // error + } + return 0; // ok +} + // Expect argument or monadic operator (hopefully inlined) // returns TRUE if it ate any non-space (-> so expression isn't empty) // returns FALSE if first non-space is delimiter (-> end of expression) @@ -670,7 +716,7 @@ static boolean expect_argument_or_monadic_operator(void) while (GetByte() == '+'); ugly_length_kluge = GlobalDynaBuf->size; // FIXME - get rid of this! symbol_fix_forward_anon_name(FALSE); // FALSE: do not increment counter - get_symbol_value(section_now->local_scope, '\0', ugly_length_kluge, 0); // no prefix, no derefs + get_symbol_value(section_now->local_scope, '\0', ugly_length_kluge, 0); // no prefix, no unpseudo goto now_expect_dyadic_op; case '-': // NEGATION operator or anonymous backward label @@ -684,7 +730,7 @@ static boolean expect_argument_or_monadic_operator(void) SKIPSPACE(); if (BYTE_FOLLOWS_ANON(GotByte)) { DynaBuf_append(GlobalDynaBuf, '\0'); - get_symbol_value(section_now->local_scope, '\0', GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no derefs + get_symbol_value(section_now->local_scope, '\0', GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no unpseudo goto now_expect_dyadic_op; } @@ -742,12 +788,13 @@ static boolean expect_argument_or_monadic_operator(void) parse_binary_literal(); // Now GotByte = non-binary char goto now_expect_dyadic_op; - case '&': // Octal value - // TODO - count consecutive '&' and allow symbol afterward, for pseudopc-de-ref! - GetByte(); // this was moved here from parse_octal_literal to prepare for the TODO above - parse_octal_literal(); // Now GotByte = non-octal char - goto now_expect_dyadic_op; + case '&': // octal value or "unpseudo" operator applied to label + if (parse_octal_or_unpseudo() == 0) + goto now_expect_dyadic_op; + // if we're here, there was an error (like "no string given"): + alu_state = STATE_ERROR; + break;//goto done; case '$': // Hexadecimal value parse_hex_literal(); // Now GotByte = non-hexadecimal char @@ -763,15 +810,13 @@ static boolean expect_argument_or_monadic_operator(void) GetByte(); // start after '.' // check for fractional part of float value if ((GotByte >= '0') && (GotByte <= '9')) { - parse_frac_part(0); - // Now GotByte = non-decimal char + parse_frac_part(0); // now GotByte = non-decimal char goto now_expect_dyadic_op; } - if (Input_read_keyword()) { - // Now GotByte = illegal char - get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no derefs - goto now_expect_dyadic_op; + if (Input_read_keyword()) { // now GotByte = illegal char + get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no unpseudo + goto now_expect_dyadic_op; // ok } // if we're here, Input_read_keyword() will have thrown an error (like "no string given"): @@ -780,10 +825,9 @@ static boolean expect_argument_or_monadic_operator(void) case CHEAP_PREFIX: // cheap local symbol //printf("looking in cheap scope %d\n", section_now->cheap_scope); GetByte(); // start after '@' - if (Input_read_keyword()) { - // Now GotByte = illegal char - get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no derefs - goto now_expect_dyadic_op; + if (Input_read_keyword()) { // now GotByte = illegal char + get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no unpseudo + goto now_expect_dyadic_op; // ok } // if we're here, Input_read_keyword() will have thrown an error (like "no string given"): @@ -822,7 +866,7 @@ static boolean expect_argument_or_monadic_operator(void) // however, apart from that check above, function calls have nothing to do with // parentheses: "sin(x+y)" gets parsed just like "not(x+y)". } else { - get_symbol_value(SCOPE_GLOBAL, '\0', GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no derefs + get_symbol_value(SCOPE_GLOBAL, '\0', GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no unpseudo goto now_expect_dyadic_op; } } diff --git a/src/output.c b/src/output.c index 754a144..63345a0 100644 --- a/src/output.c +++ b/src/output.c @@ -53,6 +53,9 @@ struct output { char xor; // output modifier }; +// for offset assembly: +static struct pseudopc *pseudopc_current_context; // current struct (NULL when not in pseudopc block) + // variables static struct output default_output; @@ -496,6 +499,9 @@ void Output_passinit(void) CPU_state.pc.flags = 0; // not defined yet CPU_state.pc.val.intval = 0; // same as output's write_idx on pass init CPU_state.add_to_pc = 0; // increase PC by this at end of statement + + // pseudopc stuff: + pseudopc_current_context = NULL; } @@ -629,18 +635,60 @@ void vcpu_end_statement(void) CPU_state.add_to_pc = 0; } + +// struct to describe a pseudopc context +struct pseudopc { + struct pseudopc *outer; // next layer (to be able to "unpseudopc" labels by more than one level) + intval_t offset; // inner minus outer pc + int flags; // flags of outer pc +}; // start offset assembly -void pseudopc_start(struct pseudopc *buffer, struct number *new_pc) +void pseudopc_start(struct number *new_pc) { - buffer->flags = CPU_state.pc.flags; - buffer->offset = (new_pc->val.intval - CPU_state.pc.val.intval) & 0xffff; + struct pseudopc *new_context; + + new_context = safe_malloc(sizeof(*new_context)); // create new struct (this must never be freed, as it gets linked to labels!) + new_context->outer = pseudopc_current_context; // let it point to previous one + pseudopc_current_context = new_context; // make it the current one + + new_context->flags = CPU_state.pc.flags; + new_context->offset = new_pc->val.intval - CPU_state.pc.val.intval; CPU_state.pc.val.intval = new_pc->val.intval; CPU_state.pc.flags |= NUMBER_IS_DEFINED; // FIXME - remove when allowing undefined! //new: CPU_state.pc.flags = new_pc->flags & (NUMBER_IS_DEFINED | NUMBER_EVER_UNDEFINED); } // end offset assembly -void pseudopc_end(struct pseudopc *buffer) +void pseudopc_end(void) { - CPU_state.pc.val.intval = (CPU_state.pc.val.intval - buffer->offset) & 0xffff; - CPU_state.pc.flags = buffer->flags; + if (pseudopc_current_context == NULL) { + Bug_found("ClosingUnopenedPseudopcBlock", 0); + } else { + CPU_state.pc.val.intval = (CPU_state.pc.val.intval - pseudopc_current_context->offset) & 0xffff; // pc might have wrapped around + CPU_state.pc.flags = pseudopc_current_context->flags; + pseudopc_current_context = pseudopc_current_context->outer; // go back to outer block + } +} +// un-pseudopc a label value by given number of levels +// returns nonzero on error (if level too high) +int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned int levels) +{ + while (levels--) { + if ((target->flags & NUMBER_IS_DEFINED) == 0) + return 0; // ok (no sense in trying to unpseudo this, and it might be an unresolved forward ref anway) + + if (context == NULL) { + Throw_error("Too many monadic '&' operators for this label."); // TODO - add to docs + return 1; // error + } + // FIXME - in future, check DEFINED flag of context! + target->val.intval = (target->val.intval - context->offset) & 0xffff; // FIXME - is masking really needed? + context = context->outer; + } + return 0; // ok +} +// return pointer to current "pseudopc" struct (may be NULL!) +// this gets called when parsing label definitions +struct pseudopc *pseudopc_get_context(void) +{ + return pseudopc_current_context; } diff --git a/src/output.h b/src/output.h index d8fb087..845a6f0 100644 --- a/src/output.h +++ b/src/output.h @@ -27,11 +27,6 @@ struct vcpu { boolean a_is_long; boolean xy_are_long; }; -// buffer to hold outer state while parsing "pseudopc" block -struct pseudopc { - intval_t offset; - int flags; -}; // variables @@ -103,10 +98,18 @@ extern void vcpu_read_pc(struct number *target); extern int vcpu_get_statement_size(void); // adjust program counter (called at end of each statement) extern void vcpu_end_statement(void); + +struct pseudopc; // start offset assembly -extern void pseudopc_start(struct pseudopc *buffer, struct number *new_pc); +extern void pseudopc_start(struct number *new_pc); // end offset assembly -extern void pseudopc_end(struct pseudopc *buffer); +extern void pseudopc_end(void); +// un-pseudopc a label value by given number of levels +// returns nonzero on error (if level too high) +extern int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned int levels); +// return pointer to current "pseudopc" struct (may be NULL!) +// this gets called when parsing label definitions +extern struct pseudopc *pseudopc_get_context(void); #endif diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index df3c86f..0b68afc 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -586,7 +586,6 @@ static const char Error_old_offset_assembly[] = // TODO - maybe add a label argument to assign the block size afterwards (for assemble-to-end-address) (or add another pseudo opcode) static enum eos po_pseudopc(void) { - struct pseudopc buffer; struct number new_pc; // get new value @@ -610,12 +609,11 @@ static enum eos po_pseudopc(void) } } */ - // remember old state in buffer, set new state - pseudopc_start(&buffer, &new_pc); + pseudopc_start(&new_pc); // if there's a block, parse that and then restore old value! if (Parse_optional_block()) { // restore old state - pseudopc_end(&buffer); + pseudopc_end(); } else { // not using a block is no longer allowed Throw_error(Error_old_offset_assembly); diff --git a/src/symbol.c b/src/symbol.c index 75800a3..c4a4f1d 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -127,6 +127,7 @@ struct symbol *symbol_find(scope_t scope, int flags) symbol->usage = 0; // usage count symbol->pass = pass.number; symbol->has_been_reported = FALSE; + symbol->pseudopc = NULL; node->body = symbol; } else { symbol = node->body; @@ -194,6 +195,7 @@ void symbol_set_object(struct symbol *symbol, struct object *new_value, boolean // parse label definition (can be either global or local). // name must be held in GlobalDynaBuf. +// TODO - this is parsing, so move elsewhere void symbol_set_label(scope_t scope, int stat_flags, int force_bit, boolean change_allowed) { struct number pc; @@ -211,6 +213,7 @@ void symbol_set_label(scope_t scope, int stat_flags, int force_bit, boolean chan result.u.number.val.intval = pc.val.intval; result.u.number.addr_refs = pc.addr_refs; symbol_set_object(symbol, &result, change_allowed); + symbol->pseudopc = pseudopc_get_context(); // global labels must open new scope for cheap locals if (scope == SCOPE_GLOBAL) section_new_cheap_scope(section_now); @@ -219,6 +222,7 @@ void symbol_set_label(scope_t scope, int stat_flags, int force_bit, boolean chan // parse symbol definition (can be either global or local, may turn out to be a label). // name must be held in GlobalDynaBuf. +// TODO - this is parsing, so move elsewhere void symbol_parse_definition(scope_t scope, int stat_flags) { struct object result; diff --git a/src/symbol.h b/src/symbol.h index b91050a..db62570 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -16,6 +16,7 @@ struct symbol { int usage; // usage count int pass; // pass of creation (for anon counters) boolean has_been_reported; // indicates "has been reported as undefined" + struct pseudopc *pseudopc; // add file ref + line num of last definition }; diff --git a/src/version.h b/src/version.h index 771d4e0..0b14963 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ #define RELEASE "0.96.5" // update before release FIXME #define CODENAME "Fenchurch" // update before release -#define CHANGE_DATE "27 May" // update before release FIXME +#define CHANGE_DATE "28 May" // update before release FIXME #define CHANGE_YEAR "2020" // update before release //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME