Local labels
A local label can take a lot of different values (it's its raison d'etre).
All the values are stored in a list and when it's time to evaluate, we pick the corresponding value in this list (see next section).
This allows forward references as in:
1000 ** [
; ... code
jr c,.done
; ... code
.doneAt phase 1, all the addresses taken are stored, so we can dispatch them at phase 2.
There is some more housekeeping, as a same label can be used as local and global.
A flag in the symbol table discriminate this case.
Resolution and scope
Let's consider the following skeleton:
init ; global label
; ...
.raz ; address0
;...
jr nz,.raz ; backward reference
;..
clear_if_needed ; global_label
;...
jr c,.raz ; forward reference
;...
.raz ; address1
;...In phase1, we want to associate .raz with a list address0, address1
This list alone wouldn't be enough to resolve .raz in both cases, since we must handle forward reference.
That is, we must answer the question: which .raz instance is referenced?
Enter the concept of scope. Each global label instantiate a fresh scope ID (0 for init, 1 for clear_if_needed in our example).
The list for .raz will contain (scopeId0, address0), (scopeId1, address1).
In phase 2, the same scope IDs are generated (we start afresh from 0).
We just have to pick the instance with the current scope ID.
Nested scope
Well, we allow:
init
; ...
dotsNb ** [
; ...
jr nc,.done
; ...
jr c,.ok
; ...
.ok
]
.doneEach repetition creates a new scope for .ok resolution (note that .ok will take dotsNb different values).
But .done is not associated to any of those scopes.
Enter the concept of nested scopes!
Entering a repetition or a macro invocation creates a child scope.
When a label isn't defined in the current scope, we recursively check in parent scopes.
All that is done in symb.o:_get_local