2016-12-28 20:32:00 +00:00
|
|
|
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
|
2020-04-26 16:24:34 +00:00
|
|
|
// Copyright (C) 1998-2020 Marco Baye
|
2012-02-27 21:14:46 +00:00
|
|
|
// Have a look at "acme.c" for further info
|
|
|
|
//
|
2014-12-03 22:18:06 +00:00
|
|
|
// pseudo opcode stuff
|
2014-12-22 00:47:52 +00:00
|
|
|
#include "pseudoopcodes.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2014-12-04 15:31:54 +00:00
|
|
|
#include "acme.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "alu.h"
|
|
|
|
#include "dynabuf.h"
|
2014-12-16 08:21:44 +00:00
|
|
|
#include "encoding.h"
|
2014-12-04 15:31:54 +00:00
|
|
|
#include "flow.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "input.h"
|
2014-12-04 15:31:54 +00:00
|
|
|
#include "macro.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "global.h"
|
|
|
|
#include "output.h"
|
2014-12-04 11:26:42 +00:00
|
|
|
#include "section.h"
|
2014-12-04 15:31:54 +00:00
|
|
|
#include "symbol.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "tree.h"
|
2014-06-02 00:47:46 +00:00
|
|
|
#include "typesystem.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
|
2014-12-22 00:47:52 +00:00
|
|
|
// different ways to handle end-of-statement:
|
|
|
|
enum eos {
|
|
|
|
SKIP_REMAINDER, // skip remainder of line - (after errors)
|
|
|
|
ENSURE_EOS, // make sure there's nothing left in statement
|
|
|
|
PARSE_REMAINDER, // parse what's left
|
|
|
|
AT_EOS_ANYWAY // actually, same as PARSE_REMAINDER
|
|
|
|
};
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// constants
|
|
|
|
static const char s_08[] = "08";
|
|
|
|
#define s_8 (s_08 + 1) // Yes, I know I'm sick
|
2014-12-04 15:31:54 +00:00
|
|
|
#define s_sl (s_asl + 1) // Yes, I know I'm sick
|
2014-12-16 08:21:44 +00:00
|
|
|
#define s_rl (s_brl + 1) // Yes, I know I'm sick
|
2020-05-06 11:40:06 +00:00
|
|
|
static const char exception_unknown_pseudo_opcode[] = "Unknown pseudo opcode.";
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
// variables
|
2014-12-22 00:47:52 +00:00
|
|
|
static struct ronode *pseudo_opcode_tree = NULL; // tree to hold pseudo opcodes
|
2014-12-03 22:18:06 +00:00
|
|
|
|
|
|
|
|
2020-05-08 09:43:52 +00:00
|
|
|
// this is not really a pseudo opcode, but similar enough to be put here:
|
2020-05-31 13:07:40 +00:00
|
|
|
// called when "*= EXPRESSION" is parsed, to set the program counter
|
2020-05-08 09:43:52 +00:00
|
|
|
void notreallypo_setpc(void) // GotByte is '*'
|
2014-12-03 22:18:06 +00:00
|
|
|
{
|
|
|
|
int segment_flags = 0;
|
2020-04-28 16:02:09 +00:00
|
|
|
struct number intresult;
|
2014-12-03 22:18:06 +00:00
|
|
|
|
2020-05-08 09:43:52 +00:00
|
|
|
// next non-space must be '='
|
|
|
|
NEXTANDSKIPSPACE();
|
|
|
|
if (GotByte != '=') {
|
|
|
|
Throw_error(exception_syntax);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetByte();
|
2015-06-14 23:16:23 +00:00
|
|
|
ALU_defined_int(&intresult); // read new address
|
2014-12-03 22:18:06 +00:00
|
|
|
// check for modifiers
|
|
|
|
while (Input_accept_comma()) {
|
|
|
|
// parse modifier. if no keyword given, give up
|
|
|
|
if (Input_read_and_lower_keyword() == 0)
|
2020-05-08 09:43:52 +00:00
|
|
|
goto fail;
|
2014-12-03 22:18:06 +00:00
|
|
|
|
|
|
|
if (strcmp(GlobalDynaBuf->buffer, "overlay") == 0) {
|
|
|
|
segment_flags |= SEGMENT_FLAG_OVERLAY;
|
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "invisible") == 0) {
|
|
|
|
segment_flags |= SEGMENT_FLAG_INVISIBLE;
|
2020-04-25 10:20:52 +00:00
|
|
|
/*TODO } else if (strcmp(GlobalDynaBuf->buffer, "limit") == 0) {
|
|
|
|
skip '='
|
|
|
|
read memory limit
|
2020-06-03 00:51:09 +00:00
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "stay" or "same" or something like that) == 0) {
|
|
|
|
mutually exclusive with all other arguments!
|
|
|
|
this would mean to keep all previous segment data,
|
|
|
|
so it could be used with "*=*-5" or "*=*+3"
|
2020-04-25 10:20:52 +00:00
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "name") == 0) {
|
|
|
|
skip '='
|
2020-05-01 21:01:23 +00:00
|
|
|
read segment name (quoted string!) */
|
2014-12-03 22:18:06 +00:00
|
|
|
} else {
|
2020-05-31 13:07:40 +00:00
|
|
|
Throw_error("Unknown \"*=\" segment modifier.");
|
2020-05-08 09:43:52 +00:00
|
|
|
goto fail;
|
2014-12-03 22:18:06 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-14 23:16:23 +00:00
|
|
|
vcpu_set_pc(intresult.val.intval, segment_flags);
|
2020-05-31 15:04:12 +00:00
|
|
|
// TODO - allow block syntax, so it is possible to put data "somewhere else" and then return to old position?
|
2020-05-08 09:43:52 +00:00
|
|
|
Input_ensure_EOS();
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
Input_skip_remainder();
|
2014-12-03 22:18:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// define default value for empty memory ("!initmem" pseudo opcode)
|
|
|
|
static enum eos po_initmem(void)
|
|
|
|
{
|
2020-04-28 16:02:09 +00:00
|
|
|
struct number intresult;
|
2014-12-03 22:18:06 +00:00
|
|
|
|
|
|
|
// ignore in all passes but in first
|
2020-05-02 10:40:10 +00:00
|
|
|
if (!FIRST_PASS)
|
2014-12-03 22:18:06 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
// get value
|
2015-06-14 23:16:23 +00:00
|
|
|
ALU_defined_int(&intresult);
|
|
|
|
if ((intresult.val.intval > 0xff) || (intresult.val.intval < -0x80))
|
2014-12-03 22:18:06 +00:00
|
|
|
Throw_error(exception_number_out_of_range);
|
2015-06-14 23:16:23 +00:00
|
|
|
if (output_initmem(intresult.val.intval & 0xff))
|
2014-12-03 22:18:06 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-22 22:55:36 +00:00
|
|
|
// change output "encryption" ("!xor" pseudo opcode)
|
|
|
|
static enum eos po_xor(void)
|
|
|
|
{
|
|
|
|
char old_value;
|
|
|
|
intval_t change;
|
|
|
|
|
|
|
|
old_value = output_get_xor();
|
2020-05-23 23:38:13 +00:00
|
|
|
ALU_any_int(&change);
|
2017-12-22 22:55:36 +00:00
|
|
|
if ((change > 0xff) || (change < -0x80)) {
|
|
|
|
Throw_error(exception_number_out_of_range);
|
|
|
|
change = 0;
|
|
|
|
}
|
|
|
|
output_set_xor(old_value ^ change);
|
|
|
|
// if there's a block, parse that and then restore old value!
|
|
|
|
if (Parse_optional_block())
|
|
|
|
output_set_xor(old_value);
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-03 22:18:06 +00:00
|
|
|
// select output file and format ("!to" pseudo opcode)
|
|
|
|
static enum eos po_to(void)
|
|
|
|
{
|
|
|
|
// bugfix: first read filename, *then* check for first pass.
|
|
|
|
// if skipping right away, quoted colons might be misinterpreted as EOS
|
|
|
|
// FIXME - fix the skipping code to handle quotes! :)
|
|
|
|
// "!sl" has been fixed as well
|
|
|
|
|
|
|
|
// read filename to global dynamic buffer
|
|
|
|
// if no file name given, exit (complaining will have been done)
|
2017-12-22 22:55:36 +00:00
|
|
|
if (Input_read_filename(FALSE, NULL))
|
2014-12-03 22:18:06 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
// only act upon this pseudo opcode in first pass
|
2020-05-02 10:40:10 +00:00
|
|
|
if (!FIRST_PASS)
|
2014-12-03 22:18:06 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
2016-02-21 12:58:22 +00:00
|
|
|
if (outputfile_set_filename())
|
2014-12-03 22:18:06 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
// select output format
|
|
|
|
// if no comma found, use default file format
|
|
|
|
if (Input_accept_comma() == FALSE) {
|
2016-02-21 12:58:22 +00:00
|
|
|
if (outputfile_prefer_cbm_format()) {
|
2020-05-31 15:04:12 +00:00
|
|
|
// output deprecation warning (unless user requests really old behaviour)
|
2020-05-31 20:55:38 +00:00
|
|
|
if (config.wanted_version >= VER_DEPRECATE_REALPC)
|
2020-05-31 15:04:12 +00:00
|
|
|
Throw_warning("Used \"!to\" without file format indicator. Defaulting to \"cbm\".");
|
2014-12-03 22:18:06 +00:00
|
|
|
}
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse output format name
|
|
|
|
// if no keyword given, give up
|
|
|
|
if (Input_read_and_lower_keyword() == 0)
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
2016-02-21 12:58:22 +00:00
|
|
|
if (outputfile_set_format()) {
|
2014-12-03 22:18:06 +00:00
|
|
|
// error occurred
|
|
|
|
Throw_error("Unknown output format.");
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
|
|
|
return ENSURE_EOS; // success
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
// helper function for !8, !16, !24 and !32 pseudo opcodes
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos iterate(void (*fn)(intval_t))
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-23 23:38:13 +00:00
|
|
|
intval_t value;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ALU_any_int(&value);
|
|
|
|
fn(value);
|
|
|
|
} while (Input_accept_comma());
|
2012-02-27 21:14:46 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert 8-bit values ("!08" / "!8" / "!by" / "!byte" pseudo opcode)
|
2014-12-05 17:46:48 +00:00
|
|
|
static enum eos po_byte(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-12-03 22:18:06 +00:00
|
|
|
return iterate(output_8);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert 16-bit values ("!16" / "!wo" / "!word" pseudo opcode)
|
2016-02-16 23:11:04 +00:00
|
|
|
static enum eos po_16(void)
|
|
|
|
{
|
|
|
|
return iterate((CPU_state.type->flags & CPUFLAG_ISBIGENDIAN) ? output_be16 : output_le16);
|
|
|
|
}
|
|
|
|
// Insert 16-bit values big-endian ("!be16" pseudo opcode)
|
|
|
|
static enum eos po_be16(void)
|
|
|
|
{
|
|
|
|
return iterate(output_be16);
|
|
|
|
}
|
|
|
|
// Insert 16-bit values little-endian ("!le16" pseudo opcode)
|
|
|
|
static enum eos po_le16(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-12-03 22:18:06 +00:00
|
|
|
return iterate(output_le16);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert 24-bit values ("!24" pseudo opcode)
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos po_24(void)
|
2016-02-16 23:11:04 +00:00
|
|
|
{
|
|
|
|
return iterate((CPU_state.type->flags & CPUFLAG_ISBIGENDIAN) ? output_be24 : output_le24);
|
|
|
|
}
|
|
|
|
// Insert 24-bit values big-endian ("!be24" pseudo opcode)
|
|
|
|
static enum eos po_be24(void)
|
|
|
|
{
|
|
|
|
return iterate(output_be24);
|
|
|
|
}
|
|
|
|
// Insert 24-bit values little-endian ("!le24" pseudo opcode)
|
|
|
|
static enum eos po_le24(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-12-03 22:18:06 +00:00
|
|
|
return iterate(output_le24);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert 32-bit values ("!32" pseudo opcode)
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos po_32(void)
|
2016-02-16 23:11:04 +00:00
|
|
|
{
|
|
|
|
return iterate((CPU_state.type->flags & CPUFLAG_ISBIGENDIAN) ? output_be32 : output_le32);
|
|
|
|
}
|
|
|
|
// Insert 32-bit values big-endian ("!be32" pseudo opcode)
|
|
|
|
static enum eos po_be32(void)
|
|
|
|
{
|
|
|
|
return iterate(output_be32);
|
|
|
|
}
|
|
|
|
// Insert 32-bit values little-endian ("!le32" pseudo opcode)
|
|
|
|
static enum eos po_le32(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-12-03 22:18:06 +00:00
|
|
|
return iterate(output_le32);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-21 19:59:56 +00:00
|
|
|
// Insert bytes given as pairs of hex digits (helper for source code generators)
|
|
|
|
static enum eos po_hex(void) // now GotByte = illegal char
|
|
|
|
{
|
|
|
|
int digits = 0;
|
|
|
|
unsigned char byte = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (digits == 2) {
|
|
|
|
Output_byte(byte);
|
|
|
|
digits = 0;
|
|
|
|
byte = 0;
|
|
|
|
}
|
|
|
|
if (GotByte >= '0' && GotByte <= '9') {
|
|
|
|
byte = (byte << 4) | (GotByte - '0');
|
|
|
|
++digits;
|
|
|
|
GetByte();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (GotByte >= 'a' && GotByte <= 'f') {
|
|
|
|
byte = (byte << 4) | (GotByte - 'a' + 10);
|
|
|
|
++digits;
|
|
|
|
GetByte();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (GotByte >= 'A' && GotByte <= 'F') {
|
|
|
|
byte = (byte << 4) | (GotByte - 'A' + 10);
|
|
|
|
++digits;
|
|
|
|
GetByte();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// if we're here, the current character is not a hex digit,
|
2017-11-03 15:14:55 +00:00
|
|
|
// which is only allowed outside of pairs:
|
2017-10-21 19:59:56 +00:00
|
|
|
if (digits == 1) {
|
2017-11-03 15:14:55 +00:00
|
|
|
Throw_error("Hex digits are not given in pairs.");
|
2017-10-21 19:59:56 +00:00
|
|
|
return SKIP_REMAINDER; // error exit
|
|
|
|
}
|
|
|
|
switch (GotByte) {
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
GetByte(); // spaces and tabs are ignored (maybe add commas, too?)
|
|
|
|
continue;
|
|
|
|
case CHAR_EOS:
|
|
|
|
return AT_EOS_ANYWAY; // normal exit
|
|
|
|
default:
|
|
|
|
Throw_error(exception_syntax); // all other characters are errors
|
|
|
|
return SKIP_REMAINDER; // error exit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-16 08:21:44 +00:00
|
|
|
// "!cbm" pseudo opcode (now obsolete)
|
2020-05-29 23:33:03 +00:00
|
|
|
static enum eos po_cbm(void)
|
2014-12-16 08:21:44 +00:00
|
|
|
{
|
2020-05-29 23:33:03 +00:00
|
|
|
if (config.wanted_version >= VER_DISABLED_OBSOLETE_STUFF) {
|
|
|
|
Throw_error("\"!cbm\" is obsolete; use \"!ct pet\" instead.");
|
|
|
|
} else {
|
|
|
|
encoder_current = &encoder_pet;
|
|
|
|
Throw_first_pass_warning("\"!cbm\" is deprecated; use \"!ct pet\" instead.");
|
|
|
|
}
|
2014-12-16 08:21:44 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// read encoding table from file
|
2017-12-22 22:55:36 +00:00
|
|
|
static enum eos user_defined_encoding(FILE *stream)
|
2014-12-16 08:21:44 +00:00
|
|
|
{
|
|
|
|
char local_table[256],
|
|
|
|
*buffered_table = encoding_loaded_table;
|
|
|
|
const struct encoder *buffered_encoder = encoder_current;
|
|
|
|
|
2017-12-22 22:55:36 +00:00
|
|
|
if (stream) {
|
|
|
|
encoding_load_from_file(local_table, stream);
|
|
|
|
fclose(stream);
|
|
|
|
}
|
2014-12-16 08:21:44 +00:00
|
|
|
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)
|
|
|
|
{
|
2020-05-06 11:40:06 +00:00
|
|
|
boolean uses_lib;
|
2017-12-22 22:55:36 +00:00
|
|
|
FILE *stream;
|
|
|
|
|
2014-12-16 08:21:44 +00:00
|
|
|
if ((GotByte == '<') || (GotByte == '"')) {
|
2020-05-25 23:12:19 +00:00
|
|
|
// encoding table from file
|
2017-12-22 22:55:36 +00:00
|
|
|
if (Input_read_filename(TRUE, &uses_lib))
|
2020-05-25 23:12:19 +00:00
|
|
|
return SKIP_REMAINDER; // missing or unterminated file name
|
2017-12-22 22:55:36 +00:00
|
|
|
|
|
|
|
stream = includepaths_open_ro(uses_lib);
|
|
|
|
return user_defined_encoding(stream);
|
2014-12-16 08:21:44 +00:00
|
|
|
} else {
|
2020-05-25 23:12:19 +00:00
|
|
|
// one of the pre-defined encodings
|
2014-12-16 08:21:44 +00:00
|
|
|
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
|
2020-05-19 13:04:39 +00:00
|
|
|
int offset;
|
2020-05-23 23:38:13 +00:00
|
|
|
intval_t value;
|
2014-12-16 08:21:44 +00:00
|
|
|
|
|
|
|
// make given encoder the current one (for ALU-parsed values)
|
|
|
|
encoder_current = inner_encoder;
|
|
|
|
do {
|
2020-05-27 17:32:48 +00:00
|
|
|
if (GotByte == '"') { // FIXME - add "&& (config.wanted_version < VER_BACKSLASHESCAPING)", otherwise stuff like "string"[index] will not work
|
2020-05-19 13:04:39 +00:00
|
|
|
DYNABUF_CLEAR(GlobalDynaBuf);
|
|
|
|
if (Input_quoted_to_dynabuf('"'))
|
|
|
|
return SKIP_REMAINDER; // unterminated or escaping error
|
2014-12-16 08:21:44 +00:00
|
|
|
|
2020-05-19 13:04:39 +00:00
|
|
|
// eat closing quote
|
2014-12-16 08:21:44 +00:00
|
|
|
GetByte();
|
2020-05-19 13:04:39 +00:00
|
|
|
// now convert to unescaped version
|
2020-05-19 16:28:36 +00:00
|
|
|
if (Input_unescape_dynabuf(0))
|
2020-05-19 13:04:39 +00:00
|
|
|
return SKIP_REMAINDER; // escaping error
|
|
|
|
|
|
|
|
// send characters
|
|
|
|
for (offset = 0; offset < GlobalDynaBuf->size; ++offset)
|
|
|
|
output_8(xor ^ encoding_encode_char(GLOBALDYNABUF_CURRENT[offset]));
|
2014-12-16 08:21:44 +00:00
|
|
|
} else {
|
|
|
|
// Parse value. No problems with single characters
|
|
|
|
// because the current encoding is
|
|
|
|
// temporarily set to the given one.
|
2020-05-23 23:38:13 +00:00
|
|
|
ALU_any_int(&value);
|
|
|
|
output_8(value);
|
2020-05-25 23:12:19 +00:00
|
|
|
// FIXME - call ALU_any_result(FLOAT2INT) instead and support lists and strings:
|
|
|
|
// for lists, call some list_iter() fn to handle components
|
|
|
|
// for strings, do not forget to XOR!
|
2014-12-16 08:21:44 +00:00
|
|
|
}
|
|
|
|
} 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)
|
|
|
|
{
|
2020-05-23 23:38:13 +00:00
|
|
|
intval_t xor;
|
2014-12-16 08:21:44 +00:00
|
|
|
|
2020-05-23 23:38:13 +00:00
|
|
|
ALU_any_int(&xor);
|
2014-12-16 08:21:44 +00:00
|
|
|
if (Input_accept_comma() == FALSE) {
|
|
|
|
Throw_error(exception_syntax);
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
2020-05-23 23:38:13 +00:00
|
|
|
return encode_string(&encoder_scr, xor);
|
2014-12-16 08:21:44 +00:00
|
|
|
}
|
|
|
|
|
2014-12-03 22:18:06 +00:00
|
|
|
// 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)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-06 11:40:06 +00:00
|
|
|
boolean uses_lib;
|
2017-12-22 22:55:36 +00:00
|
|
|
FILE *stream;
|
2012-02-27 21:14:46 +00:00
|
|
|
int byte;
|
2020-05-08 13:22:15 +00:00
|
|
|
struct number size,
|
|
|
|
skip;
|
|
|
|
|
|
|
|
size.val.intval = -1; // means "not given" => "until EOF"
|
|
|
|
skip.val.intval = 0;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
// if file name is missing, don't bother continuing
|
2017-12-22 22:55:36 +00:00
|
|
|
if (Input_read_filename(TRUE, &uses_lib))
|
2012-02-27 21:14:46 +00:00
|
|
|
return SKIP_REMAINDER;
|
2017-12-22 22:55:36 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// try to open file
|
2017-12-22 22:55:36 +00:00
|
|
|
stream = includepaths_open_ro(uses_lib);
|
|
|
|
if (stream == NULL)
|
2012-02-27 21:14:46 +00:00
|
|
|
return SKIP_REMAINDER;
|
2017-12-22 22:55:36 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// read optional arguments
|
|
|
|
if (Input_accept_comma()) {
|
2020-05-08 13:22:15 +00:00
|
|
|
// any size given?
|
|
|
|
if ((GotByte != ',') && (GotByte != CHAR_EOS)) {
|
|
|
|
// then parse it
|
|
|
|
ALU_defined_int(&size);
|
|
|
|
if (size.val.intval < 0)
|
|
|
|
Throw_serious_error(exception_negative_size);
|
|
|
|
}
|
|
|
|
// more?
|
|
|
|
if (Input_accept_comma()) {
|
|
|
|
// any skip given?
|
|
|
|
if (GotByte != CHAR_EOS) {
|
|
|
|
// then parse it
|
|
|
|
ALU_defined_int(&skip);
|
|
|
|
}
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
// check whether including is a waste of time
|
2017-10-21 19:59:56 +00:00
|
|
|
// FIXME - future changes ("several-projects-at-once")
|
|
|
|
// may be incompatible with this!
|
2020-05-08 13:22:15 +00:00
|
|
|
if ((size.val.intval >= 0) && (pass.undefined_count || pass.error_count)) {
|
|
|
|
//if ((size.val.intval >= 0) && (pass.needvalue_count || pass.error_count)) { FIXME - use!
|
|
|
|
output_skip(size.val.intval); // really including is useless anyway
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
|
|
|
// really insert file
|
2020-05-08 13:22:15 +00:00
|
|
|
fseek(stream, skip.val.intval, SEEK_SET); // set read pointer
|
2012-02-27 21:14:46 +00:00
|
|
|
// if "size" non-negative, read "size" bytes.
|
|
|
|
// otherwise, read until EOF.
|
2020-05-08 13:22:15 +00:00
|
|
|
while (size.val.intval != 0) {
|
2017-12-22 22:55:36 +00:00
|
|
|
byte = getc(stream);
|
2012-02-27 21:14:46 +00:00
|
|
|
if (byte == EOF)
|
|
|
|
break;
|
|
|
|
Output_byte(byte);
|
2020-05-08 13:22:15 +00:00
|
|
|
--size.val.intval;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
// if more should have been read, warn and add padding
|
2020-05-08 13:22:15 +00:00
|
|
|
if (size.val.intval > 0) {
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_warning("Padding with zeroes.");
|
|
|
|
do
|
|
|
|
Output_byte(0);
|
2020-05-08 13:22:15 +00:00
|
|
|
while (--size.val.intval);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-22 22:55:36 +00:00
|
|
|
fclose(stream);
|
2012-02-27 21:14:46 +00:00
|
|
|
// if verbose, produce some output
|
2020-05-02 10:40:10 +00:00
|
|
|
if (FIRST_PASS && (config.process_verbosity > 1)) {
|
2014-03-11 14:22:32 +00:00
|
|
|
int amount = vcpu_get_statement_size();
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
printf("Loaded %d (0x%04x) bytes from file offset %ld (0x%04lx).\n",
|
2020-05-08 13:22:15 +00:00
|
|
|
amount, amount, skip.val.intval, skip.val.intval);
|
2014-03-11 12:07:11 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-16 08:21:44 +00:00
|
|
|
// reserve space by sending bytes of given value ("!fi" / "!fill" pseudo opcode)
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos po_fill(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-04-28 16:02:09 +00:00
|
|
|
struct number sizeresult;
|
2015-06-14 23:16:23 +00:00
|
|
|
intval_t fill = FILLVALUE_FILL;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2015-06-14 23:16:23 +00:00
|
|
|
ALU_defined_int(&sizeresult); // FIXME - forbid addresses!
|
2012-02-27 21:14:46 +00:00
|
|
|
if (Input_accept_comma())
|
2020-05-23 23:38:13 +00:00
|
|
|
ALU_any_int(&fill); // FIXME - forbid addresses!
|
2015-06-14 23:16:23 +00:00
|
|
|
while (sizeresult.val.intval--)
|
2014-12-03 22:18:06 +00:00
|
|
|
output_8(fill);
|
2012-02-27 21:14:46 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-29 23:29:07 +00:00
|
|
|
// skip over some bytes in output without starting a new segment.
|
2017-10-21 19:59:56 +00:00
|
|
|
// 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
|
|
|
|
{
|
2020-04-28 16:02:09 +00:00
|
|
|
struct number amount;
|
2017-10-21 19:59:56 +00:00
|
|
|
|
|
|
|
ALU_defined_int(&amount); // FIXME - forbid addresses!
|
|
|
|
if (amount.val.intval < 0)
|
2020-05-06 11:40:06 +00:00
|
|
|
Throw_serious_error(exception_negative_size); // TODO - allow this?
|
2017-10-21 19:59:56 +00:00
|
|
|
else
|
|
|
|
output_skip(amount.val.intval);
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-16 08:21:44 +00:00
|
|
|
// insert byte until PC fits condition
|
|
|
|
static enum eos po_align(void)
|
|
|
|
{
|
2020-04-28 16:02:09 +00:00
|
|
|
struct number andresult,
|
2015-06-14 23:16:23 +00:00
|
|
|
equalresult;
|
2020-05-22 20:55:36 +00:00
|
|
|
intval_t fill;
|
|
|
|
struct number pc;
|
2014-12-16 08:21:44 +00:00
|
|
|
|
2020-04-25 10:20:52 +00:00
|
|
|
// TODO:
|
|
|
|
// now: !align ANDVALUE, EQUALVALUE [,FILLVALUE]
|
|
|
|
// new: !align BLOCKSIZE
|
|
|
|
// ...where block size must be a power of two
|
2015-06-14 23:16:23 +00:00
|
|
|
ALU_defined_int(&andresult); // FIXME - forbid addresses!
|
2014-12-16 08:21:44 +00:00
|
|
|
if (!Input_accept_comma())
|
|
|
|
Throw_error(exception_syntax);
|
2015-06-14 23:16:23 +00:00
|
|
|
ALU_defined_int(&equalresult); // ...allow addresses (unlikely, but possible)
|
2014-12-16 08:21:44 +00:00
|
|
|
if (Input_accept_comma())
|
2020-05-23 23:38:13 +00:00
|
|
|
ALU_any_int(&fill);
|
2014-12-16 08:21:44 +00:00
|
|
|
else
|
|
|
|
fill = CPU_state.type->default_align_value;
|
2020-05-22 20:55:36 +00:00
|
|
|
|
|
|
|
// make sure PC is defined
|
|
|
|
vcpu_read_pc(&pc);
|
|
|
|
if (!(pc.flags & NUMBER_IS_DEFINED)) {
|
|
|
|
Throw_error(exception_pc_undefined);
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((pc.val.intval++ & andresult.val.intval) != equalresult.val.intval)
|
2014-12-16 08:21:44 +00:00
|
|
|
output_8(fill);
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:33:03 +00:00
|
|
|
|
|
|
|
// not using a block is no longer allowed
|
|
|
|
static void old_offset_assembly(void)
|
|
|
|
{
|
2020-05-31 15:04:12 +00:00
|
|
|
// really old versions allowed it
|
|
|
|
if (config.wanted_version < VER_DEPRECATE_REALPC)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// then it was deprecated
|
|
|
|
if (config.wanted_version < VER_DISABLED_OBSOLETE_STUFF) {
|
2020-05-29 23:33:03 +00:00
|
|
|
Throw_first_pass_warning("\"!pseudopc/!realpc\" is deprecated; use \"!pseudopc {}\" instead.");
|
2020-05-31 15:04:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now it's obsolete
|
|
|
|
Throw_error("\"!pseudopc/!realpc\" is obsolete; use \"!pseudopc {}\" instead."); // FIXME - amend msg, tell user how to use old behaviour!
|
2020-05-29 23:33:03 +00:00
|
|
|
}
|
|
|
|
|
2014-12-16 08:21:44 +00:00
|
|
|
// start offset assembly
|
|
|
|
// 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)
|
|
|
|
{
|
2020-05-22 20:55:36 +00:00
|
|
|
struct number new_pc;
|
2014-12-16 08:21:44 +00:00
|
|
|
|
2020-05-22 20:55:36 +00:00
|
|
|
// get new value
|
|
|
|
ALU_defined_int(&new_pc); // FIXME - allow for undefined! (complaining about non-addresses would be logical, but annoying)
|
2020-05-24 19:52:10 +00:00
|
|
|
/* TODO - add this. check if code can be shared with "*="!
|
|
|
|
// check for modifiers
|
|
|
|
while (Input_accept_comma()) {
|
|
|
|
// parse modifier. if no keyword given, give up
|
|
|
|
if (Input_read_and_lower_keyword() == 0)
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
if (strcmp(GlobalDynaBuf->buffer, "limit") == 0) {
|
|
|
|
skip '='
|
|
|
|
read memory limit
|
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "name") == 0) {
|
|
|
|
skip '='
|
|
|
|
read segment name (quoted string!)
|
|
|
|
} else {
|
|
|
|
Throw_error("Unknown !pseudopc segment modifier.");
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
2020-05-28 18:40:40 +00:00
|
|
|
pseudopc_start(&new_pc);
|
2014-12-16 08:21:44 +00:00
|
|
|
// if there's a block, parse that and then restore old value!
|
|
|
|
if (Parse_optional_block()) {
|
2020-05-31 15:04:12 +00:00
|
|
|
pseudopc_end(); // restore old state
|
2014-12-16 08:21:44 +00:00
|
|
|
} else {
|
2020-05-29 23:33:03 +00:00
|
|
|
old_offset_assembly();
|
2014-12-16 08:21:44 +00:00
|
|
|
}
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// "!realpc" pseudo opcode (now obsolete)
|
2020-05-29 23:33:03 +00:00
|
|
|
static enum eos po_realpc(void)
|
2014-12-16 08:21:44 +00:00
|
|
|
{
|
2020-05-29 23:33:03 +00:00
|
|
|
old_offset_assembly();
|
2020-06-01 17:49:46 +00:00
|
|
|
pseudopc_end_all(); // restore outermost state
|
2014-12-16 08:21:44 +00:00
|
|
|
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.
|
2020-05-06 11:40:06 +00:00
|
|
|
static enum eos set_register_length(boolean *var, boolean make_long)
|
2014-12-16 08:21:44 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-02 00:47:46 +00:00
|
|
|
// force explicit label definitions to set "address" flag ("!addr"). Has to be re-entrant.
|
2014-12-05 17:46:48 +00:00
|
|
|
static enum eos po_address(void) // now GotByte = illegal char
|
2014-06-02 00:47:46 +00:00
|
|
|
{
|
|
|
|
SKIPSPACE();
|
|
|
|
if (GotByte == CHAR_SOB) {
|
|
|
|
typesystem_force_address_block();
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
typesystem_force_address_statement(TRUE);
|
|
|
|
return PARSE_REMAINDER;
|
|
|
|
}
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
|
2017-10-21 19:59:56 +00:00
|
|
|
#if 0
|
2020-05-08 13:22:15 +00:00
|
|
|
// enumerate constants ("!enum")
|
2017-10-21 19:59:56 +00:00
|
|
|
static enum eos po_enum(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-08 13:22:15 +00:00
|
|
|
struct number step;
|
2017-10-21 19:59:56 +00:00
|
|
|
|
2020-05-08 13:22:15 +00:00
|
|
|
step.val.intval = 1;
|
|
|
|
ALU_defined_int(&step);
|
2017-10-21 20:23:22 +00:00
|
|
|
Throw_serious_error("Not yet"); // FIXME
|
2017-10-21 19:59:56 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
// (re)set symbol
|
|
|
|
static enum eos po_set(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-06-05 01:11:51 +00:00
|
|
|
scope_t scope;
|
|
|
|
int force_bit;
|
2014-12-04 15:31:54 +00:00
|
|
|
|
2016-08-05 09:59:07 +00:00
|
|
|
if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before
|
2020-06-05 01:11:51 +00:00
|
|
|
return SKIP_REMAINDER; // zero length
|
2014-12-04 15:31:54 +00:00
|
|
|
|
|
|
|
force_bit = Input_get_force_bit(); // skips spaces after
|
|
|
|
if (GotByte != '=') {
|
|
|
|
Throw_error(exception_syntax);
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
|
|
|
|
2020-06-05 01:11:51 +00:00
|
|
|
parse_assignment(scope, force_bit, TRUE);
|
2014-12-04 15:31:54 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// set file name for symbol list
|
2014-12-05 17:46:48 +00:00
|
|
|
static enum eos po_symbollist(void)
|
2014-12-04 15:31:54 +00:00
|
|
|
{
|
|
|
|
// bugfix: first read filename, *then* check for first pass.
|
|
|
|
// if skipping right away, quoted colons might be misinterpreted as EOS
|
|
|
|
// FIXME - why not just fix the skipping code to handle quotes? :)
|
|
|
|
// "!to" has been fixed as well
|
|
|
|
|
|
|
|
// read filename to global dynamic buffer
|
|
|
|
// if no file name given, exit (complaining will have been done)
|
2017-12-22 22:55:36 +00:00
|
|
|
if (Input_read_filename(FALSE, NULL))
|
2014-12-04 15:31:54 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
// only process this pseudo opcode in first pass
|
2020-05-02 10:40:10 +00:00
|
|
|
if (!FIRST_PASS)
|
2014-12-04 15:31:54 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
// if symbol list file name already set, complain and exit
|
|
|
|
if (symbollist_filename) {
|
|
|
|
Throw_warning("Symbol list file name already chosen.");
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get malloc'd copy of filename
|
|
|
|
symbollist_filename = DynaBuf_get_copy(GlobalDynaBuf);
|
|
|
|
// ensure there's no garbage at end of line
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
2014-12-03 22:18:06 +00:00
|
|
|
|
2014-12-04 11:26:42 +00:00
|
|
|
// switch to new zone ("!zone" or "!zn"). has to be re-entrant.
|
|
|
|
static enum eos po_zone(void)
|
|
|
|
{
|
|
|
|
struct section entry_values; // buffer for outer zone
|
|
|
|
char *new_title;
|
|
|
|
int allocated;
|
|
|
|
|
|
|
|
// remember everything about current structure
|
2016-08-05 09:59:07 +00:00
|
|
|
entry_values = *section_now;
|
2014-12-04 11:26:42 +00:00
|
|
|
// set default values in case there is no valid title
|
|
|
|
new_title = s_untitled;
|
|
|
|
allocated = FALSE;
|
|
|
|
// Check whether a zone title is given. If yes and it can be read,
|
|
|
|
// get copy, remember pointer and remember to free it later on.
|
2020-04-28 11:18:22 +00:00
|
|
|
if (BYTE_CONTINUES_KEYWORD(GotByte)) {
|
2014-12-04 11:26:42 +00:00
|
|
|
// Because we know of one character for sure,
|
|
|
|
// there's no need to check the return value.
|
|
|
|
Input_read_keyword();
|
|
|
|
new_title = DynaBuf_get_copy(GlobalDynaBuf);
|
|
|
|
allocated = TRUE;
|
|
|
|
}
|
|
|
|
// setup new section
|
|
|
|
// section type is "subzone", just in case a block follows
|
2016-08-05 09:59:07 +00:00
|
|
|
section_new(section_now, "Subzone", new_title, allocated);
|
2014-12-04 11:26:42 +00:00
|
|
|
if (Parse_optional_block()) {
|
|
|
|
// Block has been parsed, so it was a SUBzone.
|
2016-08-05 09:59:07 +00:00
|
|
|
section_finalize(section_now); // end inner zone
|
|
|
|
*section_now = entry_values; // restore entry values
|
2014-12-04 11:26:42 +00:00
|
|
|
} else {
|
|
|
|
// no block found, so it's a normal zone change
|
2016-08-05 09:59:07 +00:00
|
|
|
section_finalize(&entry_values); // end outer zone
|
2020-05-29 23:33:03 +00:00
|
|
|
section_now->type = "Zone"; // fix type
|
2014-12-04 11:26:42 +00:00
|
|
|
}
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "!subzone" or "!sz" pseudo opcode (now obsolete)
|
2020-05-29 23:33:03 +00:00
|
|
|
static enum eos po_subzone(void)
|
2014-12-04 11:26:42 +00:00
|
|
|
{
|
2020-05-29 23:33:03 +00:00
|
|
|
if (config.wanted_version >= VER_DISABLED_OBSOLETE_STUFF)
|
|
|
|
Throw_error("\"!subzone {}\" is obsolete; use \"!zone {}\" instead.");
|
|
|
|
else
|
|
|
|
Throw_first_pass_warning("\"!subzone {}\" is deprecated; use \"!zone {}\" instead.");
|
2014-12-04 11:26:42 +00:00
|
|
|
// call "!zone" instead
|
|
|
|
return po_zone();
|
|
|
|
}
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
// include source file ("!source" or "!src"). has to be re-entrant.
|
|
|
|
static enum eos po_source(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-06 11:40:06 +00:00
|
|
|
boolean uses_lib;
|
2017-12-22 22:55:36 +00:00
|
|
|
FILE *stream;
|
2014-12-04 15:31:54 +00:00
|
|
|
char local_gotbyte;
|
|
|
|
struct input new_input,
|
|
|
|
*outer_input;
|
|
|
|
|
|
|
|
// enter new nesting level
|
|
|
|
// quit program if recursion too deep
|
|
|
|
if (--source_recursions_left < 0)
|
|
|
|
Throw_serious_error("Too deeply nested. Recursive \"!source\"?");
|
|
|
|
// read file name. quit function on error
|
2017-12-22 22:55:36 +00:00
|
|
|
if (Input_read_filename(TRUE, &uses_lib))
|
2014-12-04 15:31:54 +00:00
|
|
|
return SKIP_REMAINDER;
|
|
|
|
|
|
|
|
// if file could be opened, parse it. otherwise, complain
|
2017-12-22 22:55:36 +00:00
|
|
|
stream = includepaths_open_ro(uses_lib);
|
|
|
|
if (stream) {
|
2020-05-29 10:57:01 +00:00
|
|
|
// FIXME - just use safe_malloc and never free! this also saves us making a copy if defining macros down the road...
|
2015-06-20 14:44:17 +00:00
|
|
|
#ifdef __GNUC__
|
|
|
|
char filename[GlobalDynaBuf->size]; // GCC can do this
|
|
|
|
#else
|
|
|
|
char *filename = safe_malloc(GlobalDynaBuf->size); // VS can not
|
|
|
|
#endif
|
2014-12-04 15:31:54 +00:00
|
|
|
|
|
|
|
strcpy(filename, GLOBALDYNABUF_CURRENT);
|
|
|
|
outer_input = Input_now; // remember old input
|
|
|
|
local_gotbyte = GotByte; // CAUTION - ugly kluge
|
|
|
|
Input_now = &new_input; // activate new input
|
2017-12-22 22:55:36 +00:00
|
|
|
flow_parse_and_close_file(stream, filename);
|
2014-12-04 15:31:54 +00:00
|
|
|
Input_now = outer_input; // restore previous input
|
|
|
|
GotByte = local_gotbyte; // CAUTION - ugly kluge
|
2015-06-20 14:44:17 +00:00
|
|
|
#ifndef __GNUC__
|
|
|
|
free(filename); // GCC auto-frees
|
|
|
|
#endif
|
2014-12-04 15:31:54 +00:00
|
|
|
}
|
|
|
|
// leave nesting level
|
|
|
|
++source_recursions_left;
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
2020-05-29 14:30:03 +00:00
|
|
|
// FIXME - move this to flow.c!
|
2020-05-12 15:08:24 +00:00
|
|
|
static boolean check_ifdef_condition(void)
|
|
|
|
{
|
|
|
|
scope_t scope;
|
|
|
|
struct rwnode *node;
|
|
|
|
struct symbol *symbol;
|
|
|
|
|
|
|
|
// read symbol name
|
|
|
|
if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before
|
|
|
|
return FALSE; // there was an error, it has been reported, so return value is more or less meaningless anway
|
|
|
|
|
|
|
|
// look for it
|
|
|
|
Tree_hard_scan(&node, symbols_forest, scope, FALSE);
|
|
|
|
if (!node)
|
|
|
|
return FALSE; // not found -> no, not defined
|
|
|
|
|
|
|
|
symbol = (struct symbol *) node->body;
|
|
|
|
// in first pass, count usage
|
|
|
|
if (FIRST_PASS)
|
|
|
|
symbol->usage++;
|
2020-06-04 14:31:15 +00:00
|
|
|
// TODO - if object type is NULL, return FALSE!
|
2020-05-29 10:57:01 +00:00
|
|
|
return symbol->object.type->is_defined(&symbol->object);
|
2020-05-12 15:08:24 +00:00
|
|
|
}
|
2020-05-29 16:53:13 +00:00
|
|
|
// if/ifdef/ifndef/else function, to be able to do ELSE IF
|
2020-05-12 10:51:37 +00:00
|
|
|
enum ifmode {
|
|
|
|
IFMODE_IF, // parse expression, then block
|
|
|
|
IFMODE_IFDEF, // check symbol, then parse block or line
|
|
|
|
IFMODE_IFNDEF, // check symbol, then parse block or line
|
2020-05-12 15:08:24 +00:00
|
|
|
IFMODE_ELSE // unconditional last block
|
2020-05-12 10:51:37 +00:00
|
|
|
};
|
2020-05-29 16:53:13 +00:00
|
|
|
// function for if/ifdef/ifndef/else. has to be re-entrant.
|
2020-05-12 10:51:37 +00:00
|
|
|
static enum eos ifelse(enum ifmode mode)
|
|
|
|
{
|
2020-05-12 15:08:24 +00:00
|
|
|
boolean nothing_done = TRUE; // once a block gets executed, this becomes FALSE, so all others will be skipped even if condition met
|
|
|
|
boolean condition_met; // condition result for next block
|
|
|
|
struct number ifresult;
|
2020-05-12 10:51:37 +00:00
|
|
|
|
2020-05-12 15:08:24 +00:00
|
|
|
for (;;) {
|
2020-05-12 10:51:37 +00:00
|
|
|
// check condition according to mode
|
|
|
|
switch (mode) {
|
|
|
|
case IFMODE_IF:
|
2020-05-12 15:08:24 +00:00
|
|
|
ALU_defined_int(&ifresult);
|
|
|
|
condition_met = !!ifresult.val.intval;
|
|
|
|
if (GotByte != CHAR_SOB)
|
|
|
|
Throw_serious_error(exception_no_left_brace);
|
2020-05-12 10:51:37 +00:00
|
|
|
break;
|
|
|
|
case IFMODE_IFDEF:
|
|
|
|
condition_met = check_ifdef_condition();
|
|
|
|
break;
|
|
|
|
case IFMODE_IFNDEF:
|
|
|
|
condition_met = !check_ifdef_condition();
|
|
|
|
break;
|
|
|
|
case IFMODE_ELSE:
|
|
|
|
condition_met = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
2020-05-29 22:03:04 +00:00
|
|
|
Bug_found("IllegalIfMode", mode);
|
2020-05-12 15:08:24 +00:00
|
|
|
condition_met = TRUE; // inhibit compiler warning ;)
|
2020-05-12 10:51:37 +00:00
|
|
|
}
|
2020-05-12 15:08:24 +00:00
|
|
|
SKIPSPACE();
|
2020-05-12 10:51:37 +00:00
|
|
|
// execute this block?
|
|
|
|
if (condition_met && nothing_done) {
|
2020-05-12 15:08:24 +00:00
|
|
|
nothing_done = FALSE; // all further ones will be skipped, even if conditions meet
|
|
|
|
if (GotByte == CHAR_SOB) {
|
|
|
|
Parse_until_eob_or_eof(); // parse block
|
|
|
|
// if block isn't correctly terminated, complain and exit
|
|
|
|
if (GotByte != CHAR_EOB)
|
|
|
|
Throw_serious_error(exception_no_right_brace);
|
2020-05-12 10:51:37 +00:00
|
|
|
} else {
|
2020-05-12 15:08:24 +00:00
|
|
|
return PARSE_REMAINDER; // parse line (only for ifdef/ifndef)
|
2020-05-12 10:51:37 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-05-12 15:08:24 +00:00
|
|
|
if (GotByte == CHAR_SOB) {
|
2020-05-12 10:51:37 +00:00
|
|
|
Input_skip_or_store_block(FALSE); // skip block
|
|
|
|
} else {
|
2020-05-12 15:08:24 +00:00
|
|
|
return SKIP_REMAINDER; // skip line (only for ifdef/ifndef)
|
2020-05-12 10:51:37 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-12 15:08:24 +00:00
|
|
|
// now GotByte = '}'
|
|
|
|
NEXTANDSKIPSPACE();
|
|
|
|
// after ELSE {} it's all over. it must be.
|
|
|
|
if (mode == IFMODE_ELSE) {
|
|
|
|
// we could just return ENSURE_EOS, but checking here allows for better error message
|
|
|
|
if (GotByte != CHAR_EOS)
|
2020-05-29 22:03:04 +00:00
|
|
|
Throw_error("Expected end-of-statement after ELSE block.");
|
2020-05-12 15:08:24 +00:00
|
|
|
return SKIP_REMAINDER; // normal exit after ELSE {...}
|
|
|
|
}
|
|
|
|
|
|
|
|
// anything more?
|
|
|
|
if (GotByte == CHAR_EOS)
|
|
|
|
return AT_EOS_ANYWAY; // normal exit if there is no ELSE {...} block
|
|
|
|
|
|
|
|
// read keyword (expected to be "else")
|
|
|
|
if (Input_read_and_lower_keyword() == 0)
|
|
|
|
return SKIP_REMAINDER; // "missing string error" -> ignore rest of line
|
|
|
|
|
|
|
|
// make sure it's "else"
|
|
|
|
if (strcmp(GlobalDynaBuf->buffer, "else")) {
|
2020-05-29 22:03:04 +00:00
|
|
|
Throw_error("Expected ELSE or end-of-statement.");
|
2020-05-12 15:08:24 +00:00
|
|
|
return SKIP_REMAINDER; // an error has been reported, so ignore rest of line
|
|
|
|
}
|
|
|
|
// anything more?
|
|
|
|
SKIPSPACE();
|
|
|
|
if (GotByte == CHAR_SOB) {
|
|
|
|
// ELSE {...} -> one last round
|
|
|
|
mode = IFMODE_ELSE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// read keyword (expected to be if/ifdef/ifndef)
|
|
|
|
if (Input_read_and_lower_keyword() == 0)
|
|
|
|
return SKIP_REMAINDER; // "missing string error" -> ignore rest of line
|
|
|
|
|
|
|
|
// which one is it?
|
|
|
|
if (strcmp(GlobalDynaBuf->buffer, "if") == 0) {
|
|
|
|
mode = IFMODE_IF;
|
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "ifdef") == 0) {
|
|
|
|
mode = IFMODE_IFDEF;
|
|
|
|
} else if (strcmp(GlobalDynaBuf->buffer, "ifndef") == 0) {
|
|
|
|
mode = IFMODE_IFNDEF;
|
|
|
|
} else {
|
2020-05-29 22:03:04 +00:00
|
|
|
Throw_error("After ELSE, expected block or IF/IFDEF/IFNDEF.");
|
2020-05-12 15:08:24 +00:00
|
|
|
return SKIP_REMAINDER; // an error has been reported, so ignore rest of line
|
|
|
|
}
|
|
|
|
}
|
2020-05-12 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
// conditional assembly ("!if"). has to be re-entrant.
|
|
|
|
static enum eos po_if(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-24 19:52:10 +00:00
|
|
|
return ifelse(IFMODE_IF);
|
2014-12-04 15:31:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// conditional assembly ("!ifdef"). has to be re-entrant.
|
|
|
|
static enum eos po_ifdef(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-24 19:52:10 +00:00
|
|
|
return ifelse(IFMODE_IFDEF);
|
2014-12-04 15:31:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// conditional assembly ("!ifndef"). has to be re-entrant.
|
|
|
|
static enum eos po_ifndef(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-24 19:52:10 +00:00
|
|
|
return ifelse(IFMODE_IFNDEF);
|
2014-12-04 15:31:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-22 00:47:52 +00:00
|
|
|
// looping assembly ("!for"). has to be re-entrant.
|
|
|
|
// old syntax: !for VAR, END { BLOCK } VAR counts from 1 to END
|
|
|
|
// new syntax: !for VAR, START, END { BLOCK } VAR counts from START to END
|
|
|
|
static enum eos po_for(void) // now GotByte = illegal char
|
|
|
|
{
|
2016-08-05 09:59:07 +00:00
|
|
|
scope_t scope;
|
2014-12-22 00:47:52 +00:00
|
|
|
int force_bit;
|
2020-04-28 16:02:09 +00:00
|
|
|
struct number intresult;
|
2014-12-22 00:47:52 +00:00
|
|
|
struct for_loop loop;
|
|
|
|
|
2016-08-05 09:59:07 +00:00
|
|
|
if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before
|
2020-06-05 01:11:51 +00:00
|
|
|
return SKIP_REMAINDER; // zero length
|
2014-12-22 00:47:52 +00:00
|
|
|
|
|
|
|
// now GotByte = illegal char
|
|
|
|
force_bit = Input_get_force_bit(); // skips spaces after
|
2020-06-05 01:11:51 +00:00
|
|
|
loop.symbol = symbol_find(scope); // FIXME - if type is not NULL, complain if not number!
|
|
|
|
symbol_forcebit(loop.symbol, force_bit);
|
2014-12-22 00:47:52 +00:00
|
|
|
if (!Input_accept_comma()) {
|
|
|
|
Throw_error(exception_syntax);
|
|
|
|
return SKIP_REMAINDER;
|
|
|
|
}
|
|
|
|
|
2015-06-14 23:16:23 +00:00
|
|
|
ALU_defined_int(&intresult); // read first argument
|
|
|
|
loop.counter.addr_refs = intresult.addr_refs;
|
2014-12-22 00:47:52 +00:00
|
|
|
if (Input_accept_comma()) {
|
2020-06-05 01:11:51 +00:00
|
|
|
// new format - yay!
|
|
|
|
loop.use_old_algo = FALSE;
|
2020-05-27 17:32:48 +00:00
|
|
|
if (config.wanted_version < VER_NEWFORSYNTAX)
|
2014-12-22 00:47:52 +00:00
|
|
|
Throw_first_pass_warning("Found new \"!for\" syntax.");
|
2015-06-14 23:16:23 +00:00
|
|
|
loop.counter.first = intresult.val.intval; // use first argument
|
|
|
|
ALU_defined_int(&intresult); // read second argument
|
|
|
|
loop.counter.last = intresult.val.intval; // use second argument
|
|
|
|
// compare addr_ref counts and complain if not equal!
|
2017-10-29 23:29:07 +00:00
|
|
|
if (config.warn_on_type_mismatch
|
2015-06-14 23:16:23 +00:00
|
|
|
&& (intresult.addr_refs != loop.counter.addr_refs)) {
|
|
|
|
Throw_first_pass_warning("Wrong type for loop's END value - must match type of START value.");
|
|
|
|
}
|
|
|
|
loop.counter.increment = (loop.counter.last < loop.counter.first) ? -1 : 1;
|
2014-12-22 00:47:52 +00:00
|
|
|
} else {
|
2020-06-05 01:11:51 +00:00
|
|
|
// old format - booo!
|
|
|
|
loop.use_old_algo = TRUE;
|
2020-05-27 17:32:48 +00:00
|
|
|
if (config.wanted_version >= VER_NEWFORSYNTAX)
|
2014-12-22 00:47:52 +00:00
|
|
|
Throw_first_pass_warning("Found old \"!for\" syntax.");
|
2015-06-14 23:16:23 +00:00
|
|
|
if (intresult.val.intval < 0)
|
2014-12-22 00:47:52 +00:00
|
|
|
Throw_serious_error("Loop count is negative.");
|
2015-06-14 23:16:23 +00:00
|
|
|
loop.counter.first = 0; // CAUTION - old algo pre-increments and therefore starts with 1!
|
|
|
|
loop.counter.last = intresult.val.intval; // use given argument
|
|
|
|
loop.counter.increment = 1;
|
2014-12-22 00:47:52 +00:00
|
|
|
}
|
|
|
|
if (GotByte != CHAR_SOB)
|
|
|
|
Throw_serious_error(exception_no_left_brace);
|
|
|
|
|
|
|
|
// remember line number of loop pseudo opcode
|
|
|
|
loop.block.start = Input_now->line_number;
|
|
|
|
// read loop body into DynaBuf and get copy
|
|
|
|
loop.block.body = Input_skip_or_store_block(TRUE); // changes line number!
|
|
|
|
|
|
|
|
flow_forloop(&loop);
|
|
|
|
// free memory
|
|
|
|
free(loop.block.body);
|
|
|
|
|
|
|
|
// GotByte of OuterInput would be '}' (if it would still exist)
|
|
|
|
GetByte(); // fetch next byte
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// looping assembly ("!do"). has to be re-entrant.
|
|
|
|
static enum eos po_do(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-05 22:56:11 +00:00
|
|
|
struct do_while loop;
|
2014-12-22 00:47:52 +00:00
|
|
|
|
|
|
|
// read head condition to buffer
|
|
|
|
SKIPSPACE();
|
|
|
|
flow_store_doloop_condition(&loop.head_cond, CHAR_SOB); // must be freed!
|
|
|
|
if (GotByte != CHAR_SOB)
|
|
|
|
Throw_serious_error(exception_no_left_brace);
|
|
|
|
// remember line number of loop body,
|
|
|
|
// then read block and get copy
|
|
|
|
loop.block.start = Input_now->line_number;
|
|
|
|
// reading block changes line number!
|
|
|
|
loop.block.body = Input_skip_or_store_block(TRUE); // must be freed!
|
|
|
|
// now GotByte = '}'
|
|
|
|
NEXTANDSKIPSPACE(); // now GotByte = first non-blank char after block
|
|
|
|
// read tail condition to buffer
|
|
|
|
flow_store_doloop_condition(&loop.tail_cond, CHAR_EOS); // must be freed!
|
|
|
|
// now GotByte = CHAR_EOS
|
2020-05-05 22:56:11 +00:00
|
|
|
flow_do_while(&loop);
|
2014-12-22 00:47:52 +00:00
|
|
|
// free memory
|
|
|
|
free(loop.head_cond.body);
|
|
|
|
free(loop.block.body);
|
|
|
|
free(loop.tail_cond.body);
|
|
|
|
return AT_EOS_ANYWAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-05 22:56:11 +00:00
|
|
|
// looping assembly ("!while", alternative for people used to c-style loops). has to be re-entrant.
|
2017-10-21 19:59:56 +00:00
|
|
|
static enum eos po_while(void) // now GotByte = illegal char
|
|
|
|
{
|
2020-05-05 22:56:11 +00:00
|
|
|
struct do_while loop;
|
|
|
|
|
|
|
|
// read condition to buffer
|
|
|
|
SKIPSPACE();
|
|
|
|
flow_store_while_condition(&loop.head_cond); // must be freed!
|
|
|
|
if (GotByte != CHAR_SOB)
|
|
|
|
Throw_serious_error(exception_no_left_brace);
|
|
|
|
// remember line number of loop body,
|
|
|
|
// then read block and get copy
|
|
|
|
loop.block.start = Input_now->line_number;
|
|
|
|
// reading block changes line number!
|
|
|
|
loop.block.body = Input_skip_or_store_block(TRUE); // must be freed!
|
|
|
|
// clear tail condition
|
|
|
|
loop.tail_cond.body = NULL;
|
|
|
|
flow_do_while(&loop);
|
|
|
|
// free memory
|
|
|
|
free(loop.head_cond.body);
|
|
|
|
free(loop.block.body);
|
|
|
|
// GotByte of OuterInput would be '}' (if it would still exist)
|
|
|
|
GetByte(); // fetch next byte
|
2017-10-21 19:59:56 +00:00
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
// macro definition ("!macro").
|
|
|
|
static enum eos po_macro(void) // now GotByte = illegal char
|
|
|
|
{
|
|
|
|
// in first pass, parse. In all other passes, skip.
|
2020-05-02 10:40:10 +00:00
|
|
|
if (FIRST_PASS) {
|
2014-12-04 15:31:54 +00:00
|
|
|
Macro_parse_definition(); // now GotByte = '}'
|
|
|
|
} else {
|
|
|
|
// skip until CHAR_SOB ('{') is found.
|
|
|
|
// no need to check for end-of-statement, because such an
|
|
|
|
// error would already have been detected in first pass.
|
|
|
|
// for the same reason, there is no need to check for quotes.
|
|
|
|
while (GotByte != CHAR_SOB)
|
|
|
|
GetByte();
|
|
|
|
Input_skip_or_store_block(FALSE); // now GotByte = '}'
|
|
|
|
}
|
|
|
|
GetByte(); // Proceed with next character
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-03 22:18:06 +00:00
|
|
|
// constants
|
|
|
|
#define USERMSG_DYNABUF_INITIALSIZE 80
|
|
|
|
|
|
|
|
|
|
|
|
// variables
|
|
|
|
static struct dynabuf *user_message; // dynamic buffer (!warn/error/serious)
|
|
|
|
|
2014-06-02 00:47:46 +00:00
|
|
|
|
2014-12-03 22:18:06 +00:00
|
|
|
// helper function to show user-defined messages
|
2014-03-11 12:07:11 +00:00
|
|
|
static enum eos throw_string(const char prefix[], void (*fn)(const char *))
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-13 23:26:40 +00:00
|
|
|
struct object object;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
DYNABUF_CLEAR(user_message);
|
|
|
|
DynaBuf_add_string(user_message, prefix);
|
|
|
|
do {
|
2020-05-27 17:32:48 +00:00
|
|
|
if ((GotByte == '"') && (config.wanted_version < VER_BACKSLASHESCAPING)) {
|
2020-05-19 13:04:39 +00:00
|
|
|
DYNABUF_CLEAR(GlobalDynaBuf);
|
|
|
|
if (Input_quoted_to_dynabuf('"'))
|
|
|
|
return SKIP_REMAINDER; // unterminated or escaping error
|
|
|
|
|
|
|
|
// eat closing quote
|
2012-02-27 21:14:46 +00:00
|
|
|
GetByte();
|
2020-05-19 13:04:39 +00:00
|
|
|
// now convert to unescaped version
|
2020-05-19 16:28:36 +00:00
|
|
|
if (Input_unescape_dynabuf(0))
|
2020-05-19 13:04:39 +00:00
|
|
|
return SKIP_REMAINDER; // escaping error
|
|
|
|
|
|
|
|
DynaBuf_append(GlobalDynaBuf, '\0'); // terminate string
|
|
|
|
DynaBuf_add_string(user_message, GLOBALDYNABUF_CURRENT); // add to message
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
|
|
|
// parse value
|
2020-05-13 23:26:40 +00:00
|
|
|
ALU_any_result(&object);
|
|
|
|
object.type->print(&object, user_message);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
} while (Input_accept_comma());
|
|
|
|
DynaBuf_append(user_message, '\0');
|
|
|
|
fn(user_message->buffer);
|
|
|
|
return ENSURE_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-29 23:29:07 +00:00
|
|
|
#if 0
|
|
|
|
// show debug data given in source code
|
|
|
|
static enum eos po_debug(void)
|
|
|
|
{
|
|
|
|
// FIXME - make debug output depend on some cli switch
|
|
|
|
return throw_string("!debug: ", throw_message);
|
|
|
|
}
|
|
|
|
// show info given in source code
|
|
|
|
static enum eos po_info(void)
|
|
|
|
{
|
|
|
|
return throw_string("!info: ", throw_message);
|
|
|
|
}
|
|
|
|
#endif
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
// throw warning as given in source code
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos po_warn(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
return throw_string("!warn: ", Throw_warning);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// throw error as given in source code
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos po_error(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
return throw_string("!error: ", Throw_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// throw serious error as given in source code
|
2014-12-03 22:18:06 +00:00
|
|
|
static enum eos po_serious(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
return throw_string("!serious: ", Throw_serious_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-04 15:31:54 +00:00
|
|
|
// end of source file ("!endoffile" or "!eof")
|
2014-12-05 17:46:48 +00:00
|
|
|
static enum eos po_endoffile(void)
|
2014-12-04 15:31:54 +00:00
|
|
|
{
|
|
|
|
// well, it doesn't end right here and now, but at end-of-line! :-)
|
|
|
|
Input_ensure_EOS();
|
|
|
|
Input_now->state = INPUTSTATE_EOF;
|
|
|
|
return AT_EOS_ANYWAY;
|
|
|
|
}
|
|
|
|
|
2014-12-03 22:18:06 +00:00
|
|
|
// pseudo opcode table
|
2014-12-04 15:31:54 +00:00
|
|
|
static struct ronode pseudo_opcode_list[] = {
|
|
|
|
PREDEFNODE("initmem", po_initmem),
|
2017-12-22 22:55:36 +00:00
|
|
|
PREDEFNODE("xor", po_xor),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("to", po_to),
|
2014-12-05 17:46:48 +00:00
|
|
|
PREDEFNODE(s_8, po_byte),
|
|
|
|
PREDEFNODE(s_08, po_byte),
|
|
|
|
PREDEFNODE("by", po_byte),
|
|
|
|
PREDEFNODE("byte", po_byte),
|
2016-02-16 23:11:04 +00:00
|
|
|
PREDEFNODE("wo", po_16),
|
|
|
|
PREDEFNODE("word", po_16),
|
2016-02-21 12:58:22 +00:00
|
|
|
PREDEFNODE("16", po_16),
|
2016-02-16 23:11:04 +00:00
|
|
|
PREDEFNODE("be16", po_be16),
|
|
|
|
PREDEFNODE("le16", po_le16),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("24", po_24),
|
2016-02-16 23:11:04 +00:00
|
|
|
PREDEFNODE("be24", po_be24),
|
|
|
|
PREDEFNODE("le24", po_le24),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("32", po_32),
|
2016-02-16 23:11:04 +00:00
|
|
|
PREDEFNODE("be32", po_be32),
|
|
|
|
PREDEFNODE("le32", po_le32),
|
2017-10-29 23:29:07 +00:00
|
|
|
PREDEFNODE("h", po_hex),
|
|
|
|
PREDEFNODE("hex", po_hex),
|
2020-05-29 23:33:03 +00:00
|
|
|
PREDEFNODE("cbm", po_cbm), // obsolete
|
2014-12-16 08:21:44 +00:00
|
|
|
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),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("bin", po_binary),
|
|
|
|
PREDEFNODE("binary", po_binary),
|
|
|
|
PREDEFNODE("fi", po_fill),
|
|
|
|
PREDEFNODE("fill", po_fill),
|
2017-10-29 23:29:07 +00:00
|
|
|
PREDEFNODE("skip", po_skip),
|
2014-12-16 08:21:44 +00:00
|
|
|
PREDEFNODE("align", po_align),
|
|
|
|
PREDEFNODE("pseudopc", po_pseudopc),
|
2020-05-29 23:33:03 +00:00
|
|
|
PREDEFNODE("realpc", po_realpc), // obsolete
|
2014-12-16 08:21:44 +00:00
|
|
|
PREDEFNODE("cpu", po_cpu),
|
|
|
|
PREDEFNODE("al", po_al),
|
|
|
|
PREDEFNODE("as", po_as),
|
|
|
|
PREDEFNODE(s_rl, po_rl),
|
|
|
|
PREDEFNODE("rs", po_rs),
|
2014-12-05 17:46:48 +00:00
|
|
|
PREDEFNODE("addr", po_address),
|
|
|
|
PREDEFNODE("address", po_address),
|
2017-10-29 23:29:07 +00:00
|
|
|
// PREDEFNODE("enum", po_enum),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("set", po_set),
|
2014-12-05 17:46:48 +00:00
|
|
|
PREDEFNODE(s_sl, po_symbollist),
|
|
|
|
PREDEFNODE("symbollist", po_symbollist),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("zn", po_zone),
|
2020-05-29 23:33:03 +00:00
|
|
|
PREDEFNODE("zone", po_zone),
|
|
|
|
PREDEFNODE("sz", po_subzone), // obsolete
|
|
|
|
PREDEFNODE("subzone", po_subzone), // obsolete
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("src", po_source),
|
|
|
|
PREDEFNODE("source", po_source),
|
|
|
|
PREDEFNODE("if", po_if),
|
|
|
|
PREDEFNODE("ifdef", po_ifdef),
|
|
|
|
PREDEFNODE("ifndef", po_ifndef),
|
2014-12-22 00:47:52 +00:00
|
|
|
PREDEFNODE("for", po_for),
|
|
|
|
PREDEFNODE("do", po_do),
|
2020-05-06 10:00:32 +00:00
|
|
|
PREDEFNODE("while", po_while),
|
2014-12-04 15:31:54 +00:00
|
|
|
PREDEFNODE("macro", po_macro),
|
|
|
|
// PREDEFNODE("debug", po_debug),
|
|
|
|
// PREDEFNODE("info", po_info),
|
|
|
|
PREDEFNODE("warn", po_warn),
|
|
|
|
PREDEFNODE(s_error, po_error),
|
|
|
|
PREDEFNODE("serious", po_serious),
|
2014-12-05 17:46:48 +00:00
|
|
|
PREDEFNODE("eof", po_endoffile),
|
|
|
|
PREDEFLAST("endoffile", po_endoffile),
|
2012-02-27 21:14:46 +00:00
|
|
|
// ^^^^ this marks the last element
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// register pseudo opcodes and create dynamic buffer
|
2014-12-03 22:18:06 +00:00
|
|
|
void pseudoopcodes_init(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
user_message = DynaBuf_create(USERMSG_DYNABUF_INITIALSIZE);
|
2014-12-04 15:31:54 +00:00
|
|
|
Tree_add_table(&pseudo_opcode_tree, pseudo_opcode_list);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
2014-12-04 11:26:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
// parse a pseudo opcode. has to be re-entrant.
|
2014-12-04 15:31:54 +00:00
|
|
|
void pseudoopcode_parse(void) // now GotByte = "!"
|
2014-12-04 11:26:42 +00:00
|
|
|
{
|
|
|
|
void *node_body;
|
|
|
|
enum eos (*fn)(void),
|
|
|
|
then = SKIP_REMAINDER; // prepare for errors
|
|
|
|
|
|
|
|
GetByte(); // read next byte
|
|
|
|
// on missing keyword, return (complaining will have been done)
|
|
|
|
if (Input_read_and_lower_keyword()) {
|
|
|
|
// search for tree item
|
|
|
|
if ((Tree_easy_scan(pseudo_opcode_tree, &node_body, GlobalDynaBuf))
|
|
|
|
&& node_body) {
|
|
|
|
fn = (enum eos (*)(void)) node_body;
|
|
|
|
SKIPSPACE();
|
|
|
|
// call function
|
|
|
|
then = fn();
|
|
|
|
} else {
|
2020-05-06 11:40:06 +00:00
|
|
|
Throw_error(exception_unknown_pseudo_opcode);
|
2014-12-04 11:26:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (then == SKIP_REMAINDER)
|
|
|
|
Input_skip_remainder();
|
|
|
|
else if (then == ENSURE_EOS)
|
|
|
|
Input_ensure_EOS();
|
|
|
|
// the other two possibilities (PARSE_REMAINDER and AT_EOS_ANYWAY)
|
|
|
|
// will lead to the remainder of the line being parsed by the mainloop.
|
|
|
|
}
|