As you have earlier learned, SpectNetIDE uses nested scopes for labels and symbols. When it resolves them to their values, the engine starts from the innermost scope and traverses all outer scopes until it finds the symbol value, or goes through all scopes without success — and it declares the sought symbol undefined. This type of resolution does not allow an outer scope to reach a symbol within a nested scope.
SpectNetIDE allows you to declare modules. A module is a logical scope for label and symbol resolution; it works entirely the same way as other scopes. However, a module offers something special: you can access labels and symbols declared within a module. Just as other lexical scopes, modules can be nested.
Module Declarations
A module is a section of code enclosed between the .module
and .endmodule
statements. Each module needs to have a name. You have three options to define the name:
; #1: Label
MyModule:
.module
; ...
.endmodule
; #2: Module ID
.module OtherModule
; ...
.endmodule
; #3: Label and ID
Module3:
.module ThirdModule
; ...
.endmodule
You can use either a label preceding the module declaration or an identifier after the .module
token, or even both. The identifier takes precedence over the name, so the names of the three modules in this sample are MyModule
, OtherModule
, and ThirdModule
, respectively.
SpectNetIDE offers syntax variations for
.module
and.endmodule
. You can use the following tokens instead of.module
:module
,.scope
, orscope
..endmodule
has other variants, too:endmodule
,.endscope
,endscope
,.moduleend
,moduleend
,.scopeend
,scopeend
. You can write all these tokens with entirely lowercase or uppercase characters.
Names and Labels
When you define a module without a name, the assembler raises an error. So, this code is invalid because of the missing module name:
.module
; ...
.endmodule
You can’t use a temporary name for the module, so both of these declarations raise an issue:
`TempModule:
.module
.endmodule
Module4:
.module `Temp4
.endmodule
Nonetheless, this construct is valid, as here the module uses its identifier for the name, and not its label:
`Temp5:
.module Fifth
.endmodule
If you need to address the start point of a module, you need to declare it with a label (of course, it may have an ID, too). You can refer to this label as the start of the module. However, you cannot use the module identifier for this purpose:
MyModule:
.module
ld hl,MyModule ; OK, label name is used
EndOfMod:
.endmodule
.module OtherModule
ld hl,OtherModule ; Error, module name cannot be used as a label
.endmodule
Module3:
.module ThirdModule
ld hl,Module3 ; OK, label name is used
ld de,ThirdModule ; Error, module name cannot be used as a label
.endmodule
The label preceding the .endmodule
statement belongs to the scope of the module.
Nested Modules
You can nest modules into each other:
.module Main
; ...
.module Nested
.endmodule
; ...
.module OtherNested
; ...
.module DeeplyNested
; ...
.endmodule
; ...
.endmodule
; ...
.endmodule
The full name of modules reflects the hierarchy of their nesting. The modules in this sample have these full names in the order of their declaration:
Main
Main.Nested
Main.OtherNested
Main.OtherNested.DeeplyNested
The compiler does not allow two modules have the same full name. From this point of view, this naming is valid, even if we have two modules with the name Nested
:
.module Main
; ...
.module Nested
.endmodule
; ...
.module OtherNested
; ...
.module Nested ; OK!
.endmodule
; ...
.endmodule
; ...
.endmodule
As you see, the full names are unique:
Main.Nested
Main.OtherNested
Main.OtherNested.Nested
Label and Symbol Resolution
With simple identifiers, the name resolution happens the way you already learned, from the innermost scope toward the outermost one. However, besides simple IDs, you can use scoped identifiers that have multiple segments.
Scoped identifiers have this syntax:
::
? identifier (.
identifier)*
When the compiler finds a scoped identifier, it uses a different resolution technique. Instead of starting from the innermost scope toward the outermost, it begins with the current module and traverses toward the nested modules:
.module Main
MyValue .equ 1
ld a,Main.MyValue ; ld a,1
.module Nested
MyValue .equ 11
.endmodule
ld b,Nested.MyValue ; ld b,11
ld c,Main.Nested.MyValue ; ld c,11
.module OtherNested
MyValue .equ 12
ld d,Nested.MyValue ; ld d,121
.module Nested
MyValue .equ 121
ld e,Nested.MyValue ; ld e,121
ld h,Main.MyValue ; ld h,1
.endmodule
ld l,Nested.MyValue ; ld l,121
.endmodule
.endmodule
You can use the same name for modules on different hierarchy levels, such as in this case:
.module Main
MyValue .equ 1
ld a,Main.MyValue ; ld a,1
.module Nested
MyValue .equ 11
ld b,Nested.MyValue ; ld b,111
.module Nested
MyValue .equ 111
ld c,Nested.MyValue ; ld c,111
.endmodule
.endmodule
.endmodule
.module Nested
MyValue .equ 2
.endmodule
As you see, the name Nested
is used in several modules:
Main
Main.Nested
Main.Nested.Nested
Nested
With the standard name resolution, we cannot access the MyValue
symbol of the outer Nested
module from within Main.Nested.Nested
, as that module is in a different subtree. In such a situation we can use the global scope token, ::
, in front of the scoped identifier. This token instructs the assembler to start identifier resolution from the global module.
Now, we can access that MyValue
symbol:
.module Main
MyValue .equ 1
ld a,Main.MyValue ; ld a,1
.module Nested
MyValue .equ 11
ld b,Nested.MyValue ; ld b,111
.module Nested
MyValue .equ 111
ld c,Nested.MyValue ; ld c,111
ld d,::Nested.MyValue ; ld d,2 -- !!!
.endmodule
.endmodule
.endmodule
.module Nested
MyValue .equ 2
.endmodule
Using Module-Local Names
You can hide symbols within modules using the module-local identifier notation. Every label and symbol that starts with @
is a private symbol within its module. It means that the particular symbol is visible only within its containing module (and the ones nested in the containing module). In this code, every local identifier can be accessed:
.module Main
MyValue .equ 1
ld a,Main.MyValue ; ld a,1
.module Nested
@MyValue .equ 11
ld b,Nested.MyValue ; ld b,111
.module @Nested
MyValue .equ 111
ld c,@MyValue ; ld c,11
ld d,::Nested.MyValue ; ld d,2
.endmodule
ld e,@MyValue ; ld e,11
ld h,@Nested.MyValue ; ld h,111
.endmodule
.endmodule
.module Nested
MyValue .equ 2
.endmodule
However, module-local names cannot be used outside of their containing module, so this code snippet raises an error:
.module Main
MyValue .equ 1
ld a,Main.MyValue
.module Nested
@MyValue .equ 11
.module @Nested
MyValue .equ 111
ld c,Nested.@MyValue ; Error
.endmodule
.endmodule
ld d,Nested.@Nested.MyValue ; Error
.endmodule