Modules
The Klive Assembler allows you to organize your code into modules. A module introduces a named scope that groups labels, symbols, variables, structures, macros, and even other (nested) modules together. Modules help you avoid naming collisions in larger projects, control the visibility of symbols, and structure your code into self-contained logical units.
Note: Modules are pure compile-time constructs. They do not emit any extra bytes; they only affect how the assembler resolves names while compiling your source.
Defining a Module
You can define a module with a .module (.scope) statement and close it with .endmodule (.endscope, .moduleend, or .scopeend). The compiler accepts every flavor of these keywords, with or without a leading dot, and with both lowercase and uppercase letters. For example, all of these forms are valid: .module, module, .MODULE, MODULE, .scope, scope.
A module must have a unique name. You can specify the name in any of the following equivalent ways:
; Version #1: name as a label
MyModule: .module
; ...
.endmodule
; Version #2: name as a label without colon
MyModule .module
; ...
.endmodule
; Version #3: name as the .module argument
.module MyModule
; ...
.endmodule
; Version #4: hanging label
MyModule:
.module
; ...
.endmoduleIf you provide both a label and a name argument, the argument wins. In this snippet the module’s effective name is moduleID, not myModule:
myModule: .module moduleID
ld a,b
.endmoduleThe compiler reports an error if you do not name the module:
.module ; ERROR Z0901: You cannot define a module without a name.
; ...
.endmoduleYou also cannot use a temporary name (a name starting with the backtick character) for a module:
.module `MyModule ; ERROR: temporary name is not allowed
; ...
.endmoduleA module name must be unique within its parent scope. Trying to declare two modules with the same name in the same scope raises an error:
.module MyModule
; ...
.endmodule
.module MyModule ; ERROR Z0903: Module with name 'MyModule' already exists.
; ...
.endmoduleLikewise, every .module must be closed by an .endmodule. A missing closing statement, or an .endmodule with no matching .module, results in a compile-time error.
Module Scopes
A module defines its own symbol scope. Labels and symbols declared inside a module belong to the module and are independent of identical names declared in other modules. As a consequence, you can reuse the same label names in different modules without producing conflicts:
.org #6000
.module MyModule
ld a,b
ld bc,t1
t1:
ld a,b
.endmodule
.module MyModule2
ld a,b
ld bc,t1 ; resolves to t1 inside MyModule2
t1:
ld a,b
.endmoduleTemporary labels (those starting with a backtick) follow the same rule — each module has its own pool of temporary labels:
.module MyModule
ld bc,`t1
`t1:
ld a,b
.endmodule
.module MyModule2
ld bc,`t1 ; refers to the `t1 declared inside MyModule2
`t1:
ld a,b
.endmoduleVisibility from Inside a Module
While a module’s symbols are private to the module, the module itself can see every symbol declared in its enclosing scopes. Symbol resolution works from the innermost scope outwards: the assembler first looks for the symbol in the current module, then in its parent module, and so on, finally checking the global scope.
.org #6000
Start:
ld a,b
.module MyModule
nop
ld bc,Start ; resolves to the outer Start label
.endmoduleIf a label declared inside a module shadows a label with the same name in an outer scope, the inner one wins:
.org #6000
Outside:
ld a,b
.module MyModule
nop
Outside: ; shadows the outer Outside inside MyModule
nop
ld bc,Outside ; refers to the inner Outside
.endmoduleModule-Local Symbols (@)
Prefix a label with @ to mark it as module-local. Module-local symbols can only be referenced from the same module; the compiler does not export them to outer scopes or other modules:
.module MyModule
@LocalSym:
nop
ld bc,@LocalSym ; OK — visible inside MyModule
.endmodule
ld bc,@LocalSym ; ERROR: @LocalSym is not visible outside MyModuleNested Modules
Modules can be nested to any depth. Each nested module introduces its own scope, and the enclosing module acts as the parent scope. Symbol resolution walks through every parent module, all the way out to the global scope:
.org #6000
Start:
.module MyModule
ld a,b
ld bc,t1
t1:
ld a,b
.module Nested
t1: ; independent from MyModule.t1
ld a,b
ld bc,t1 ; refers to Nested.t1
.endmodule
ld hl,t1 ; refers to MyModule.t1
.endmoduleReferencing Symbols Across Modules
Although a module’s symbols are not visible by their simple name from the outside, you can reference them explicitly using qualified names. A qualified name lists the module path separated by dots, ending with the symbol name.
For example, given this code:
.org #6000
Start:
ld a,b
.module MyModule
ld a,b
ld bc,MyId
MyId:
ld bc,::MyId ; refers to the global MyId
.endmodule
MyId:
ld hl,MyModule.MyId ; refers to MyId inside MyModuleYou can address MyId inside MyModule from the outer scope as MyModule.MyId. Qualified names work for nested modules, too. For instance, Outer.Inner.Symbol references Symbol declared inside the Inner module that is itself nested in Outer.
Global Resolution with ::
Prefix a symbol (or a qualified name) with :: to start the resolution from the global (root) scope, regardless of how deeply you are nested in modules. This is helpful when an inner symbol shadows an outer one and you need to refer to the outer one explicitly:
.org #6000
MyStart:
ld a,b
.module MyModule
MyStart:
ld a,b
ld bc,MyStart ; resolves to MyModule.MyStart (the inner one)
ld bc,::MyStart ; resolves to the global MyStart
.endmoduleYou can combine :: with a qualified name to address any module from the global scope:
ld hl,::MyModule.MyIdModules and Other Constructs
Modules cooperate with other Klive features in a natural way:
- Each module has its own table of structures and macros. A struct or macro defined inside a module is referenced from the outside through the module’s qualified name.
- Variables (those declared with
.var) follow the same scoping rules as other symbols. - Local scopes created by control-flow statements such as
.loop,.while,.repeat,.for, and.proclive inside the current module. When symbol resolution leaves the innermost local scope, it continues with the enclosing module before stepping out to the parent module. - A module may contain any number of nested modules, structs, macros, and statements. There is no fixed nesting limit.