1
0
mirror of https://github.com/ksherlock/x65.git synced 2025-08-08 11:25:02 +00:00

Label Pools and Conditional Assembly

Awesome stuff added.
This commit is contained in:
Carl-Henrik Skårstedt
2015-10-01 23:16:36 -07:00
parent 80c3016857
commit d5edf1d593
2 changed files with 635 additions and 145 deletions

107
README.md
View File

@@ -90,21 +90,22 @@ Next_Function: ; next global label, the local label above is now erased.
Directives are assembler commands that control the code generation but that does not generate code by itself. Some assemblers prefix directives with a period (.org instead of org) so a leading period is accepted but not required for directives. Directives are assembler commands that control the code generation but that does not generate code by itself. Some assemblers prefix directives with a period (.org instead of org) so a leading period is accepted but not required for directives.
* **ORG** (same as **PC**): Set the current compiling address. * [**ORG**](#org) (same as **PC**): Set the current compiling address.
* **LOAD** Set the load address for binary formats that support it. * [**LOAD**](#load) Set the load address for binary formats that support it.
* **ALIGN** Align the address to a multiple by filling with 0s * [**ALIGN**](#align) Align the address to a multiple by filling with 0s
* **MACRO** Declare a macro * [**MACRO**](#macro) Declare a macro
* **EVAL** Log an expression during assembly. * [**EVAL**](#eval) Log an expression during assembly.
* **BYTES** Insert comma separated bytes at this address (same as **BYTE**) * [**BYTES**](#bytes) Insert comma separated bytes at this address (same as **BYTE**)
* **WORDS** Insert comma separated 16 bit values at this address (same as **WORD**) * [**WORDS**](#words) Insert comma separated 16 bit values at this address (same as **WORD**)
* **TEXT** Insert text at this address * [**TEXT**](#text) Insert text at this address
* **INCLUDE** Include another source file and assemble at this address * [**INCLUDE**](#include) Include another source file and assemble at this address
* **INCBIN** Include a binary file at this address * [**INCBIN**](#incbin) Include a binary file at this address
* **CONST** Assign a value to a label and make it constant (error if reassigned with other value) * [**CONST**](#const) Assign a value to a label and make it constant (error if reassigned with other value)
* **LABEL** Decorative directive to assign an expression to a label * [**LABEL**](#label) Decorative directive to assign an expression to a label
* **INCSYM** Include a symbol file with an optional set of wanted symbols. * [**INCSYM**](#incsym) Include a symbol file with an optional set of wanted symbols.
* [**POOL**](#pool) Add a label pool for temporary address labels
**ORG** <a name="org">**ORG**
``` ```
org $2000 org $2000
@@ -113,7 +114,7 @@ org $2000
Sets the current assembler address to this address Sets the current assembler address to this address
**LOAD** <a name="load">**LOAD**
``` ```
load $2000 load $2000
@@ -121,7 +122,7 @@ load $2000
For c64 .prg files this prefixes the binary file with this address. For c64 .prg files this prefixes the binary file with this address.
**ALIGN** <a name="align">**ALIGN**
``` ```
align $100 align $100
@@ -129,11 +130,11 @@ align $100
Add bytes of 0 up to the next address divisible by the alignment Add bytes of 0 up to the next address divisible by the alignment
**MACRO** <a name="macro">**MACRO**
See the 'Macro' section below See the 'Macro' section below
**EVAL** <a name="eval">**EVAL**
Example: Example:
``` ```
@@ -146,7 +147,7 @@ Eval (15): Current PC : "*" = $2010
When eval is encountered on a line print out "EVAL (\<line#\>) \<message\>: \<expression\> = \<evaluated expression\>" to stdout. This can be useful to see the size of things or debugging expressions. When eval is encountered on a line print out "EVAL (\<line#\>) \<message\>: \<expression\> = \<evaluated expression\>" to stdout. This can be useful to see the size of things or debugging expressions.
**BYTES** <a name="bytes">**BYTES**
Adds the comma separated values on the current line to the assembled output, for example Adds the comma separated values on the current line to the assembled output, for example
@@ -161,11 +162,11 @@ RandomBytes:
byte is also recognized byte is also recognized
**WORDS** <a name="words">**WORDS**
Adds comma separated 16 bit values similar to how **BYTES** work Adds comma separated 16 bit values similar to how **BYTES** work
**TEXT** <a name="text">**TEXT**
Copies the string in quotes on the same line. The plan is to do a petscii conversion step. Use the modifier 'petscii' or 'petscii_shifted' to convert alphabetic characters to range. Copies the string in quotes on the same line. The plan is to do a petscii conversion step. Use the modifier 'petscii' or 'petscii_shifted' to convert alphabetic characters to range.
@@ -175,7 +176,7 @@ Example:
text petscii_shifted "This might work" text petscii_shifted "This might work"
``` ```
**INCLUDE** <a name="include">**INCLUDE**
Include another source file. This should also work with .sym files to import labels from another build. The plan is for Asm6502 to export .sym files as well. Include another source file. This should also work with .sym files to import labels from another build. The plan is for Asm6502 to export .sym files as well.
@@ -186,7 +187,7 @@ include "wizfx.s"
``` ```
**INCBIN** <a name="incbin">**INCBIN**
Include binary data from a file, this inserts the binary data at the current address. Include binary data from a file, this inserts the binary data at the current address.
@@ -196,7 +197,7 @@ Example:
incbin "wizfx.gfx" incbin "wizfx.gfx"
``` ```
**CONST** <a name="const">**CONST**
Prefix a label assignment with 'const' or '.const' to cause an error if the label gets reassigned. Prefix a label assignment with 'const' or '.const' to cause an error if the label gets reassigned.
@@ -204,7 +205,7 @@ Prefix a label assignment with 'const' or '.const' to cause an error if the labe
const zpData = $fe const zpData = $fe
``` ```
**LABEL** <a name="label">**LABEL**
Decorative directive to assign an expression to a label, label assignments are followed by '=' and an expression. Decorative directive to assign an expression to a label, label assignments are followed by '=' and an expression.
@@ -214,14 +215,50 @@ label zpDest = $fc
zpDest = $fa zpDest = $fa
``` ```
**INCSYM** Include a symbol file with an optional set of wanted symbols. <a name="incsym">**INCSYM**
Open a symbol file and extract a set of symbols, or all symbols if no set was specified. Include a symbol file with an optional set of wanted symbols.
Open a symbol file and extract a set of symbols, or all symbols if no set was specified. Local labels will be discarded if possible.
``` ```
incsym Part1_Init, Part1_Update, Part1_Exit "part1.sym" incsym Part1_Init, Part1_Update, Part1_Exit "part1.sym"
``` ```
<a name="pool">**POOL**
Add a label pool for temporary address labels. This is similar to how stack frame variables are assigned in C.
A label pool is a mini stack of addresses that can be assigned as temporary labels with a scope ('{' and '}'). This can be handy for large functions trying to minimize use of zero page addresses, the function can declare a range (or set of ranges) of available zero page addresses and labels can be assigned within a scope and be deleted on scope closure. The format of a label pool is: "pool <pool name> start-end, start-end" and labels can then be allocated from that range by '<pool name> <label name>[.b][.w]' where .b means allocate one byte and .w means allocate two bytes. The label pools themselves are local to the scope they are defined in so you can have label pools that are only valid for a section of your code. Label pools works with any addresses, not just zero page addresses.
Example:
```
Function_Name: {
pool zpWork $f6-$100 ; these zero page addresses are available for temporary labels
zpWork zpTrg.w ; zpTrg will be $fe
zpWork zpSrc.w ; zpSrc will be $fc
lda #>Src
sta zpSrc
lda #<Src
sta zpSrc+1
lda #>Dest
sta zpDst
lda #<Dest
sta zpDst+1
{
zpWork zpLen ; zpLen will be $fb
lda #Length
sta zpLen
}
nop
{
zpWork zpOff ; zpOff will be $fb (shared with previous scope zpLen)
}
rts
```
## <a name="expressions">Expression syntax ## <a name="expressions">Expression syntax
Expressions contain values, such as labels or raw numbers and operators including +, -, \*, /, & (and), | (or), ^ (eor), << (shift left), >> (shift right) similar to how expressions work in C. Parenthesis are supported for managing order of operations where C style precedence needs to be overrided. In addition there are some special characters supported: Expressions contain values, such as labels or raw numbers and operators including +, -, \*, /, & (and), | (or), ^ (eor), << (shift left), >> (shift right) similar to how expressions work in C. Parenthesis are supported for managing order of operations where C style precedence needs to be overrided. In addition there are some special characters supported:
@@ -270,12 +307,14 @@ Scopes are lines inbetween '{' and '}' including macros. The purpose of scopes i
This means you can write This means you can write
``` ```
lda #0
ldx #8
{ {
sta Label,x lda #0
dex ldx #8
bpl ! {
sta Label,x
dex
bpl !
}
} }
``` ```
(where ; represents line breaks) to construct a loop without adding a label. (where ; represents line breaks) to construct a loop without adding a label.
@@ -305,18 +344,20 @@ FindFirstSpace
Currently the assembler is in the first public 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 (such as the TEXT directive not bothering to convert ascii to petscii for example). Currently the assembler is in the first public 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 (such as the TEXT directive not bothering to convert ascii to petscii for example).
**TODO** **TODO**
* Bracket scoping closure ('}') should clean up local variables within that scope (better handling of local variables within macros).
* Macro parameters should replace only whole words instead of any substring * Macro parameters should replace only whole words instead of any substring
* Add 'import' directive as a catch-all include/incbin/etc. alternative * Add 'import' directive as a catch-all include/incbin/etc. alternative
* ifdef / if / elif / else / endif conditional code generation directives * ifdef / if / elif / else / endif conditional code generation directives
* rept / irp macro helpers (repeat, indefinite repeat) * rept / irp macro helpers (repeat, indefinite repeat)
**FIXED** **FIXED**
* Label Pools added
* Bracket scoping closure ('}') cleans up local variables within that scope (better handling of local variables within macros).
* Context stack cleanup * Context stack cleanup
* % in expressions is interpreted as binary value if immediately followed by 0 or 1 * % in expressions is interpreted as binary value if immediately followed by 0 or 1
* Add a const directive for labels that shouldn't be allowed to change (currently ignoring const) * Add a const directive for labels that shouldn't be allowed to change (currently ignoring const)
* TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic) * TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic)
Revisions: Revisions:
* 2015-10-01 Added Label Pools
* 2015-09-29 Moved Asm6502 out of Struse Samples. * 2015-09-29 Moved Asm6502 out of Struse Samples.
* 2015-09-28 First commit * 2015-09-28 First commit

View File

@@ -44,10 +44,17 @@
// Max number of nested scopes (within { and }) // Max number of nested scopes (within { and })
#define MAX_SCOPE_DEPTH 32 #define MAX_SCOPE_DEPTH 32
// Max number of nested conditional expressions
#define MAX_CONDITIONAL_DEPTH 64
// The maximum complexity of expressions to be evaluated // The maximum complexity of expressions to be evaluated
#define MAX_EVAL_VALUES 32 #define MAX_EVAL_VALUES 32
#define MAX_EVAL_OPER 64 #define MAX_EVAL_OPER 64
// Max capacity of each label pool
#define MAX_POOL_RANGES 4
#define MAX_POOL_BYTES 128
// Internal status and error type // Internal status and error type
enum StatusCode { enum StatusCode {
STATUS_OK, // everything is fine STATUS_OK, // everything is fine
@@ -66,6 +73,11 @@ enum StatusCode {
ERROR_UNEXPECTED_CHARACTER_IN_ADDRESSING_MODE, ERROR_UNEXPECTED_CHARACTER_IN_ADDRESSING_MODE,
ERROR_UNEXPECTED_LABEL_ASSIGMENT_FORMAT, ERROR_UNEXPECTED_LABEL_ASSIGMENT_FORMAT,
ERROR_MODIFYING_CONST_LABEL, ERROR_MODIFYING_CONST_LABEL,
ERROR_OUT_OF_LABELS_IN_POOL,
ERROR_INTERNAL_LABEL_POOL_ERROR,
ERROR_POOL_RANGE_EXPRESSION_EVAL,
ERROR_LABEL_POOL_REDECLARATION,
ERROR_POOL_LABEL_ALREADY_DEFINED,
ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution
@@ -75,10 +87,15 @@ enum StatusCode {
ERROR_BAD_MACRO_FORMAT, ERROR_BAD_MACRO_FORMAT,
ERROR_ALIGN_MUST_EVALUATE_IMMEDIATELY, ERROR_ALIGN_MUST_EVALUATE_IMMEDIATELY,
ERROR_OUT_OF_MEMORY_FOR_MACRO_EXPANSION, ERROR_OUT_OF_MEMORY_FOR_MACRO_EXPANSION,
ERROR_CONDITION_COULD_NOT_BE_RESOLVED,
ERROR_ENDIF_WITHOUT_CONDITION,
ERROR_ELSE_WITHOUT_IF,
STATUSCODE_COUNT
}; };
// The following strings are in the same order as StatusCode // The following strings are in the same order as StatusCode
const char *aStatusStrings[] = { const char *aStatusStrings[STATUSCODE_COUNT] = {
"ok", "ok",
"not ready", "not ready",
"Unexpected character in expression", "Unexpected character in expression",
@@ -95,13 +112,23 @@ const char *aStatusStrings[] = {
"Unexpected character in addressing mode", "Unexpected character in addressing mode",
"Unexpected label assignment format", "Unexpected label assignment format",
"Changing value of label that is constant", "Changing value of label that is constant",
"Out of labels in pool",
"Internal label pool release confusion",
"Label pool range evaluation failed",
"Label pool was redeclared within its scope",
"Pool label already defined",
"Errors after this point will stop execution", "Errors after this point will stop execution",
"Target address must evaluate immediately for this operation", "Target address must evaluate immediately for this operation",
"Scoping is too deep", "Scoping is too deep",
"Unbalanced scope closure", "Unbalanced scope closure",
"Unexpected macro formatting", "Unexpected macro formatting",
"Align must evaluate immediately", "Align must evaluate immediately",
"Out of memory for macro expansion", "Out of memory for macro expansion",
"Conditional could not be resolved",
"#endif encountered outside conditional block",
"#else or #elif outside conditional block",
}; };
// Operators are either instructions or directives // Operators are either instructions or directives
@@ -172,6 +199,8 @@ enum OP_INDICES {
OPI_JMP = 1, OPI_JMP = 1,
}; };
#define RELATIVE_JMP_DELTA 0x20
// opcode names in groups (prefix by group size) // opcode names in groups (prefix by group size)
const char aInstr[] = { const char aInstr[] = {
"BRK,JSR,RTI,RTS\n" "BRK,JSR,RTI,RTS\n"
@@ -205,10 +234,13 @@ unsigned char CC00Mask[] = { 0x0a, 0x08, 0x08, 0x2a, 0xae, 0x0e, 0x0e };
unsigned char CC10ModeAdd[] = { 0xff, 4, 0, 12, 0xff, 20, 0xff, 28 }; unsigned char CC10ModeAdd[] = { 0xff, 4, 0, 12, 0xff, 20, 0xff, 28 };
unsigned char CC10Mask[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0xae, 0xaa, 0xaa }; unsigned char CC10Mask[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0xae, 0xaa, 0xaa };
// hardtexted strings
static const strref c_comment("//"); static const strref c_comment("//");
static const strref word_char_range("!0-9a-zA-Z_@$!"); static const strref word_char_range("!0-9a-zA-Z_@$!#");
static const strref label_char_range("!0-9a-zA-Z_@$!."); static const strref label_char_range("!0-9a-zA-Z_@$!.");
static const strref keyword_equ("equ"); static const strref keyword_equ("equ");
static const strref str_label("label");
static const strref str_const("const");
// pairArray is basically two vectors sharing a size without using constructors // pairArray is basically two vectors sharing a size without using constructors
template <class H, class V> class pairArray { template <class H, class V> class pairArray {
@@ -324,12 +356,31 @@ typedef struct {
// Source context is current file (include file, etc.) or current macro. // Source context is current file (include file, etc.) or current macro.
typedef struct { typedef struct {
strref source_name; // source file name (error output) strref source_name; // source file name (error output)
strref source_file; // entire source file (req. for line #) strref source_file; // entire source file (req. for line #)
strref code_segment; // the segment of the file for this context strref code_segment; // the segment of the file for this context
strref read_source; // current position/length in source file strref read_source; // current position/length in source file
} SourceContext; } SourceContext;
// All local labels are removed when a global label is defined but some when a scope ends
typedef struct {
strref label;
int scope_depth;
bool scope_reserve; // not released for global label, only scope
} LocalLabelRecord;
// Label pools allows C like stack frame label allocation
typedef struct {
strref pool_name;
short numRanges; // normally 1 range, support multiple for ease of use
short scopeDepth; // for scope closure cleanup
unsigned short ranges[MAX_POOL_RANGES*2]; // 2 shorts per range
unsigned int usedMap[(MAX_POOL_BYTES+15)>>4]; // 2 bits per byte to store byte count of label
StatusCode Reserve(int numBytes, unsigned int &addr);
StatusCode Release(unsigned int addr);
} LabelPool;
// Context stack is a stack of currently processing text
class ContextStack { class ContextStack {
private: private:
std::vector<SourceContext> stack; std::vector<SourceContext> stack;
@@ -365,6 +416,12 @@ enum AssemblerDirective {
AD_CONST, AD_CONST,
AD_LABEL, AD_LABEL,
AD_INCSYM, AD_INCSYM,
AD_LABPOOL,
AD_IF,
AD_IFDEF,
AD_ELSE,
AD_ELIF,
AD_ENDIF,
}; };
// The state of the assembly // The state of the assembly
@@ -372,9 +429,10 @@ class Asm {
public: public:
pairArray<unsigned int, Label> labels; pairArray<unsigned int, Label> labels;
pairArray<unsigned int, Macro> macros; pairArray<unsigned int, Macro> macros;
pairArray<unsigned int, LabelPool> labelPools;
std::vector<LateEval> lateEval; std::vector<LateEval> lateEval;
std::vector<strref> localLabels; // remove these labels when a global pc label is added std::vector<LocalLabelRecord> localLabels; // remove these labels when a global pc label is added
std::vector<char*> loadedData; // free when std::vector<char*> loadedData; // free when assembly is completed
strovl symbols; strovl symbols;
// context for macros / include files // context for macros / include files
@@ -388,8 +446,11 @@ public:
unsigned int load_address; unsigned int load_address;
int scope_address[MAX_SCOPE_DEPTH]; int scope_address[MAX_SCOPE_DEPTH];
int scope_depth; int scope_depth;
int conditional_depth;
char conditional_nesting[MAX_CONDITIONAL_DEPTH];
bool conditional_consumed[MAX_CONDITIONAL_DEPTH];
bool set_load_address; bool set_load_address;
bool symbol_export; bool symbol_export, last_label_local;
// Convert source to binary // Convert source to binary
void Assemble(strref source, strref filename); void Assemble(strref source, strref filename);
@@ -408,14 +469,26 @@ public:
StatusCode EvalExpression(strref expression, int pc, int scope_pc, StatusCode EvalExpression(strref expression, int pc, int scope_pc,
int scope_end_pc, int &result); int scope_end_pc, int &result);
// Conditional statement eval
StatusCode EvalStatement(strref line, bool &result);
// Access labels // Access labels
Label* GetLabel(strref label); Label* GetLabel(strref label);
Label* AddLabel(unsigned int hash); Label* AddLabel(unsigned int hash);
StatusCode AssignLabel(strref label, strref line, bool make_constant = false); StatusCode AssignLabel(strref label, strref line, bool make_constant = false);
StatusCode AddressLabel(strref label); StatusCode AddressLabel(strref label);
void LabelAdded(Label *pLabel); void LabelAdded(Label *pLabel, bool local=false);
void IncSym(strref line); void IncludeSymbols(strref line);
// Manage locals
void MarkLabelLocal(strref label, bool scope_label = false);
void FlushLocalLabels(int scope_exit = -1);
// Label pools
LabelPool* GetLabelPool(strref pool_name);
StatusCode AddLabelPool(strref name, strref args);
StatusCode AssignPoolLabel(LabelPool &pool, strref args);
void FlushLabelPools(int scope_exit);
// Late expression evaluation // Late expression evaluation
void AddLateEval(int pc, int scope_pc, unsigned char *target, void AddLateEval(int pc, int scope_pc, unsigned char *target,
@@ -424,10 +497,6 @@ public:
strref expression, LateEvalType type); strref expression, LateEvalType type);
StatusCode CheckLateEval(strref added_label=strref(), int scope_end = -1); StatusCode CheckLateEval(strref added_label=strref(), int scope_end = -1);
// Manage locals
void MarkLabelLocal(strref label);
void FlushLocalLabels();
// Assembler steps // Assembler steps
StatusCode ApplyDirective(AssemblerDirective dir, strref line, strref source_file); StatusCode ApplyDirective(AssemblerDirective dir, strref line, strref source_file);
AddressingMode GetAddressMode(strref line, bool flipXY, AddressingMode GetAddressMode(strref line, bool flipXY,
@@ -436,9 +505,10 @@ public:
StatusCode BuildSegment(OP_ID *pInstr, int numInstructions); StatusCode BuildSegment(OP_ID *pInstr, int numInstructions);
// constructor // constructor
Asm() : address(0x1000), load_address(0x1000), scope_depth(0), set_load_address(false), Asm() {
output(nullptr), curr(nullptr), output_capacity(0), symbol_export(false) Cleanup();
{ localLabels.reserve(256); } localLabels.reserve(256);
}
}; };
// Binary search over an array of unsigned integers, may contain multiple instances of same key // Binary search over an array of unsigned integers, may contain multiple instances of same key
@@ -513,6 +583,7 @@ void Asm::Cleanup() {
free(symbols.charstr()); free(symbols.charstr());
symbols.set_overlay(nullptr,0); symbols.set_overlay(nullptr,0);
} }
labelPools.clear();
loadedData.clear(); loadedData.clear();
labels.clear(); labels.clear();
macros.clear(); macros.clear();
@@ -521,6 +592,17 @@ void Asm::Cleanup() {
output = nullptr; output = nullptr;
curr = nullptr; curr = nullptr;
output_capacity = 0; output_capacity = 0;
address = 0x1000;
load_address = 0x1000;
scope_depth = 0;
conditional_depth = 0;
conditional_nesting[0] = 0;
conditional_consumed[0] = false;
set_load_address = false;
output_capacity = false;
symbol_export = false;
last_label_local = false;
} }
// Make sure there is room to assemble in // Make sure there is room to assemble in
@@ -576,28 +658,6 @@ StatusCode Asm::AddMacro(strref macro, strref source_name, strref source_file)
} }
// mark a label as a local label
void Asm::MarkLabelLocal(strref label)
{
localLabels.push_back(label);
}
// find all local labels and remove them
void Asm::FlushLocalLabels()
{
std::vector<strref>::iterator i = localLabels.begin();
while (i!=localLabels.end()) {
unsigned int index = FindLabelIndex(i->fnv1a(), labels.getKeys(), labels.count());
while (index<labels.count()) {
if (i->same_str_case(labels.getValue(index).label_name)) {
labels.remove(index);
break;
}
}
i = localLabels.erase(i);
}
}
// if an expression could not be evaluated, add it along with // if an expression could not be evaluated, add it along with
// the action to perform if it can be evaluated later. // the action to perform if it can be evaluated later.
void Asm::AddLateEval(int pc, int scope_pc, unsigned char *target, strref expression, strref source_file, LateEvalType type) void Asm::AddLateEval(int pc, int scope_pc, unsigned char *target, strref expression, strref source_file, LateEvalType type)
@@ -686,7 +746,8 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end)
if (num_new_labels<MAX_LABELS_EVAL_ALL) if (num_new_labels<MAX_LABELS_EVAL_ALL)
new_labels[num_new_labels++] = label->label_name; new_labels[num_new_labels++] = label->label_name;
evaluated_label = true; evaluated_label = true;
LabelAdded(label); char f = i->label[0], l = i->label.get_last();
LabelAdded(label, f=='.' || f=='!' || f=='@' || l=='$');
break; break;
} }
default: default:
@@ -716,28 +777,31 @@ Label *Asm::GetLabel(strref label)
return nullptr; return nullptr;
} }
static const strref str_label("label");
static const strref str_const("const");
// If exporting labels, append this label to the list // If exporting labels, append this label to the list
void Asm::LabelAdded(Label *pLabel) void Asm::LabelAdded(Label *pLabel, bool local)
{ {
if (pLabel && pLabel->evaluated && symbol_export) { if (pLabel && pLabel->evaluated && symbol_export) {
int space = 1 + str_label.get_len() + 1 + pLabel->label_name.get_len() + 1 + 9 + 2; int space = 1 + str_label.get_len() + 1 + pLabel->label_name.get_len() + 1 + 9 + 2;
if ((symbols.get_len()+space) > symbols.cap()) { if ((symbols.get_len()+space) > symbols.cap()) {
strl_t new_size = ((symbols.get_len()+space)+8*1024); strl_t new_size = ((symbols.get_len()+space)+8*1024);
char *new_charstr = (char*)malloc(new_size); if (char *new_charstr = (char*)malloc(new_size)) {
if (symbols.charstr()) { if (symbols.charstr()) {
memcpy(new_charstr, symbols.charstr(), symbols.get_len()); memcpy(new_charstr, symbols.charstr(), symbols.get_len());
free(symbols.charstr()); free(symbols.charstr());
}
symbols.set_overlay(new_charstr, new_size, symbols.get_len());
} }
symbols.set_overlay(new_charstr, new_size, symbols.get_len());
} }
symbols.append('.'); if (local && !last_label_local)
symbols.append("{\n");
else if (!local && last_label_local)
symbols.append("}\n");
symbols.append(local ? " ." : ".");
symbols.append(pLabel->constant ? str_const : str_label); symbols.append(pLabel->constant ? str_const : str_label);
symbols.append(' '); symbols.append(' ');
symbols.append(pLabel->label_name); symbols.append(pLabel->label_name);
symbols.sprintf_append("=$%04x\n", pLabel->value); symbols.sprintf_append("=$%04x\n", pLabel->value);
last_label_local = local;
} }
} }
@@ -928,6 +992,224 @@ Label* Asm::AddLabel(unsigned int hash) {
return labels.getValues() + index; return labels.getValues() + index;
} }
// mark a label as a local label
void Asm::MarkLabelLocal(strref label, bool scope_reserve)
{
LocalLabelRecord rec;
rec.label = label;
rec.scope_depth = scope_depth;
rec.scope_reserve = scope_reserve;
localLabels.push_back(rec);
}
// find all local labels or up to given scope level and remove them
void Asm::FlushLocalLabels(int scope_exit)
{
// iterate from end of local label records and early out if the label scope is lower than the current.
std::vector<LocalLabelRecord>::iterator i = localLabels.end();
while (i!=localLabels.begin()) {
--i;
if (i->scope_depth < scope_depth)
break;
strref label = i->label;
if (!i->scope_reserve || i->scope_depth<=scope_exit) {
unsigned int index = FindLabelIndex(label.fnv1a(), labels.getKeys(), labels.count());
while (index<labels.count()) {
if (label.same_str_case(labels.getValue(index).label_name)) {
if (i->scope_reserve) {
if (LabelPool *pool = GetLabelPool(labels.getValue(index).expression)) {
pool->Release(labels.getValue(index).value);
break;
}
}
labels.remove(index);
break;
}
++index;
}
i = localLabels.erase(i);
}
}
}
// Get a label pool by name
LabelPool* Asm::GetLabelPool(strref pool_name)
{
unsigned int pool_hash = pool_name.fnv1a();
unsigned int ins = FindLabelIndex(pool_hash, labelPools.getKeys(), labelPools.count());
while (ins < labelPools.count() && pool_hash == labelPools.getKey(ins)) {
if (pool_name.same_str(labelPools.getValue(ins).pool_name)) {
return &labelPools.getValue(ins);
}
ins++;
}
return nullptr;
}
// When going out of scope, label pools are deleted.
void Asm::FlushLabelPools(int scope_exit)
{
unsigned int i = 0;
while (i<labelPools.count()) {
if (labelPools.getValue(i).scopeDepth >= scope_exit)
labelPools.remove(i);
else
++i;
}
}
// Add a label pool
StatusCode Asm::AddLabelPool(strref name, strref args)
{
unsigned int pool_hash = name.fnv1a();
unsigned int ins = FindLabelIndex(pool_hash, labelPools.getKeys(), labelPools.count());
unsigned int index = ins;
while (index < labelPools.count() && pool_hash == labelPools.getKey(index)) {
if (name.same_str(labelPools.getValue(index).pool_name))
return ERROR_LABEL_POOL_REDECLARATION;
index++;
}
// check that there is at least one valid address
int ranges = 0;
int num32 = 0;
unsigned short aRng[256];
while (strref arg = args.split_token_trim(',')) {
strref start = arg[0]=='(' ? arg.scoped_block_skip() : arg.split_token_trim('-');
int addr0 = 0, addr1 = 0;
if (STATUS_OK != EvalExpression(start, address, scope_address[scope_depth], -1, addr0))
return ERROR_POOL_RANGE_EXPRESSION_EVAL;
if (STATUS_OK != EvalExpression(arg, address, scope_address[scope_depth], -1, addr1))
return ERROR_POOL_RANGE_EXPRESSION_EVAL;
if (addr1<=addr0 || addr0<0)
return ERROR_POOL_RANGE_EXPRESSION_EVAL;
aRng[ranges++] = addr0;
aRng[ranges++] = addr1;
num32 += (addr1-addr0+15)>>4;
if (ranges >(MAX_POOL_RANGES*2) ||
num32 > ((MAX_POOL_BYTES+15)>>4))
return ERROR_POOL_RANGE_EXPRESSION_EVAL;
}
if (!ranges)
return ERROR_POOL_RANGE_EXPRESSION_EVAL;
LabelPool pool;
pool.pool_name = name;
pool.numRanges = ranges>>1;
pool.scopeDepth = scope_depth;
memset(pool.usedMap, 0, sizeof(unsigned int) * num32);
for (int r = 0; r<ranges; r++)
pool.ranges[r] = aRng[r];
labelPools.insert(ins, pool_hash);
LabelPool &poolValue = labelPools.getValue(ins);
poolValue = pool;
return STATUS_OK;
}
StatusCode Asm::AssignPoolLabel(LabelPool &pool, strref label)
{
strref type = label;
label = type.split_token('.');
int bytes = 1;
if (strref::tolower(type[0])=='w')
bytes = 2;
if (GetLabel(label))
return ERROR_POOL_LABEL_ALREADY_DEFINED;
unsigned int addr;
StatusCode error = pool.Reserve(bytes, addr);
if (error != STATUS_OK)
return error;
Label *pLabel = AddLabel(label.fnv1a());
pLabel->label_name = label;
pLabel->expression = pool.pool_name;
pLabel->evaluated = true;
pLabel->value = addr;
pLabel->zero_page = addr<0x100;
pLabel->pc_relative = true;
pLabel->constant = true;
MarkLabelLocal(label, true);
return error;
}
// Request a label from a pool
StatusCode LabelPool::Reserve(int numBytes, unsigned int &ret_addr)
{
unsigned int *map = usedMap;
unsigned short *pRanges = ranges;
for (int r = 0; r<numRanges; r++) {
int sequence = 0;
unsigned int a0 = *pRanges++, a1 = *pRanges++;
unsigned int addr = a1-1, *range_map = map;
while (addr>=a0 && sequence<numBytes) {
unsigned int chk = *map++, m = 3;
while (m && addr >= a0) {
if ((m & chk)==0) {
sequence++;
if (sequence == numBytes)
break;
} else
sequence = 0;
--addr;
m <<= 2;
}
}
if (sequence == numBytes) {
unsigned int index = (a1-addr-numBytes);
unsigned int *addr_map = range_map + (index>>4);
unsigned int m = numBytes << (index << 1);
for (int b = 0; b<numBytes; b++) {
*addr_map |= m;
unsigned int _m = m << 2;
if (!_m) { m <<= 30; addr_map++; } else { m = _m; }
}
ret_addr = addr;
return STATUS_OK;
}
}
return ERROR_OUT_OF_LABELS_IN_POOL;
}
// Release a label from a pool (at scope closure)
StatusCode LabelPool::Release(unsigned int addr) {
unsigned int *map = usedMap;
unsigned short *pRanges = ranges;
for (int r = 0; r<numRanges; r++) {
unsigned short a0 = *pRanges++, a1 = *pRanges++;
if (addr>=a0 && addr<a1) {
unsigned int index = (a1-addr-1);
map += index>>4;
index &= 0xf;
unsigned int u = *map, m = 3 << (index << 1);
unsigned int b = u & m, bytes = b >> (index << 1);
if (bytes) {
for (unsigned int f = 0; f<bytes; f++) {
u &= ~m;
unsigned int _m = m>>2;
if (!_m) { m <<= 30; *map-- = u; } else { m = _m; }
}
*map = u;
return STATUS_OK;
} else
return ERROR_INTERNAL_LABEL_POOL_ERROR;
} else
map += (a1-a0+15)>>4;
}
return STATUS_OK;
}
// unique key binary search // unique key binary search
int LookupOpCodeIndex(unsigned int hash, OP_ID *lookup, int count) int LookupOpCodeIndex(unsigned int hash, OP_ID *lookup, int count)
{ {
@@ -967,6 +1249,13 @@ DirectiveName aDirectiveNames[] {
{ "CONST", AD_CONST }, { "CONST", AD_CONST },
{ "LABEL", AD_LABEL }, { "LABEL", AD_LABEL },
{ "INCSYM", AD_INCSYM }, { "INCSYM", AD_INCSYM },
{ "LABPOOL", AD_LABPOOL },
{ "POOL", AD_LABPOOL },
{ "#IF", AD_IF },
{ "#IFDEF", AD_IFDEF },
{ "#ELSE", AD_ELSE },
{ "#ELIF", AD_ELIF },
{ "#ENDIF", AD_ENDIF },
}; };
static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]); static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]);
@@ -1083,10 +1372,47 @@ AddressingMode Asm::GetAddressMode(strref line, bool flipXY, StatusCode &error,
return addrMode; return addrMode;
} }
// Conditional statement eval (true/false)
StatusCode Asm::EvalStatement(strref line, bool &result)
{
int equ = line.find('=');
if (equ >=0) {
// (EXP) == (EXP)
strref left = line.get_clipped(equ);
bool equal = left.get_last()!='!';
left.trim_whitespace();
strref right = line + equ + 1;
if (right.get_first()=='=')
++right;
right.trim_whitespace();
int value_left, value_right;
if (STATUS_OK != EvalExpression(left, address, scope_address[scope_depth], -1, value_left))
return ERROR_CONDITION_COULD_NOT_BE_RESOLVED;
if (STATUS_OK != EvalExpression(right, address, scope_address[scope_depth], -1, value_right))
return ERROR_CONDITION_COULD_NOT_BE_RESOLVED;
result = (value_left==value_right && equal) || (value_left!=value_right && !equal);
} else {
bool invert = line.get_first()=='!';
if (invert)
++line;
int value;
if (STATUS_OK != EvalExpression(line, address, scope_address[scope_depth], -1, value))
return ERROR_CONDITION_COULD_NOT_BE_RESOLVED;
result = (value!=0 && !invert) || (value==0 && invert);
}
return STATUS_OK;
}
// Action based on assembler directive // Action based on assembler directive
StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref source_file) StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref source_file)
{ {
StatusCode error = STATUS_OK; StatusCode error = STATUS_OK;
if (conditional_nesting[conditional_depth]) {
if (dir!=AD_IF && dir!=AD_IFDEF && dir!=AD_ELSE && dir!=AD_ELIF && dir!=AD_ELSE && dir!=AD_ENDIF)
return STATUS_OK;
}
switch (dir) { switch (dir) {
case AD_ORG: { // org / pc: current address of code case AD_ORG: { // org / pc: current address of code
int addr; int addr;
@@ -1122,6 +1448,8 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
} }
case AD_ALIGN: // align: align address to multiple of value, fill space with 0 case AD_ALIGN: // align: align address to multiple of value, fill space with 0
if (line) { if (line) {
if (line[0]=='=' || keyword_equ.is_prefix_word(line))
line.next_word_ws();
int value; int value;
int status = EvalExpression(line, address, scope_address[scope_depth], -1, value); int status = EvalExpression(line, address, scope_address[scope_depth], -1, value);
if (status == STATUS_NOT_READY) if (status == STATUS_NOT_READY)
@@ -1137,14 +1465,23 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
break; break;
case AD_EVAL: { // eval: display the result of an expression in stdout case AD_EVAL: { // eval: display the result of an expression in stdout
int value = 0; int value = 0;
strref description = line.split_token_trim(':'); strref description = line.find(':')>=0 ? line.split_token_trim(':') : strref();
line.trim_whitespace(); line.trim_whitespace();
if (line && EvalExpression(line, address, scope_address[scope_depth], -1, value) == STATUS_OK) if (line && EvalExpression(line, address, scope_address[scope_depth], -1, value) == STATUS_OK) {
printf("EVAL(%d): " STRREF_FMT ": \"" STRREF_FMT "\" = $%x\n", if (description) {
contextStack.curr().source_file.count_lines(description)+1, STRREF_ARG(description), STRREF_ARG(line), value); printf("EVAL(%d): " STRREF_FMT ": \"" STRREF_FMT "\" = $%x\n",
else contextStack.curr().source_file.count_lines(description)+1, STRREF_ARG(description), STRREF_ARG(line), value);
} else {
printf("EVAL(%d): \"" STRREF_FMT "\" = $%x\n",
contextStack.curr().source_file.count_lines(line)+1, STRREF_ARG(line), value);
}
} else if (description) {
printf("EVAL(%d): \"" STRREF_FMT ": " STRREF_FMT"\"\n", printf("EVAL(%d): \"" STRREF_FMT ": " STRREF_FMT"\"\n",
contextStack.curr().source_file.count_lines(description)+1, STRREF_ARG(description), STRREF_ARG(line)); contextStack.curr().source_file.count_lines(description)+1, STRREF_ARG(description), STRREF_ARG(line));
} else {
printf("EVAL(%d): \"" STRREF_FMT "\"\n",
contextStack.curr().source_file.count_lines(line)+1, STRREF_ARG(line));
}
break; break;
} }
case AD_BYTES: // bytes: add bytes by comma separated values/expressions case AD_BYTES: // bytes: add bytes by comma separated values/expressions
@@ -1171,7 +1508,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
CheckOutputCapacity(2); CheckOutputCapacity(2);
*curr++ = (char)value; *curr++ = (char)value;
*curr++ = (char)(value>>8); *curr++ = (char)(value>>8);
address+=2; address += 2;
} }
break; break;
case AD_TEXT: { // text: add text within quotes case AD_TEXT: { // text: add text within quotes
@@ -1208,7 +1545,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
break; break;
} }
case AD_MACRO: { // macro: create an assembler macro case AD_MACRO: { // macro: create an assembler macro
strref from_here = contextStack.curr().code_segment + strref from_here = contextStack.curr().code_segment +
strl_t(line.get()-contextStack.curr().code_segment.get()); strl_t(line.get()-contextStack.curr().code_segment.get());
int block_start = from_here.find('{'); int block_start = from_here.find('{');
if (block_start > 0) { if (block_start > 0) {
@@ -1255,10 +1592,92 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
break; break;
} }
case AD_INCSYM: { case AD_INCSYM: {
IncSym(line); IncludeSymbols(line);
break; break;
} }
case AD_LABPOOL: {
strref label = line.split_range_trim(word_char_range, line[0]=='.' ? 1 : 0);
AddLabelPool(label, line);
break;
}
case AD_IF: {
if (conditional_nesting[conditional_depth])
conditional_nesting[conditional_depth]++;
else {
// #if within #if?
if (conditional_consumed[conditional_depth]) {
conditional_depth++;
conditional_consumed[conditional_depth] = false;
conditional_nesting[conditional_depth] = 0;
}
bool conditional_result;
error = EvalStatement(line, conditional_result);
if (conditional_result)
conditional_consumed[conditional_depth] = true;
else
conditional_nesting[conditional_depth] = 1;
}
break;
}
case AD_IFDEF:
if (conditional_nesting[conditional_depth])
conditional_nesting[conditional_depth]++;
else {
// #if within #if?
if (conditional_consumed[conditional_depth]) {
conditional_depth++;
conditional_consumed[conditional_depth] = false;
conditional_nesting[conditional_depth] = 0;
}
if (GetLabel(line.get_trimmed_ws()) == nullptr)
conditional_nesting[conditional_depth] = 1;
else
conditional_consumed[conditional_depth] = true;
}
break;
case AD_ELSE:
if (conditional_nesting[conditional_depth]==0) {
if (conditional_consumed[conditional_depth])
conditional_nesting[conditional_depth]++;
else
error = ERROR_ELSE_WITHOUT_IF;
} else if (conditional_nesting[conditional_depth]==1 &&
!conditional_consumed[conditional_depth]) {
conditional_nesting[conditional_depth] = 0;
conditional_consumed[conditional_depth] = true;
}
break;
case AD_ELIF:
if (conditional_nesting[conditional_depth]==0) {
if (conditional_consumed[conditional_depth])
conditional_nesting[conditional_depth]++;
else
error = ERROR_ELSE_WITHOUT_IF;
}
if (conditional_nesting[conditional_depth]==1 &&
!conditional_consumed[conditional_depth]) {
bool conditional_result;
error = EvalStatement(line, conditional_result);
if (conditional_result) {
conditional_nesting[conditional_depth] = 0;
conditional_consumed[conditional_depth] = true;
}
}
break;
case AD_ENDIF:
if (conditional_nesting[conditional_depth]) {
conditional_nesting[conditional_depth]--;
if (!conditional_nesting[conditional_depth]) {
conditional_consumed[conditional_depth] = false;
if (conditional_depth)
conditional_depth--;
}
} else if (conditional_consumed[conditional_depth]) {
conditional_consumed[conditional_depth] = false;
if (conditional_depth)
conditional_depth--;
} else
error = ERROR_ENDIF_WITHOUT_CONDITION;
} }
return error; return error;
} }
@@ -1314,7 +1733,7 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
break; break;
case OPG_SUBROUT: case OPG_SUBROUT:
if (index==1) { // jsr if (index==OPI_JSR) { // jsr
if (addrMode != AM_ABSOLUTE) if (addrMode != AM_ABSOLUTE)
error = ERROR_INVALID_ADDRESSING_MODE_FOR_BRANCH; error = ERROR_INVALID_ADDRESSING_MODE_FOR_BRANCH;
else else
@@ -1329,8 +1748,8 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
case OPG_CC00: case OPG_CC00:
// jump relative exception // jump relative exception
if (addrMode==AM_RELATIVE && index==OPI_JMP) { if (addrMode==AM_RELATIVE && index==OPI_JMP) {
base_opcode += 0x20; base_opcode += RELATIVE_JMP_DELTA;
addrMode = AM_ABSOLUTE; addrMode = AM_ABSOLUTE; // the relative address is in an absolute location ;)
} }
if (addrMode>7 || (CC00Mask[index]&(1<<addrMode))==0) if (addrMode>7 || (CC00Mask[index]&(1<<addrMode))==0)
error = ERROR_BAD_ADDRESSING_MODE; error = ERROR_BAD_ADDRESSING_MODE;
@@ -1348,7 +1767,6 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
} }
} }
break; break;
case OPG_CC01: case OPG_CC01:
if (addrMode>7 || (addrMode==AM_IMMEDIATE && index==OPI_STA)) if (addrMode>7 || (addrMode==AM_IMMEDIATE && index==OPI_STA))
error = ERROR_BAD_ADDRESSING_MODE; error = ERROR_BAD_ADDRESSING_MODE;
@@ -1394,7 +1812,6 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
break; break;
} }
} }
// Add the instruction and argument to the code // Add the instruction and argument to the code
if (error == STATUS_OK) { if (error == STATUS_OK) {
CheckOutputCapacity(4); CheckOutputCapacity(4);
@@ -1474,29 +1891,33 @@ StatusCode Asm::BuildMacro(Macro &m, strref arg_list)
return STATUS_OK; return STATUS_OK;
} }
void Asm::IncSym(strref line) // include symbols listed from a .sym file or all if no listing
void Asm::IncludeSymbols(strref line)
{ {
// include symbols listed or all if no listing
strref symlist = line.before('"').get_trimmed_ws(); strref symlist = line.before('"').get_trimmed_ws();
line = line.between('"', '"'); line = line.between('"', '"');
size_t size; size_t size;
if (char *buffer = LoadText(line, size)) { if (char *buffer = LoadText(line, size)) {
strref symfile(buffer, strl_t(size)); strref symfile(buffer, strl_t(size));
while (strref symdef = symfile.line()) { while (symfile) {
strref symtype = symdef.split_token(' '); symfile.skip_whitespace();
strref label = symdef.split_token_trim('='); if (symfile[0]=='{') // don't include local labels
// first word is either .label or .const symfile.scoped_block_skip();
bool constant = symtype.same_str(".const"); if (strref symdef = symfile.line()) {
if (symlist) { strref symtype = symdef.split_token(' ');
strref symchk = symlist; strref label = symdef.split_token_trim('=');
while (strref symwant = symchk.split_token_trim(',')) { bool constant = symtype.same_str(".const"); // first word is either .label or .const
if (symwant.same_str_case(label)) { if (symlist) {
AssignLabel(label, symdef, constant); strref symchk = symlist;
break; while (strref symwant = symchk.split_token_trim(',')) {
if (symwant.same_str_case(label)) {
AssignLabel(label, symdef, constant);
break;
}
} }
} } else
} else AssignLabel(label, symdef, constant);
AssignLabel(label, symdef, constant); }
} }
loadedData.push_back(buffer); loadedData.push_back(buffer);
} }
@@ -1526,15 +1947,19 @@ StatusCode Asm::AssignLabel(strref label, strref line, bool make_constant)
pLabel->pc_relative = false; pLabel->pc_relative = false;
pLabel->constant = make_constant; pLabel->constant = make_constant;
bool local = label[0]=='.' || label[0]=='@' || label[0]=='!' || label.get_last()=='$';
if (!pLabel->evaluated) if (!pLabel->evaluated)
AddLateEval(label, address, scope_address[scope_depth], line, LET_LABEL); AddLateEval(label, address, scope_address[scope_depth], line, LET_LABEL);
else { else {
LabelAdded(pLabel); if (local)
MarkLabelLocal(label);
LabelAdded(pLabel, local);
return CheckLateEval(label); return CheckLateEval(label);
} }
return STATUS_OK; return STATUS_OK;
} }
// Adding a fixed address label
StatusCode Asm::AddressLabel(strref label) StatusCode Asm::AddressLabel(strref label)
{ {
Label *pLabel = GetLabel(label); Label *pLabel = GetLabel(label);
@@ -1553,8 +1978,9 @@ StatusCode Asm::AddressLabel(strref label)
pLabel->pc_relative = true; pLabel->pc_relative = true;
pLabel->constant = constLabel; pLabel->constant = constLabel;
pLabel->zero_page = false; pLabel->zero_page = false;
LabelAdded(pLabel); bool local = label[0]=='.' || label[0]=='@' || label[0]=='!' || label.get_last()=='$';
if (label[0]=='.' || label[0]=='@' || label[0]=='!' || label.get_last()=='$') LabelAdded(pLabel, local);
if (local)
MarkLabelLocal(label); MarkLabelLocal(label);
else else
FlushLocalLabels(); FlushLocalLabels();
@@ -1581,26 +2007,30 @@ StatusCode Asm::BuildSegment(OP_ID *pInstr, int numInstructions)
++operation; ++operation;
} }
if (!operation) { if (!operation) {
// scope open / close if (!conditional_nesting[conditional_depth]) {
switch (line[0]) { // scope open / close
case '{': switch (line[0]) {
if (scope_depth>=(MAX_SCOPE_DEPTH-1)) case '{':
error = ERROR_TOO_DEEP_SCOPE; if (scope_depth>=(MAX_SCOPE_DEPTH-1))
else { error = ERROR_TOO_DEEP_SCOPE;
scope_address[++scope_depth] = address; else {
scope_address[++scope_depth] = address;
++line;
line.skip_whitespace();
}
break;
case '}':
// check for late eval of anything with an end scope
CheckLateEval(strref(), address);
FlushLocalLabels(scope_depth);
FlushLabelPools(scope_depth);
--scope_depth;
if (scope_depth<0)
error = ERROR_UNBALANCED_SCOPE_CLOSURE;
++line; ++line;
line.skip_whitespace(); line.skip_whitespace();
} break;
break; }
case '}':
// check for late eval of anything with an end scope
CheckLateEval(strref(), address);
--scope_depth;
if (scope_depth<0)
error = ERROR_UNBALANCED_SCOPE_CLOSURE;
++line;
line.skip_whitespace();
break;
} }
} else { } else {
// ignore leading period for instructions and directives - not for labels // ignore leading period for instructions and directives - not for labels
@@ -1609,14 +2039,15 @@ StatusCode Asm::BuildSegment(OP_ID *pInstr, int numInstructions)
if (op_idx >= 0 && line[0]!=':') { if (op_idx >= 0 && line[0]!=':') {
if (pInstr[op_idx].type==OT_DIRECTIVE) { if (pInstr[op_idx].type==OT_DIRECTIVE) {
error = ApplyDirective((AssemblerDirective)pInstr[op_idx].index, line, contextStack.curr().source_file); error = ApplyDirective((AssemblerDirective)pInstr[op_idx].index, line, contextStack.curr().source_file);
line.clear(); } else if (!conditional_nesting[conditional_depth] && pInstr[op_idx].type==OT_MNEMONIC) {
} else if (pInstr[op_idx].type==OT_MNEMONIC) {
OP_ID &id = pInstr[op_idx]; OP_ID &id = pInstr[op_idx];
int group = id.group; int group = id.group;
int index = id.index; int index = id.index;
error = AddOpcode(line, group, index, contextStack.curr().source_file); error = AddOpcode(line, group, index, contextStack.curr().source_file);
line.clear();
} }
line.clear();
} else if (conditional_nesting[conditional_depth]) {
line.clear(); // do nothing if conditional nesting so clear the current line
} else if (line.get_first()=='=') { } else if (line.get_first()=='=') {
++line; ++line;
error = AssignLabel(label, line); error = AssignLabel(label, line);
@@ -1624,20 +2055,33 @@ StatusCode Asm::BuildSegment(OP_ID *pInstr, int numInstructions)
} else { } else {
unsigned int nameHash = label.fnv1a(); unsigned int nameHash = label.fnv1a();
unsigned int macro = FindLabelIndex(nameHash, macros.getKeys(), macros.count()); unsigned int macro = FindLabelIndex(nameHash, macros.getKeys(), macros.count());
bool gotMacro = false; bool gotConstruct = false;
while (macro < macros.count() && nameHash==macros.getKey(macro)) { while (macro < macros.count() && nameHash==macros.getKey(macro)) {
if (macros.getValue(macro).name.same_str_case(label)) { if (macros.getValue(macro).name.same_str_case(label)) {
error = BuildMacro(macros.getValue(macro), line); error = BuildMacro(macros.getValue(macro), line);
gotMacro = true; gotConstruct = true;
line.clear(); // don't process codes from here line.clear(); // don't process codes from here
break; break;
} }
macro++; macro++;
} }
if (!gotMacro) { if (!gotConstruct) {
error = AddressLabel(label); unsigned int labPool = FindLabelIndex(nameHash, labelPools.getKeys(), labelPools.count());
if (line[0]==':') gotConstruct = false;
++line; while (labPool < labelPools.count() && nameHash==labelPools.getKey(labPool)) {
if (labelPools.getValue(labPool).pool_name.same_str_case(label)) {
error = AssignPoolLabel(labelPools.getValue(labPool), line);
gotConstruct = true;
line.clear(); // don't process codes from here
break;
}
labPool++;
}
if (!gotConstruct) {
error = AddressLabel(label);
if (line[0]==':')
++line;
}
// there may be codes after the label // there may be codes after the label
} }
} }
@@ -1685,6 +2129,11 @@ void Asm::Assemble(strref source, strref filename)
errorText.append(aStatusStrings[error]); errorText.append(aStatusStrings[error]);
fwrite(errorText.get(), errorText.get_len(), 1, stderr); fwrite(errorText.get(), errorText.get_len(), 1, stderr);
} }
// close last local label of symbol file
if (symbol_export && last_label_local)
symbols.append("}\n");
for (std::vector<LateEval>::iterator i = lateEval.begin(); i!=lateEval.end(); ++i) { for (std::vector<LateEval>::iterator i = lateEval.begin(); i!=lateEval.end(); ++i) {
strown<512> errorText; strown<512> errorText;
int line = i->source_file.count_lines(i->expression); int line = i->source_file.count_lines(i->expression);