'&' 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
This commit is contained in:
marcobaye 2020-05-28 18:40:40 +00:00
parent 636080ce25
commit dddf3f3d10
7 changed files with 140 additions and 42 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
};

View File

@ -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