From 2331709a35add2ca592208de19bf4c84fd2b6f32 Mon Sep 17 00:00:00 2001 From: marcobaye Date: Tue, 16 Dec 2014 08:21:44 +0000 Subject: [PATCH] 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 --- src/acme.c | 6 +- src/alu.c | 15 ++- src/cpu.c | 163 +------------------------- src/cpu.h | 7 +- src/encoding.c | 215 ++++++---------------------------- src/encoding.h | 17 ++- src/flow.c | 87 ++++++++------ src/global.c | 5 + src/global.h | 3 + src/pseudoopcodes.c | 277 ++++++++++++++++++++++++++++++++++++++++++-- 10 files changed, 402 insertions(+), 393 deletions(-) diff --git a/src/acme.c b/src/acme.c index e2df97f..1951107 100644 --- a/src/acme.c +++ b/src/acme.c @@ -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(); diff --git a/src/alu.c b/src/alu.c index b94f2eb..2db14b4 100644 --- a/src/alu.c +++ b/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; diff --git a/src/cpu.c b/src/cpu.c index 984dfd4..9ffad47 100644 --- a/src/cpu.c +++ b/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); -} diff --git a/src/cpu.h b/src/cpu.h index 3e466dc..f5595c0 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -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); diff --git a/src/encoding.c b/src/encoding.c index c2ae479..0d0d18a 100644 --- a/src/encoding.c +++ b/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; } diff --git a/src/encoding.h b/src/encoding.h index 60ff776..1db0920 100644 --- a/src/encoding.h +++ b/src/encoding.h @@ -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 diff --git a/src/flow.c b/src/flow.c index 62f690d..2a6cfd7 100644 --- a/src/flow.c +++ b/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! diff --git a/src/global.c b/src/global.c index f63eca4..774f68c 100644 --- a/src/global.c +++ b/src/global.c @@ -40,6 +40,11 @@ const char s_scrxor[] = "scrxor"; char s_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."; diff --git a/src/global.h b/src/global.h index 3ead1ff..74d0b36 100644 --- a/src/global.h +++ b/src/global.h @@ -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[]; diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index 599cb8a..b578bca 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -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),