// insert byte until PC fits condition static enum eos_t PO_align(void) { intval_t and, equal, fill, test = CPU_pc.intval; // make sure PC is defined. if ((CPU_pc.flags & MVALUE_DEFINED) == 0) { Throw_error(exception_pc_undefined); CPU_pc.flags |= MVALUE_DEFINED; // do not complain again return SKIP_REMAINDER; } and = ALU_defined_int(); if (!Input_accept_comma()) Throw_error(exception_syntax); equal = ALU_defined_int(); if (Input_accept_comma()) fill = ALU_any_int(); else fill = CPU_now->default_align_value; while ((test++ & and) != equal) Output_8b(fill); return ENSURE_EOS; }
// Looping assembly ("!for"). Has to be re-entrant. static enum eos_t PO_for(void) {// Now GotByte = illegal char input_t loop_input, *outer_input; result_t loop_counter; intval_t maximum; char* loop_body;// pointer to loop's body block label_t* label; zone_t zone; int force_bit, loop_start;// line number of "!for" pseudo opcode if(Input_read_zone_and_keyword(&zone) == 0) // skips spaces before return(SKIP_REMAINDER); // Now GotByte = illegal char force_bit = Input_get_force_bit(); // skips spaces after label = Label_find(zone, force_bit); if(Input_accept_comma() == FALSE) { Throw_error(exception_syntax); return(SKIP_REMAINDER); } maximum = ALU_defined_int(); if(maximum < 0) Throw_serious_error("Loop count is negative."); if(GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); // remember line number of loop pseudo opcode loop_start = Input_now->line_number; // read loop body into DynaBuf and get copy loop_body = Input_skip_or_store_block(TRUE); // changes line number! // switching input makes us lose GotByte. But we know it's '}' anyway! // set up new input loop_input = *Input_now;// copy current input structure into new loop_input.source_is_ram = TRUE; // set new byte source // remember old input outer_input = Input_now; // activate new input // (not yet useable; pointer and line number are still missing) Input_now = &loop_input; // init counter loop_counter.flags = MVALUE_DEFINED | MVALUE_EXISTS; loop_counter.val.intval = 0; // if count == 0, skip loop if(maximum) { do { loop_counter.val.intval++;// increment counter Label_set_value(label, &loop_counter, TRUE); parse_ram_block(loop_start, loop_body); } while(loop_counter.val.intval < maximum); } else Label_set_value(label, &loop_counter, TRUE); // Free memory free(loop_body); // restore previous input: Input_now = outer_input; // GotByte of OuterInput would be '}' (if it would still exist) GetByte(); // fetch next byte return(ENSURE_EOS); }
// Check for comma. If there, append to GlobalDynaBuf. static int pipe_comma(void) { int result; result = Input_accept_comma(); if (result) DYNABUF_APPEND(GlobalDynaBuf, ','); return result; }
// Reserve space by sending bytes of given value ("!fi" / "!fill" pseudo opcode) static enum eos_t PO_fill(void) { intval_t fill = FILLVALUE_FILL, size = ALU_defined_int(); if(Input_accept_comma()) fill = ALU_any_int(); while(size--) Output_8b(fill); return(ENSURE_EOS); }
// show user-defined message static enum eos_t throw_string(const char prefix[], void (*fn)(const char*)) { result_t result; DYNABUF_CLEAR(user_message); DynaBuf_add_string(user_message, prefix); do { if(GotByte == '"') { // parse string GetQuotedByte(); // read initial character // send characters until closing quote is reached while(GotByte && (GotByte != '"')) { DYNABUF_APPEND(user_message, GotByte); GetQuotedByte(); } if(GotByte == CHAR_EOS) return(AT_EOS_ANYWAY); // after closing quote, proceed with next char GetByte(); } else { // parse value ALU_any_result(&result); if(result.flags & MVALUE_IS_FP) { // floating point if(result.flags & MVALUE_DEFINED) DynaBuf_add_double( user_message, result.val.fpval); else DynaBuf_add_string( user_message, "<UNDEFINED FLOAT>"); } else { // integer if(result.flags & MVALUE_DEFINED) DynaBuf_add_signed_long( user_message, result.val.intval); else DynaBuf_add_string( user_message, "<UNDEFINED INT>"); } } } while(Input_accept_comma()); DynaBuf_append(user_message, '\0'); fn(user_message->buffer); return(ENSURE_EOS); }
// Include binary file static enum eos_t PO_binary(void) { FILE* fd; int byte; intval_t size = -1, // means "not given" => "until EOF" skip = 0, interleave = 1; int skipByte; // if file name is missing, don't bother continuing if(Input_read_filename(TRUE)) return(SKIP_REMAINDER); // try to open file fd = fopen(GLOBALDYNABUF_CURRENT, FILE_READBINARY); if(fd == NULL) { Throw_error(exception_cannot_open_input_file); return(SKIP_REMAINDER); } // read optional arguments if(Input_accept_comma()) { if(ALU_optional_defined_int(&size) && (size <0)) Throw_serious_error("Negative size argument."); if(Input_accept_comma()) { ALU_optional_defined_int(&skip);// read skip if(Input_accept_comma()) { ALU_optional_defined_int(&interleave);// read interleave if (interleave <= 1) { Throw_serious_error("Negative size argument."); } } } } skipByte = interleave; // check whether including is a waste of time if((size >= 0) && (pass_undefined_count || pass_real_errors)) Output_fake(size); // really including is useless anyway else { // really insert file fseek(fd, skip, SEEK_SET); // set read pointer // if "size" non-negative, read "size" bytes. // otherwise, read until EOF. while(size != 0) { byte = getc(fd); if(byte == EOF) break; skipByte++; if (skipByte >= interleave) { Output_byte(byte); size--; skipByte = 0; } } // if more should have been read, warn and add padding if(size > 0) { Throw_warning("Padding with zeroes."); do Output_byte(0); while(--size); } } fclose(fd); // if verbose, produce some output if((pass_count == 0) && (Process_verbosity > 1)) printf("Loaded %d ($%x) bytes from file offset %ld ($%lx).\n", CPU_2add, CPU_2add, skip, skip); return(ENSURE_EOS); }
// Helper function for !8, !16, !24 and !32 pseudo opcodes static enum eos_t output_objects(void (*fn)(intval_t)) { do fn(ALU_any_int()); while(Input_accept_comma()); return(ENSURE_EOS); }
// Parse macro call ("+MACROTITLE"). Has to be re-entrant. void Macro_parse_call(void) // Now GotByte = dot or first char of macro name { char local_gotbyte; struct symbol *symbol; struct section new_section, *outer_section; struct input new_input, *outer_input; struct macro *actual_macro; struct rwnode *macro_node, *symbol_node; zone_t macro_zone, symbol_zone; int arg_count = 0; // Enter deeper nesting level // Quit program if recursion too deep. if (--macro_recursions_left < 0) Throw_serious_error("Too deeply nested. Recursive macro calls?"); macro_zone = get_zone_and_title(); // now GotByte = first non-space after title // internal_name = MacroTitle ARG_SEPARATOR (grows to signature) // Accept n>=0 comma-separated arguments before CHAR_EOS. // Valid argument formats are: // EXPRESSION (everything that does NOT start with '~' // ~.LOCAL_LABEL_BY_REFERENCE // ~GLOBAL_LABEL_BY_REFERENCE // now GotByte = non-space if (GotByte != CHAR_EOS) { // any at all? do { // if arg table cannot take another element, enlarge if (argtable_size <= arg_count) enlarge_arg_table(); // Decide whether call-by-reference or call-by-value // In both cases, GlobalDynaBuf may be used. if (GotByte == REFERENCE_CHAR) { // read call-by-reference arg DynaBuf_append(internal_name, ARGTYPE_NUM_REF); GetByte(); // skip '~' character Input_read_zone_and_keyword(&symbol_zone); // GotByte = illegal char arg_table[arg_count].symbol = symbol_find(symbol_zone, 0); } else { // read call-by-value arg DynaBuf_append(internal_name, ARGTYPE_NUM_VAL); ALU_any_result(&(arg_table[arg_count].result)); } ++arg_count; } while (Input_accept_comma()); } // now arg_table contains the arguments // now GlobalDynaBuf = unused // check for "unknown macro" // Search for macro. Do not create if not found. search_for_macro(¯o_node, macro_zone, FALSE); if (macro_node == NULL) { Throw_error("Macro not defined (or wrong signature)."); Input_skip_remainder(); } else { // make macro_node point to the macro struct actual_macro = macro_node->body; local_gotbyte = GotByte; // CAUTION - ugly kluge // set up new input new_input.original_filename = actual_macro->def_filename; new_input.line_number = actual_macro->def_line_number; new_input.source_is_ram = TRUE; new_input.state = INPUTSTATE_NORMAL; // FIXME - fix others! new_input.src.ram_ptr = actual_macro->parameter_list; // remember old input outer_input = Input_now; // activate new input Input_now = &new_input; // remember old section outer_section = Section_now; // start new section (with new zone) // FALSE = title mustn't be freed Section_new_zone(&new_section, "Macro", actual_macro->original_name, FALSE); GetByte(); // fetch first byte of parameter list // assign arguments if (GotByte != CHAR_EOS) { // any at all? arg_count = 0; do { // Decide whether call-by-reference // or call-by-value // In both cases, GlobalDynaBuf may be used. if (GotByte == REFERENCE_CHAR) { // assign call-by-reference arg GetByte(); // skip '~' character Input_read_zone_and_keyword(&symbol_zone); if ((Tree_hard_scan(&symbol_node, symbols_forest, symbol_zone, TRUE) == FALSE) && (pass_count == 0)) Throw_error("Macro parameter twice."); symbol_node->body = arg_table[arg_count].symbol; } else { // assign call-by-value arg Input_read_zone_and_keyword(&symbol_zone); symbol = symbol_find(symbol_zone, 0); // FIXME - add a possibility to symbol_find to make it possible to find out // whether symbol was just created. Then check for the same error message here // as above ("Macro parameter twice."). symbol->result = arg_table[arg_count].result; } ++arg_count; } while (Input_accept_comma()); } // and now, finally, parse the actual macro body Input_now->state = INPUTSTATE_NORMAL; // FIXME - fix others! // maybe call parse_ram_block(actual_macro->def_line_number, actual_macro->body) Input_now->src.ram_ptr = actual_macro->body; Parse_until_eob_or_eof(); if (GotByte != CHAR_EOB) Bug_found("IllegalBlockTerminator", GotByte); // end section (free title memory, if needed) Section_finalize(&new_section); // restore previous section Section_now = outer_section; // restore previous input: Input_now = outer_input; // restore old Gotbyte context GotByte = local_gotbyte; // CAUTION - ugly kluge Input_ensure_EOS(); } ++macro_recursions_left; // leave this nesting level }