2012-02-27 21:14:46 +00:00
|
|
|
// ACME - a crossassembler for producing 6502/65c02/65816 code.
|
2014-03-11 12:07:11 +00:00
|
|
|
// Copyright (C) 1998-2014 Marco Baye
|
2012-02-27 21:14:46 +00:00
|
|
|
// Have a look at "acme.c" for further info
|
|
|
|
//
|
|
|
|
// Flow control stuff (loops, conditional assembly etc.)
|
|
|
|
//
|
|
|
|
// Macros, conditional assembly, loops and sourcefile-includes are all based on
|
|
|
|
// parsing blocks of code. When defining macros or using loops or conditional
|
|
|
|
// assembly, the block starts with "{" and ends with "}". In the case of
|
|
|
|
// "!source", the given file is treated like a block - the outermost assembler
|
|
|
|
// function uses the same technique to parse the top level file.
|
|
|
|
//
|
|
|
|
// 24 Nov 2007 Added "!ifndef"
|
2014-12-22 00:47:52 +00:00
|
|
|
#include "flow.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include "acme.h"
|
|
|
|
#include "alu.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "dynabuf.h"
|
2014-12-03 22:18:06 +00:00
|
|
|
#include "global.h" // FIXME - remove when no longer needed
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "input.h"
|
|
|
|
#include "mnemo.h"
|
2014-06-07 00:12:10 +00:00
|
|
|
#include "symbol.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "tree.h"
|
|
|
|
|
|
|
|
|
|
|
|
// helper functions for "!for" and "!do"
|
|
|
|
|
2014-12-22 00:47:52 +00:00
|
|
|
// parse a loop body (TODO - also use for macro body?)
|
|
|
|
static void parse_ram_block(struct block *block)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-12-22 00:47:52 +00:00
|
|
|
Input_now->line_number = block->start; // set line number to loop start
|
|
|
|
Input_now->src.ram_ptr = block->body; // set RAM read pointer to loop
|
|
|
|
// parse block
|
2012-02-27 21:14:46 +00:00
|
|
|
Parse_until_eob_or_eof();
|
|
|
|
if (GotByte != CHAR_EOB)
|
|
|
|
Bug_found("IllegalBlockTerminator", GotByte);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-22 00:47:52 +00:00
|
|
|
// back end function for "!for" pseudo opcode
|
|
|
|
void flow_forloop(struct for_loop *loop)
|
|
|
|
{
|
|
|
|
struct input loop_input,
|
|
|
|
*outer_input;
|
|
|
|
struct result loop_counter;
|
|
|
|
|
|
|
|
// 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 = loop->counter_first;
|
|
|
|
symbol_set_value(loop->symbol, &loop_counter, TRUE);
|
|
|
|
if (loop->old_algo) {
|
|
|
|
// old algo for old syntax:
|
|
|
|
// if count == 0, skip loop
|
|
|
|
if (loop->counter_last) {
|
|
|
|
do {
|
|
|
|
loop_counter.val.intval += loop->counter_increment;
|
|
|
|
symbol_set_value(loop->symbol, &loop_counter, TRUE);
|
|
|
|
parse_ram_block(&loop->block);
|
|
|
|
} while (loop_counter.val.intval < loop->counter_last);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// new algo for new syntax:
|
|
|
|
do {
|
|
|
|
parse_ram_block(&loop->block);
|
|
|
|
loop_counter.val.intval += loop->counter_increment;
|
|
|
|
symbol_set_value(loop->symbol, &loop_counter, TRUE);
|
|
|
|
} while (loop_counter.val.intval != (loop->counter_last + loop->counter_increment));
|
|
|
|
}
|
|
|
|
// restore previous input:
|
|
|
|
Input_now = outer_input;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// try to read a condition into DynaBuf and store copy pointer in
|
2013-06-26 23:01:00 +00:00
|
|
|
// given loop_condition structure.
|
2012-02-27 21:14:46 +00:00
|
|
|
// if no condition given, NULL is written to structure.
|
|
|
|
// call with GotByte = first interesting character
|
2014-12-22 00:47:52 +00:00
|
|
|
void flow_store_doloop_condition(struct loop_condition *condition, char terminator)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
// write line number
|
|
|
|
condition->line = Input_now->line_number;
|
2014-11-25 16:15:22 +00:00
|
|
|
// set defaults
|
|
|
|
condition->is_until = FALSE;
|
|
|
|
condition->body = NULL;
|
|
|
|
// check for empty condition
|
|
|
|
if (GotByte == terminator)
|
2012-02-27 21:14:46 +00:00
|
|
|
return;
|
2014-11-25 16:15:22 +00:00
|
|
|
|
|
|
|
// seems as if there really *is* a condition, so check for until/while
|
2012-02-27 21:14:46 +00:00
|
|
|
if (Input_read_and_lower_keyword()) {
|
2014-11-25 16:15:22 +00:00
|
|
|
if (strcmp(GlobalDynaBuf->buffer, "while") == 0) {
|
|
|
|
//condition.is_until = FALSE;
|
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "until") == 0) {
|
|
|
|
condition->is_until = TRUE;
|
|
|
|
} else {
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_error(exception_syntax);
|
|
|
|
return;
|
|
|
|
}
|
2014-12-04 15:31:54 +00:00
|
|
|
// write given condition into buffer
|
2012-02-27 21:14:46 +00:00
|
|
|
SKIPSPACE();
|
|
|
|
DYNABUF_CLEAR(GlobalDynaBuf);
|
|
|
|
Input_until_terminator(terminator);
|
|
|
|
DynaBuf_append(GlobalDynaBuf, CHAR_EOS); // ensure terminator
|
|
|
|
condition->body = DynaBuf_get_copy(GlobalDynaBuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// check a condition expression
|
|
|
|
static int check_condition(struct loop_condition *condition)
|
|
|
|
{
|
|
|
|
intval_t expression;
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
// first, check whether there actually *is* a condition
|
2012-02-27 21:14:46 +00:00
|
|
|
if (condition->body == NULL)
|
2013-06-26 23:01:00 +00:00
|
|
|
return TRUE; // non-existing conditions are always true
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
// set up input for expression evaluation
|
|
|
|
Input_now->line_number = condition->line;
|
|
|
|
Input_now->src.ram_ptr = condition->body;
|
|
|
|
GetByte(); // proceed with next char
|
|
|
|
expression = ALU_defined_int();
|
|
|
|
if (GotByte)
|
|
|
|
Throw_serious_error(exception_syntax);
|
2014-11-25 16:15:22 +00:00
|
|
|
return condition->is_until ? !expression : !!expression;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-16 08:21:44 +00:00
|
|
|
// back end function for "!do" pseudo opcode
|
2014-12-22 00:47:52 +00:00
|
|
|
void flow_doloop(struct do_loop *loop)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-12-16 08:21:44 +00:00
|
|
|
struct input loop_input;
|
|
|
|
struct input *outer_input;
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// 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 useable yet, as pointer and
|
|
|
|
// line number are not yet set up)
|
|
|
|
Input_now = &loop_input;
|
2014-12-22 00:47:52 +00:00
|
|
|
for (;;) {
|
2014-12-04 15:31:54 +00:00
|
|
|
// check head condition
|
2014-12-22 00:47:52 +00:00
|
|
|
if (!check_condition(&loop->head_cond))
|
|
|
|
break;
|
|
|
|
parse_ram_block(&loop->block);
|
|
|
|
// check tail condition
|
|
|
|
if (!check_condition(&loop->tail_cond))
|
|
|
|
break;
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
// restore previous input:
|
|
|
|
Input_now = outer_input;
|
|
|
|
GotByte = CHAR_EOS; // CAUTION! Very ugly kluge.
|
|
|
|
// But by switching input, we lost the outer input's GotByte. We know
|
|
|
|
// it was CHAR_EOS. We could just call GetByte() to get real input, but
|
|
|
|
// then the main loop could choke on unexpected bytes. So we pretend
|
|
|
|
// that we got the outer input's GotByte value magically back.
|
2014-12-16 08:21:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// helper functions for "!if", "!ifdef" and "!ifndef"
|
|
|
|
|
|
|
|
// parse or skip a block. Returns whether block's '}' terminator was missing.
|
|
|
|
// afterwards: GotByte = '}'
|
|
|
|
static int skip_or_parse_block(int parse)
|
|
|
|
{
|
|
|
|
if (!parse) {
|
|
|
|
Input_skip_or_store_block(FALSE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// if block was correctly terminated, return FALSE
|
|
|
|
Parse_until_eob_or_eof();
|
|
|
|
// if block isn't correctly terminated, complain and exit
|
|
|
|
if (GotByte != CHAR_EOB)
|
|
|
|
Throw_serious_error(exception_no_right_brace);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// parse {block} [else {block}]
|
2014-12-04 15:31:54 +00:00
|
|
|
void flow_parse_block_else_block(int parse_first)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
// 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;
|
2014-11-25 16:15:22 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// now GotByte = '}'. Check for "else" part.
|
|
|
|
// If end of statement, return immediately.
|
|
|
|
NEXTANDSKIPSPACE();
|
|
|
|
if (GotByte == CHAR_EOS)
|
|
|
|
return;
|
2014-11-25 16:15:22 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// parse a whole source code file
|
2014-12-22 00:47:52 +00:00
|
|
|
void flow_parse_and_close_file(FILE *fd, const char *filename)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
}
|