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:
marcobaye 2014-12-16 08:21:44 +00:00
parent cc2ddb9b42
commit 2331709a35
10 changed files with 402 additions and 393 deletions

View File

@ -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();

View File

@ -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
View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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!

View File

@ -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.";

View File

@ -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[];

View File

@ -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),