# How to Define and Use Data Constants and Strings in NanoCore Assembly

> Learn to define and use data constants and strings in NanoCore assembly with .CONST .DB and .STRING directives. Optimize your code by understanding compile-time constant definition and resolution.

- Repository: [Afaan Bilal/nanocore](https://github.com/afaanbilal/nanocore)
- Tags: how-to-guide
- Published: 2026-02-23

---

**NanoCore's two-pass assembler supports compile-time constants via `.CONST`, raw byte embedding via `.DB`, and string literals via `.STRING` directives, storing values in a `constants: HashMap<String, u8>` and resolving them during the second pass.**

NanoCore is a lightweight virtual machine with a two-pass assembler that allows developers to define and use data constants and strings in NanoCore assembly code through specialized directives. This guide examines the implementation in `afaanbilal/nanocore` and demonstrates how to embed compile-time values, raw bytes, and text literals in your `.nca` source files.

## Defining Compile-Time Constants with .CONST

The `.CONST` directive creates symbolic names for byte-wide values that the assembler substitutes during compilation.

### How the Assembler Parses Constants

In [`src/assembler.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/assembler.rs), the `Assembler::is_constant` method (lines 55-61) identifies lines beginning with `.CONST`. During the first pass, `map_constants()` scans the source and extracts name/value pairs, storing them in the `constants: HashMap<String, u8>` structure (lines 56-90).

### First Pass: Building the Constant Table

The assembler iterates through every line, splitting tokens to find constant definitions. Each `.CONST NAME VALUE` entry populates the hash map before any instruction encoding occurs, enabling forward references.

### Second Pass: Resolving Constant References

When encoding instructions, `resolve_number()` (lines 94-100) checks the `constants` map first (lines 95-97). If the token matches a defined constant, its numerical value substitutes for the operand.

```assembly
; programs/constants_demo.nca
.CONST MAX_VAL 10        ; decimal literal
.CONST START 0x20        ; hexadecimal literal

LDI R0 MAX_VAL          ; loads 10 into R0
LDI R1 START            ; loads 0x20 into R1
ADD R0 R1               ; R0 = 0x2A
PRINT R0                ; prints '*'
HLT

```

The compiled binary contains the bytes:

```hex
02 00 0A   ; LDI R0 10
02 01 20   ; LDI R1 0x20
09 01      ; ADD R0 R1
19 00      ; PRINT R0
00         ; HLT

```

## Embedding Raw Data with .DB

The `.DB` directive inserts raw byte values directly into the program memory.

In [`src/assembler.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/assembler.rs), the second pass handles lines starting with `.DB` by parsing all subsequent tokens as decimal or hexadecimal numbers and appending them verbatim to the output buffer.

```assembly
; programs/table_demo.nca
.DB 0x01 0x02 0x03 0x04   ; four bytes of data

LDI R0 0x00               ; start address of table (0)
LDR R1 R0                 ; load first entry (0x01) into R1
PRINT R1                  ; prints control character
HLT

```

The generated binary begins with `01 02 03 04` followed by the instruction encoding.

## Storing String Literals with .STRING

The `.STRING` directive embeds UTF-8 text as sequential bytes.

In [`src/assembler.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/assembler.rs) (lines 64-77), the assembler detects `.STRING` lines, extracts the text between the first and last double-quote, and pushes each character's UTF-8 byte value onto the program buffer.

```assembly
; programs/hello.nca
.STRING "Hello"

LDI R0 0x00      ; address of the first character
PRINT_LOOP:
    PRINT R0    ; prints the byte at address R0
    INC R0
    LDI R1 0x05 ; length of "Hello"
    SUB R0 R1   ; stop when R0 == length
    JNZ PRINT_LOOP
HLT

```

After assembly, the binary contains the ASCII codes `48 65 6C 6C 6F` followed by the instructions.

## Complete Example: Combining Constants, Data, and Strings

This mixed program demonstrates symbolic constants for memory addresses and string lengths:

```assembly
; programs/mixed_demo.nca
.CONST MSG_ADDR 0x00
.CONST MSG_LEN  0x0B

.STRING "NanoCore!"

LDI R0 MSG_ADDR   ; load start address of string
PRINT_LOOP:
    PRINT R0
    INC R0
    LDI R1 MSG_LEN
    SUB R0 R1
    JNZ PRINT_LOOP
HLT

```

The constants provide symbolic names for the string's location and length. The `.STRING` directive embeds the text, and the loop prints the entire message before halting.

**Compiled output (hex):**

```

48 61 6E 6F 43 6F 72 65 21   ; "NanoCore!"
02 00 00                     ; LDI R0 MSG_ADDR
19 00                        ; PRINT R0
0D 00                        ; INC R0
02 01 0B                     ; LDI R1 MSG_LEN
0B 01 00                     ; SUB R0 R1
18 0A                        ; JNZ PRINT_LOOP
00                           ; HLT

```

## Summary

- **`.CONST NAME VALUE`** defines byte-wide compile-time constants stored in `constants: HashMap<String, u8>` and resolved during the second pass via `resolve_number()`.
- **`.DB`** embeds raw decimal or hexadecimal bytes directly into the program memory.
- **`.STRING "text"`** converts UTF-8 characters to sequential bytes for text storage and manipulation.
- The two-pass architecture in [`src/assembler.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/assembler.rs) enables forward references and symbolic substitution before instruction encoding.

## Frequently Asked Questions

### What is the maximum value for a NanoCore constant?

NanoCore constants are stored as `u8` values in the `constants: HashMap<String, u8>`, limiting them to the range **0-255** (one byte). Decimal and hexadecimal literals (e.g., `255` or `0xFF`) are parsed accordingly during the first pass in `map_constants()`.

### Can I use constants as memory addresses in NanoCore assembly?

Yes. Constants defined with `.CONST` can represent memory addresses, string lengths, or immediate values. When used with load instructions like `LDI`, the assembler resolves the constant name to its numeric value during the second pass via `resolve_number()` before encoding the instruction.

### How does NanoCore handle string encoding?

The `.STRING` directive in [`src/assembler.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/assembler.rs) extracts text between double quotes and pushes the **UTF-8 byte values** of each character onto the program buffer. This means ASCII characters occupy one byte each, while multi-byte UTF-8 characters are stored as sequential bytes that can be accessed individually via memory operations.

### Are forward references supported for constants?

Yes. The two-pass design of the NanoCore assembler enables forward references for constants. During the first pass, `map_constants()` collects all `.CONST` definitions into the `constants` hash map before the second pass begins encoding instructions. This allows you to use a constant name before its definition appears in the source code.