Esempio n. 1
0
// 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;
}
Esempio n. 2
0
File: encoding.c Progetto: lhz/acme
// try to load encoding table from given file
void encoding_load(char target[256], const char *filename)
{
	FILE	*fd	= fopen(filename, FILE_READBINARY);

	if (fd) {
		if (fread(target, sizeof(char), 256, fd) != 256)
			Throw_error("Conversion table incomplete.");
		fclose(fd);
	} else {
		Throw_error(exception_cannot_open_input_file);
	}
}
Esempio n. 3
0
// Try to read a file name. If "allow_library" is TRUE, library access by using
// <...> quoting is possible as well. The file name given in the assembler
// source code is converted from UNIX style to platform style.
// Returns whether error occurred (TRUE on error). Filename in GlobalDynaBuf.
// Errors are handled and reported, but caller should call
// Input_skip_remainder() then.
bool Input_read_filename(bool allow_library) {
	char	*lib_prefix,
		end_quote;

	DYNABUF_CLEAR(GlobalDynaBuf);
	SKIPSPACE();
	// check for library access
	if(GotByte == '<') {
		// if library access forbidden, complain
		if(allow_library == FALSE) {
			Throw_error("Writing to library not supported.");
			return(TRUE);
		}
		// read platform's lib prefix
		lib_prefix = PLATFORM_LIBPREFIX;
#ifndef NO_NEED_FOR_ENV_VAR
		// if lib prefix not set, complain
		if(lib_prefix == NULL) {
			Throw_error("\"ACME\" environment variable not found.");
			return(TRUE);
		}
#endif
		// copy lib path and set quoting char
		DynaBuf_add_string(GlobalDynaBuf, lib_prefix);
		end_quote = '>';
	} else {
		if(GotByte == '"')
			end_quote = '"';
		else {
			Throw_error("File name quotes not found (\"\" or <>).");
			return(TRUE);
		}
	}
	// read first character, complain if closing quote
	if(GetQuotedByte() == end_quote) {
		Throw_error("No file name given.");
		return(TRUE);
	}
	// read characters until closing quote (or EOS) is reached
	// append platform-converted characters to current string
	while((GotByte != CHAR_EOS) && (GotByte != end_quote)) {
		DYNABUF_APPEND(GlobalDynaBuf, PLATFORM_CONVERTPATHCHAR(GotByte));
		GetQuotedByte();
	}
	// on error, return
	if(GotByte == CHAR_EOS)
		return(TRUE);
	GetByte();	// fetch next to forget closing quote
	// terminate string
	DynaBuf_append(GlobalDynaBuf, '\0');	// add terminator
	return(FALSE);	// no error
}
Esempio n. 4
0
// (Re)set label
static enum eos_t PO_set(void)	// Now GotByte = illegal char
{
	struct result_t	result;
	int		force_bit;
	struct label_t	*label;
	zone_t		zone;

	if (Input_read_zone_and_keyword(&zone) == 0)	// skips spaces before
		// Now GotByte = illegal char
		return SKIP_REMAINDER;

	force_bit = Input_get_force_bit();	// skips spaces after
	label = Label_find(zone, force_bit);
	if (GotByte != '=') {
		Throw_error(exception_syntax);
		return SKIP_REMAINDER;
	}

	// label = parsed value
	GetByte();	// proceed with next char
	ALU_any_result(&result);
	// clear label's force bits and set new ones
	label->result.flags &= ~(MVALUE_FORCEBITS | MVALUE_ISBYTE);
	if (force_bit) {
		label->result.flags |= force_bit;
		result.flags &= ~(MVALUE_FORCEBITS | MVALUE_ISBYTE);
	}
	Label_set_value(label, &result, TRUE);
	return ENSURE_EOS;
}
Esempio n. 5
0
// if cpu type and value match, set register length variable to value.
// if cpu type and value don't match, complain instead.
static void check_and_set_reg_length(int *var, int make_long)
{
	if (((CPU_now->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) && make_long)
		Throw_error("Chosen CPU does not support long registers.");
	else
		*var = make_long;
}
Esempio n. 6
0
// Search for label. Create if nonexistant. If created, give it flags "Flags".
// The label name must be held in GlobalDynaBuf.
struct label_t *Label_find(zone_t zone, int flags)
{
	struct node_ra_t	*node;
	struct label_t		*label;
	int			node_created,
				force_bits	= flags & MVALUE_FORCEBITS;

	node_created = Tree_hard_scan(&node, Label_forest, zone, TRUE);
	// if node has just been created, create label as well
	if (node_created) {
		// Create new label structure
		label = safe_malloc(sizeof(*label));
		// Finish empty label item
		label->result.flags = flags;
		if (flags & MVALUE_IS_FP)
			label->result.val.fpval = 0;
		else
			label->result.val.intval = 0;
		label->usage = 0;	// usage count
		label->pass = pass_count;
		node->body = label;
	} else {
		label = node->body;
	}
	// make sure the force bits don't clash
	if ((node_created == FALSE) && force_bits)
		if ((label->result.flags & MVALUE_FORCEBITS) != force_bits)
			Throw_error("Too late for postfix.");
	return label;
}
Esempio n. 7
0
// Include source file ("!source" or "!src"). Has to be re-entrant.
static enum eos_t PO_source(void) {// Now GotByte = illegal char
	FILE*	fd;
	char	local_gotbyte;
	input_t	new_input,
		*outer_input;

	// Enter new nesting level.
	// Quit program if recursion too deep.
	if(--source_recursions_left < 0)
		Throw_serious_error("Too deeply nested. Recursive \"!source\"?");
	// Read file name. Quit function on error.
	if(Input_read_filename(TRUE))
		return(SKIP_REMAINDER);
	// If file could be opened, parse it. Otherwise, complain.
	if((fd = fopen(GLOBALDYNABUF_CURRENT, FILE_READBINARY))) {
		char	*filename = (char *) safe_malloc(GlobalDynaBuf->size);	// MPi: Arrays must be a constant expression

		strcpy(filename, GLOBALDYNABUF_CURRENT);
		outer_input = Input_now;// remember old input
		local_gotbyte = GotByte;// CAUTION - ugly kluge
		Input_now = &new_input;// activate new input
		Parse_and_close_file(fd, filename);
		Input_now = outer_input;// restore previous input
		GotByte = local_gotbyte;// CAUTION - ugly kluge
		free(filename);
	} else
		Throw_error(exception_cannot_open_input_file);
	// Leave nesting level
	source_recursions_left++;
	return(ENSURE_EOS);
}
Esempio n. 8
0
// Parse {block} [else {block}]
static void parse_block_else_block(bool parse_first) {
	// Parse first block.
	// If it's not correctly terminated, return immediately (because
	// in that case, there's no use in checking for an "else" part).
	if(skip_or_parse_block(parse_first))
		return;
	// now GotByte = '}'. Check for "else" part.
	// If end of statement, return immediately.
	NEXTANDSKIPSPACE();
	if(GotByte == CHAR_EOS)
		return;
	// read keyword and check whether really "else"
	if(Input_read_and_lower_keyword()) {
		if(strcmp(GlobalDynaBuf->buffer, "else"))
			Throw_error(exception_syntax);
		else {
			SKIPSPACE();
			if(GotByte != CHAR_SOB)
				Throw_serious_error(exception_no_left_brace);
			skip_or_parse_block(!parse_first);
			// now GotByte = '}'
			GetByte();
		}
	}
	Input_ensure_EOS();
}
Esempio n. 9
0
// Ensure that the remainder of the current statement is empty, for example
// after mnemonics using implied addressing.
void Input_ensure_EOS(void) {// Now GotByte = first char to test
	SKIPSPACE();
	if(GotByte) {
		Throw_error("Garbage data at end of statement.");
		Input_skip_remainder();
	}
}
Esempio n. 10
0
// Assign value to label. The function acts upon the label's flag bits and
// produces an error if needed.
void Label_set_value(struct label_t *label, struct result_t *newvalue, int change_allowed)
{
	int	oldflags	= label->result.flags;

	// value stuff
	if ((oldflags & MVALUE_DEFINED) && (change_allowed == FALSE)) {
		// Label is already defined, so compare new and old values
		// if different type OR same type but different value, complain
		if (((oldflags ^ newvalue->flags) & MVALUE_IS_FP)
		|| ((oldflags & MVALUE_IS_FP)
		? (label->result.val.fpval != newvalue->val.fpval)
		: (label->result.val.intval != newvalue->val.intval)))
			Throw_error("Label already defined.");
	} else {
		// Label is not defined yet OR redefinitions are allowed
		label->result = *newvalue;
	}
	// flags stuff
	// Ensure that "unsure" labels without "isByte" state don't get that
	if ((oldflags & (MVALUE_UNSURE | MVALUE_ISBYTE)) == MVALUE_UNSURE)
		newvalue->flags &= ~MVALUE_ISBYTE;
	if (change_allowed) {
		oldflags = (oldflags & MVALUE_UNSURE) | newvalue->flags;
	} else {
		if ((oldflags & MVALUE_FORCEBITS) == 0)
			if ((oldflags & (MVALUE_UNSURE | MVALUE_DEFINED)) == 0)
				oldflags |= newvalue->flags & MVALUE_FORCEBITS;
		oldflags |= newvalue->flags & ~MVALUE_FORCEBITS;
	}
	label->result.flags = oldflags;
}
Esempio n. 11
0
// Try to read a condition into DynaBuf and store copy pointer in
// given loopcond_t structure.
// If no condition given, NULL is written to structure.
// Call with GotByte = first interesting character
static void store_condition(loopcond_t* condition, char terminator) {
	void*	node_body;

	// write line number
	condition->line = Input_now->line_number;
	// Check for empty condition
	if(GotByte == terminator) {
		// Write NULL condition, then return
		condition->body = NULL;
		return;
	}
	// Seems as if there really *is* a condition.
	// Read UNTIL/WHILE keyword
	if(Input_read_and_lower_keyword()) {
		// Search for new tree item
		if(!Tree_easy_scan(condkey_tree, &node_body, GlobalDynaBuf)) {
			Throw_error(exception_syntax);
			condition->body = NULL;
			return;
		}
		condition->type = (enum cond_key_t) node_body;
		// Write given condition into buffer
		SKIPSPACE();
		DYNABUF_CLEAR(GlobalDynaBuf);
		Input_until_terminator(terminator);
		DynaBuf_append(GlobalDynaBuf, CHAR_EOS);// ensure terminator
		condition->body = DynaBuf_get_copy(GlobalDynaBuf);
	}
	return;
}
Esempio n. 12
0
// 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);
}
Esempio n. 13
0
File: cpu.c Progetto: Jedzia/acm3
// Select CPU ("!cpu" pseudo opcode)
static enum eos_t PO_cpu(void) {
	struct cpu_t*	cpu_buffer	= CPU_now;	// remember current cpu

	if(Input_read_and_lower_keyword())
		if(!CPU_find_cpu_struct(&CPU_now))
			Throw_error("Unknown processor.");
	// If there's a block, parse that and then restore old value!
	if(Parse_optional_block())
		CPU_now = cpu_buffer;
	return(ENSURE_EOS);
}
Esempio n. 14
0
// Append to GlobalDynaBuf while characters are legal for keywords.
// Throws "missing string" error if none.
// Returns number of characters added.
int Input_append_keyword_to_global_dynabuf(void) {
	int	length	= 0;

	// add characters to buffer until an illegal one comes along
	while(BYTEFLAGS(GotByte) & CONTS_KEYWORD) {
		DYNABUF_APPEND(GlobalDynaBuf, GotByte);
		length++;
		GetByte();
	}
	if(length == 0)
		Throw_error(exception_missing_string);
	return(length);
}
Esempio n. 15
0
// Parse a whole source code file
void Parse_and_close_file(FILE* fd, const char* filename) {
	// be verbose
	if(Process_verbosity > 2)
		printf("Parsing source file '%s'\n", filename);
	// set up new input
	Input_new_file(filename, fd);
	// Parse block and check end reason
	Parse_until_eob_or_eof();
	if(GotByte != CHAR_EOF)
		Throw_error("Found '}' instead of end-of-file.");
	// close sublevel src
	fclose(Input_now->src.fd);
}
Esempio n. 16
0
File: encoding.c Progetto: lhz/acme
// lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure)
const struct encoder *encoding_find(void)
{
	void	*node_body;

	// make sure tree is initialised
	if (encoder_tree == NULL)
		Tree_add_table(&encoder_tree, encoder_list);
	// perform lookup
	if (!Tree_easy_scan(encoder_tree, &node_body, GlobalDynaBuf)) {
		Throw_error("Unknown encoding.");
		return NULL;
	}

	return node_body;
}
Esempio n. 17
0
// read optional info about parameter length
int Input_get_force_bit(void) {
	char	byte;
	int	force_bit	= 0;

	if(GotByte == '+') {
		byte = GetByte();
		if(byte == '1')
			force_bit = MVALUE_FORCE08;
		else if(byte == '2')
			force_bit = MVALUE_FORCE16;
		else if(byte == '3')
			force_bit = MVALUE_FORCE24;
		if(force_bit)
			GetByte();
		else
			Throw_error("Illegal postfix.");
	}
	SKIPSPACE();
	return(force_bit);
}
Esempio n. 18
0
// This function delivers the next byte from the currently active byte source
// in un-shortened high-level format.
// This function complains if CHAR_EOS (end of statement) is read.
char GetQuotedByte(void) {
	int	from_file;	// must be an int to catch EOF

	// If byte source is RAM, then no conversion is necessary,
	// because in RAM the source already has high-level format
	if(Input_now->source_is_ram)
		GotByte = *(Input_now->src.ram_ptr++);
	// Otherwise, the source is a file.
	else {
		// fetch a fresh byte from the current source file
		from_file = hacked_getc(Input_now);
		switch(from_file) {

			case EOF:
			// remember to send an end-of-file
			Input_now->state = INPUTSTATE_EOF;
			GotByte = CHAR_EOS;	// end of statement
			break;

			case CHAR_LF:// LF character
			// remember to send a start-of-line
			Input_now->state = INPUTSTATE_LF;
			GotByte = CHAR_EOS;	// end of statement
			break;

			case CHAR_CR:// CR character
			// remember to check for CRLF + send a start-of-line
			Input_now->state = INPUTSTATE_CR;
			GotByte = CHAR_EOS;	// end of statement
			break;

			default:
			GotByte = from_file;
		}

	}
	// now check for end of statement
	if(GotByte == CHAR_EOS)
		Throw_error("Quotes still open at end of line.");
	return(GotByte);
}
Esempio n. 19
0
// 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);
}
Esempio n. 20
0
File: cpu.c Progetto: Jedzia/acm3
// If cpu type and value match, set register length variable to value.
// If cpu type and value don't match, complain instead.
static void check_and_set_reg_length(bool *var, bool long_reg) {
	if(long_reg && ((CPU_now->long_regs) == NULL))
		Throw_error("Chosen CPU does not support long registers.");
	else
		*var = long_reg;
}
Esempio n. 21
0
// Deliver source code from current file (!) in shortened high-level format
static char get_processed_from_file(void) {
	int	from_file;

	do {
		switch(Input_now->state) {

			case INPUTSTATE_NORMAL:
			// fetch a fresh byte from the current source file
				//from_file = getc(Input_now->src.fd);
			from_file = hacked_getc(Input_now);
			// now process it
			/*FALLTHROUGH*/

			case INPUTSTATE_AGAIN:
			// Process the latest byte again. Of course, this only
			// makes sense if the loop has executed at least once,
			// otherwise the contents of from_file are undefined.
			// If the source is changed so there is a possibility
			// to enter INPUTSTATE_AGAIN mode without first having
			// defined "from_file", trouble may arise...
			Input_now->state = INPUTSTATE_NORMAL;
			// EOF must be checked first because it cannot be used
			// as an index into Byte_flags[]
			if(from_file == EOF) {
				// remember to send an end-of-file
				Input_now->state = INPUTSTATE_EOF;
				return(CHAR_EOS);// end of statement
			}
			// check whether character is special one
			// if not, everything's cool and froody, so return it
			if((BYTEFLAGS(from_file) & BYTEIS_SYNTAX) == 0)
				return((char) from_file);
			// check special characters ("0x00 TAB LF CR SPC :;}")
			switch(from_file) {

				case CHAR_TAB:// TAB character
				case ' ':
				// remember to skip all following blanks
				Input_now->state = INPUTSTATE_SKIPBLANKS;
				return(' ');

				case CHAR_LF:// LF character
				// remember to send a start-of-line
				Input_now->state = INPUTSTATE_LF;
				return(CHAR_EOS);// end of statement

				case CHAR_CR:// CR character
				// remember to check CRLF + send start-of-line
				Input_now->state = INPUTSTATE_CR;
				return(CHAR_EOS);// end of statement

				case CHAR_EOB:
				// remember to send an end-of-block
				Input_now->state = INPUTSTATE_EOB;
				return(CHAR_EOS);// end of statement

				case CHAR_STATEMENT_DELIMITER:
				// just deliver an EOS instead
				return(CHAR_EOS);// end of statement

				case CHAR_COMMENT_SEPARATOR:
				// remember to skip remainder of line
				Input_now->state = INPUTSTATE_COMMENT;
				return(CHAR_EOS);// end of statement

				default:
				// complain if byte is 0
				Throw_error("Source file contains illegal character.");
				return((char) from_file);
			}

			case INPUTSTATE_SKIPBLANKS:
			// read until non-blank, then deliver that
				do {
//				from_file = getc(Input_now->src.fd);
					from_file = hacked_getc(Input_now);
				} while((from_file == CHAR_TAB) || (from_file == ' '));
			// re-process last byte
			Input_now->state = INPUTSTATE_AGAIN;
			break;

			case INPUTSTATE_LF:
			// return start-of-line, then continue in normal mode
			Input_now->state = INPUTSTATE_NORMAL;
			return(CHAR_SOL);// new line

			case INPUTSTATE_CR:
			// return start-of-line, remember to check for LF
			Input_now->state = INPUTSTATE_SKIPLF;
			return(CHAR_SOL);// new line

			case INPUTSTATE_SKIPLF:
				from_file = hacked_getc(Input_now);
			// if LF, ignore it and fetch another byte
			// otherwise, process current byte
			if(from_file == CHAR_LF)
				Input_now->state = INPUTSTATE_NORMAL;
			else
				Input_now->state = INPUTSTATE_AGAIN;
			break;

			case INPUTSTATE_COMMENT:
			// read until end-of-line or end-of-file
			do
				from_file = hacked_getc(Input_now);
			while((from_file != EOF) && (from_file != CHAR_CR) && (from_file != CHAR_LF));
			// re-process last byte
			Input_now->state = INPUTSTATE_AGAIN;
			break;

			case INPUTSTATE_EOB:
			// deliver EOB
			Input_now->state = INPUTSTATE_NORMAL;
			return(CHAR_EOB);// end of block

			case INPUTSTATE_EOF:
			// deliver EOF
			Input_now->state = INPUTSTATE_NORMAL;
			return(CHAR_EOF);// end of file

			default:
			Bug_found("StrangeInputMode", Input_now->state);
		}
	} while(TRUE);
}
Esempio n. 22
0
File: macro.c Progetto: lhz/acme
// 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(&macro_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
}