mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement state file output for RGBASM (#1435)
This commit is contained in:
@@ -38,6 +38,7 @@ _rgbasm_completions() {
|
|||||||
[p]="pad-value:unk"
|
[p]="pad-value:unk"
|
||||||
[Q]="q-precision:unk"
|
[Q]="q-precision:unk"
|
||||||
[r]="recursion-depth:unk"
|
[r]="recursion-depth:unk"
|
||||||
|
[s]="state:unk"
|
||||||
[W]="warning:warning"
|
[W]="warning:warning"
|
||||||
[X]="max-errors:unk"
|
[X]="max-errors:unk"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ _rgbgfx_completions() {
|
|||||||
[a]="attr-map:glob-*.attrmap"
|
[a]="attr-map:glob-*.attrmap"
|
||||||
[A]="auto-attr-map:normal"
|
[A]="auto-attr-map:normal"
|
||||||
[b]="base-tiles:unk"
|
[b]="base-tiles:unk"
|
||||||
|
[c]="colors:unk"
|
||||||
[d]="depth:unk"
|
[d]="depth:unk"
|
||||||
[L]="slice:unk"
|
[L]="slice:unk"
|
||||||
[N]="nb-tiles:unk"
|
[N]="nb-tiles:unk"
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ local args=(
|
|||||||
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||||
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||||
'(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
|
'(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
|
||||||
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}'"
|
'(-M --dependfile)'{-M,--dependfile}"+[Write deps in make format]:output file:_files -g '*.{d,mk}'"
|
||||||
-MG'[Assume missing files should be generated]'
|
-MG'[Assume missing files should be generated]'
|
||||||
-MP'[Add phony targets to all deps]'
|
-MP'[Add phony targets to all deps]'
|
||||||
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||||
@@ -54,6 +54,7 @@ local args=(
|
|||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
|
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
|
||||||
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||||
|
'(-s --state)'{-s,--state}"+[Write features of final state]:state file:_files -g '*.dump.asm'"
|
||||||
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
||||||
'(-X --max-errors)'{-X,--max-errors}'+[Set maximum errors before aborting]:maximum errors:'
|
'(-X --max-errors)'{-X,--max-errors}'+[Set maximum errors before aborting]:maximum errors:'
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ local args=(
|
|||||||
|
|
||||||
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
||||||
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
|
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
|
||||||
|
'(-c --colors)'{-c,--colors}'+[Specify color palettes]:palette spec:'
|
||||||
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
||||||
'(-L --slice)'{-L,--slice}'+[Only process a portion of the image]:input slice:'
|
'(-L --slice)'{-L,--slice}'+[Only process a portion of the image]:input slice:'
|
||||||
'(-N --nb-tiles)'{-N,--nb-tiles}'+[Limit number of tiles]:tile count:'
|
'(-N --nb-tiles)'{-N,--nb-tiles}'+[Limit number of tiles]:tile count:'
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
|
|
||||||
#define DEFAULT_CHARMAP_NAME "main"
|
#define DEFAULT_CHARMAP_NAME "main"
|
||||||
|
|
||||||
|
bool charmap_ForEach(
|
||||||
|
void (*mapFunc)(std::string const &),
|
||||||
|
void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||||
|
);
|
||||||
void charmap_New(std::string const &name, std::string const *baseName);
|
void charmap_New(std::string const &name, std::string const *baseName);
|
||||||
void charmap_Set(std::string const &name);
|
void charmap_Set(std::string const &name);
|
||||||
void charmap_Push();
|
void charmap_Push();
|
||||||
|
|||||||
@@ -6,12 +6,23 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
struct Expression;
|
struct Expression;
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
|
|
||||||
|
enum StateFeature {
|
||||||
|
STATE_EQU,
|
||||||
|
STATE_VAR,
|
||||||
|
STATE_EQUS,
|
||||||
|
STATE_CHAR,
|
||||||
|
STATE_MACRO,
|
||||||
|
NB_STATE_FEATURES
|
||||||
|
};
|
||||||
|
|
||||||
extern std::string objectFileName;
|
extern std::string objectFileName;
|
||||||
|
|
||||||
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
|
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
|
||||||
@@ -21,5 +32,6 @@ void out_CreateAssert(
|
|||||||
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
|
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
|
||||||
);
|
);
|
||||||
void out_WriteObject();
|
void out_WriteObject();
|
||||||
|
void out_WriteState(std::string name, std::vector<StateFeature> const &features);
|
||||||
|
|
||||||
#endif // RGBDS_ASM_OUTPUT_HPP
|
#endif // RGBDS_ASM_OUTPUT_HPP
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ struct Symbol {
|
|||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
|
||||||
uint32_t ID; // ID of the symbol in the object file (-1 if none)
|
uint32_t ID; // ID of the symbol in the object file (-1 if none)
|
||||||
|
uint32_t defIndex; // Ordering of the symbol in the state file
|
||||||
|
|
||||||
bool isDefined() const { return type != SYM_REF; }
|
bool isDefined() const { return type != SYM_REF; }
|
||||||
bool isNumeric() const { return type == SYM_LABEL || type == SYM_EQU || type == SYM_VAR; }
|
bool isNumeric() const { return type == SYM_LABEL || type == SYM_EQU || type == SYM_VAR; }
|
||||||
|
|||||||
46
man/rgbasm.1
46
man/rgbasm.1
@@ -23,6 +23,7 @@
|
|||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
.Op Fl Q Ar fix_precision
|
.Op Fl Q Ar fix_precision
|
||||||
.Op Fl r Ar recursion_depth
|
.Op Fl r Ar recursion_depth
|
||||||
|
.Op Fl s Ar features Ns : Ns Ar state_file
|
||||||
.Op Fl W Ar warning
|
.Op Fl W Ar warning
|
||||||
.Op Fl X Ar max_errors
|
.Op Fl X Ar max_errors
|
||||||
.Ar asmfile
|
.Ar asmfile
|
||||||
@@ -143,8 +144,45 @@ The argument may start with a
|
|||||||
to match the Q notation, for example,
|
to match the Q notation, for example,
|
||||||
.Ql Fl Q Ar .16 .
|
.Ql Fl Q Ar .16 .
|
||||||
.It Fl r Ar recursion_depth , Fl \-recursion-depth Ar recursion_depth
|
.It Fl r Ar recursion_depth , Fl \-recursion-depth Ar recursion_depth
|
||||||
Specifies the recursion depth past which RGBASM will assume being in an infinite loop.
|
Specifies the recursion depth past which
|
||||||
|
.Nm
|
||||||
|
will assume being in an infinite loop.
|
||||||
The default is 64.
|
The default is 64.
|
||||||
|
.It Fl s Ar features Ns : Ns Ar state_file , Fl \-state Ar features Ns : Ns Ar state_file
|
||||||
|
Write the specified
|
||||||
|
.Ar features
|
||||||
|
to
|
||||||
|
.Ar state_file ,
|
||||||
|
based on the final state of
|
||||||
|
.Nm
|
||||||
|
at the end of its input.
|
||||||
|
The expected
|
||||||
|
.Ar features
|
||||||
|
are a comma-separated subset of the following:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Cm equ
|
||||||
|
Write all numeric constants as
|
||||||
|
.Ql Ic def Ar name Ic equ Ar value .
|
||||||
|
.It Cm var
|
||||||
|
Write all variables as
|
||||||
|
.Ql Ic def Ar name Ic = Ar value .
|
||||||
|
.It Cm equs
|
||||||
|
Write all string constants as
|
||||||
|
.Ql Ic def Ar name Ic equs Qq Ar value .
|
||||||
|
.It Cm char
|
||||||
|
Write all characters as
|
||||||
|
.Ql Ic charmap Ar name , Ar value .
|
||||||
|
.It Cm macro
|
||||||
|
Write all macros as
|
||||||
|
.Ql Ic macro Ar name No ... Ic endm .
|
||||||
|
.It Cm all
|
||||||
|
Acts like
|
||||||
|
.Cm equ,var,equs,char,macro .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
This flag may be specified multiple times with different feature subsets to write them to different files (see
|
||||||
|
.Sx EXAMPLES
|
||||||
|
below).
|
||||||
.It Fl V , Fl \-version
|
.It Fl V , Fl \-version
|
||||||
Print the version of the program and exit.
|
Print the version of the program and exit.
|
||||||
.It Fl v , Fl \-verbose
|
.It Fl v , Fl \-verbose
|
||||||
@@ -327,6 +365,12 @@ The resulting object file is not yet a usable ROM image\(emit must first be run
|
|||||||
.Xr rgblink 1
|
.Xr rgblink 1
|
||||||
and then
|
and then
|
||||||
.Xr rgbfix 1 .
|
.Xr rgbfix 1 .
|
||||||
|
.Pp
|
||||||
|
Writing the final assembler state to a file:
|
||||||
|
.Dl $ rgbasm -s all:state.dump.asm foo.asm
|
||||||
|
.Pp
|
||||||
|
Or to multiple files:
|
||||||
|
.Dl $ rgbasm -s equ,var:numbers.dump.asm -s equs:strings.dump.asm foo.asm
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs on
|
Please report bugs on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "asm/charmap.hpp"
|
#include "asm/charmap.hpp"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -17,9 +18,9 @@
|
|||||||
// Essentially a tree, where each nodes stores a single character's worth of info:
|
// Essentially a tree, where each nodes stores a single character's worth of info:
|
||||||
// whether there exists a mapping that ends at the current character,
|
// whether there exists a mapping that ends at the current character,
|
||||||
struct CharmapNode {
|
struct CharmapNode {
|
||||||
std::vector<int32_t> value; // The mapped value, if there exists a mapping that ends here
|
std::vector<int32_t> value; // The mapped value, if there exists a mapping that ends here
|
||||||
// This MUST be indexes and not pointers, because pointers get invalidated by reallocation!
|
// These MUST be indexes and not pointers, because pointers get invalidated by reallocation!
|
||||||
size_t next[256]; // Indexes of where to go next, 0 = nowhere
|
size_t next[256]; // Indexes of where to go next, 0 = nowhere
|
||||||
|
|
||||||
bool isTerminal() const { return !value.empty(); }
|
bool isTerminal() const { return !value.empty(); }
|
||||||
};
|
};
|
||||||
@@ -27,49 +28,64 @@ struct CharmapNode {
|
|||||||
struct Charmap {
|
struct Charmap {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<CharmapNode> nodes; // first node is reserved for the root node
|
std::vector<CharmapNode> nodes; // first node is reserved for the root node
|
||||||
|
// FIXME: strictly speaking, this is redundant, we could walk the trie to get mappings instead
|
||||||
|
std::unordered_map<size_t, std::string> mappings; // keys are indexes of terminal nodes
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unordered_map<std::string, Charmap> charmaps;
|
static std::deque<Charmap> charmapList;
|
||||||
|
static std::unordered_map<std::string, size_t> charmapMap; // Indexes into `charmapList`
|
||||||
|
|
||||||
static Charmap *currentCharmap;
|
static Charmap *currentCharmap;
|
||||||
std::stack<Charmap *> charmapStack;
|
std::stack<Charmap *> charmapStack;
|
||||||
|
|
||||||
|
bool charmap_ForEach(
|
||||||
|
void (*mapFunc)(std::string const &),
|
||||||
|
void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||||
|
) {
|
||||||
|
for (Charmap &charmap : charmapList) {
|
||||||
|
mapFunc(charmap.name);
|
||||||
|
for (size_t i = 0; i < charmap.nodes.size(); ++i) {
|
||||||
|
if (CharmapNode const &node = charmap.nodes[i]; node.isTerminal())
|
||||||
|
charFunc(charmap.mappings[i], node.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !charmapList.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void charmap_New(std::string const &name, std::string const *baseName) {
|
void charmap_New(std::string const &name, std::string const *baseName) {
|
||||||
Charmap *base = nullptr;
|
size_t baseIdx = (size_t)-1;
|
||||||
|
|
||||||
if (baseName != nullptr) {
|
if (baseName != nullptr) {
|
||||||
auto search = charmaps.find(*baseName);
|
if (auto search = charmapMap.find(*baseName); search == charmapMap.end())
|
||||||
|
|
||||||
if (search == charmaps.end())
|
|
||||||
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
||||||
else
|
else
|
||||||
base = &search->second;
|
baseIdx = search->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charmaps.find(name) != charmaps.end()) {
|
if (charmapMap.find(name) != charmapMap.end()) {
|
||||||
error("Charmap '%s' already exists\n", name.c_str());
|
error("Charmap '%s' already exists\n", name.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init the new charmap's fields
|
// Init the new charmap's fields
|
||||||
Charmap &charmap = charmaps[name];
|
charmapMap[name] = charmapList.size();
|
||||||
|
Charmap &charmap = charmapList.emplace_back();
|
||||||
|
|
||||||
if (base)
|
if (baseIdx != (size_t)-1)
|
||||||
charmap.nodes = base->nodes; // Copies `base->nodes`
|
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[baseIdx].nodes`
|
||||||
else
|
else
|
||||||
charmap.nodes.emplace_back(); // Zero-init the root node
|
charmap.nodes.emplace_back(); // Zero-init the root node
|
||||||
|
|
||||||
charmap.name = name;
|
charmap.name = name;
|
||||||
|
|
||||||
currentCharmap = &charmap;
|
currentCharmap = &charmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Set(std::string const &name) {
|
void charmap_Set(std::string const &name) {
|
||||||
auto search = charmaps.find(name);
|
if (auto search = charmapMap.find(name); search == charmapMap.end())
|
||||||
|
|
||||||
if (search == charmaps.end())
|
|
||||||
error("Charmap '%s' doesn't exist\n", name.c_str());
|
error("Charmap '%s' doesn't exist\n", name.c_str());
|
||||||
else
|
else
|
||||||
currentCharmap = &search->second;
|
currentCharmap = &charmapList[search->second];
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Push() {
|
void charmap_Push() {
|
||||||
@@ -98,6 +114,8 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
|||||||
// Switch to and zero-init the new node
|
// Switch to and zero-init the new node
|
||||||
nextIdxRef = charmap.nodes.size();
|
nextIdxRef = charmap.nodes.size();
|
||||||
nextIdx = nextIdxRef;
|
nextIdx = nextIdxRef;
|
||||||
|
// Save the mapping of this node
|
||||||
|
charmap.mappings[charmap.nodes.size()] = mapping;
|
||||||
// This may reallocate `charmap.nodes` and invalidate `nextIdxRef`,
|
// This may reallocate `charmap.nodes` and invalidate `nextIdxRef`,
|
||||||
// which is why we keep the actual value in `nextIdx`
|
// which is why we keep the actual value in `nextIdx`
|
||||||
charmap.nodes.emplace_back();
|
charmap.nodes.emplace_back();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "asm/main.hpp"
|
#include "asm/main.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -47,7 +48,7 @@ static std::string make_escape(std::string &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Short options
|
// Short options
|
||||||
static char const *optstring = "b:D:Eg:I:M:o:P:p:Q:r:VvW:wX:";
|
static char const *optstring = "b:D:Eg:I:M:o:P:p:Q:r:s:VvW:wX:";
|
||||||
|
|
||||||
// Variables for the long-only options
|
// Variables for the long-only options
|
||||||
static int depType; // Variants of `-M`
|
static int depType; // Variants of `-M`
|
||||||
@@ -77,6 +78,7 @@ static option const longopts[] = {
|
|||||||
{"pad-value", required_argument, nullptr, 'p'},
|
{"pad-value", required_argument, nullptr, 'p'},
|
||||||
{"q-precision", required_argument, nullptr, 'Q'},
|
{"q-precision", required_argument, nullptr, 'Q'},
|
||||||
{"recursion-depth", required_argument, nullptr, 'r'},
|
{"recursion-depth", required_argument, nullptr, 'r'},
|
||||||
|
{"state", required_argument, nullptr, 's'},
|
||||||
{"version", no_argument, nullptr, 'V'},
|
{"version", no_argument, nullptr, 'V'},
|
||||||
{"verbose", no_argument, nullptr, 'v'},
|
{"verbose", no_argument, nullptr, 'v'},
|
||||||
{"warning", required_argument, nullptr, 'W'},
|
{"warning", required_argument, nullptr, 'W'},
|
||||||
@@ -89,14 +91,16 @@ static void printUsage() {
|
|||||||
"Usage: rgbasm [-EVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
"Usage: rgbasm [-EVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
||||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||||
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
||||||
" [-r depth] [-W warning] [-X max_errors] <file>\n"
|
" [-r depth] [-s features:state_file] [-W warning] [-X max_errors]\n"
|
||||||
|
" <file>\n"
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
" -E, --export-all export all labels\n"
|
" -E, --export-all export all labels\n"
|
||||||
" -M, --dependfile <path> set the output dependency file\n"
|
" -M, --dependfile <path> set the output dependency file\n"
|
||||||
" -o, --output <path> set the output object file\n"
|
" -o, --output <path> set the output object file\n"
|
||||||
" -p, --pad-value <value> set the value to use for `ds'\n"
|
" -p, --pad-value <value> set the value to use for `ds'\n"
|
||||||
" -V, --version print RGBASM version and exit\n"
|
" -s, --state <features>:<path> set an output state file\n"
|
||||||
" -W, --warning <warning> enable or disable warnings\n"
|
" -V, --version print RGBASM version and exit\n"
|
||||||
|
" -W, --warning <warning> enable or disable warnings\n"
|
||||||
"\n"
|
"\n"
|
||||||
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
|
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
|
||||||
stderr
|
stderr
|
||||||
@@ -126,6 +130,7 @@ int main(int argc, char *argv[]) {
|
|||||||
sym_SetExportAll(false);
|
sym_SetExportAll(false);
|
||||||
uint32_t maxDepth = DEFAULT_MAX_DEPTH;
|
uint32_t maxDepth = DEFAULT_MAX_DEPTH;
|
||||||
char const *dependFileName = nullptr;
|
char const *dependFileName = nullptr;
|
||||||
|
std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs;
|
||||||
std::string newTarget;
|
std::string newTarget;
|
||||||
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
|
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
|
||||||
if (isatty(STDERR_FILENO))
|
if (isatty(STDERR_FILENO))
|
||||||
@@ -227,6 +232,58 @@ int main(int argc, char *argv[]) {
|
|||||||
errx("Invalid argument for option 'r'");
|
errx("Invalid argument for option 'r'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 's': {
|
||||||
|
// Split "<features>:<name>" so `musl_optarg` is "<features>" and `name` is "<name>"
|
||||||
|
char *name = strchr(musl_optarg, ':');
|
||||||
|
if (!name)
|
||||||
|
errx("Invalid argument for option 's'");
|
||||||
|
*name++ = '\0';
|
||||||
|
|
||||||
|
std::vector<StateFeature> features;
|
||||||
|
for (char *feature = musl_optarg; feature;) {
|
||||||
|
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
|
||||||
|
char *next = strchr(feature, ',');
|
||||||
|
if (next)
|
||||||
|
*next++ = '\0';
|
||||||
|
// Trim whitespace from the beginning of `feature`...
|
||||||
|
feature += strspn(feature, " \t");
|
||||||
|
// ...and from the end
|
||||||
|
if (char *end = strpbrk(feature, " \t"); end)
|
||||||
|
*end = '\0';
|
||||||
|
// A feature must be specified
|
||||||
|
if (*feature == '\0')
|
||||||
|
errx("Empty feature for option 's'");
|
||||||
|
// Parse the `feature` and update the `features` list
|
||||||
|
if (!strcasecmp(feature, "all")) {
|
||||||
|
if (!features.empty())
|
||||||
|
warnx("Redundant feature before \"%s\" for option 's'", feature);
|
||||||
|
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
|
||||||
|
} else {
|
||||||
|
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
|
||||||
|
: !strcasecmp(feature, "var") ? STATE_VAR
|
||||||
|
: !strcasecmp(feature, "equs") ? STATE_EQUS
|
||||||
|
: !strcasecmp(feature, "char") ? STATE_CHAR
|
||||||
|
: !strcasecmp(feature, "macro") ? STATE_MACRO
|
||||||
|
: NB_STATE_FEATURES;
|
||||||
|
if (value == NB_STATE_FEATURES) {
|
||||||
|
errx("Invalid feature for option 's': \"%s\"", feature);
|
||||||
|
} else if (std::find(RANGE(features), value) != features.end()) {
|
||||||
|
warnx("Ignoring duplicate feature for option 's': \"%s\"", feature);
|
||||||
|
} else {
|
||||||
|
features.push_back(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
feature = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateFileSpecs.find(name) != stateFileSpecs.end())
|
||||||
|
warnx("Overriding state filename %s", name);
|
||||||
|
if (verbose)
|
||||||
|
printf("State filename %s\n", name);
|
||||||
|
stateFileSpecs.emplace(name, std::move(features));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgbasm %s\n", get_package_version_string());
|
printf("rgbasm %s\n", get_package_version_string());
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -334,5 +391,8 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
out_WriteObject();
|
out_WriteObject();
|
||||||
|
|
||||||
|
for (auto [name, features] : stateFileSpecs)
|
||||||
|
out_WriteState(name, features);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,18 @@
|
|||||||
|
|
||||||
#include "asm/output.hpp"
|
#include "asm/output.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
#include "helpers.hpp" // assume, Defer
|
#include "helpers.hpp" // assume, Defer
|
||||||
|
|
||||||
|
#include "asm/charmap.hpp"
|
||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
#include "asm/lexer.hpp"
|
#include "asm/lexer.hpp"
|
||||||
#include "asm/main.hpp"
|
#include "asm/main.hpp"
|
||||||
@@ -354,3 +355,171 @@ void out_SetFileName(std::string const &name) {
|
|||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Output filename %s\n", objectFileName.c_str());
|
printf("Output filename %s\n", objectFileName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dumpString(std::string const &escape, FILE *file) {
|
||||||
|
for (char c : escape) {
|
||||||
|
// Escape characters that need escaping
|
||||||
|
switch (c) {
|
||||||
|
case '\\':
|
||||||
|
case '"':
|
||||||
|
case '{':
|
||||||
|
putc('\\', file);
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
putc(c, file);
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
fputs("\\n", file);
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
fputs("\\r", file);
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
fputs("\\t", file);
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
fputs("\\0", file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dumpEquConstants(FILE *file) {
|
||||||
|
static std::vector<Symbol *> equConstants; // `static` so `sym_ForEach` callback can see it
|
||||||
|
equConstants.clear();
|
||||||
|
|
||||||
|
sym_ForEach([](Symbol &sym) {
|
||||||
|
if (!sym.isBuiltin && sym.type == SYM_EQU)
|
||||||
|
equConstants.push_back(&sym);
|
||||||
|
});
|
||||||
|
// Constants are ordered by file, then by definition order
|
||||||
|
std::sort(RANGE(equConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
|
return sym1->defIndex < sym2->defIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Symbol const *sym : equConstants) {
|
||||||
|
uint32_t value = static_cast<uint32_t>(sym->getOutputValue());
|
||||||
|
fprintf(file, "def %s equ $%" PRIx32 "\n", sym->name.c_str(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !equConstants.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dumpVariables(FILE *file) {
|
||||||
|
static std::vector<Symbol *> variables; // `static` so `sym_ForEach` callback can see it
|
||||||
|
variables.clear();
|
||||||
|
|
||||||
|
sym_ForEach([](Symbol &sym) {
|
||||||
|
if (!sym.isBuiltin && sym.type == SYM_VAR)
|
||||||
|
variables.push_back(&sym);
|
||||||
|
});
|
||||||
|
// Variables are ordered by file, then by definition order
|
||||||
|
std::sort(RANGE(variables), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
|
return sym1->defIndex < sym2->defIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Symbol const *sym : variables) {
|
||||||
|
uint32_t value = static_cast<uint32_t>(sym->getOutputValue());
|
||||||
|
fprintf(file, "def %s = $%" PRIx32 "\n", sym->name.c_str(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !variables.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dumpEqusConstants(FILE *file) {
|
||||||
|
static std::vector<Symbol *> equsConstants; // `static` so `sym_ForEach` callback can see it
|
||||||
|
equsConstants.clear();
|
||||||
|
|
||||||
|
sym_ForEach([](Symbol &sym) {
|
||||||
|
if (!sym.isBuiltin && sym.type == SYM_EQUS)
|
||||||
|
equsConstants.push_back(&sym);
|
||||||
|
});
|
||||||
|
// Constants are ordered by file, then by definition order
|
||||||
|
std::sort(RANGE(equsConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
|
return sym1->defIndex < sym2->defIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Symbol const *sym : equsConstants) {
|
||||||
|
fprintf(file, "def %s equs \"", sym->name.c_str());
|
||||||
|
dumpString(*sym->getEqus(), file);
|
||||||
|
fputs("\"\n", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !equsConstants.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dumpCharmaps(FILE *file) {
|
||||||
|
static FILE *charmapFile; // `static` so `charmap_ForEach` callbacks can see it
|
||||||
|
charmapFile = file;
|
||||||
|
|
||||||
|
// Characters are ordered by charmap, then by definition order
|
||||||
|
return charmap_ForEach(
|
||||||
|
[](std::string const &name) { fprintf(charmapFile, "newcharmap %s\n", name.c_str()); },
|
||||||
|
[](std::string const &mapping, std::vector<int32_t> value) {
|
||||||
|
fputs("charmap \"", charmapFile);
|
||||||
|
dumpString(mapping, charmapFile);
|
||||||
|
putc('"', charmapFile);
|
||||||
|
for (int32_t v : value)
|
||||||
|
fprintf(charmapFile, ", $%" PRIx32, v);
|
||||||
|
putc('\n', charmapFile);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dumpMacros(FILE *file) {
|
||||||
|
static std::vector<Symbol *> macros; // `static` so `sym_ForEach` callback can see it
|
||||||
|
macros.clear();
|
||||||
|
|
||||||
|
sym_ForEach([](Symbol &sym) {
|
||||||
|
if (!sym.isBuiltin && sym.type == SYM_MACRO)
|
||||||
|
macros.push_back(&sym);
|
||||||
|
});
|
||||||
|
// Macros are ordered by file, then by definition order
|
||||||
|
std::sort(RANGE(macros), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
|
return sym1->defIndex < sym2->defIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Symbol const *sym : macros) {
|
||||||
|
auto const &body = sym->getMacro();
|
||||||
|
fprintf(file, "macro %s\n", sym->name.c_str());
|
||||||
|
fwrite(body.ptr.get(), 1, body.size, file);
|
||||||
|
fputs("endm\n", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !macros.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void out_WriteState(std::string name, std::vector<StateFeature> const &features) {
|
||||||
|
FILE *file;
|
||||||
|
if (name != "-") {
|
||||||
|
file = fopen(name.c_str(), "w");
|
||||||
|
} else {
|
||||||
|
name = "<stdout>";
|
||||||
|
file = fdopen(STDOUT_FILENO, "w");
|
||||||
|
}
|
||||||
|
if (!file)
|
||||||
|
err("Failed to open state file '%s'", name.c_str());
|
||||||
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
|
static char const *dumpHeadings[NB_STATE_FEATURES] = {
|
||||||
|
"Numeric constants",
|
||||||
|
"Variables",
|
||||||
|
"String constants",
|
||||||
|
"Character maps",
|
||||||
|
"Macros",
|
||||||
|
};
|
||||||
|
static bool (* const dumpFuncs[NB_STATE_FEATURES])(FILE *) = {
|
||||||
|
dumpEquConstants,
|
||||||
|
dumpVariables,
|
||||||
|
dumpEqusConstants,
|
||||||
|
dumpCharmaps,
|
||||||
|
dumpMacros,
|
||||||
|
};
|
||||||
|
|
||||||
|
fputs("; File generated by rgbasm\n", file);
|
||||||
|
for (StateFeature feature : features) {
|
||||||
|
fprintf(file, "\n; %s\n", dumpHeadings[feature]);
|
||||||
|
if (!dumpFuncs[feature](file))
|
||||||
|
fprintf(file, "; No values\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ static void updateSymbolFilename(Symbol &sym) {
|
|||||||
|
|
||||||
// Create a new symbol by name
|
// Create a new symbol by name
|
||||||
static Symbol &createSymbol(std::string const &symName) {
|
static Symbol &createSymbol(std::string const &symName) {
|
||||||
|
static uint32_t nextDefIndex = 0;
|
||||||
|
|
||||||
Symbol &sym = symbols[symName];
|
Symbol &sym = symbols[symName];
|
||||||
|
|
||||||
sym.name = symName;
|
sym.name = symName;
|
||||||
@@ -115,6 +117,7 @@ static Symbol &createSymbol(std::string const &symName) {
|
|||||||
sym.src = fstk_GetFileStack();
|
sym.src = fstk_GetFileStack();
|
||||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||||
sym.ID = -1;
|
sym.ID = -1;
|
||||||
|
sym.defIndex = nextDefIndex++;
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -784,7 +784,7 @@ int main(int argc, char *argv[]) {
|
|||||||
fputs("#none, ", stderr);
|
fputs("#none, ", stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fputc('\n', stderr);
|
putc('\n', stderr);
|
||||||
}
|
}
|
||||||
fputs("\t]\n", stderr);
|
fputs("\t]\n", stderr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ void sym_AddSymbol(Symbol &symbol) {
|
|||||||
symbol.src->dump(symbol.lineNo);
|
symbol.src->dump(symbol.lineNo);
|
||||||
fprintf(stderr, " and in %s from ", other->objFileName);
|
fprintf(stderr, " and in %s from ", other->objFileName);
|
||||||
other->src->dump(other->lineNo);
|
other->src->dump(other->lineNo);
|
||||||
fputc('\n', stderr);
|
putc('\n', stderr);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user