mirror of
https://github.com/uffejakobsen/acme.git
synced 2025-02-16 19:32:16 +00:00
refactored program counter / outbuffer stuff
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@403 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
parent
be4580af53
commit
ec4db48d07
@ -359,9 +359,12 @@ static void perform_pass(bits passflags)
|
||||
report_open(report, config.report_filename);
|
||||
}
|
||||
cputype_passinit(); // set default cpu type
|
||||
output_passinit(); // set initial pc or start with undefined pc
|
||||
output_passinit(); // clear segment list, disable output, undefine pc, ...
|
||||
encoding_passinit(); // set default encoding
|
||||
section_passinit(); // set initial zone (untitled)
|
||||
// if start address was given on command line, use it to define pc:
|
||||
if (config.initial_pc != NO_VALUE_GIVEN)
|
||||
programcounter_set(config.initial_pc, 0); // 0 -> no segment flags
|
||||
// process toplevel files
|
||||
for (ii = 0; ii < toplevel_src_count; ++ii) {
|
||||
fd = fopen(toplevel_sources_plat[ii], FILE_READBINARY);
|
||||
|
@ -388,7 +388,7 @@ static void parse_program_counter(unsigned int unpseudo_count) // Now GotByte =
|
||||
struct object *arg;
|
||||
|
||||
GetByte();
|
||||
vcpu_read_pc(&pc);
|
||||
programcounter_read(&pc);
|
||||
// if needed, output "value not defined" error
|
||||
if (pc.ntype == NUMTYPE_UNDEFINED)
|
||||
is_not_defined(NULL, "*", 1);
|
||||
|
@ -221,7 +221,7 @@ static void set_label(scope_t scope, bits force_bit, bits powers)
|
||||
}
|
||||
symbol = symbol_find(scope);
|
||||
result.type = &type_number;
|
||||
vcpu_read_pc(&result.u.number); // FIXME - if undefined, check pass.flags.complain_about_undefined and maybe throw "value not defined"!
|
||||
programcounter_read(&result.u.number); // FIXME - if undefined, check pass.flags.complain_about_undefined and maybe throw "value not defined"!
|
||||
symbol_set_object(symbol, &result, powers);
|
||||
if (force_bit)
|
||||
symbol_set_force_bit(symbol, force_bit);
|
||||
|
@ -800,7 +800,7 @@ static void near_branch(int preoffset)
|
||||
struct number target;
|
||||
intval_t offset = 0; // dummy value, to not throw more errors than necessary
|
||||
|
||||
vcpu_read_pc(&pc);
|
||||
programcounter_read(&pc);
|
||||
get_int_arg(&target, TRUE);
|
||||
typesystem_want_addr(&target);
|
||||
if ((pc.ntype == NUMTYPE_INT) && (target.ntype == NUMTYPE_INT)) {
|
||||
@ -835,7 +835,7 @@ static void far_branch(int preoffset)
|
||||
struct number target;
|
||||
intval_t offset = 0; // dummy value, to not throw more errors than necessary
|
||||
|
||||
vcpu_read_pc(&pc);
|
||||
programcounter_read(&pc);
|
||||
get_int_arg(&target, TRUE);
|
||||
typesystem_want_addr(&target);
|
||||
if ((pc.ntype == NUMTYPE_INT) && (target.ntype == NUMTYPE_INT)) {
|
||||
|
102
src/output.c
102
src/output.c
@ -51,7 +51,6 @@ struct output {
|
||||
struct pseudopc {
|
||||
struct pseudopc *outer; // next layer (to be able to "unpseudopc" labels by more than one level)
|
||||
intval_t offset; // inner minus outer pc
|
||||
enum numtype ntype; // type of outer pc (INT/UNDEFINED)
|
||||
};
|
||||
static struct pseudopc outermost_pseudopc_context; // dummy struct when "!pseudopc" not in use
|
||||
static struct pseudopc *pseudopc_current_context = &outermost_pseudopc_context; // current struct
|
||||
@ -60,9 +59,14 @@ static struct pseudopc *pseudopc_current_context = &outermost_pseudopc_context;
|
||||
// variables
|
||||
static struct output default_output;
|
||||
static struct output *out = &default_output; // FIXME - never changes! is the ptr a preparation for "assembling several different parts in one go"?
|
||||
static int statement_size; // add to PC after statement
|
||||
static intval_t program_counter; // current program counter (pseudopc value)
|
||||
static enum numtype pc_ntype;
|
||||
static int statement_size; // added to program counter after each statement
|
||||
// "program_counter" differs substantially from "out->write_idx":
|
||||
// - "write_idx" changes after each byte, "program_counter" only changes between statements
|
||||
// (when "statement_size" gets added)
|
||||
// - of course the values can be wildly different because of offset assembly
|
||||
// - because "!pseudopc" can be nested and we need all offsets separately for
|
||||
// the '&' operator anyway, we do not keep a total offset.
|
||||
|
||||
// report binary output
|
||||
static void report_binary(char value)
|
||||
@ -109,9 +113,11 @@ static void border_crossed(int current_offset)
|
||||
}
|
||||
|
||||
|
||||
// function ptr to write byte into output buffer (might point to real fn or error trigger)
|
||||
// function ptr to write byte into output buffer (points to real_output or no_output)
|
||||
void (*output_byte)(intval_t byte);
|
||||
|
||||
// test for "has user set program counter yet?"
|
||||
#define PC_NOT_SET (program_counter == NO_VALUE_GIVEN)
|
||||
|
||||
// send low byte to output buffer and remember to later increase program counter
|
||||
static void real_output(intval_t byte)
|
||||
@ -143,13 +149,13 @@ static void real_output(intval_t byte)
|
||||
static void complain_and_use_dummy_pc(void)
|
||||
{
|
||||
Throw_error(exception_pc_undefined);
|
||||
vcpu_set_pc(cpu_current_type->dummy_pc, 0); // 0 = no flags
|
||||
programcounter_set(cpu_current_type->dummy_pc, 0); // 0 = no flags
|
||||
}
|
||||
|
||||
// throw error (pc undefined) and use fake pc from now on
|
||||
static void no_output(intval_t byte)
|
||||
{
|
||||
complain_and_use_dummy_pc();
|
||||
complain_and_use_dummy_pc(); // this lets output_byte point to the real_output
|
||||
output_byte(byte); // try again
|
||||
}
|
||||
|
||||
@ -168,7 +174,7 @@ void output_skip(int size)
|
||||
}
|
||||
|
||||
// check whether ptr undefined
|
||||
if (output_byte == no_output)
|
||||
if (PC_NOT_SET)
|
||||
complain_and_use_dummy_pc();
|
||||
|
||||
// CAUTION - there are two copies of these checks!
|
||||
@ -192,14 +198,14 @@ void output_skip(int size)
|
||||
void outbuf_set_outfile_start(void)
|
||||
{
|
||||
// check whether ptr undefined
|
||||
if (output_byte == no_output)
|
||||
if (PC_NOT_SET)
|
||||
complain_and_use_dummy_pc();
|
||||
out->forced_start_idx = out->write_idx;
|
||||
}
|
||||
void outbuf_set_outfile_limit(void)
|
||||
{
|
||||
// check whether ptr undefined
|
||||
if (output_byte == no_output)
|
||||
if (PC_NOT_SET)
|
||||
complain_and_use_dummy_pc();
|
||||
out->forced_limit_idx = out->write_idx;
|
||||
}
|
||||
@ -254,7 +260,7 @@ static void check_segment(intval_t new_pc)
|
||||
}
|
||||
|
||||
|
||||
// init structs
|
||||
// called once on startup, inits structs
|
||||
void output_init(void)
|
||||
{
|
||||
// init ring list of segments (FIXME - move to passinit)
|
||||
@ -265,7 +271,7 @@ void output_init(void)
|
||||
}
|
||||
|
||||
|
||||
// clear segment list and disable output
|
||||
// called before each pass, clears segment list and disables output
|
||||
void output_passinit(void)
|
||||
{
|
||||
//struct segment *temp;
|
||||
@ -294,9 +300,8 @@ void output_passinit(void)
|
||||
} else {
|
||||
out->buffer = NULL;
|
||||
}
|
||||
// FIXME - this should be NO_VALUE_GIVEN, but then pseudopc offset would be off, and
|
||||
// we cannot use NO_VALUE_GIVEN for pc as long as setting pc uses diff to old value!
|
||||
out->write_idx = 0; // ...so we set it to the same value as pc on pass init!
|
||||
// program counter has not been set yet, so the write index is also unknown:
|
||||
out->write_idx = NO_VALUE_GIVEN; // must be same value as pc on pass init!
|
||||
// invalidate start and end (first byte actually written will fix them)
|
||||
out->lowest_written = OUTBUF_MAXSIZE; // FIXME - add code so OUTBUF_MAXSIZE-1 is a hard limit!
|
||||
out->highest_written = -1;
|
||||
@ -321,31 +326,21 @@ void output_passinit(void)
|
||||
// no "encryption":
|
||||
out->xor = 0;
|
||||
// needed size of buffer will be calculated at end of pass, so
|
||||
//out->needed_bufsize = do not init
|
||||
//out->needed_bufsize = do not overwrite result of previous pass
|
||||
|
||||
// deactivate output - any byte written will trigger error:
|
||||
output_byte = no_output;
|
||||
|
||||
//vcpu stuff:
|
||||
pc_ntype = NUMTYPE_UNDEFINED; // not defined yet
|
||||
// FIXME - number type is "undefined", but still the intval 0 below will
|
||||
// be used to calculate diff when pc is first set.
|
||||
program_counter = 0; // same as output's write_idx on pass init
|
||||
// program counter stuff:
|
||||
program_counter = NO_VALUE_GIVEN; // must be same value as write_idx on pass init!
|
||||
statement_size = 0; // increase PC by this at end of statement
|
||||
|
||||
// pseudopc stuff:
|
||||
// init dummy pseudopc struct
|
||||
outermost_pseudopc_context.outer = NULL;
|
||||
outermost_pseudopc_context.offset = 0;
|
||||
outermost_pseudopc_context.ntype = NUMTYPE_UNDEFINED;
|
||||
// and use it:
|
||||
pseudopc_current_context = &outermost_pseudopc_context;
|
||||
|
||||
// this was moved over from caller - does it make sense to merge into some if/else?
|
||||
|
||||
// if start address was given on command line, use it:
|
||||
if (config.initial_pc != NO_VALUE_GIVEN)
|
||||
vcpu_set_pc(config.initial_pc, 0); // 0 -> no segment flags
|
||||
}
|
||||
|
||||
|
||||
@ -384,12 +379,12 @@ static void end_segment(void)
|
||||
}
|
||||
|
||||
|
||||
// make sure last code segment is closed
|
||||
// (this gets called exactly once at the end of each pass, as opposed to
|
||||
// the static fn "end_segment" which is also called when a new segment starts)
|
||||
// called after each pass, closes last code segment and calculates outbuffer size
|
||||
void output_endofpass(void)
|
||||
{
|
||||
// properly finalize previous segment (link to list, announce)
|
||||
end_segment();
|
||||
|
||||
// calculate size of output buffer
|
||||
if (out->highest_written >= out->lowest_written) {
|
||||
out->needed_bufsize = out->highest_written + 1;
|
||||
@ -407,7 +402,7 @@ static void start_segment(intval_t address_change, bits segment_flags)
|
||||
end_segment();
|
||||
|
||||
// calculate start of new segment
|
||||
out->write_idx = (out->write_idx + address_change);
|
||||
out->write_idx = out->write_idx + address_change;
|
||||
if (out->write_idx < 0) {
|
||||
Throw_serious_error("Tried to write to negative addresses.");
|
||||
} else if (out->write_idx >= OUTBUF_MAXSIZE) {
|
||||
@ -437,28 +432,33 @@ void output_set_xor(char xor)
|
||||
}
|
||||
|
||||
|
||||
// set program counter to defined value
|
||||
// if start address was given on command line, main loop will call this before each pass.
|
||||
// set program counter to defined value -> start a new segment.
|
||||
// this will in turn set the outbuf index according to the current pseudopc offset.
|
||||
// if start address was given on command line, this will be called at the start of each pass.
|
||||
// in addition to that, it will be called on each "*= VALUE".
|
||||
void vcpu_set_pc(intval_t new_pc, bits segment_flags)
|
||||
void programcounter_set(intval_t new_pc, bits segment_flags)
|
||||
{
|
||||
intval_t pc_change;
|
||||
|
||||
pc_change = new_pc - program_counter;
|
||||
program_counter = new_pc; // FIXME - oversized values are accepted without error and will be wrapped at end of statement!
|
||||
pc_ntype = NUMTYPE_INT;
|
||||
program_counter = new_pc;
|
||||
// now tell output buffer to start a new segment
|
||||
start_segment(pc_change, segment_flags);
|
||||
}
|
||||
|
||||
|
||||
// get program counter
|
||||
void vcpu_read_pc(struct number *target)
|
||||
// read program counter
|
||||
void programcounter_read(struct number *target)
|
||||
{
|
||||
target->ntype = pc_ntype;
|
||||
// check whether ptr undefined
|
||||
if (PC_NOT_SET) {
|
||||
target->ntype = NUMTYPE_UNDEFINED;
|
||||
} else {
|
||||
target->ntype = NUMTYPE_INT;
|
||||
}
|
||||
target->flags = 0; // FIXME - if defined, check for FITS_BYTE etc.? use pc_flags?
|
||||
target->val.intval = program_counter;
|
||||
target->addr_refs = 1; // yes, PC counts as address
|
||||
target->addr_refs = 1; // program counter is an address
|
||||
}
|
||||
|
||||
|
||||
@ -474,6 +474,10 @@ void output_end_statement(void)
|
||||
{
|
||||
program_counter += statement_size;
|
||||
statement_size = 0; // reset
|
||||
// we could check if we overran cpu's address space here and maybe throw
|
||||
// an error, but then the user would have to add "!pseudopc" blocks even
|
||||
// if they just wanted to re-cut some existing cartridge file using
|
||||
// "!binary", without using any assembler mnemonics.
|
||||
}
|
||||
|
||||
|
||||
@ -525,25 +529,20 @@ void pseudopc_start(struct number *new_pc)
|
||||
struct pseudopc *new_context;
|
||||
|
||||
// check whether ptr undefined
|
||||
if (output_byte == no_output)
|
||||
if (PC_NOT_SET)
|
||||
complain_and_use_dummy_pc();
|
||||
|
||||
new_context = safe_malloc(sizeof(*new_context)); // create new struct (this must never be freed, as it gets linked to labels!)
|
||||
new_context->outer = pseudopc_current_context; // let it point to previous one
|
||||
pseudopc_current_context = new_context; // make it the current one
|
||||
|
||||
new_context->ntype = pc_ntype;
|
||||
new_context->offset = new_pc->val.intval - program_counter;
|
||||
program_counter = new_pc->val.intval;
|
||||
pc_ntype = NUMTYPE_INT;
|
||||
new_context->offset = new_pc->val.intval - program_counter; // remember offset
|
||||
pseudopc_current_context = new_context; // make new struct the current one
|
||||
program_counter = new_pc->val.intval; // set new pc
|
||||
//new: pc_flags = new_pc->flags & (NUMBER_IS_DEFINED | NUMBER_EVER_UNDEFINED);
|
||||
}
|
||||
// end offset assembly
|
||||
void pseudopc_end(void)
|
||||
{
|
||||
program_counter = program_counter - pseudopc_current_context->offset;
|
||||
// FIXME - if pc can wrap around, then we should have fixed the offset!
|
||||
pc_ntype = pseudopc_current_context->ntype;
|
||||
program_counter = program_counter - pseudopc_current_context->offset; // remove offset
|
||||
pseudopc_current_context = pseudopc_current_context->outer; // go back to outer block
|
||||
if (pseudopc_current_context == NULL)
|
||||
BUG("PseudoPCContext", 0);
|
||||
@ -564,8 +563,7 @@ int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned
|
||||
Throw_error("Un-pseudopc operator '&' has no !pseudopc context.");
|
||||
return 1; // error
|
||||
}
|
||||
// FIXME - in future, check both target and context for NUMTYPE_UNDEFINED!
|
||||
target->val.intval = target->val.intval - context->offset;
|
||||
target->val.intval = target->val.intval - context->offset; // remove offset
|
||||
context = context->outer;
|
||||
}
|
||||
return 0; // ok
|
||||
|
17
src/output.h
17
src/output.h
@ -19,12 +19,15 @@
|
||||
|
||||
// prototypes
|
||||
|
||||
// init structs (called once on startup)
|
||||
// called once on startup, inits structs
|
||||
extern void output_init(void);
|
||||
|
||||
// clear segment list and disable output (called on each pass)
|
||||
// called before each pass, clears segment list and disables output
|
||||
extern void output_passinit(void);
|
||||
|
||||
// called after each pass, closes last code segment and calculates outbuffer size
|
||||
extern void output_endofpass(void);
|
||||
|
||||
// skip over some bytes in output buffer without starting a new segment
|
||||
// (used by "!skip", and also called by "!binary" if really calling
|
||||
// output_byte would be a waste of time)
|
||||
@ -38,18 +41,16 @@ extern void (*output_byte)(intval_t);
|
||||
extern void outbuf_set_outfile_start(void);
|
||||
extern void outbuf_set_outfile_limit(void);
|
||||
|
||||
// make sure last code segment is closed
|
||||
extern void output_endofpass(void);
|
||||
|
||||
// get/set "encryption" byte
|
||||
extern char output_get_xor(void);
|
||||
extern void output_set_xor(char xor);
|
||||
|
||||
// set program counter to defined value (TODO - allow undefined!)
|
||||
extern void vcpu_set_pc(intval_t new_pc, bits segment_flags);
|
||||
// set program counter to defined value -> start a new segment.
|
||||
// this will in turn set the outbuf index according to the current pseudopc offset.
|
||||
extern void programcounter_set(intval_t new_pc, bits segment_flags);
|
||||
|
||||
// get program counter
|
||||
extern void vcpu_read_pc(struct number *target);
|
||||
extern void programcounter_read(struct number *target);
|
||||
|
||||
// get size of current statement (until now) - needed for "!bin" verbose output
|
||||
extern int output_get_statement_size(void);
|
||||
|
@ -619,7 +619,6 @@ static enum eos po_fill(void)
|
||||
|
||||
// skip over some bytes in output without starting a new segment.
|
||||
// in contrast to "*=*+AMOUNT", "!skip AMOUNT" does not start a new segment.
|
||||
// (...and it will be needed in future for assemble-to-end-address)
|
||||
static enum eos po_skip(void) // now GotByte = illegal char
|
||||
{
|
||||
struct number amount;
|
||||
@ -655,7 +654,7 @@ static enum eos po_align(void)
|
||||
fill = cpu_current_type->default_align_value;
|
||||
|
||||
// make sure PC is defined
|
||||
vcpu_read_pc(&pc);
|
||||
programcounter_read(&pc);
|
||||
if (pc.ntype == NUMTYPE_UNDEFINED) {
|
||||
Throw_error(exception_pc_undefined);
|
||||
return SKIP_REMAINDER;
|
||||
@ -1230,7 +1229,7 @@ static enum eos tracewatch(boolean enter_monitor)
|
||||
struct number pc;
|
||||
bits flags = 0;
|
||||
|
||||
vcpu_read_pc(&pc);
|
||||
programcounter_read(&pc);
|
||||
SKIPSPACE();
|
||||
// check for flags
|
||||
if (GotByte != CHAR_EOS) {
|
||||
@ -1580,7 +1579,7 @@ void notreallypo_setpc(void) // GotByte is '*'
|
||||
Throw_error("Program counter cannot be negative.");
|
||||
intresult.val.intval = cpu_current_type->dummy_pc;
|
||||
}
|
||||
vcpu_set_pc(intresult.val.intval, segment_flags);
|
||||
programcounter_set(intresult.val.intval, segment_flags);
|
||||
|
||||
// if wanted, perform "!outfilestart":
|
||||
if (do_outfilestart)
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#define RELEASE "0.97" // update before release FIXME
|
||||
#define CODENAME "Zem" // update before release
|
||||
#define CHANGE_DATE "5 Aug" // update before release FIXME
|
||||
#define CHANGE_DATE "6 Aug" // update before release FIXME
|
||||
#define CHANGE_YEAR "2024" // update before release
|
||||
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
|
||||
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME
|
||||
|
Loading…
x
Reference in New Issue
Block a user