mirror of
https://github.com/ksherlock/merlin-utils.git
synced 2025-08-05 20:25:09 +00:00
script support (wip)
This commit is contained in:
2
Makefile
2
Makefile
@@ -15,7 +15,7 @@ clean:
|
||||
o:
|
||||
mkdir o
|
||||
|
||||
merlin-link: o/link.o o/mapped_file.o o/omf.o o/set_file_type.o afp/libafp.a
|
||||
merlin-link: o/main.o o/link.o o/script.o o/mapped_file.o o/omf.o o/set_file_type.o afp/libafp.a
|
||||
$(LINK.o) $^ $(LDLIBS) -o $@
|
||||
|
||||
o/mapped_file.o : mapped_file.cpp mapped_file.h unique_resource.h
|
||||
|
400
link.cpp
400
link.cpp
@@ -8,21 +8,15 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/* old version of stdlib have this stuff in utility */
|
||||
#if __has_include(<charconv>)
|
||||
#define HAVE_CHARCONV
|
||||
#include <charconv>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#include <err.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <afp/finder_info.h>
|
||||
|
||||
@@ -31,6 +25,7 @@
|
||||
|
||||
#include "omf.h"
|
||||
#include "rel.h"
|
||||
#include "link.h"
|
||||
|
||||
void save_omf(const std::string &path, std::vector<omf::segment> &segments, bool compress, bool expressload);
|
||||
int set_file_type(const std::string &path, uint16_t file_type, uint32_t aux_type, std::error_code &ec);
|
||||
@@ -40,35 +35,41 @@ void set_file_type(const std::string &path, uint16_t file_type, uint32_t aux_typ
|
||||
typedef std::basic_string_view<uint8_t> byte_view;
|
||||
|
||||
|
||||
struct symbol {
|
||||
std::string name;
|
||||
std::string file;
|
||||
uint32_t value = 0;
|
||||
unsigned id = 0;
|
||||
unsigned count = 0;
|
||||
|
||||
bool absolute = false;
|
||||
bool defined = false;
|
||||
};
|
||||
|
||||
|
||||
std::unordered_map<std::string, unsigned> symbol_map;
|
||||
std::vector<symbol> symbol_table;
|
||||
|
||||
struct pending_reloc : public omf::reloc {
|
||||
unsigned id = 0;
|
||||
};
|
||||
|
||||
std::vector<pending_reloc> relocations;
|
||||
|
||||
|
||||
std::vector<omf::segment> segments;
|
||||
struct cookie {
|
||||
std::string file;
|
||||
std::vector<unsigned> remap;
|
||||
|
||||
uint32_t begin = 0;
|
||||
uint32_t end = 0;
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
std::unordered_map<std::string, unsigned> symbol_map;
|
||||
std::vector<symbol> symbol_table;
|
||||
|
||||
std::vector<pending_reloc> relocations;
|
||||
std::vector<omf::segment> segments;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* nb - pointer may be invalidated by next call */
|
||||
symbol *find_symbol(const std::string &name) {
|
||||
symbol *find_symbol(const std::string &name, bool insert) {
|
||||
|
||||
auto iter = symbol_map.find(name);
|
||||
if (iter != symbol_map.end()) return &symbol_table[iter->second];
|
||||
if (!insert) return nullptr;
|
||||
|
||||
unsigned id = symbol_table.size();
|
||||
symbol_map.emplace(name, id);
|
||||
@@ -81,16 +82,7 @@ symbol *find_symbol(const std::string &name) {
|
||||
|
||||
|
||||
|
||||
struct cookie {
|
||||
std::string file;
|
||||
std::vector<unsigned> remap;
|
||||
//std::vector<std::pair<unsigned, unsigned>> zero;
|
||||
|
||||
uint32_t begin = 0;
|
||||
uint32_t end = 0;
|
||||
};
|
||||
|
||||
void process_labels(byte_view &data, cookie &cookie) {
|
||||
static void process_labels(byte_view &data, cookie &cookie) {
|
||||
|
||||
for(;;) {
|
||||
assert(data.size());
|
||||
@@ -144,7 +136,7 @@ void process_labels(byte_view &data, cookie &cookie) {
|
||||
}
|
||||
|
||||
|
||||
void process_reloc(byte_view &data, cookie &cookie) {
|
||||
static void process_reloc(byte_view &data, cookie &cookie) {
|
||||
|
||||
auto &seg = segments.back();
|
||||
|
||||
@@ -254,21 +246,8 @@ void process_reloc(byte_view &data, cookie &cookie) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void add_libraries() {
|
||||
auto iter = libs.begin();
|
||||
auto end = libs.end();
|
||||
|
||||
for(;;) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void process_unit(const std::string &path) {
|
||||
static void process_unit(const std::string &path) {
|
||||
|
||||
cookie cookie;
|
||||
/* skip over relocs, do symbols first */
|
||||
@@ -327,7 +306,7 @@ void process_unit(const std::string &path) {
|
||||
}
|
||||
|
||||
|
||||
void resolve(void) {
|
||||
static void resolve(void) {
|
||||
|
||||
/* this needs to be updated if supporting multiple segments */
|
||||
auto &seg = segments.back();
|
||||
@@ -377,7 +356,7 @@ static void print_symbols2(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void print_symbols(void) {
|
||||
static void print_symbols(void) {
|
||||
|
||||
if (symbol_table.empty()) return;
|
||||
|
||||
@@ -402,113 +381,21 @@ void print_symbols(void) {
|
||||
|
||||
}
|
||||
|
||||
void finish(void) {
|
||||
|
||||
resolve();
|
||||
print_symbols();
|
||||
|
||||
void usage(int ex) {
|
||||
try {
|
||||
save_omf(save_file, segments, compress, express);
|
||||
set_file_type(save_file, 0xb3, 0x0000);
|
||||
} catch (std::exception &ex) {
|
||||
errx(EX_OSERR, "%s: %s", save_file.c_str(), ex.what());
|
||||
}
|
||||
|
||||
fputs("merlin-link [-o outfile] infile...\n", stderr);
|
||||
exit(ex);
|
||||
}
|
||||
|
||||
/* older std libraries lack charconv and std::from_chars */
|
||||
bool parse_number(const char *begin, const char *end, uint32_t &value, int base = 10) {
|
||||
|
||||
#if defined(HAVE_CHARCONV)
|
||||
auto r = std::from_chars(begin, end, value, base);
|
||||
if (r.ec != std::errc() || r.ptr != end) return false;
|
||||
#else
|
||||
auto xerrno = errno;
|
||||
errno = 0;
|
||||
char *ptr = nullptr;
|
||||
value = std::strtoul(begin, &ptr, base);
|
||||
std::swap(errno, xerrno);
|
||||
if (xerrno || ptr != end) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void add_define(std::string str) {
|
||||
/* -D key[=value]
|
||||
value = 0x, $, % or base 10 */
|
||||
|
||||
uint32_t value = 0;
|
||||
|
||||
auto ix = str.find('=');
|
||||
if (ix == 0) usage(EX_USAGE);
|
||||
if (ix == str.npos) {
|
||||
value = 1;
|
||||
} else {
|
||||
|
||||
int base = 10;
|
||||
auto pos = ++ix;
|
||||
|
||||
char c = str[pos]; /* returns 0 if == size */
|
||||
|
||||
switch(c) {
|
||||
case '%':
|
||||
base = 2; ++pos; break;
|
||||
case '$':
|
||||
base = 16; ++pos; break;
|
||||
case '0':
|
||||
c = str[pos+1];
|
||||
if (c == 'x' || c == 'X') {
|
||||
base = 16; pos += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!parse_number(str.data() + pos, str.data() + str.length(), value, base))
|
||||
usage(EX_USAGE);
|
||||
|
||||
str.resize(ix-1);
|
||||
}
|
||||
|
||||
|
||||
symbol *e = find_symbol(str);
|
||||
if (e->defined && e->absolute && e->value == value) return;
|
||||
|
||||
if (e->defined) {
|
||||
warnx("%s previously defined", str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
e->defined = true;
|
||||
e->absolute = true;
|
||||
e->file = "-D";
|
||||
e->value = value;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int c;
|
||||
std::string gs_out = "gs.out";
|
||||
bool express = true;
|
||||
bool compress = true;
|
||||
|
||||
|
||||
while ((c = getopt(argc, argv, "o:D:XC")) != -1) {
|
||||
switch(c) {
|
||||
case 'o':
|
||||
gs_out = optarg;
|
||||
break;
|
||||
case 'X': express = false; break;
|
||||
case 'C': compress = false; break;
|
||||
case 'D': add_define(optarg); break;
|
||||
case ':':
|
||||
case '?':
|
||||
default:
|
||||
usage(EX_USAGE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (!argc) usage(EX_USAGE);
|
||||
void process_files(int argc, char **argv) {
|
||||
|
||||
segments.emplace_back();
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
@@ -519,15 +406,200 @@ int main(int argc, char **argv) {
|
||||
errx(EX_DATAERR, "%s: %s", path, ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
print_symbols();
|
||||
|
||||
try {
|
||||
save_omf(gs_out, segments, compress, express);
|
||||
set_file_type(gs_out, 0xb3, 0x0000);
|
||||
exit(0);
|
||||
} catch (std::exception &ex) {
|
||||
errx(EX_OSERR, "%s: %s", gs_out.c_str(), ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
unsigned lkv = 1;
|
||||
unsigned ver = 2;
|
||||
unsigned ftype = 0xb3;
|
||||
unsigned atype = 0x0000;
|
||||
unsigned kind = 0x0000;
|
||||
unsigned do_level = 0;
|
||||
bool end = false;
|
||||
uint32_t active_bits = 1;
|
||||
bool active = true;
|
||||
|
||||
std::unordered_map<std::string, uint32_t> symbol_table;
|
||||
|
||||
}
|
||||
|
||||
void evaluate(label_t label, opcode_t opcode, operand_t operand) {
|
||||
|
||||
switch(opcode) {
|
||||
case OP_DO:
|
||||
if (active_bits & 0x80000000) throw std::runtime_error("too much do do");
|
||||
active_bits <<= 1;
|
||||
active_bits |= std::get<uint32_t>(operand) ? 1 : 0;
|
||||
active = (active_bits & (active_bits + 1)) == 0;
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_ELS:
|
||||
if (active_bits < 2)
|
||||
throw std::runtime_error("els without do");
|
||||
|
||||
active_bits ^= 0x01;
|
||||
active = (active_bits & (active_bits + 1)) == 0;
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_FIN:
|
||||
active_bits >>= 1;
|
||||
if (!active_bits) {
|
||||
active = 1;
|
||||
throw std::runtime_error("fin without do");
|
||||
}
|
||||
active = (active_bits & (active_bits + 1)) == 0;
|
||||
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (!active) return;
|
||||
|
||||
switch(opcode) {
|
||||
|
||||
case OP_END:
|
||||
if (!end && lkv == 2) {
|
||||
/* finish up */
|
||||
segments.pop_back();
|
||||
finish();
|
||||
}
|
||||
end = true;
|
||||
break;
|
||||
|
||||
case OP_DAT: {
|
||||
/* 29-DEC-88 4:18:37 PM */
|
||||
time_t t = time(nullptr);
|
||||
struct tm tm = localtime(&t);
|
||||
char buffer[32];
|
||||
|
||||
strftime(buffer, sizeof(buffer), "%d-%b-%y %H:%M:%S %p", tm);
|
||||
for(char &c : buffer) c = std::toupper(c);
|
||||
|
||||
printf(stdout, "%s\n", buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case OP_TYP:
|
||||
ftype = std::get<uint32_t>(operand);
|
||||
break;
|
||||
case OP_ADR:
|
||||
atype = std::get<uint32_t>(operand);
|
||||
break;
|
||||
|
||||
case OP_KND:
|
||||
kind = std::get<uint32_t>(operand);
|
||||
break;
|
||||
|
||||
case OP_LKV:
|
||||
/* specify linker version */
|
||||
/* 0 = binary, 1 = Linker.GS, 2 = Linker.XL, 3 = convert to OMF object file */
|
||||
|
||||
switch (std::get<uint32_t>(operand)) {
|
||||
case 0: throw std::runtime_error("binary linker not supported");
|
||||
case 3: throw std::runtime_error("object file linker not supported");
|
||||
case 1:
|
||||
case 2:
|
||||
lkv = std::get<uint32_t>(operand);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("bad linker version");
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_VER:
|
||||
/* OMF version, 1 or 2 */
|
||||
if (std::get<uint32_t>(operand) != 2)
|
||||
throw std::runtime_error("bad OMF version");
|
||||
break;
|
||||
|
||||
|
||||
case OP_LNK:
|
||||
if (end) throw std::runtime_error("link after end");
|
||||
//link_unit(std::get<std::string>(operand));
|
||||
break;
|
||||
|
||||
case OP_SAV:
|
||||
if (end) throw std::runtime_error("save after end");
|
||||
if (save_file.empty()) save_file = std::get<std::string>(operand);
|
||||
|
||||
/* if linker version 1, save to disk */
|
||||
/* if linker version 2, finish the current segment */
|
||||
if (lkv == 1) {
|
||||
auto &seg = segments.back();
|
||||
seg.segname = std::get<std::string>(operand);
|
||||
seg.kind = kind;
|
||||
finish();
|
||||
end = true;
|
||||
}
|
||||
if (lkv == 2) {
|
||||
auto &seg = segments.back();
|
||||
seg.segname = std::get<std::string>(operand);
|
||||
seg.kind = kind;
|
||||
|
||||
/* if this is the first segment, also save the
|
||||
/* add a new segment */
|
||||
segments.emplace_back();
|
||||
}
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case OP_ASM:
|
||||
default:
|
||||
throw std::runtime_error("opcode not yet supported");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void process_script(const char *path) {
|
||||
|
||||
extern void parse_line(const char *);
|
||||
|
||||
FILE *fp;
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) {
|
||||
warn("Unable to open %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int no = 1;
|
||||
int errors = 0;
|
||||
char *line = NULL;
|
||||
size_t cap = 0;
|
||||
for(;; ++no) {
|
||||
|
||||
ssize_t len = getline(&line, &cap, fp);
|
||||
if (len == 0) break;
|
||||
if (len < 0) {
|
||||
warn("read error");
|
||||
++errors;
|
||||
break;
|
||||
}
|
||||
/* strip trailing ws */
|
||||
while (len && isspace(line[len-1])) --len;
|
||||
line[len] = 0;
|
||||
if (len == 0) continue;
|
||||
|
||||
try {
|
||||
parse_line(line);
|
||||
} catch (std::exception &ex) {
|
||||
if (~active & 0x01) continue;
|
||||
|
||||
fprintf(stderr, "%s in line: %d\n", ex.what(), no);
|
||||
fprintf(stderr, "%s\n", line);
|
||||
if (++errors >= 10) {
|
||||
fputs("Too many errors, aborting\n", stderr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(line);
|
||||
exit(errors ? EX_DATAERR : 0);
|
||||
}
|
||||
|
||||
|
31
link.h
Normal file
31
link.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef link_h
|
||||
#define link_h
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
extern bool verbose;
|
||||
extern bool compress;
|
||||
extern bool express;
|
||||
extern std::string save_file;
|
||||
|
||||
|
||||
struct symbol {
|
||||
std::string name;
|
||||
std::string file;
|
||||
uint32_t value = 0;
|
||||
unsigned id = 0;
|
||||
unsigned count = 0;
|
||||
|
||||
bool absolute = false;
|
||||
bool defined = false;
|
||||
};
|
||||
|
||||
|
||||
void process_script(const char *argv);
|
||||
void process_files(int argc, char **argv);
|
||||
|
||||
|
||||
symbol *find_symbol(const std::string &name, bool insert = true);
|
||||
|
||||
#endif
|
157
main.cpp
Normal file
157
main.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cctype>
|
||||
|
||||
/* old version of stdlib have this stuff in utility */
|
||||
#if __has_include(<charconv>)
|
||||
#define HAVE_CHARCONV
|
||||
#include <charconv>
|
||||
#endif
|
||||
|
||||
#include <err.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "link.h"
|
||||
|
||||
static void usage(int ex) {
|
||||
|
||||
fputs(
|
||||
"merlin-link [options] infile...\n"
|
||||
"\noptions:\n"
|
||||
"-C inhibit SUPER compression\n"
|
||||
"-D symbol=value define symbol\n"
|
||||
"-X inhibit expressload segment\n"
|
||||
"-o outfile specify output file (default gs.out)\n"
|
||||
"-v be verbose\n"
|
||||
"\n",
|
||||
stderr);
|
||||
|
||||
exit(ex);
|
||||
}
|
||||
|
||||
/* older std libraries lack charconv and std::from_chars */
|
||||
static bool parse_number(const char *begin, const char *end, uint32_t &value, int base = 10) {
|
||||
|
||||
#if defined(HAVE_CHARCONV)
|
||||
auto r = std::from_chars(begin, end, value, base);
|
||||
if (r.ec != std::errc() || r.ptr != end) return false;
|
||||
#else
|
||||
auto xerrno = errno;
|
||||
errno = 0;
|
||||
char *ptr = nullptr;
|
||||
value = std::strtoul(begin, &ptr, base);
|
||||
std::swap(errno, xerrno);
|
||||
if (xerrno || ptr != end) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void add_define(std::string str) {
|
||||
/* -D key[=value]
|
||||
value = 0x, $, % or base 10 */
|
||||
|
||||
uint32_t value = 0;
|
||||
|
||||
auto ix = str.find('=');
|
||||
if (ix == 0) usage(EX_USAGE);
|
||||
if (ix == str.npos) {
|
||||
value = 1;
|
||||
} else {
|
||||
|
||||
int base = 10;
|
||||
auto pos = ++ix;
|
||||
|
||||
char c = str[pos]; /* returns 0 if == size */
|
||||
|
||||
switch(c) {
|
||||
case '%':
|
||||
base = 2; ++pos; break;
|
||||
case '$':
|
||||
base = 16; ++pos; break;
|
||||
case '0':
|
||||
c = str[pos+1];
|
||||
if (c == 'x' || c == 'X') {
|
||||
base = 16; pos += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!parse_number(str.data() + pos, str.data() + str.length(), value, base))
|
||||
usage(EX_USAGE);
|
||||
|
||||
str.resize(ix-1);
|
||||
}
|
||||
|
||||
|
||||
symbol *e = find_symbol(str);
|
||||
if (e->defined && e->absolute && e->value == value) return;
|
||||
|
||||
if (e->defined) {
|
||||
warnx("%s previously defined", str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
e->defined = true;
|
||||
e->absolute = true;
|
||||
e->file = "-D";
|
||||
e->value = value;
|
||||
}
|
||||
|
||||
/* .ends_with() is c++20 */
|
||||
static bool is_S(std::string_view sv) {
|
||||
size_t s = sv.size();
|
||||
// && is a sequence point.
|
||||
return s >= 2 && std::toupper(sv[--s]) == 'S' && sv[--s] == '.';
|
||||
}
|
||||
|
||||
|
||||
bool verbose = false;
|
||||
std::string save_file = "gs.out";
|
||||
bool express = true;
|
||||
bool compress = true;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int c;
|
||||
bool script = false;
|
||||
|
||||
while ((c = getopt(argc, argv, "o:D:XCSv")) != -1) {
|
||||
switch(c) {
|
||||
case 'o':
|
||||
save_file = optarg;
|
||||
break;
|
||||
case 'X': express = false; break;
|
||||
case 'C': compress = false; break;
|
||||
case 'D': add_define(optarg); break;
|
||||
case 'v': verbose = true;
|
||||
case ':':
|
||||
case '?':
|
||||
default:
|
||||
usage(EX_USAGE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (!argc) usage(EX_USAGE);
|
||||
|
||||
if (script && argc > 1) usage(EX_USAGE);
|
||||
if (argc == 1 && is_S(*argv)) script = true;
|
||||
|
||||
if (script) process_script(*argv);
|
||||
else process_files(argc, argv);
|
||||
|
||||
exit(0);
|
||||
}
|
397
script.re2c
397
script.re2c
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <variant>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
@@ -18,6 +19,8 @@
|
||||
|
||||
#include <err.h>
|
||||
|
||||
#include "script.h"
|
||||
|
||||
|
||||
/*!re2c
|
||||
re2c:define:YYCTYPE = char;
|
||||
@@ -29,73 +32,67 @@
|
||||
eof = "\x00";
|
||||
*/
|
||||
|
||||
enum {
|
||||
CMD_NONE = 0,
|
||||
#define x(op) CMD_##op,
|
||||
#include "ops.h"
|
||||
#undef x
|
||||
CMD_EQ
|
||||
};
|
||||
namespace {
|
||||
std::unordered_map<std::string, int> opcodes = {
|
||||
#define x(op) { #op, OP_##op },
|
||||
|
||||
static std::unordered_map<std::string, int> commands = {
|
||||
#define x(op) { #op, CMD_##op },
|
||||
#include "ops.h"
|
||||
#undef x
|
||||
|
||||
#include "ops.h"
|
||||
#undef x
|
||||
|
||||
/* aliases */
|
||||
{ "AUX", CMD_ADR },
|
||||
{ "REZ", CMD_RES },
|
||||
{ "LIN", CMD_LNK },
|
||||
{ "KIN", CMD_KND },
|
||||
{ "=", CMD_EQ }
|
||||
};
|
||||
/* aliases */
|
||||
{ "AUX", OP_ADR },
|
||||
{ "REZ", OP_RES },
|
||||
{ "LIN", OP_LNK },
|
||||
{ "KIN", OP_KND },
|
||||
{ "=", OP_EQ }
|
||||
};
|
||||
|
||||
|
||||
static std::unordered_map<std::string, int> types = {
|
||||
std::unordered_map<std::string, int> types = {
|
||||
|
||||
{ "NON", 0x00 },
|
||||
{ "BAD", 0x01 },
|
||||
{ "BIN", 0x06 },
|
||||
{ "TXT", 0x04 },
|
||||
{ "DIR", 0x0f },
|
||||
{ "ADB", 0x19 },
|
||||
{ "AWP", 0x1a },
|
||||
{ "ASP", 0x1b },
|
||||
{ "GSB", 0xab },
|
||||
{ "TDF", 0xac },
|
||||
{ "BDF", 0xad },
|
||||
{ "SRC", 0xb0 },
|
||||
{ "OBJ", 0xb1 },
|
||||
{ "LIB", 0xb2 },
|
||||
{ "S16", 0xb3 },
|
||||
{ "RTL", 0xb4 },
|
||||
{ "EXE", 0xb5 },
|
||||
{ "PIF", 0xb6 },
|
||||
{ "TIF", 0xb7 },
|
||||
{ "NDA", 0xb8 },
|
||||
{ "CDA", 0xb9 },
|
||||
{ "TOL", 0xba },
|
||||
{ "DRV", 0xbb },
|
||||
{ "DOC", 0xbf },
|
||||
{ "PNT", 0xc0 },
|
||||
{ "PIC", 0xc1 },
|
||||
{ "FON", 0xcb },
|
||||
{ "PAS", 0xef },
|
||||
{ "CMD", 0xf0 },
|
||||
{ "LNK", 0xf8 },
|
||||
{ "BAS", 0xfc },
|
||||
{ "VAR", 0xfd },
|
||||
{ "REL", 0xfe },
|
||||
{ "SYS", 0xff },
|
||||
|
||||
};
|
||||
{ "NON", 0x00 },
|
||||
{ "BAD", 0x01 },
|
||||
{ "BIN", 0x06 },
|
||||
{ "TXT", 0x04 },
|
||||
{ "DIR", 0x0f },
|
||||
{ "ADB", 0x19 },
|
||||
{ "AWP", 0x1a },
|
||||
{ "ASP", 0x1b },
|
||||
{ "GSB", 0xab },
|
||||
{ "TDF", 0xac },
|
||||
{ "BDF", 0xad },
|
||||
{ "SRC", 0xb0 },
|
||||
{ "OBJ", 0xb1 },
|
||||
{ "LIB", 0xb2 },
|
||||
{ "S16", 0xb3 },
|
||||
{ "RTL", 0xb4 },
|
||||
{ "EXE", 0xb5 },
|
||||
{ "PIF", 0xb6 },
|
||||
{ "TIF", 0xb7 },
|
||||
{ "NDA", 0xb8 },
|
||||
{ "CDA", 0xb9 },
|
||||
{ "TOL", 0xba },
|
||||
{ "DRV", 0xbb },
|
||||
{ "DOC", 0xbf },
|
||||
{ "PNT", 0xc0 },
|
||||
{ "PIC", 0xc1 },
|
||||
{ "FON", 0xcb },
|
||||
{ "PAS", 0xef },
|
||||
{ "CMD", 0xf0 },
|
||||
{ "LNK", 0xf8 },
|
||||
{ "BAS", 0xfc },
|
||||
{ "VAR", 0xfd },
|
||||
{ "REL", 0xfe },
|
||||
{ "SYS", 0xff },
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static uint32_t number_operand(const char *YYCURSOR, bool required = true) {
|
||||
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
uint32_t rv = 0;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
@@ -146,7 +143,8 @@ exit:
|
||||
|
||||
static uint32_t type_operand(const char *YYCURSOR, bool required = true) {
|
||||
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
uint32_t rv = 0;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
@@ -189,10 +187,10 @@ static uint32_t type_operand(const char *YYCURSOR, bool required = true) {
|
||||
std::string s(iter, YYCURSOR);
|
||||
for(char &c : s) c = std::toupper(c);
|
||||
auto iter = types.find(s);
|
||||
if (iter == types.end) {
|
||||
if (iter == types.end()) {
|
||||
throw std::invalid_argument("bad operand");
|
||||
}
|
||||
rv = *iter;
|
||||
rv = iter->second;
|
||||
}
|
||||
*/
|
||||
exit:
|
||||
@@ -205,7 +203,8 @@ exit:
|
||||
|
||||
|
||||
static int x_number_operand(const char *YYCURSOR) {
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
uint32_t rv = 0;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
@@ -247,24 +246,28 @@ exit:
|
||||
}
|
||||
|
||||
static std::string x_label_operand(const char *YYCURSOR) {
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
std::string rv;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
|
||||
ident {
|
||||
std::string s(iter, YYCURSOR);
|
||||
//look up symbol, verify it's an absolute value, etc
|
||||
goto exit;
|
||||
}
|
||||
*/
|
||||
exit:
|
||||
char c = *YYCURSOR;
|
||||
if (isspace(c) || c == 0) return rv;
|
||||
if (isspace(c) || c == 0) {
|
||||
//look up symbol, verify it's an absolute value, etc
|
||||
return rv;
|
||||
}
|
||||
throw std::invalid_argument("bad operand");
|
||||
}
|
||||
|
||||
static std::string x_string_operand(const char *YYCURSOR) {
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
std::string rv;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
@@ -285,17 +288,18 @@ exit:
|
||||
|
||||
static int ovr_operand(const char *YYCURSOR) {
|
||||
int rv = 0;
|
||||
const char *YYMARKER = nullptr;
|
||||
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
[;*] | eof {
|
||||
return 1;
|
||||
return OVR_NONE;
|
||||
}
|
||||
'ALL' {
|
||||
rv = -1;
|
||||
rv = OVR_ALL;
|
||||
}
|
||||
'OFF' {
|
||||
rv = 0;
|
||||
rv = OVR_OFF;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -307,7 +311,8 @@ static int ovr_operand(const char *YYCURSOR) {
|
||||
|
||||
static std::string label_operand(const char *YYCURSOR, bool required = true) {
|
||||
std::string rv;
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
[;*] | eof {
|
||||
@@ -320,6 +325,7 @@ static std::string label_operand(const char *YYCURSOR, bool required = true) {
|
||||
}
|
||||
*/
|
||||
|
||||
exit:
|
||||
char c = *YYCURSOR;
|
||||
if (isspace(c) || c == 0) return rv;
|
||||
|
||||
@@ -329,7 +335,8 @@ static std::string label_operand(const char *YYCURSOR, bool required = true) {
|
||||
|
||||
static std::string path_operand(const char *YYCURSOR, bool required = true) {
|
||||
std::string rv;
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
[;*] | eof {
|
||||
@@ -348,6 +355,7 @@ static std::string path_operand(const char *YYCURSOR, bool required = true) {
|
||||
|
||||
*/
|
||||
|
||||
exit:
|
||||
char c = *YYCURSOR;
|
||||
if (isspace(c) || c == 0) return rv;
|
||||
|
||||
@@ -358,7 +366,7 @@ static std::string path_operand(const char *YYCURSOR, bool required = true) {
|
||||
static void no_operand(const char *YYCURSOR) {
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
[;*] | eof { return }
|
||||
[;*] | eof { return; }
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -366,7 +374,8 @@ static void no_operand(const char *YYCURSOR) {
|
||||
static std::string string_operand(const char *YYCURSOR, bool required = true) {
|
||||
|
||||
std::string rv;
|
||||
char *iter = YYCURSOR;
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
/*!re2c
|
||||
* { throw std::invalid_argument("bad operand"); }
|
||||
[;*] | eof {
|
||||
@@ -378,7 +387,7 @@ static std::string string_operand(const char *YYCURSOR, bool required = true) {
|
||||
goto exit;
|
||||
}
|
||||
*/
|
||||
|
||||
exit:
|
||||
char c = *YYCURSOR;
|
||||
if (isspace(c) || c == 0) return rv;
|
||||
throw std::invalid_argument("bad operand");
|
||||
@@ -386,21 +395,24 @@ static std::string string_operand(const char *YYCURSOR, bool required = true) {
|
||||
|
||||
|
||||
|
||||
static void parse_line(const char *YYCURSOR) {
|
||||
void parse_line(const char *YYCURSOR) {
|
||||
|
||||
unsigned cmd = 0;
|
||||
std::string label;
|
||||
label_t label;
|
||||
opcode_t opcode = OP_NONE;
|
||||
operand_t operand;
|
||||
|
||||
const char *iter = YYCURSOR;
|
||||
const char *YYMARKER = nullptr;
|
||||
|
||||
/*!re2c
|
||||
|
||||
* { throw std::invalid_argument("bad label") }
|
||||
* { throw std::invalid_argument("bad label"); }
|
||||
[;*] | eof {
|
||||
return;
|
||||
}
|
||||
ws { goto opcode }
|
||||
ws { goto opcode; }
|
||||
ident / (ws|eof) {
|
||||
label(iter, YYCURSOR);
|
||||
label = std::string(iter, YYCURSOR);
|
||||
goto opcode;
|
||||
}
|
||||
|
||||
@@ -409,27 +421,26 @@ static void parse_line(const char *YYCURSOR) {
|
||||
|
||||
opcode:
|
||||
|
||||
|
||||
while (isspace(*YYCURSOR)) ++YYCURSOR;
|
||||
iter = YYCURSOR;
|
||||
|
||||
/*!re2c
|
||||
|
||||
* { throw std::invalid_argument("bad opcode"); }
|
||||
[;*]|eof { return 0; }
|
||||
[;*]|eof { return; }
|
||||
|
||||
'=' / (ws|eof) { cmd = CMD_EQ; goto operand; }
|
||||
'=' / (ws|eof) { opcode = OP_EQ; goto operand; }
|
||||
|
||||
[A-Za-z]+ / (ws|eof) {
|
||||
size_t = l YYCURSOR - iter;
|
||||
size_t l = YYCURSOR - iter;
|
||||
if (l > 3) l = 3;
|
||||
std::string s(iter, iter + l);
|
||||
for (char &c in s): c = std::toupper(c);
|
||||
auto iter = commands.find(s);
|
||||
if (!iter == commands.end()) {
|
||||
for (char &c : s) c = std::toupper(c);
|
||||
auto iter = opcodes.find(s);
|
||||
if (iter == opcodes.end()) {
|
||||
throw std::invalid_argument("bad opcode");
|
||||
}
|
||||
cmd = *iter;
|
||||
opcode = iter->second;
|
||||
goto operand;
|
||||
}
|
||||
*/
|
||||
@@ -439,51 +450,53 @@ operand:
|
||||
while (isspace(*YYCURSOR)) ++YYCURSOR;
|
||||
iter = YYCURSOR;
|
||||
|
||||
std::string str_operand;
|
||||
long int_operand;
|
||||
|
||||
switch(cmd) {
|
||||
case CMD_LNK:
|
||||
case CMD_PUT:
|
||||
case CMD_ASM:
|
||||
case CMD_SAV:
|
||||
case CMD_LIB:
|
||||
case CMD_IF:
|
||||
case CMD_PFX:
|
||||
case CMD_IMP:
|
||||
case CMD_RES:
|
||||
case CMD_FIL:
|
||||
str_operand = path_operand(YYCURSOR);
|
||||
switch(opcode) {
|
||||
case OP_LNK:
|
||||
case OP_PUT:
|
||||
case OP_ASM:
|
||||
case OP_SAV:
|
||||
case OP_LIB:
|
||||
case OP_IF:
|
||||
case OP_PFX:
|
||||
case OP_IMP:
|
||||
case OP_RES:
|
||||
case OP_FIL:
|
||||
operand = path_operand(YYCURSOR);
|
||||
break;
|
||||
case CMD_ORG:
|
||||
case CMD_ADR:
|
||||
case CMD_DS:
|
||||
case CMD_KND:
|
||||
case CMD_VER:
|
||||
case CMD_ALI:
|
||||
case CMD_LKV:
|
||||
case CMD_DO:
|
||||
case CMD_EQU:
|
||||
case CMD_EQ:
|
||||
case CMD_GEQ:
|
||||
int_operand = number_operand(YYCURSOR);
|
||||
case OP_ORG:
|
||||
case OP_ADR:
|
||||
case OP_DS:
|
||||
case OP_KND:
|
||||
case OP_VER:
|
||||
case OP_ALI:
|
||||
case OP_LKV:
|
||||
case OP_DO:
|
||||
case OP_EQU:
|
||||
case OP_EQ:
|
||||
case OP_GEQ:
|
||||
operand = number_operand(YYCURSOR);
|
||||
break;
|
||||
case CMD_TYP:
|
||||
int_operand = type_operand(YYCURSOR);
|
||||
case OP_TYP:
|
||||
operand = type_operand(YYCURSOR);
|
||||
break;
|
||||
case CMD_OVR:
|
||||
int_operand = ovr_operand(YYCURSOR);
|
||||
case OP_OVR:
|
||||
operand = ovr_operand(YYCURSOR);
|
||||
break;
|
||||
case CMD_POS:
|
||||
case CMD_LEN:
|
||||
str_operand = label_operand(YYCURSOR, false);
|
||||
case OP_POS:
|
||||
case OP_LEN: {
|
||||
std::string tmp = label_operand(YYCURSOR, false);
|
||||
if (!tmp.empty()) operand = std::move(tmp);
|
||||
break;
|
||||
case CMD_KBD:
|
||||
str_operand = string_operand(YYCURSOR, false);
|
||||
}
|
||||
case OP_KBD: {
|
||||
std::string tmp = string_operand(YYCURSOR, false);
|
||||
if (!tmp.empty()) operand = std::move(tmp);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_CMD:
|
||||
str_operand = string(YYCURSOR);
|
||||
case OP_CMD:
|
||||
str_operand = std::string(YYCURSOR);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -491,143 +504,11 @@ operand:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(cmd) {
|
||||
|
||||
case CMD_NONE:
|
||||
case CMD_NOL: /* asm: default list off */
|
||||
case CMD_PUT: /* check if source file is outdated */
|
||||
case CMD_OVR: /* override outdated check and force assembly */
|
||||
case CMD_FAS: /* fast linker. only one SAV allowed */
|
||||
/* nop */
|
||||
break;
|
||||
|
||||
case CMD_LNK:
|
||||
/* link file ... */
|
||||
break;
|
||||
case CMD_ORG:
|
||||
/* set the link origin. also used as aux type */
|
||||
break;
|
||||
case CMD_ADR:
|
||||
/* set the file aux type */
|
||||
break;
|
||||
|
||||
case CMD_SAV:
|
||||
/* save linked file. for xl linker, specifies segment name */
|
||||
break;
|
||||
|
||||
case CMD_TYP:
|
||||
/* set the file type */
|
||||
break;
|
||||
|
||||
case CMD_EXT:
|
||||
/* print addresses of all resolved externals (not just errors)
|
||||
disabled after each SAV
|
||||
*/
|
||||
break;
|
||||
case CMD_ENT:
|
||||
/* print the entry list */
|
||||
/* flag symbol w ? if unused */
|
||||
break;
|
||||
case CMD_DAT:
|
||||
/* print the current date and time */
|
||||
break;
|
||||
|
||||
case CMD_END:
|
||||
/* end of command file (optional) */
|
||||
break;
|
||||
|
||||
case CMD_LIB:
|
||||
/* search directory for unresolved symbols */
|
||||
break;
|
||||
|
||||
case CMD_LKV:
|
||||
/* specify linker version */
|
||||
/* 0 = binary, 1 = Linker.GS, 2 = Linker.XL, 3 = convert to OMF object file */
|
||||
switch (int_operand) {
|
||||
case 0: throw std::runtime_error("binary linker not supported");
|
||||
case 3: throw std::runtime_error("object file linker not supported");
|
||||
case 1:
|
||||
case 2:
|
||||
/* default file type = S16 */
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("bad linker version");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_VER:
|
||||
/*specify OMF version. 1 or 2 */
|
||||
break;
|
||||
|
||||
case CMD_KND:
|
||||
/* set the OMF kind flag */
|
||||
/* 8-bit for v 1, 16-bit for v2 */
|
||||
break;
|
||||
|
||||
case CMD_ALI:
|
||||
/* OMF align field. default = 0 */
|
||||
break;
|
||||
|
||||
case CMD_DS:
|
||||
/* OMF RESSPC field */
|
||||
break;
|
||||
|
||||
|
||||
case CMD_LEN:
|
||||
/* Puts "LABEL" in symbol dictionary a san ENTry whose value is equal to the number of bytes of the last linked file. */
|
||||
break;
|
||||
case CMD_POS:
|
||||
|
||||
|
||||
|
||||
case CMD_ASM:
|
||||
default:
|
||||
throw std::runtime_error("opcode not supported");
|
||||
}
|
||||
|
||||
|
||||
void evaluate(label_t label, opcode_t opcode, operand_t operand);
|
||||
|
||||
evaluate(label, opcode, opcode);
|
||||
}
|
||||
|
||||
int process_link_file(const std::string &path) {
|
||||
|
||||
FILE *fp;
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) {
|
||||
warn("Unable to open %s", path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int no = 1;
|
||||
int errors = 0;
|
||||
const char *line = NULL;
|
||||
size_t cap = 0;
|
||||
for(;; ++no) {
|
||||
|
||||
ssize_t len = getline(&line, &cap, fp);
|
||||
if (len == 0) break;
|
||||
if (len < 0) {
|
||||
warn("read error");
|
||||
++errors;
|
||||
break;
|
||||
}
|
||||
/* strip trailing ws */
|
||||
while (len && isspace(line[len-1])) --len;
|
||||
line[len] = 0;
|
||||
if (len == 0) continue;
|
||||
|
||||
try {
|
||||
parse_line(line);
|
||||
} catch (std::exception &ex) {
|
||||
fprintf(stderr, "%s in line: %d\n", ex.what(), no);
|
||||
fprintf(stderr, "%s\n", line);
|
||||
if (++errors >= 10) {
|
||||
fputs("Too many errors, aborting\n", stderr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(line);
|
||||
return errors ? -1 : 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user