mirror of
https://github.com/uffejakobsen/acme.git
synced 2025-01-12 05:31:19 +00:00
yet more refactoring. no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@52 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
parent
cc2ddb9b42
commit
2331709a35
@ -17,7 +17,7 @@
|
||||
|
||||
#define RELEASE "0.95.4" // update before release (FIXME)
|
||||
#define CODENAME "Fenchurch" // update before release
|
||||
#define CHANGE_DATE "5 Dec" // update before release
|
||||
#define CHANGE_DATE "16 Dec" // update before release
|
||||
#define CHANGE_YEAR "2014" // update before release
|
||||
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" // FIXME
|
||||
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME
|
||||
@ -242,7 +242,7 @@ static int perform_pass(void)
|
||||
|
||||
// call modules' "pass init" functions
|
||||
Output_passinit(); // disable output, PC undefined
|
||||
CPU_passinit(default_cpu); // set default cpu type
|
||||
cputype_passinit(default_cpu); // set default cpu type
|
||||
// if start address was given on command line, use it:
|
||||
if (start_address != ILLEGAL_START_ADDRESS)
|
||||
vcpu_set_pc(start_address, 0);
|
||||
@ -558,8 +558,6 @@ int main(int argc, const char *argv[])
|
||||
cliargs_get_rest(&toplevel_src_count, &toplevel_sources, "No top level sources given");
|
||||
// Init modules (most of them will just build keyword trees)
|
||||
ALU_init();
|
||||
CPU_init();
|
||||
Encoding_init();
|
||||
Flow_init();
|
||||
Macro_init();
|
||||
Mnemo_init();
|
||||
|
15
src/alu.c
15
src/alu.c
@ -129,7 +129,11 @@ static struct operator ops_modulo = {OPHANDLE_MODULO, 26}; // dyadic
|
||||
static struct operator ops_negate = {OPHANDLE_NEGATE, 28}; // monadic
|
||||
static struct operator ops_powerof = {OPHANDLE_POWEROF, 29}; // dyadic, right-associative
|
||||
static struct operator ops_not = {OPHANDLE_NOT, 30}; // monadic
|
||||
// function calls act as if they were monadic operators
|
||||
// function calls act as if they were monadic operators.
|
||||
// they need high priorities to make sure they are evaluated once the
|
||||
// parentheses' content is known:
|
||||
// "sin(3 + 4) DYADIC_OPERATOR 5" becomes "sin 7 DYADIC_OPERATOR 5",
|
||||
// so function calls' priority must be higher than all dyadic operators.
|
||||
static struct operator ops_addr = {OPHANDLE_ADDR, 32}; // function
|
||||
static struct operator ops_int = {OPHANDLE_INT, 32}; // function
|
||||
static struct operator ops_float = {OPHANDLE_FLOAT, 32}; // function
|
||||
@ -559,7 +563,7 @@ static void parse_function_call(void)
|
||||
DynaBuf_to_lower(function_dyna_buf, GlobalDynaBuf);
|
||||
// search for tree item
|
||||
if (Tree_easy_scan(function_tree, &node_body, function_dyna_buf))
|
||||
PUSH_OPERATOR((struct operator*) node_body);
|
||||
PUSH_OPERATOR((struct operator *) node_body);
|
||||
else
|
||||
Throw_error("Unknown function.");
|
||||
}
|
||||
@ -697,6 +701,13 @@ static void expect_operand_or_monadic_operator(void)
|
||||
} else {
|
||||
if (GotByte == '(') {
|
||||
parse_function_call();
|
||||
// i thought about making the parentheses optional, so you can write "a = sin b"
|
||||
// just like "a = not b". but then each new function name would have to be made
|
||||
// a reserved keyword, otherwise stuff like "a = sin * < b" would be ambiguous:
|
||||
// it could mean either "compare sine of PC to b" or "multiply 'sin' by low byte
|
||||
// of b".
|
||||
// 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(ZONE_GLOBAL);
|
||||
goto now_expect_dyadic;
|
||||
|
163
src/cpu.c
163
src/cpu.c
@ -55,7 +55,6 @@ static struct cpu_type cpu_type_65816 = {
|
||||
CPUFLAG_SUPPORTSLONGREGS, // allows A and XY to be 16bits wide
|
||||
234 // !align fills with "NOP"
|
||||
};
|
||||
#define s_rl (s_brl + 1) // Yes, I know I'm sick
|
||||
|
||||
|
||||
// variables
|
||||
@ -93,7 +92,9 @@ const struct cpu_type *cputype_find(void)
|
||||
|
||||
// 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)
|
||||
// FIXME - error message might be confusing if it is thrown not because of
|
||||
// initial change, but because of reverting back to old cpu type after "{}" block!
|
||||
void vcpu_check_and_set_reg_length(int *var, int make_long)
|
||||
{
|
||||
if (((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) && make_long)
|
||||
Throw_error("Chosen CPU does not support long registers.");
|
||||
@ -102,165 +103,9 @@ static void check_and_set_reg_length(int *var, int make_long)
|
||||
}
|
||||
|
||||
|
||||
// set register length, block-wise if needed.
|
||||
static enum eos set_register_length(int *var, int make_long)
|
||||
{
|
||||
int old_size = *var;
|
||||
|
||||
// set new register length (or complain - whichever is more fitting)
|
||||
check_and_set_reg_length(var, make_long);
|
||||
// if there's a block, parse that and then restore old value!
|
||||
if (Parse_optional_block())
|
||||
check_and_set_reg_length(var, old_size); // restore old length
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// set default values for pass
|
||||
void CPU_passinit(const struct cpu_type *cpu_type)
|
||||
void cputype_passinit(const struct cpu_type *cpu_type)
|
||||
{
|
||||
// handle cpu type (default is 6502)
|
||||
CPU_state.type = cpu_type ? cpu_type : &cpu_type_6502;
|
||||
}
|
||||
|
||||
|
||||
// FIXME - move to pseudoopcodes.c:
|
||||
|
||||
|
||||
// insert byte until PC fits condition
|
||||
static enum eos po_align(void)
|
||||
{
|
||||
// FIXME - read cpu state via function call!
|
||||
intval_t and,
|
||||
equal,
|
||||
fill,
|
||||
test = CPU_state.pc.val.intval;
|
||||
|
||||
// make sure PC is defined.
|
||||
if ((CPU_state.pc.flags & MVALUE_DEFINED) == 0) {
|
||||
Throw_error(exception_pc_undefined);
|
||||
CPU_state.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_state.type->default_align_value;
|
||||
while ((test++ & and) != equal)
|
||||
output_8(fill);
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// select CPU ("!cpu" pseudo opcode)
|
||||
static enum eos po_cpu(void)
|
||||
{
|
||||
const struct cpu_type *cpu_buffer = CPU_state.type; // remember current cpu
|
||||
const struct cpu_type *new_cpu_type;
|
||||
|
||||
if (Input_read_and_lower_keyword()) {
|
||||
new_cpu_type = cputype_find();
|
||||
if (new_cpu_type)
|
||||
CPU_state.type = new_cpu_type;
|
||||
else
|
||||
Throw_error("Unknown processor.");
|
||||
}
|
||||
// if there's a block, parse that and then restore old value!
|
||||
if (Parse_optional_block())
|
||||
CPU_state.type = cpu_buffer;
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
static const char Error_old_offset_assembly[] =
|
||||
"\"!pseudopc/!realpc\" is obsolete; use \"!pseudopc {}\" instead.";
|
||||
|
||||
|
||||
// "!realpc" pseudo opcode (now obsolete)
|
||||
static enum eos po_realpc(void)
|
||||
{
|
||||
Throw_error(Error_old_offset_assembly);
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// start offset assembly
|
||||
// FIXME - split into PO (move to pseudoopcodes.c) and backend (move to output.c?)
|
||||
// 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)
|
||||
{
|
||||
// FIXME - read pc using a function call!
|
||||
intval_t new_pc,
|
||||
new_offset;
|
||||
int outer_flags = CPU_state.pc.flags;
|
||||
|
||||
// set new
|
||||
new_pc = ALU_defined_int(); // FIXME - allow for undefined!
|
||||
new_offset = (new_pc - CPU_state.pc.val.intval) & 0xffff;
|
||||
CPU_state.pc.val.intval = new_pc;
|
||||
CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined!
|
||||
// if there's a block, parse that and then restore old value!
|
||||
if (Parse_optional_block()) {
|
||||
// restore old
|
||||
CPU_state.pc.val.intval = (CPU_state.pc.val.intval - new_offset) & 0xffff;
|
||||
CPU_state.pc.flags = outer_flags;
|
||||
} else {
|
||||
// not using a block is no longer allowed
|
||||
Throw_error(Error_old_offset_assembly);
|
||||
}
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// switch to long accumulator ("!al" pseudo opcode)
|
||||
static enum eos po_al(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.a_is_long, TRUE);
|
||||
}
|
||||
|
||||
|
||||
// switch to short accumulator ("!as" pseudo opcode)
|
||||
static enum eos po_as(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.a_is_long, FALSE);
|
||||
}
|
||||
|
||||
|
||||
// switch to long index registers ("!rl" pseudo opcode)
|
||||
static enum eos po_rl(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.xy_are_long, TRUE);
|
||||
}
|
||||
|
||||
|
||||
// switch to short index registers ("!rs" pseudo opcode)
|
||||
static enum eos po_rs(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.xy_are_long, FALSE);
|
||||
}
|
||||
|
||||
|
||||
// pseudo opcode table
|
||||
static struct ronode pseudo_opcodes[] = {
|
||||
PREDEFNODE("align", po_align),
|
||||
PREDEFNODE("cpu", po_cpu),
|
||||
PREDEFNODE("pseudopc", po_pseudopc),
|
||||
PREDEFNODE("realpc", po_realpc), // obsolete
|
||||
PREDEFNODE("al", po_al),
|
||||
PREDEFNODE("as", po_as),
|
||||
PREDEFNODE(s_rl, po_rl),
|
||||
PREDEFLAST("rs", po_rs),
|
||||
// ^^^^ this marks the last element
|
||||
};
|
||||
|
||||
|
||||
// register pseudo opcodes (done later)
|
||||
void CPU_init(void)
|
||||
{
|
||||
Tree_add_table(&pseudo_opcode_tree, pseudo_opcodes);
|
||||
}
|
||||
|
@ -23,10 +23,11 @@ struct cpu_type {
|
||||
#define CPUFLAG_8B_AND_AB_NEED_0_ARG (1u << 2) // warn if "ane/lxa #$xx" uses non-zero arg
|
||||
|
||||
|
||||
// register pseudo opcodes (done later)
|
||||
extern void CPU_init(void);
|
||||
// if cpu type and value match, set register length variable to value.
|
||||
// if cpu type and value don't match, complain instead.
|
||||
extern void vcpu_check_and_set_reg_length(int *var, int make_long);
|
||||
// set default value for pass
|
||||
extern void CPU_passinit(const struct cpu_type *cpu_type);
|
||||
extern void cputype_passinit(const struct cpu_type *cpu_type);
|
||||
// lookup cpu type held in DynaBuf and return its struct pointer (or NULL on failure)
|
||||
extern const struct cpu_type *cputype_find(void);
|
||||
|
||||
|
215
src/encoding.c
215
src/encoding.c
@ -23,16 +23,10 @@ struct encoder {
|
||||
};
|
||||
|
||||
|
||||
// constants
|
||||
static const char s_pet[] = "pet";
|
||||
static const char s_raw[] = "raw";
|
||||
static const char s_scr[] = "scr";
|
||||
|
||||
|
||||
// variables
|
||||
static char outermost_table[256]; // space for encoding table...
|
||||
static char *loaded_table = outermost_table; // ...loaded from file
|
||||
static struct encoder *current_encoder; // gets set before each pass
|
||||
const struct encoder *encoder_current; // gets set before each pass
|
||||
char *encoding_loaded_table = outermost_table; // ...loaded from file
|
||||
|
||||
|
||||
// encoder functions:
|
||||
@ -68,98 +62,27 @@ static char encoderfn_scr(char byte)
|
||||
// convert raw to whatever is defined in table
|
||||
static char encoderfn_file(char byte)
|
||||
{
|
||||
return loaded_table[(unsigned char) byte];
|
||||
return encoding_loaded_table[(unsigned char) byte];
|
||||
}
|
||||
|
||||
|
||||
// predefined encoder structs:
|
||||
|
||||
|
||||
static struct encoder encoder_raw = {
|
||||
const struct encoder encoder_raw = {
|
||||
encoderfn_raw
|
||||
};
|
||||
static struct encoder encoder_pet = {
|
||||
const struct encoder encoder_pet = {
|
||||
encoderfn_pet
|
||||
};
|
||||
static struct encoder encoder_scr = {
|
||||
const struct encoder encoder_scr = {
|
||||
encoderfn_scr
|
||||
};
|
||||
static struct encoder encoder_file = {
|
||||
const struct encoder encoder_file = {
|
||||
encoderfn_file
|
||||
};
|
||||
|
||||
|
||||
// functions
|
||||
|
||||
// convert character using current encoding (exported for use by alu.c)
|
||||
char encoding_encode_char(char byte)
|
||||
{
|
||||
return current_encoder->fn(byte);
|
||||
}
|
||||
|
||||
// insert string(s)
|
||||
static enum eos encode_string(struct encoder *inner_encoder, char xor)
|
||||
{
|
||||
struct encoder *outer_encoder = current_encoder; // buffer encoder
|
||||
|
||||
// make given encoder the current one (for ALU-parsed values)
|
||||
current_encoder = inner_encoder;
|
||||
do {
|
||||
if (GotByte == '"') {
|
||||
// read initial character
|
||||
GetQuotedByte();
|
||||
// send characters until closing quote is reached
|
||||
while (GotByte && (GotByte != '"')) {
|
||||
output_8(xor ^ current_encoder->fn(GotByte));
|
||||
GetQuotedByte();
|
||||
}
|
||||
if (GotByte == CHAR_EOS)
|
||||
return AT_EOS_ANYWAY;
|
||||
|
||||
// after closing quote, proceed with next char
|
||||
GetByte();
|
||||
} else {
|
||||
// Parse value. No problems with single characters
|
||||
// because the current encoding is
|
||||
// temporarily set to the given one.
|
||||
output_8(ALU_any_int());
|
||||
}
|
||||
} while (Input_accept_comma());
|
||||
current_encoder = outer_encoder; // reactivate buffered encoder
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
// read encoding table from file
|
||||
static enum eos user_defined_encoding(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char local_table[256],
|
||||
*buffered_table = loaded_table;
|
||||
struct encoder *buffered_encoder = current_encoder;
|
||||
|
||||
fd = fopen(GLOBALDYNABUF_CURRENT, FILE_READBINARY);
|
||||
if (fd) {
|
||||
if (fread(local_table, sizeof(char), 256, fd) != 256)
|
||||
Throw_error("Conversion table incomplete.");
|
||||
fclose(fd);
|
||||
} else {
|
||||
Throw_error(exception_cannot_open_input_file);
|
||||
}
|
||||
current_encoder = &encoder_file; // activate new encoding
|
||||
loaded_table = local_table; // activate local table
|
||||
// If there's a block, parse that and then restore old values
|
||||
if (Parse_optional_block()) {
|
||||
current_encoder = buffered_encoder;
|
||||
} else {
|
||||
// if there's *no* block, the table must be used from now on.
|
||||
// copy the local table to the "outer" table
|
||||
memcpy(buffered_table, local_table, 256);
|
||||
}
|
||||
// re-activate "outer" table (it might have been changed by memcpy())
|
||||
loaded_table = buffered_table;
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
// keywords for "!convtab" pseudo opcode
|
||||
static struct ronode *encoder_tree = NULL; // tree to hold encoders
|
||||
static struct ronode encoder_list[] = {
|
||||
@ -171,112 +94,48 @@ static struct ronode encoder_list[] = {
|
||||
};
|
||||
|
||||
|
||||
// use one of the pre-defined encodings (raw, pet, scr)
|
||||
static enum eos predefined_encoding(void)
|
||||
{
|
||||
void *node_body;
|
||||
char local_table[256],
|
||||
*buffered_table = loaded_table;
|
||||
struct encoder *buffered_encoder = current_encoder;
|
||||
|
||||
// use one of the pre-defined encodings
|
||||
if (Input_read_and_lower_keyword()) {
|
||||
// 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)) {
|
||||
current_encoder = (struct encoder *) node_body; // activate new encoder
|
||||
} else {
|
||||
Throw_error("Unknown encoding.");
|
||||
}
|
||||
}
|
||||
loaded_table = local_table; // activate local table
|
||||
// If there's a block, parse that and then restore old values
|
||||
if (Parse_optional_block())
|
||||
current_encoder = buffered_encoder;
|
||||
// re-activate "outer" table
|
||||
loaded_table = buffered_table;
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// exported functions
|
||||
|
||||
|
||||
// convert character using current encoding (exported for use by alu.c and pseudoopcodes.c)
|
||||
char encoding_encode_char(char byte)
|
||||
{
|
||||
return encoder_current->fn(byte);
|
||||
}
|
||||
|
||||
// set "raw" as default encoding
|
||||
void encoding_passinit(void)
|
||||
{
|
||||
current_encoder = &encoder_raw;
|
||||
encoder_current = &encoder_raw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FIXME - move this to pseudoopcodes.c:
|
||||
|
||||
// insert text string (default format)
|
||||
static enum eos po_text(void)
|
||||
// try to load encoding table from given file
|
||||
void encoding_load(char target[256], const char *filename)
|
||||
{
|
||||
return encode_string(current_encoder, 0);
|
||||
}
|
||||
FILE *fd = fopen(filename, FILE_READBINARY);
|
||||
|
||||
// insert raw string
|
||||
static enum eos po_raw(void)
|
||||
{
|
||||
return encode_string(&encoder_raw, 0);
|
||||
}
|
||||
|
||||
// insert PetSCII string
|
||||
static enum eos po_pet(void)
|
||||
{
|
||||
return encode_string(&encoder_pet, 0);
|
||||
}
|
||||
|
||||
// insert screencode string
|
||||
static enum eos po_scr(void)
|
||||
{
|
||||
return encode_string(&encoder_scr, 0);
|
||||
}
|
||||
|
||||
// insert screencode string, XOR'd
|
||||
static enum eos po_scrxor(void)
|
||||
{
|
||||
intval_t num = ALU_any_int();
|
||||
|
||||
if (Input_accept_comma() == FALSE) {
|
||||
Throw_error(exception_syntax);
|
||||
return SKIP_REMAINDER;
|
||||
}
|
||||
return encode_string(&encoder_scr, num);
|
||||
}
|
||||
|
||||
// Set current encoding ("!convtab" pseudo opcode)
|
||||
static enum eos po_convtab(void)
|
||||
{
|
||||
if ((GotByte == '<') || (GotByte == '"')) {
|
||||
// if file name is missing, don't bother continuing
|
||||
if (Input_read_filename(TRUE))
|
||||
return SKIP_REMAINDER;
|
||||
return user_defined_encoding();
|
||||
if (fd) {
|
||||
if (fread(target, sizeof(char), 256, fd) != 256)
|
||||
Throw_error("Conversion table incomplete.");
|
||||
fclose(fd);
|
||||
} else {
|
||||
return predefined_encoding();
|
||||
Throw_error(exception_cannot_open_input_file);
|
||||
}
|
||||
}
|
||||
|
||||
// pseudo opcode table
|
||||
static struct ronode pseudo_opcodes[] = {
|
||||
PREDEFNODE("ct", po_convtab),
|
||||
PREDEFNODE("convtab", po_convtab),
|
||||
PREDEFNODE(s_pet, po_pet),
|
||||
PREDEFNODE(s_raw, po_raw),
|
||||
PREDEFNODE(s_scr, po_scr),
|
||||
PREDEFNODE(s_scrxor, po_scrxor),
|
||||
PREDEFNODE("text", po_text),
|
||||
PREDEFLAST("tx", po_text),
|
||||
// ^^^^ this marks the last element
|
||||
};
|
||||
|
||||
// register pseudo opcodes
|
||||
void Encoding_init(void)
|
||||
// lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure)
|
||||
const struct encoder *encoding_find(void)
|
||||
{
|
||||
Tree_add_table(&pseudo_opcode_tree, pseudo_opcodes);
|
||||
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;
|
||||
}
|
||||
|
@ -7,14 +7,25 @@
|
||||
#define encoding_H
|
||||
|
||||
|
||||
//struct encoder;
|
||||
extern const struct encoder *encoder_current; // gets set before each pass
|
||||
extern const struct encoder encoder_raw;
|
||||
extern const struct encoder encoder_pet;
|
||||
extern const struct encoder encoder_scr;
|
||||
extern const struct encoder encoder_file;
|
||||
extern char *encoding_loaded_table; // ...loaded from file
|
||||
|
||||
|
||||
// prototypes
|
||||
|
||||
// register pseudo opcodes (FIXME - remove!)
|
||||
extern void Encoding_init(void);
|
||||
// convert character using current encoding
|
||||
extern char encoding_encode_char(char);
|
||||
extern char encoding_encode_char(char byte);
|
||||
// set "raw" as default encoding
|
||||
extern void encoding_passinit(void);
|
||||
// try to load encoding table from given file
|
||||
extern void encoding_load(char target[256], const char *filename);
|
||||
// lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure)
|
||||
extern const struct encoder *encoding_find(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
87
src/flow.c
87
src/flow.c
@ -102,36 +102,20 @@ static int check_condition(struct loop_condition *condition)
|
||||
}
|
||||
|
||||
|
||||
// looping assembly ("!do"). has to be re-entrant.
|
||||
static enum eos po_do(void) // now GotByte = illegal char
|
||||
struct loop_total {
|
||||
struct loop_condition head_cond;
|
||||
int start; // line number of loop pseudo opcode
|
||||
char *body;
|
||||
struct loop_condition tail_cond;
|
||||
};
|
||||
|
||||
// back end function for "!do" pseudo opcode
|
||||
static void do_loop(struct loop_total *loop)
|
||||
{
|
||||
struct loop_condition condition1,
|
||||
condition2;
|
||||
struct input loop_input,
|
||||
*outer_input;
|
||||
char *loop_body;
|
||||
int go_on,
|
||||
loop_start; // line number of loop pseudo opcode
|
||||
// init
|
||||
condition1.is_until = FALSE;
|
||||
condition1.body = NULL;
|
||||
condition2.is_until = FALSE;
|
||||
condition2.body = NULL;
|
||||
|
||||
// read head condition to buffer
|
||||
SKIPSPACE();
|
||||
store_condition(&condition1, CHAR_SOB);
|
||||
if (GotByte != CHAR_SOB)
|
||||
Throw_serious_error(exception_no_left_brace);
|
||||
// remember line number of loop body,
|
||||
// then read block and get copy
|
||||
loop_start = Input_now->line_number;
|
||||
loop_body = Input_skip_or_store_block(TRUE); // changes line number!
|
||||
// now GotByte = '}'
|
||||
NEXTANDSKIPSPACE(); // now GotByte = first non-blank char after block
|
||||
// read tail condition to buffer
|
||||
store_condition(&condition2, CHAR_EOS);
|
||||
// now GotByte = CHAR_EOS
|
||||
struct input loop_input;
|
||||
struct input *outer_input;
|
||||
int go_on;
|
||||
|
||||
// set up new input
|
||||
loop_input = *Input_now; // copy current input structure into new
|
||||
loop_input.source_is_ram = TRUE; // set new byte source
|
||||
@ -142,17 +126,17 @@ static enum eos po_do(void) // now GotByte = illegal char
|
||||
Input_now = &loop_input;
|
||||
do {
|
||||
// check head condition
|
||||
go_on = check_condition(&condition1);
|
||||
go_on = check_condition(&loop->head_cond);
|
||||
if (go_on) {
|
||||
parse_ram_block(loop_start, loop_body);
|
||||
parse_ram_block(loop->start, loop->body);
|
||||
// check tail condition
|
||||
go_on = check_condition(&condition2);
|
||||
go_on = check_condition(&loop->tail_cond);
|
||||
}
|
||||
} while (go_on);
|
||||
// free memory
|
||||
free(condition1.body);
|
||||
free(loop_body);
|
||||
free(condition2.body);
|
||||
free(loop->head_cond.body);
|
||||
free(loop->body);
|
||||
free(loop->tail_cond.body);
|
||||
// restore previous input:
|
||||
Input_now = outer_input;
|
||||
GotByte = CHAR_EOS; // CAUTION! Very ugly kluge.
|
||||
@ -160,6 +144,37 @@ static enum eos po_do(void) // now GotByte = illegal char
|
||||
// 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.
|
||||
}
|
||||
|
||||
|
||||
// move to pseudoopcodes.c:
|
||||
|
||||
// looping assembly ("!do"). has to be re-entrant.
|
||||
static enum eos po_do(void) // now GotByte = illegal char
|
||||
{
|
||||
struct loop_total loop;
|
||||
|
||||
// init
|
||||
loop.head_cond.is_until = FALSE;
|
||||
loop.head_cond.body = NULL;
|
||||
loop.tail_cond.is_until = FALSE;
|
||||
loop.tail_cond.body = NULL;
|
||||
|
||||
// read head condition to buffer
|
||||
SKIPSPACE();
|
||||
store_condition(&loop.head_cond, CHAR_SOB);
|
||||
if (GotByte != CHAR_SOB)
|
||||
Throw_serious_error(exception_no_left_brace);
|
||||
// remember line number of loop body,
|
||||
// then read block and get copy
|
||||
loop.start = Input_now->line_number;
|
||||
loop.body = Input_skip_or_store_block(TRUE); // changes line number!
|
||||
// now GotByte = '}'
|
||||
NEXTANDSKIPSPACE(); // now GotByte = first non-blank char after block
|
||||
// read tail condition to buffer
|
||||
store_condition(&loop.tail_cond, CHAR_EOS);
|
||||
// now GotByte = CHAR_EOS
|
||||
do_loop(&loop);
|
||||
return AT_EOS_ANYWAY;
|
||||
}
|
||||
|
||||
@ -185,6 +200,7 @@ static enum eos po_for(void) // now GotByte = illegal char
|
||||
|
||||
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
|
||||
symbol = symbol_find(zone, force_bit);
|
||||
@ -192,6 +208,7 @@ static enum eos po_for(void) // now GotByte = illegal char
|
||||
Throw_error(exception_syntax);
|
||||
return SKIP_REMAINDER;
|
||||
}
|
||||
|
||||
first_arg = ALU_defined_int();
|
||||
if (Input_accept_comma()) {
|
||||
old_algo = FALSE; // new format - yay!
|
||||
|
@ -40,6 +40,11 @@ const char s_scrxor[] = "scrxor";
|
||||
char s_untitled[] = "<untitled>"; // FIXME - this is actually const
|
||||
const char s_Zone[] = "Zone";
|
||||
const char s_subzone[] = "subzone";
|
||||
const char s_pet[] = "pet";
|
||||
const char s_raw[] = "raw";
|
||||
const char s_scr[] = "scr";
|
||||
|
||||
|
||||
// Exception messages during assembly
|
||||
const char exception_cannot_open_input_file[] = "Cannot open input file.";
|
||||
const char exception_missing_string[] = "No string given.";
|
||||
|
@ -36,6 +36,9 @@ extern char s_untitled[];
|
||||
extern const char s_Zone[];
|
||||
#define s_zone (s_subzone + 3) // Yes, I know I'm sick
|
||||
extern const char s_subzone[];
|
||||
extern const char s_pet[];
|
||||
extern const char s_raw[];
|
||||
extern const char s_scr[];
|
||||
// error messages during assembly
|
||||
extern const char exception_cannot_open_input_file[];
|
||||
extern const char exception_missing_string[];
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "cpu.h"
|
||||
#include "alu.h"
|
||||
#include "dynabuf.h"
|
||||
#include "encoding.h"
|
||||
#include "flow.h"
|
||||
#include "input.h"
|
||||
#include "macro.h"
|
||||
@ -27,6 +28,7 @@ static const char s_08[] = "08";
|
||||
#define s_8 (s_08 + 1) // Yes, I know I'm sick
|
||||
#define s_16 (s_65816 + 3) // Yes, I know I'm sick
|
||||
#define s_sl (s_asl + 1) // Yes, I know I'm sick
|
||||
#define s_rl (s_brl + 1) // Yes, I know I'm sick
|
||||
|
||||
|
||||
// variables
|
||||
@ -161,6 +163,132 @@ static enum eos po_32(void)
|
||||
}
|
||||
|
||||
|
||||
// "!cbm" pseudo opcode (now obsolete)
|
||||
static enum eos obsolete_po_cbm(void)
|
||||
{
|
||||
Throw_error("\"!cbm\" is obsolete; use \"!ct pet\" instead.");
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
// read encoding table from file
|
||||
static enum eos user_defined_encoding(void)
|
||||
{
|
||||
char local_table[256],
|
||||
*buffered_table = encoding_loaded_table;
|
||||
const struct encoder *buffered_encoder = encoder_current;
|
||||
|
||||
encoding_load(local_table, GLOBALDYNABUF_CURRENT);
|
||||
encoder_current = &encoder_file; // activate new encoding
|
||||
encoding_loaded_table = local_table; // activate local table
|
||||
// If there's a block, parse that and then restore old values
|
||||
if (Parse_optional_block()) {
|
||||
encoder_current = buffered_encoder;
|
||||
} else {
|
||||
// if there's *no* block, the table must be used from now on.
|
||||
// copy the local table to the "outer" table
|
||||
memcpy(buffered_table, local_table, 256);
|
||||
}
|
||||
// re-activate "outer" table (it might have been changed by memcpy())
|
||||
encoding_loaded_table = buffered_table;
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
// use one of the pre-defined encodings (raw, pet, scr)
|
||||
static enum eos predefined_encoding(void)
|
||||
{
|
||||
char local_table[256],
|
||||
*buffered_table = encoding_loaded_table;
|
||||
const struct encoder *buffered_encoder = encoder_current;
|
||||
|
||||
if (Input_read_and_lower_keyword()) {
|
||||
const struct encoder *new_encoder = encoding_find();
|
||||
|
||||
if (new_encoder)
|
||||
encoder_current = new_encoder; // activate new encoder
|
||||
}
|
||||
encoding_loaded_table = local_table; // activate local table
|
||||
// if there's a block, parse that and then restore old values
|
||||
if (Parse_optional_block())
|
||||
encoder_current = buffered_encoder;
|
||||
// re-activate "outer" table
|
||||
encoding_loaded_table = buffered_table;
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
// set current encoding ("!convtab" pseudo opcode)
|
||||
static enum eos po_convtab(void)
|
||||
{
|
||||
if ((GotByte == '<') || (GotByte == '"')) {
|
||||
// if file name is missing, don't bother continuing
|
||||
if (Input_read_filename(TRUE))
|
||||
return SKIP_REMAINDER;
|
||||
return user_defined_encoding();
|
||||
} else {
|
||||
return predefined_encoding();
|
||||
}
|
||||
}
|
||||
// insert string(s)
|
||||
static enum eos encode_string(const struct encoder *inner_encoder, char xor)
|
||||
{
|
||||
const struct encoder *outer_encoder = encoder_current; // buffer encoder
|
||||
|
||||
// make given encoder the current one (for ALU-parsed values)
|
||||
encoder_current = inner_encoder;
|
||||
do {
|
||||
if (GotByte == '"') {
|
||||
// read initial character
|
||||
GetQuotedByte();
|
||||
// send characters until closing quote is reached
|
||||
while (GotByte && (GotByte != '"')) {
|
||||
output_8(xor ^ encoding_encode_char(GotByte));
|
||||
GetQuotedByte();
|
||||
}
|
||||
if (GotByte == CHAR_EOS)
|
||||
return AT_EOS_ANYWAY;
|
||||
|
||||
// after closing quote, proceed with next char
|
||||
GetByte();
|
||||
} else {
|
||||
// Parse value. No problems with single characters
|
||||
// because the current encoding is
|
||||
// temporarily set to the given one.
|
||||
output_8(ALU_any_int());
|
||||
}
|
||||
} while (Input_accept_comma());
|
||||
encoder_current = outer_encoder; // reactivate buffered encoder
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
// insert text string (default format)
|
||||
static enum eos po_text(void)
|
||||
{
|
||||
return encode_string(encoder_current, 0);
|
||||
}
|
||||
// insert raw string
|
||||
static enum eos po_raw(void)
|
||||
{
|
||||
return encode_string(&encoder_raw, 0);
|
||||
}
|
||||
// insert PetSCII string
|
||||
static enum eos po_pet(void)
|
||||
{
|
||||
return encode_string(&encoder_pet, 0);
|
||||
}
|
||||
// insert screencode string
|
||||
static enum eos po_scr(void)
|
||||
{
|
||||
return encode_string(&encoder_scr, 0);
|
||||
}
|
||||
// insert screencode string, XOR'd
|
||||
static enum eos po_scrxor(void)
|
||||
{
|
||||
intval_t num = ALU_any_int();
|
||||
|
||||
if (Input_accept_comma() == FALSE) {
|
||||
Throw_error(exception_syntax);
|
||||
return SKIP_REMAINDER;
|
||||
}
|
||||
return encode_string(&encoder_scr, num);
|
||||
}
|
||||
|
||||
// Include binary file ("!binary" pseudo opcode)
|
||||
// FIXME - split this into "parser" and "worker" fn and move worker fn somewhere else.
|
||||
static enum eos po_binary(void)
|
||||
@ -222,7 +350,7 @@ static enum eos po_binary(void)
|
||||
}
|
||||
|
||||
|
||||
// Reserve space by sending bytes of given value ("!fi" / "!fill" pseudo opcode)
|
||||
// reserve space by sending bytes of given value ("!fi" / "!fill" pseudo opcode)
|
||||
static enum eos po_fill(void)
|
||||
{
|
||||
intval_t fill = FILLVALUE_FILL,
|
||||
@ -236,6 +364,128 @@ static enum eos po_fill(void)
|
||||
}
|
||||
|
||||
|
||||
// insert byte until PC fits condition
|
||||
static enum eos po_align(void)
|
||||
{
|
||||
// FIXME - read cpu state via function call!
|
||||
intval_t and,
|
||||
equal,
|
||||
fill,
|
||||
test = CPU_state.pc.val.intval;
|
||||
|
||||
// make sure PC is defined.
|
||||
if ((CPU_state.pc.flags & MVALUE_DEFINED) == 0) {
|
||||
Throw_error(exception_pc_undefined);
|
||||
CPU_state.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_state.type->default_align_value;
|
||||
while ((test++ & and) != equal)
|
||||
output_8(fill);
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
static const char Error_old_offset_assembly[] =
|
||||
"\"!pseudopc/!realpc\" is obsolete; use \"!pseudopc {}\" instead.";
|
||||
// start offset assembly
|
||||
// FIXME - split in two parts and move backend to output.c?
|
||||
// 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)
|
||||
{
|
||||
// FIXME - read pc using a function call!
|
||||
intval_t new_pc,
|
||||
new_offset;
|
||||
int outer_flags = CPU_state.pc.flags;
|
||||
|
||||
// set new
|
||||
new_pc = ALU_defined_int(); // FIXME - allow for undefined!
|
||||
new_offset = (new_pc - CPU_state.pc.val.intval) & 0xffff;
|
||||
CPU_state.pc.val.intval = new_pc;
|
||||
CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined!
|
||||
// if there's a block, parse that and then restore old value!
|
||||
if (Parse_optional_block()) {
|
||||
// restore old
|
||||
CPU_state.pc.val.intval = (CPU_state.pc.val.intval - new_offset) & 0xffff;
|
||||
CPU_state.pc.flags = outer_flags;
|
||||
} else {
|
||||
// not using a block is no longer allowed
|
||||
Throw_error(Error_old_offset_assembly);
|
||||
}
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// "!realpc" pseudo opcode (now obsolete)
|
||||
static enum eos obsolete_po_realpc(void)
|
||||
{
|
||||
Throw_error(Error_old_offset_assembly);
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// select CPU ("!cpu" pseudo opcode)
|
||||
static enum eos po_cpu(void)
|
||||
{
|
||||
const struct cpu_type *cpu_buffer = CPU_state.type; // remember current cpu
|
||||
const struct cpu_type *new_cpu_type;
|
||||
|
||||
if (Input_read_and_lower_keyword()) {
|
||||
new_cpu_type = cputype_find();
|
||||
if (new_cpu_type)
|
||||
CPU_state.type = new_cpu_type; // activate new cpu type
|
||||
else
|
||||
Throw_error("Unknown processor.");
|
||||
}
|
||||
// if there's a block, parse that and then restore old value
|
||||
if (Parse_optional_block())
|
||||
CPU_state.type = cpu_buffer;
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
||||
// set register length, block-wise if needed.
|
||||
static enum eos set_register_length(int *var, int make_long)
|
||||
{
|
||||
int old_size = *var;
|
||||
|
||||
// set new register length (or complain - whichever is more fitting)
|
||||
vcpu_check_and_set_reg_length(var, make_long);
|
||||
// if there's a block, parse that and then restore old value!
|
||||
if (Parse_optional_block())
|
||||
vcpu_check_and_set_reg_length(var, old_size); // restore old length
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
// switch to long accumulator ("!al" pseudo opcode)
|
||||
static enum eos po_al(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.a_is_long, TRUE);
|
||||
}
|
||||
// switch to short accumulator ("!as" pseudo opcode)
|
||||
static enum eos po_as(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.a_is_long, FALSE);
|
||||
}
|
||||
// switch to long index registers ("!rl" pseudo opcode)
|
||||
static enum eos po_rl(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.xy_are_long, TRUE);
|
||||
}
|
||||
// switch to short index registers ("!rs" pseudo opcode)
|
||||
static enum eos po_rs(void)
|
||||
{
|
||||
return set_register_length(&CPU_state.xy_are_long, FALSE);
|
||||
}
|
||||
|
||||
|
||||
// force explicit label definitions to set "address" flag ("!addr"). Has to be re-entrant.
|
||||
static enum eos po_address(void) // now GotByte = illegal char
|
||||
{
|
||||
@ -363,13 +613,6 @@ static enum eos obsolete_po_subzone(void)
|
||||
return po_zone();
|
||||
}
|
||||
|
||||
// "!cbm" pseudo opcode (now obsolete)
|
||||
static enum eos obsolete_po_cbm(void)
|
||||
{
|
||||
Throw_error("\"!cbm\" is obsolete; use \"!ct pet\" instead.");
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
// include source file ("!source" or "!src"). has to be re-entrant.
|
||||
static enum eos po_source(void) // now GotByte = illegal char
|
||||
{
|
||||
@ -600,10 +843,27 @@ static struct ronode pseudo_opcode_list[] = {
|
||||
PREDEFNODE("word", po_word),
|
||||
PREDEFNODE("24", po_24),
|
||||
PREDEFNODE("32", po_32),
|
||||
PREDEFNODE(s_cbm, obsolete_po_cbm),
|
||||
PREDEFNODE("ct", po_convtab),
|
||||
PREDEFNODE("convtab", po_convtab),
|
||||
PREDEFNODE("tx", po_text),
|
||||
PREDEFNODE("text", po_text),
|
||||
PREDEFNODE(s_raw, po_raw),
|
||||
PREDEFNODE(s_pet, po_pet),
|
||||
PREDEFNODE(s_scr, po_scr),
|
||||
PREDEFNODE(s_scrxor, po_scrxor),
|
||||
PREDEFNODE("bin", po_binary),
|
||||
PREDEFNODE("binary", po_binary),
|
||||
PREDEFNODE("fi", po_fill),
|
||||
PREDEFNODE("fill", po_fill),
|
||||
PREDEFNODE("align", po_align),
|
||||
PREDEFNODE("pseudopc", po_pseudopc),
|
||||
PREDEFNODE("realpc", obsolete_po_realpc),
|
||||
PREDEFNODE("cpu", po_cpu),
|
||||
PREDEFNODE("al", po_al),
|
||||
PREDEFNODE("as", po_as),
|
||||
PREDEFNODE(s_rl, po_rl),
|
||||
PREDEFNODE("rs", po_rs),
|
||||
PREDEFNODE("addr", po_address),
|
||||
PREDEFNODE("address", po_address),
|
||||
PREDEFNODE("set", po_set),
|
||||
@ -614,7 +874,6 @@ static struct ronode pseudo_opcode_list[] = {
|
||||
PREDEFNODE(s_zone, po_zone),
|
||||
PREDEFNODE("sz", obsolete_po_subzone),
|
||||
PREDEFNODE(s_subzone, obsolete_po_subzone),
|
||||
PREDEFNODE(s_cbm, obsolete_po_cbm),
|
||||
PREDEFNODE("src", po_source),
|
||||
PREDEFNODE("source", po_source),
|
||||
PREDEFNODE("if", po_if),
|
||||
|
Loading…
x
Reference in New Issue
Block a user