1
0
mirror of https://github.com/ksherlock/x65.git synced 2024-06-11 01:29:26 +00:00

Import directive

- IMPORT directive added, works like Kick Assembler
- -endm option also applies to REPT
- REPEAT directive is an alias of REPT
- Merlin macro fix uses ]1, ]2, .. as macro params
- Erroneously flushed local labels when starting to assemble a macro
This commit is contained in:
Carl-Henrik Skårstedt 2015-10-25 14:29:41 -07:00
parent 98886793df
commit c2c6aaef0f
2 changed files with 360 additions and 119 deletions

View File

@ -48,7 +48,7 @@ x65 [-DLabel] [-iIncDir] (source.s) (dest.prg) [-lst[=file.lst]] [-opcodes[=file
x65 filename.s code.prg [options]
* -i(path): Add include path
* -D(label)[=(value)]: Define a label with an optional value (otherwise defined as 1)
* -cpu=6502/65c02/65c02wdc/65816: assemble with opcodes for a different cpu
* -cpu=6502/6502ill/65c02/65c02wdc/65816: assemble with opcodes for a different cpu
* -obj (file.x65): generate object file for later linking
* -bin: Raw binary
* -c64: Include load address (default)
@ -56,10 +56,10 @@ x65 filename.s code.prg [options]
* -sym (file.sym): symbol file
* -merlin: use Merlin syntax
* -lst / -lst=(file.lst): generate disassembly text from result (file or stdout)
* -opcodes / -opcodes=(file.s): dump all available opcodes (file or stdout)
* -opcodes / -opcodes=(file.s): dump all available opcodes for current CPU (file or stdout)
* -sect: display sections loaded and built
* -vice (file.vs): export a vice symbol file
* -endm: macros end with endm or endmacro instead of scoped ('{' - '}')
* -endm: macro and rept end with endm/endr or endmacro/endrepeat instead of scoped ('{' - '}')
## Features
@ -141,6 +141,7 @@ Directives are assembler commands that control the code generation but that does
* [**TEXT**](#text) Insert text at this address
* [**INCLUDE**](#include) Include another source file and assemble at this address
* [**INCBIN**](#incbin) Include a binary file at this address
* [**IMPORT**](#import) Catch-all file inclusion (source, bin, text, object, symbols)
* [**CONST**](#const) Assign a value to a label and make it constant (error if reassigned with other value)
* [**LABEL**](#label) Decorative directive to assign an expression to a label
* [**INCSYM**](#incsym) Include a symbol file with an optional set of wanted symbols.
@ -330,6 +331,29 @@ Example:
incbin "wizfx.gfx"
```
<a name="import">**IMPORT**
Insert multiple types of data or code at the current address. Import takes an additional parameter to determine what to do with the file data, and can accept reading in a portion of binary data.
The options for import are:
* source: same as **INCLUDE**
* binary: same as **INCBIN**
* c64: same as **INCBIN** but skip first two bytes of file as if this was a c64 prg file
* text: include text data from another file, default is petscii otherwise add another directive from the **TEXT** directive
* object: same as **INCOBJ**
* symbols: same as **INCSYM**, specify list of desired symbols prior to filename.
After the filename for binary and c64 files follow comma separated values for skip data size and max load size. c64 mode will add the two extra bytes to the skip size.
```
import source "EQ.S"
import binary "GFX.BIN",0,256
import c64 "FONT.BIN",8,8*26
import text petscii_shifted "LICENSE.TXT"
import object "engine.x65"
import symbols InitEffect, UpdateEffect "effect.sym"
```
<a name="const">**CONST**
Prefix a label assignment with 'const' or '.const' to cause an error if the label gets reassigned.
@ -451,7 +475,7 @@ EVAL(29): "Mixed.things.thing_one.count" = $2
<a name="rept">**REPT**
Repeat a scoped block of code a number of times. The syntax is rept \<count\> { \<code\> }.
Repeat a scoped block of code a number of times. The syntax is rept \<count\> { \<code\> }. The full word **REPEAT** is also recognized.
Example:
@ -483,6 +507,15 @@ rept columns {
height_buf = height_buf+1
}
```
Note that if the -endm command line option is used (macros are not defined with curly brackets but inbetween macro and endm*) this also affects rept so the syntax for a repeat block changes to
```
.REPEAT 4
lsr
.ENDREPEAT
```
<a name="incdir">**INCDIR**
Adds a folder to search for INCLUDE, INCBIN, etc. files in
@ -504,6 +537,13 @@ An alternative method is to add .b/.w to immediate mode instructions that suppor
```
This alleviates some confusion about which mode a certain line might be assembled for when looking at source code.
Note that in case a 4 digit hex value is used in 8 bit mode and an immediate mode is allowed but is not currently enable a two byte value will be emitted
```
lda #$0043 ; will be 16 bit regardless of accumulator mode if in 65816 mode
lda #$43 ; will be 8 bit or 16 bit depending on accumulator mode
```
Similarly for instructions that accept 3 byte addresses (bank + address) adding .l instructs the assembler to choose a bank address:
```
@ -713,17 +753,22 @@ FindFirstSpace
### Development Status
Currently the assembler is in an early revision and while features are tested individually it is fairly certain that untested combinations of features will indicate flaws and certain features are not in a complete state.
Currently the assembler is in a limited release and while all features are in place and tested, more testing is needed to verify the completeness. Primarily tested with personal archive of sources written for Kick assmebler, DASM, TASM, XASM, etc. and passing most of Apple II Prince of Persia and Pinball Construction set.
**TODO**
* Set 65816 immediate op size on command line for files that make that assumption
* Macro parameters should replace only whole words instead of any substring
* Add 'import' directive as a catch-all include/incbin/etc. alternative
* irp (indefinite repeat)
* boolean operators (==, <, >, etc.) for better conditional expressions
**FIXED**
* Merlin specific directives are not available in non-merlin syntax mode to avoid confusion
* Merlin macros use ]1, ]2, ]3 as arguments instead of the parameter list after [**MAC**](#merlin)
* -endm option also affects [**REPT**](#rept)
* Added [**IMPORT**](#import) directive as a catch-all include/incbin/etc. alternative
* Added 6502 illegal opcodes (cpu=6502ill)
* 65816
* 65C02 enabled through directives (PROCESSOR/CPU/XC)
* 65C02 enabled through directives ([**PROCESSOR**/**CPU**](#cpu)/[**XC**](#merlin))
* 65c02 (currently only through command line, not as a directive)
* Now accepts negative numbers, Merlin LUP and MAC keyword support
* Merlin syntax fixes (no '!' in labels, don't skip ':' if first character of label), symbol file fix for included object files with resolved labels for relative sections. List output won't disassemble lines that wasn't built from source code.
@ -736,7 +781,7 @@ Currently the assembler is in an early revision and while features are tested in
* Added directives from older assemblers
* Added ENUM, sharing some functionality with STRUCT
* Added INCDIR and command line options
* [REPT](#rept)
* [**REPT**](#rept)
* fixed a flaw in expressions that ignored the next operator after raw hex values if no whitespace
* expressions now handles high byte/low byte (\>, \<) as RPN tokens and not special cased.
* structs

418
x65.cpp
View File

@ -215,6 +215,7 @@ enum AssemblerDirective {
AD_INCLUDE, // INCLUDE: Load and assemble another file at this address
AD_INCBIN, // INCBIN: Load and directly insert another file at this address
AD_CONST, // CONST: Prevent a label from mutating during assemble
AD_IMPORT, // IMPORT: Include or Incbin or Incobj or Incsym
AD_LABEL, // LABEL: Create a mutable label (optional)
AD_INCSYM, // INCSYM: Reference labels from another assemble
AD_LABPOOL, // POOL: Create a pool of addresses to assign as labels dynamically
@ -783,6 +784,12 @@ static const strref str_label("label");
static const strref str_const("const");
static const strref struct_byte("byte");
static const strref struct_word("word");
static const strref import_source("source");
static const strref import_binary("binary");
static const strref import_c64("c64");
static const strref import_text("text");
static const strref import_object("object");
static const strref import_symbols("symbols");
static const char* aAddrModeFmt[] = {
"%s ($%02x,x)", // 00
"%s $%02x", // 01
@ -838,6 +845,7 @@ DirectiveName aDirectiveNames[] {
{ "TEXT", AD_TEXT },
{ "INCLUDE", AD_INCLUDE },
{ "INCBIN", AD_INCBIN },
{ "IMPORT", AD_IMPORT },
{ "CONST", AD_CONST },
{ "LABEL", AD_LABEL },
{ "INCSYM", AD_INCSYM },
@ -856,6 +864,7 @@ DirectiveName aDirectiveNames[] {
{ "STRUCT", AD_STRUCT },
{ "ENUM", AD_ENUM },
{ "REPT", AD_REPT },
{ "REPEAT", AD_REPT }, // ca65 version of rept
{ "INCDIR", AD_INCDIR },
{ "A16", AD_A16 }, // A16: Set 16 bit accumulator mode
{ "A8", AD_A8 }, // A8: Set 8 bit accumulator mode
@ -863,8 +872,14 @@ DirectiveName aDirectiveNames[] {
{ "XY8", AD_XY8 }, // XY8: Set 8 bit index register mode
{ "I16", AD_XY16 }, // I16: Set 16 bit index register mode
{ "I8", AD_XY8 }, // I8: Set 8 bit index register mode
{ "MX", AD_MX },
{ "DO", AD_IF }, // MERLIN
{ "DUMMY", AD_DUMMY },
{ "DUMMY_END", AD_DUMMY_END },
{ "DS", AD_DS }, // Define space
};
// Merlin specific directives separated from regular directives to avoid confusion
DirectiveName aDirectiveNamesMerlin[] {
{ "MX", AD_MX }, // MERLIN
{ "DA", AD_WORDS }, // MERLIN
{ "DW", AD_WORDS }, // MERLIN
{ "ASC", AD_TEXT }, // MERLIN
@ -883,7 +898,6 @@ DirectiveName aDirectiveNames[] {
{ "DEND", AD_DUMMY_END }, // MERLIN
{ "LST", AD_LST }, // MERLIN
{ "LSTDO", AD_LST }, // MERLIN
{ "DS", AD_DS }, // MERLIN
{ "LUP", AD_REPT }, // MERLIN
{ "MAC", AD_MACRO }, // MERLIN
{ "SAV", AD_SAV }, // MERLIN
@ -891,6 +905,7 @@ DirectiveName aDirectiveNames[] {
};
static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]);
static const int nDirectiveNamesMerlin = sizeof(aDirectiveNamesMerlin) / sizeof(aDirectiveNamesMerlin[0]);
// Binary search over an array of unsigned integers, may contain multiple instances of same key
unsigned int FindLabelIndex(unsigned int hash, unsigned int *table, unsigned int count)
@ -1107,6 +1122,7 @@ typedef struct Section {
void AddWord(int w);
void AddTriple(int l);
void AddBin(unsigned const char *p, int size);
void AddText(strref line, strref text_prefix);
void SetByte(size_t offs, int b) { output[offs] = b; }
void SetWord(size_t offs, int w) { output[offs] = w; output[offs+1] = w>>8; }
void SetTriple(size_t offs, int w) { output[offs] = w; output[offs+1] = w>>8; output[offs+2] = w>>16; }
@ -1205,6 +1221,7 @@ struct ExtLabels {
pairArray<unsigned int, Label> labels;
};
// EvalExpression needs a location reference to work out some addresses
struct EvalContext {
int pc; // current address at point of eval
int scope_pc; // current scope open at point of eval
@ -1223,7 +1240,8 @@ typedef struct {
strref code_segment; // the segment of the file for this context
strref read_source; // current position/length in source file
strref next_source; // next position/length in source file
int repeat; // how many times to repeat this code segment
short repeat; // how many times to repeat this code segment
bool scoped_context;
void restart() { read_source = code_segment; }
bool complete() { repeat--; return repeat <= 0; }
} SourceContext;
@ -1246,6 +1264,7 @@ public:
context.read_source = code_seg;
context.next_source = code_seg;
context.repeat = rept;
context.scoped_context = false;
stack.push_back(context);
currContext = &stack[stack.size()-1];
}
@ -1289,7 +1308,7 @@ public:
// Conditional assembly vars
int conditional_depth;
strref conditional_source[MAX_CONDITIONAL_DEPTH]; // start of conditional for error fixing
strref conditional_source[MAX_CONDITIONAL_DEPTH]; // start of conditional for error report
char conditional_nesting[MAX_CONDITIONAL_DEPTH];
bool conditional_consumed[MAX_CONDITIONAL_DEPTH];
@ -1302,13 +1321,13 @@ public:
int lastEvalValue;
Reloc::Type lastEvalPart;
strref export_base_name;
strref last_label;
bool accumulator_16bit;
bool index_reg_16bit;
bool errorEncountered;
bool list_assembly;
bool end_macro_directive;
strref export_base_name; // binary output name if available
strref last_label; // most recently defined label for Merlin macro
bool accumulator_16bit; // 65816 specific software dependent immediate mode
bool index_reg_16bit; // -"-
bool errorEncountered; // if any error encountered, don't export binary
bool list_assembly; // generate assembler listing
bool end_macro_directive; // whether to use { } or macro / endmacro for macro scope
// Convert source to binary
void Assemble(strref source, strref filename, bool obj_target);
@ -1327,14 +1346,14 @@ public:
// Operations on current section
void SetSection(strref name, int address); // fixed address section
void SetSection(strref name); // relative address section
void SetSection(strref name); // relative address section
StatusCode AppendSection(Section &relSection, Section &trgSection);
StatusCode LinkSections(strref name); // link relative address sections with this name here
StatusCode LinkSections(strref name); // link relative address sections with this name here
void LinkLabelsToAddress(int section_id, int section_address);
StatusCode LinkRelocs(int section_id, int section_address);
void DummySection(int address);
void DummySection();
void EndSection();
void DummySection(int address); // non-data section (fixed)
void DummySection(); // non-data section (relative)
void EndSection(); // pop current section
Section& CurrSection() { return *current_section; }
unsigned char* BuildExport(strref append, int &file_size, int &addr);
int GetExportNames(strref *aNames, int maxNames);
@ -1345,8 +1364,8 @@ public:
void AddBin(unsigned const char *p, int size) { CurrSection().AddBin(p, size); }
// Object file handling
StatusCode WriteObjectFile(strref filename);
StatusCode ReadObjectFile(strref filename);
StatusCode WriteObjectFile(strref filename); // write x65 object file
StatusCode ReadObjectFile(strref filename); // read x65 object file
// Macro management
StatusCode AddMacro(strref macro, strref source_name, strref source_file, strref &left);
@ -1395,6 +1414,9 @@ public:
StatusCode ApplyDirective(AssemblerDirective dir, strref line, strref source_file);
StatusCode Directive_Rept(strref line, strref source_file);
StatusCode Directive_Macro(strref line, strref source_file);
StatusCode Directive_Include(strref line);
StatusCode Directive_Incbin(strref line, int skip=0, int len=0);
StatusCode Directive_Import(strref line);
// Assembler steps
StatusCode GetAddressMode(strref line, bool flipXY, AddrMode &addrMode,
@ -1472,7 +1494,8 @@ int sortHashLookup(const void *A, const void *B) {
return _A->op_hash > _B->op_hash ? 1 : -1;
}
int BuildInstructionTable(OPLookup *pInstr, int maxInstructions, struct mnem *opcodes, int count, const char **aliases)
int BuildInstructionTable(OPLookup *pInstr, int maxInstructions, struct mnem *opcodes,
int count, const char **aliases, bool merlin)
{
// create an instruction table (mnemonic hash lookup)
int numInstructions = 0;
@ -1508,6 +1531,15 @@ int BuildInstructionTable(OPLookup *pInstr, int maxInstructions, struct mnem *op
op_hash.type = OT_DIRECTIVE;
}
if (merlin) {
for (int d = 0; d<nDirectiveNamesMerlin; d++) {
OPLookup &op_hash = pInstr[numInstructions++];
op_hash.op_hash = strref(aDirectiveNamesMerlin[d].name).fnv1a_lower();
op_hash.index = (unsigned char)aDirectiveNamesMerlin[d].directive;
op_hash.type = OT_DIRECTIVE;
}
}
// sort table by hash for binary search lookup
qsort(pInstr, numInstructions, sizeof(OPLookup), sortHashLookup);
return numInstructions;
@ -1520,7 +1552,8 @@ void Asm::SetCPU(CPUIndex CPU) {
list_cpu = cpu;
opcode_table = aCPUs[CPU].opcodes;
opcode_count = aCPUs[CPU].num_opcodes;
num_instructions = BuildInstructionTable(aInstructions, MAX_OPCODES_DIRECTIVES, opcode_table, opcode_count, aCPUs[CPU].aliases);
num_instructions = BuildInstructionTable(aInstructions, MAX_OPCODES_DIRECTIVES, opcode_table,
opcode_count, aCPUs[CPU].aliases, syntax == SYNTAX_MERLIN);
if (CPU != CPU_65816) {
accumulator_16bit = false;
index_reg_16bit = false;
@ -1967,6 +2000,34 @@ void Section::AddBin(unsigned const char *p, int size) {
address += size;
}
// Add text data to a section
void Section::AddText(strref line, strref text_prefix) {
// https://en.wikipedia.org/wiki/PETSCII
// ascii: no change
// shifted: a-z => $41.. A-Z => $61..
// unshifted: a-z, A-Z => $41
CheckOutputCapacity(line.get_len());
{
if (!text_prefix || text_prefix.same_str("ascii")) {
AddBin((unsigned const char*)line.get(), line.get_len());
} else if (text_prefix.same_str("petscii")) {
while (line) {
char c = line[0];
AddByte((c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : (c > 0x60 ? ' ' : line[0]));
++line;
}
} else if (text_prefix.same_str("petscii_shifted")) {
while (line) {
char c = line[0];
AddByte((c >= 'a' && c <= 'z') ? (c - 'a' + 0x61) :
((c >= 'A' && c <= 'Z') ? (c - 'A' + 0x61) : (c > 0x60 ? ' ' : line[0])));
++line;
}
}
}
}
// Add a relocation marker to a section
void Section::AddReloc(int base, int offset, int section, Reloc::Type type)
{
@ -1984,9 +2045,6 @@ void Asm::CheckOutputCapacity(unsigned int addSize) {
//
//
// MACROS
@ -2061,7 +2119,6 @@ StatusCode Asm::AddMacro(strref macro, strref source_name, strref source_file, s
left = macro;
pMacro->macro = source;
source.skip_whitespace();
left = source;
} else if (end_macro_directive) {
int f = -1;
const strref endm("endm");
@ -2102,7 +2159,48 @@ StatusCode Asm::BuildMacro(Macro &m, strref arg_list)
(macro_src[0] == '(' ? macro_src.scoped_block_skip() : strref());
params.trim_whitespace();
arg_list.trim_whitespace();
if (params) {
if (syntax == SYNTAX_MERLIN) {
// need to include comment field because separator is ;
if (contextStack.curr().read_source.is_substr(arg_list.get()))
arg_list = (contextStack.curr().read_source +
strl_t(arg_list.get()-contextStack.curr().read_source.get())
).line();
arg_list = arg_list.before_or_full(c_comment);
strref arg = arg_list;
strown<16> tag;
int t_max = 16;
int dSize = 0;
for (int t=1; t<t_max; t++) {
tag.sprintf("]%d", t);
strref a = arg.split_token_trim(';');
if (!a) {
t_max = t;
break;
}
int count = macro_src.substr_case_count(tag.get_strref());
dSize += count * ((int)a.get_len() - (int)tag.get_len());
}
int mac_size = macro_src.get_len() + dSize + 32;
if (char *buffer = (char*)malloc(mac_size)) {
loadedData.push_back(buffer);
strovl macexp(buffer, mac_size);
macexp.copy(macro_src);
arg = arg_list;
for (int t=1; t<t_max; t++) {
tag.sprintf("]%d", t);
strref a = arg.split_token_trim(';');
macexp.replace(tag.get_strref(), a);
}
contextStack.push(m.source_name, macexp.get_strref(), macexp.get_strref());
if (scope_depth>=(MAX_SCOPE_DEPTH-1))
return ERROR_TOO_DEEP_SCOPE;
else
scope_address[++scope_depth] = CurrSection().GetPC();
contextStack.curr().scoped_context = true;
return STATUS_OK;
} else
return ERROR_OUT_OF_MEMORY_FOR_MACRO_EXPANSION;
} else if (params) {
if (arg_list[0]=='(')
arg_list = arg_list.scoped_block_skip();
strref pchk = params;
@ -2127,12 +2225,20 @@ StatusCode Asm::BuildMacro(Macro &m, strref arg_list)
macexp.replace(param, a);
}
contextStack.push(m.source_name, macexp.get_strref(), macexp.get_strref());
return FlushLocalLabels();
if (end_macro_directive) {
contextStack.push(m.source_name, macexp.get_strref(), macexp.get_strref());
if (scope_depth>=(MAX_SCOPE_DEPTH-1))
return ERROR_TOO_DEEP_SCOPE;
else
scope_address[++scope_depth] = CurrSection().GetPC();
contextStack.curr().scoped_context = true;
}
return STATUS_OK;
} else
return ERROR_OUT_OF_MEMORY_FOR_MACRO_EXPANSION;
}
contextStack.push(m.source_name, m.source_file, macro_src);
return FlushLocalLabels();
return STATUS_OK;
}
@ -3372,7 +3478,7 @@ StatusCode Asm::Directive_Rept(strref line, strref source_file)
if (read_source.is_substr(line.get())) {
read_source.skip(strl_t(line.get() - read_source.get()));
strref expression;
if (syntax == SYNTAX_MERLIN) {
if (syntax == SYNTAX_MERLIN || end_macro_directive) {
expression = line; // Merlin repeat body begins next line
read_source.line();
} else {
@ -3389,12 +3495,12 @@ StatusCode Asm::Directive_Rept(strref line, strref source_file)
if (STATUS_OK != EvalExpression(expression, etx, count))
return ERROR_REPT_COUNT_EXPRESSION;
strref recur;
if (syntax == SYNTAX_MERLIN) {
if (syntax == SYNTAX_MERLIN || end_macro_directive) {
recur = read_source; // Merlin repeat body ends at "--^"
while (strref next_line = read_source.line()) {
next_line = next_line.before_or_full(';');
next_line = next_line.before_or_full(c_comment);
int term = next_line.find("--^");
int term = next_line.find(end_macro_directive ? "endr" : "--^");
if (term >= 0) {
recur = recur.get_substr(0, strl_t(next_line.get() + term - recur.get()));
break;
@ -3422,6 +3528,131 @@ StatusCode Asm::Directive_Macro(strref line, strref source_file)
return STATUS_OK;
}
// include: read in a source file and assemble at this point
StatusCode Asm::Directive_Include(strref line)
{
strref file = line.between('"', '"');
if (!file) // MERLIN: No quotes around PUT filenames
file = line.split_range(filename_end_char_range);
size_t size = 0;
char *buffer = nullptr;
if ((buffer = LoadText(file, size))) {
loadedData.push_back(buffer);
strref src(buffer, strl_t(size));
contextStack.push(file, src, src);
} else if (syntax == SYNTAX_MERLIN) {
// MERLIN include file name rules
if (file[0]>='!' && file[0]<='&' && (buffer = LoadText(file+1, size))) {
loadedData.push_back(buffer); // MERLIN: prepend with !-& to not auto-prepend with T.
strref src(buffer, strl_t(size));
contextStack.push(file+1, src, src);
} else {
strown<512> fileadd(file[0]>='!' && file[0]<='&' ? (file+1) : file);
fileadd.append(".s");
if ((buffer = LoadText(fileadd.get_strref(), size))) {
loadedData.push_back(buffer); // MERLIN: !+filename appends .S to filenames
strref src(buffer, strl_t(size));
contextStack.push(file, src, src);
} else {
fileadd.copy("T."); // MERLIN: just filename prepends T. to filenames
fileadd.append(file[0]>='!' && file[0]<='&' ? (file+1) : file);
if ((buffer = LoadText(fileadd.get_strref(), size))) {
loadedData.push_back(buffer);
strref src(buffer, strl_t(size));
contextStack.push(file, src, src);
}
}
}
}
if (!size)
return ERROR_COULD_NOT_INCLUDE_FILE;
return STATUS_OK;
}
// incbin: import binary data in place
StatusCode Asm::Directive_Incbin(strref line, int skip, int len)
{
line = line.between('"', '"');
strown<512> filename(line);
size_t size = 0;
if (char *buffer = LoadBinary(line, size)) {
int bin_size = (int)size - skip;
if (bin_size>len)
bin_size = len;
if (bin_size>0)
AddBin((const unsigned char*)buffer+skip, bin_size);
free(buffer);
return STATUS_OK;
}
return ERROR_COULD_NOT_INCLUDE_FILE;
}
// import is a catch-all file reference
StatusCode Asm::Directive_Import(strref line)
{
line.skip_whitespace();
int skip = 0; // binary import skip this amount
int len = 0; // binary import load up to this amount
strref param; // read out skip & max len parameters
int q = line.find('"');
if (q>=0) {
param = line + q;
param.scoped_block_skip();
param.trim_whitespace();
if (param[0]==',') {
++param;
param.skip_whitespace();
if (param) {
struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
EvalExpression(param.split_token_trim(','), etx, skip);
if (param)
EvalExpression(param, etx, len);
}
}
}
if (line[0]=='"')
return Directive_Incbin(line);
else if (import_source.is_prefix_word(line)) {
line += import_source.get_len();
line.skip_whitespace();
return Directive_Include(line);
} else if (import_binary.is_prefix_word(line)) {
line += import_binary.get_len();
line.skip_whitespace();
return Directive_Incbin(line, skip, len);
} else if (import_c64.is_prefix_word(line)) {
line += import_c64.get_len();
line.skip_whitespace();
return Directive_Incbin(line, 2+skip, len); // 2 = load address skip size
} else if (import_text.is_prefix_word(line)) {
line += import_text.get_len();
line.skip_whitespace();
strref text_type = "petscii";
if (line[0]!='"') {
text_type = line.get_word_ws();
line += text_type.get_len();
line.skip_whitespace();
}
CurrSection().AddText(line, text_type);
return STATUS_OK;
} else if (import_object.is_prefix_word(line)) {
line += import_object.get_len();
line.trim_whitespace();
return ReadObjectFile(line[0]=='"' ? line.between('"', '"') : line);
} else if (import_symbols.is_prefix_word(line)) {
line += import_symbols.get_len();
line.skip_whitespace();
IncludeSymbols(line);
return STATUS_OK;
}
return STATUS_OK;
}
// Action based on assembler directive
StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref source_file)
{
@ -3682,82 +3913,24 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
SetCPU(CPU_65C02);
break;
case AD_TEXT: { // text: add text within quotes
// https://en.wikipedia.org/wiki/PETSCII
// ascii: no change
// shifted: a-z => $41.. A-Z => $61..
// unshifted: a-z, A-Z => $41
strref text_prefix = line.before('"').get_trimmed_ws();
line = line.between('"', '"');
CheckOutputCapacity(line.get_len());
{
if (!text_prefix || text_prefix.same_str("ascii")) {
AddBin((unsigned const char*)line.get(), line.get_len());
} else if (text_prefix.same_str("petscii")) {
while (line) {
char c = line[0];
AddByte((c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : (c > 0x60 ? ' ' : line[0]));
++line;
}
} else if (text_prefix.same_str("petscii_shifted")) {
while (line) {
char c = line[0];
AddByte((c >= 'a' && c <= 'z') ? (c - 'a' + 0x61) :
((c >= 'A' && c <= 'Z') ? (c - 'A' + 0x61) : (c > 0x60 ? ' ' : line[0])));
++line;
}
}
}
CurrSection().AddText(line, text_prefix);
break;
}
case AD_MACRO:
error = Directive_Macro(line, source_file);
break;
case AD_INCLUDE: { // include: assemble another file in place
strref file = line.between('"', '"');
if (!file) // MERLIN: No quotes around PUT filenames
file = line.split_range(filename_end_char_range);
size_t size = 0;
char *buffer = nullptr;
if ((buffer = LoadText(file, size))) {
loadedData.push_back(buffer);
strref src(buffer, strl_t(size));
contextStack.push(file, src, src);
} else if (file[0]>='!' && file[0]<='&' && (buffer = LoadText(file+1, size))) {
loadedData.push_back(buffer); // MERLIN: prepend with !-& to not auto-prepend with T.
strref src(buffer, strl_t(size));
contextStack.push(file+1, src, src);
} else {
strown<512> fileadd(file[0]>='!' && file[0]<='&' ? (file+1) : file);
fileadd.append(".s");
if ((buffer = LoadText(fileadd.get_strref(), size))) {
loadedData.push_back(buffer); // MERLIN: !+filename appends .S to filenames
strref src(buffer, strl_t(size));
contextStack.push(file, src, src);
} else {
fileadd.copy("T."); // MERLIN: just filename prepends T. to filenames
fileadd.append(file[0]>='!' && file[0]<='&' ? (file+1) : file);
if ((buffer = LoadText(fileadd.get_strref(), size))) {
loadedData.push_back(buffer);
strref src(buffer, strl_t(size));
contextStack.push(file, src, src);
}
}
}
if (!size)
error = ERROR_COULD_NOT_INCLUDE_FILE;
break;
}
case AD_INCBIN: { // incbin: import binary data in place
line = line.between('"', '"');
strown<512> filename(line);
size_t size = 0;
if (char *buffer = LoadBinary(line, size)) {
AddBin((const unsigned char*)buffer, (int)size);
free(buffer);
}
break;
}
case AD_INCLUDE: // assemble another file in place
return Directive_Include(line);
case AD_INCBIN:
return Directive_Incbin(line);
case AD_IMPORT:
return Directive_Import(line);
case AD_LABEL:
case AD_CONST: {
line.trim_whitespace();
@ -3769,15 +3942,15 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
error = ERROR_UNEXPECTED_LABEL_ASSIGMENT_FORMAT;
break;
}
case AD_INCSYM: {
case AD_INCSYM:
IncludeSymbols(line);
break;
}
case AD_LABPOOL: {
strref label = line.split_range_trim(word_char_range, line[0]=='.' ? 1 : 0);
AddLabelPool(label, line);
case AD_LABPOOL:
AddLabelPool(line.split_range_trim(word_char_range, line[0]=='.' ? 1 : 0), line);
break;
}
case AD_IF:
if (NewConditional()) { // Start new conditional block
CheckConditionalDepth(); // Check if nesting
@ -3789,6 +3962,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
SetConditional();
}
break;
case AD_IFDEF:
if (NewConditional()) { // Start new conditional block
CheckConditionalDepth(); // Check if nesting
@ -3800,6 +3974,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
SetConditional();
}
break;
case AD_ELSE:
if (ConditionalAsm()) {
if (ConditionalConsumed())
@ -3809,6 +3984,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
} else if (ConditionalAvail())
EnableConditional(true);
break;
case AD_ELIF:
if (ConditionalAsm()) {
if (ConditionalConsumed())
@ -3821,6 +3997,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
EnableConditional(conditional_result);
}
break;
case AD_ENDIF:
if (ConditionalAsm()) {
if (ConditionalConsumed())
@ -3833,6 +4010,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
CloseConditional();
}
break;
case AD_ENUM:
case AD_STRUCT: {
strref read_source = contextStack.curr().read_source;
@ -3854,25 +4032,30 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
error = ERROR_STRUCT_CANT_BE_ASSEMBLED;
break;
}
case AD_REPT:
error = Directive_Rept(line, source_file);
break;
return Directive_Rept(line, source_file);
case AD_INCDIR:
AddIncludeFolder(line.between('"', '"'));
break;
case AD_A16: // A16: Set 16 bit accumulator mode
accumulator_16bit = true;
break;
case AD_A8: // A8: Set 8 bit accumulator mode
accumulator_16bit = false;
break;
case AD_XY16: // A16: Set 16 bit accumulator mode
index_reg_16bit = true;
break;
case AD_XY8: // A8: Set 8 bit accumulator mode
index_reg_16bit = false;
break;
case AD_MX:
if (line) {
line.trim_whitespace();
@ -3882,9 +4065,11 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
accumulator_16bit = !!(value&2);
}
break;
case AD_LST:
line.clear();
break;
case AD_DUMMY:
line.trim_whitespace();
if (line) {
@ -3896,6 +4081,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
}
DummySection();
break;
case AD_DUMMY_END:
while (CurrSection().IsDummySection()) {
EndSection();
@ -3903,6 +4089,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
break;
}
break;
case AD_DS: {
int value;
strref size = line.split_token_trim(',');
@ -4035,7 +4222,7 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
break;
case AMM_BLK_MOV:
addrMode = AMB_BLK_MOV;
expression = line;
expression = line.before_or_full(',');
break;
default:
error = GetAddressMode(line, !!(validModes & AMM_FLIPXY), addrMode, op_param, expression);
@ -4155,7 +4342,10 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
case AMB_IMM: // 2 #$12
if (op_param && (validModes&(AMM_IMM_DBL_A | AMM_IMM_DBL_XY)))
codeArg = op_param == 2 ? CA_TWO_BYTES : CA_ONE_BYTE;
else if (((validModes&AMM_IMM_DBL_A) && accumulator_16bit) ||
else if ((validModes&(AMM_IMM_DBL_A | AMM_IMM_DBL_XY)) &&
expression[0]=='$' && (expression+1).len_hex()==4)
codeArg = CA_TWO_BYTES;
else if (((validModes&AMM_IMM_DBL_A) && accumulator_16bit) ||
((validModes&AMM_IMM_DBL_XY) && index_reg_16bit))
codeArg = CA_TWO_BYTES;
else
@ -4657,9 +4847,15 @@ void Asm::Assemble(strref source, strref filename, bool obj_target)
scope_address[scope_depth] = CurrSection().GetPC();
while (contextStack.has_work()) {
error = BuildSegment();
if (contextStack.curr().complete())
if (contextStack.curr().complete()) {
if (contextStack.curr().scoped_context && scope_depth) {
CheckLateEval(strref(), CurrSection().GetPC());
FlushLocalLabels(scope_depth);
FlushLabelPools(scope_depth);
--scope_depth;
}
contextStack.pop();
else
} else
contextStack.curr().restart();
}
if (error == STATUS_OK) {