cge_nes 0.1.1

Cycle-accurate NES (Nintendo Entertainment System) emulator library: CPU, PPU, cartridge, input, and iNES ROM loading.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
# cge_nes


[![Crates.io](https://img.shields.io/crates/v/cge_nes.svg)](https://crates.io/crates/cge_nes)
[![Documentation](https://docs.rs/cge_nes/badge.svg)](https://docs.rs/cge_nes)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/CasiussDev/cge_nes/blob/main/LICENSE-MIT)
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://github.com/CasiussDev/cge_nes/blob/main/LICENSE-APACHE)

A cycle-accurate NES emulator core written in
Rust (2021 edition). Front-end agnostic: the library handles CPU, PPU,
cartridge, and input emulation; you bring your own rendering, windowing, and
host input.

**Features:**
- Cycle-accurate 6502 CPU with multi-cycle instruction execution
- Bit-accurate PPU with precise sprite rendering, palette handling, and all
  four mirroring modes
- iNES ROM loading and cartridge mapper support (mappers 0, 1, 2, 3, 4)
- Composable memory devices (CPU bus and PPU bus) built from `devices6502`
- Decoupled input: you implement `HostInput`, the emulator polls each frame
- `no_std`-friendly core
- Single dependency (`cge_nes`) gets you the whole stack

**Roadmap:** APU (audio) emulation is not yet implemented in this release
but is planned for a future one.

**Quick facts:**
- 256×240 pixel frame buffer, one already-resolved NES palette color per pixel
- 262 scanlines per frame (~29,780 CPU cycles, ~60 Hz NTSC)
- CPU and PPU synchronized every cycle; PPU runs at 3× the CPU clock

## Contents

- [Getting started]#getting-started
- [Architecture]#architecture
- [Module layout]#module-layout
- [Public API]#public-api
- [Features]#features
- [Build]#build
- [Testing]#testing
- [Quick start]#quick-start
- [Front-end integration]#front-end-integration
- [Module reference]#module-reference
- [Development notes]#development-notes
- [License]#license

## Getting started


**Prerequisites:**
- Rust 1.75+ (Rust 2021 edition)
- Windows / Linux / macOS

**Add to your project:**
```toml
[dependencies]
cge_nes = "0.1"
```

**Build the library, examples, and binaries:**
```bash
cargo build
cargo build --examples
cargo build --bins
```

## Architecture


### High-Level Design


```
┌────────────────────────────────────────────────────┐
│                  Your Front-End                    │
│        (Rendering, Input, Frame Loop, UI)          │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│                    cge_nes                         │
│  ┌──────────────────────────────────────────────┐  │
│  │  nes::NesConsole                             │  │
│  │  ├─ cpu (cpu6502) + cpu_bus (devices6502)    │  │
│  │  ├─ ppu::Ppu + ppu_bus                       │  │
│  │  ├─ cartridge::Cartridge trait               │  │
│  │  ├─ input::HostInput trait                   │  │
│  │  └─ nes::dma (OAM DMA)                       │  │
│  └──────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────┘
```

### Key Concepts


- **CPU & Memory**: The `Cpu` from `cpu6502` executes against a memory map
  built from `devices6502` traits. CPU RAM, PPU registers, and cartridge
  ROM/RAM are all addressable in one 16-bit address space.
- **PPU & Video Output**: The `Ppu` renders pixels scanline-by-scanline,
  reading CHR ROM, name tables, attribute tables, and OAM. Frame buffer is
  exposed as `&[Color]` (one NES palette color per pixel).
- **Cartridge Loading**: `rom_loader` parses iNES ROM files and instantiates
  the matching mapper. Mappers handle bank switching, mirroring, and PRG RAM.
- **Input**: `input::HostInput` is the trait your front-end implements. The
  emulator polls it once per frame per controller port via `run_frame`.

### Execution Model


The NES runs in a frame loop:

1. **Frame setup**: Reset scanline counter, clear frame buffer.
2. **Scanline loop** (262 scanlines per frame):
   - **CPU cycles**: Execute multiple 6502 instructions per scanline
     (~114 CPU cycles).
   - **PPU rendering**: PPU advances each cycle in lockstep with CPU;
     pixels are generated from pattern / name / attribute / OAM lookups.
   - **Sprites & priority**: OAM sprites evaluated and rendered with correct
     priority each visible scanline.
3. **Frame complete**: Frame buffer is ready for your front-end to render.
4. **Input polling** (optional, your front-end decides): Sample host input,
   update controller state.
5. **Repeat**.

## Module layout


```
cge_nes/
├── README.md               # This file — single source of docs
├── LICENSE                  # MIT (also LICENSE-MIT, LICENSE-APACHE)
├── docs/                   # Hardware reference (mappers, etc.)
├── examples/
│   └── run_frame.rs        # Canonical front-end bootstrap example
├── src/
│   ├── lib.rs              # Public surface + module declarations + re-exports
│   ├── bin/
│   │   └── ines_header_inspector.rs   # Debug tool: dumps iNES headers from test_assets/
│   ├── cartridge/          # Cartridge trait (shared abstraction)
│   ├── input/              # HostInput trait + gamepad state
│   ├── ppu/                # PPU emulation
│   ├── nes/                # System integration (CPU + PPU + cartridge + input)
│   └── rom_loader/         # iNES parser + mapper dispatch
└── test_assets/            # Real .nes ROMs (gitignored, for manual testing)
```

External dependencies (from crates.io):
- `cpu6502` — 6502 CPU emulator
- `devices6502` — composable memory devices
- `bitflags`, `arrayvec`, `ringbuffer` — small utility crates

## Public API


The crate root re-exports every commonly-needed type. Most front-ends only
need a handful of these:

```rust
use cge_nes::{
    // System integration
    NesConsole,
    // ROM loading
    load_rom, LoadRomResult, RomError,
    // Cartridge contract (implement for custom mappers)
    Cartridge, ChrRomContentStatus,
    // Input
    ConnectedSocket, GamepadButtonsPressedFlags, GamepadState,
    HostInput, InputDeviceState,
    // Video
    Color,
};
```

Lower-level access (PPU internals, mapper implementations, etc.) is available
through the module hierarchy:

```rust
use cge_nes::ppu::{Ppu, PpuCartMemorySpace, FrameEvent, Register};
use cge_nes::rom_loader::ines::{HeaderData, Mirroring};
use cge_nes::nes::system::NesConsole;   // same as the re-exported NesConsole
```

## Features


Per-feature flags:

| Feature | Purpose |
|---------|---------|
| `instr_log` | CPU instruction logging (writes every instruction to `nes_cpu_log.txt`). Enables `cpu6502/logging` under the hood. |
| `oam_array_raw` | Alternative OAM memory layout (profiling / optimization experiments). |
| `show_name_table_change` | Debug name / attribute table access during rendering. |
| `mapper_debug_log` | Mapper bank-switching traces. |

```bash
cargo build --features instr_log
cargo build --features oam_array_raw,show_name_table_change
cargo build --features mapper_debug_log
cargo build --all-features    # enables everything; some pre-existing PP   U debug paths require a manual fix-up
```

## Build


```bash
# Default build (library + examples + binaries)

cargo build

# With specific features

cargo build --features instr_log
cargo build --features oam_array_raw,show_name_table_change
cargo build --features mapper_debug_log

# Release

cargo build --release
```

## Testing


```bash
# Run all library + integration + binary tests

cargo test

# Per-target

cargo test --lib
cargo test --tests
cargo test --bins

# Show backtrace for a failing test

RUST_BACKTRACE=1 cargo test render_scanline
```

## Quick start


The canonical bootstrap lives as a runnable example at
[`examples/run_frame.rs`](examples/run_frame.rs):

```bash
cargo run --example run_frame -- path/to/game.nes
```

It loads the ROM, runs one frame, and prints the buffer dimensions plus the
count of non-background pixels. The source is the recommended starting point
for a front-end. The skeleton, condensed:

```rust
use cge_nes::{
    ConnectedSocket, HostInput, InputDeviceState, NesConsole, load_rom,
};

struct NullHostInput;
impl HostInput for NullHostInput {
    fn input_device_state(&mut self, _player: ConnectedSocket) -> InputDeviceState {
        InputDeviceState::Disconnected
    }
}

let rom_data = std::fs::read("game.nes")?;
let cartridge = load_rom(&mut rom_data.as_slice())?;

let mut console = NesConsole::new();
console.insert_cartridge(cartridge);
console.switch_on();

let mut host = NullHostInput;
console.run_frame(&mut host);

let frame: &[cge_nes::Color] = console.screen_colors();
// render `frame` to your window of choice.
```

Replace `NullHostInput` with an adapter that polls your keyboard / gamepad
each frame.

## Front-end integration


This library is **front-end agnostic**. You supply:

1. **Rendering**: convert `&[Color]` (256×240 already-resolved NES palette
   colors) to your target pixel format and display.
2. **Input handling**: implement `HostInput` to expose host keyboard /
   gamepad state as `GamepadState`.
3. **Frame timing**: drive `run_frame` at your own cadence (~60 Hz for
   authentic speed, or unlimited for testing).
4. **Audio** (optional): APU emulation is not yet implemented in this
   crate (planned for a future release); if you need audio now, expose the
   APU register reads/writes from your front-end and render the samples
   yourself.

Suggested front-end stacks:
- **SDL2** — simple cross-platform graphics and input
- **wgpu** — modern GPU-accelerated rendering
- **Bevy** — full-featured game engine with ECS
- **druid / iced** — GUI framework for emulator settings / UI
- **rodio** — audio playback (once APU support lands in this crate, or
  if you implement it yourself)

## Module reference


### `cartridge` — Cartridge trait


The contract every NES mapper must satisfy. The system bus dispatches reads
and writes here; front-end code does not call these directly.

```rust
pub trait Cartridge {
    fn read_cpu_mapped(&self, addr: u16) -> u8;
    fn write_cpu_mapped(&mut self, data: u8, addr: u16) -> ChrRomContentStatus;
    fn read_ppu_mapped(&mut self, addr: u16) -> u8;
    fn write_ppu_mapped(&mut self, data: u8, addr: u16) -> ChrRomContentStatus;
    fn irq_pin(&self) -> bool { false }                                   // override for MMC3
    fn requires_cycle_accurate_sprites(&self) -> bool { false }           // override for MMC3
    fn notify_vram_addr_change(&mut self, _old_addr: u16, _new_addr: u16) {} // A12-clocked counters
}
```

`ChrRomContentStatus` (returned by writes) lets the PPU decide whether its
caches need invalidating.

### `input` — Host input


```rust
pub trait HostInput {
    fn input_device_state(&mut self, player: ConnectedSocket) -> InputDeviceState;
}

pub enum InputDeviceState {
    Gamepad(GamepadState),
    Disconnected,  // default
}

pub struct GamepadState {
    pub buttons_pressed: GamepadButtonsPressedFlags, // bitflags: A, B, SELECT, START, UP, DOWN, LEFT, RIGHT
}

pub enum ConnectedSocket { Player1 = 0, Player2 = 1 }
```

`InputRegisters` (also re-exported) drives the serial shift-register protocol
at `$4016` / `$4017`. Most front-ends don't need to touch it directly —
`NesConsole::run_frame` handles strobe and shift transparently.

### `ppu` — Picture Processing Unit


**Rendering pipeline** (per visible scanline, 0–239):

1. **Name table lookup**: For each tile (32 × 8 = 256 pixels wide), read
   tile ID from the name table.
2. **Attribute table lookup**: Read palette index (0–3) from the attribute
   table.
3. **Pattern table lookup**: Fetch the 8×8 tile bitmap from CHR ROM using
   the tile ID.
4. **Sprite evaluation**: For each scanline, evaluate OAM to find up to 8
   sprites per scanline.
5. **Pixel composition**: For each pixel, composite background and sprite
   layers with priority.

**Memory organization**:
- **PPU registers** (`$2000–$2007`):
  - `$2000` PPUCTRL — NMI enable, sprite size, name-table base
  - `$2001` PPUMASK — rendering enable, emphasis bits
  - `$2002` PPUSTATUS — VBLANK, sprite overflow, sprite-0 hit
  - `$2003` OAMADDR / `$2004` OAMDATA — OAM access
  - `$2005` PPUSCROLL — background scroll X/Y
  - `$2006` PPUADDR / `$2007` PPUDATA — VRAM access with read buffer
- **VRAM** (`$0000–$3FFF`):
  - `$0000–$1FFF` — CHR ROM (graphics from cartridge)
  - `$2000–$2FFF` — name tables with mirroring
  - `$3000–$3EFF` — mirror of name tables
  - `$3F00–$3FFF` — palette RAM (16 entries × 4 backgrounds + 4 sprites)
- **OAM** — 256 bytes, 64 sprites × 4 bytes (Y, tile ID, attributes, X)

**Mirroring modes**:
- **Horizontal** — name tables 0 & 1 are horizontally mirrored; 2 & 3 too
- **Vertical** — name tables 0 & 2 are vertically mirrored; 1 & 3 too
- **One-screen** — single name table repeated (single-screen boards)
- **Four-screen** — cartridge provides all four name tables (e.g.
  MMC3 with no mirroring bit set)

**API**:
```rust
impl Ppu {
    pub fn new() -> Self;
    pub fn reset(&mut self);
    pub fn screen_colors(&self) -> &[Color];    // 256×240 already-resolved colors

    /// Advance the PPU by one cycle.
    pub fn run_cycle(&mut self, cart: &mut impl PpuCartMemorySpace) -> FrameEvent;
    pub fn nmi_signal(&self) -> bool;

    pub fn write_ppu_register(&mut self, value: u8, register: Register, cart: &mut impl PpuCartMemorySpace);
    pub fn read_ppu_register(&mut self, register: Register, cart: &mut impl PpuCartMemorySpace) -> u8;
    pub fn write_ppu_register_by_addr(&mut self, addr: u16, value: u8, cart: &mut impl PpuCartMemorySpace);
    pub fn read_ppu_register_by_addr(&mut self, addr: u16, cart: &mut impl PpuCartMemorySpace) -> u8;
}

pub enum FrameEvent { None, EndOfScanline, ReadyToPresent, EndOfFrame }
pub enum Register { PpuControl, PpuMask, PpuStatus, OamAddr, OamData, PpuScroll, PpuAddr, Data }
```

### `nes` — System integration


`NesConsole` wires together CPU, PPU, cartridge, and input.

**System components**:
- **CPU bus** — 16-bit address space:
  - `$0000–$07FF` — CPU internal RAM (2 KB, mirrored 4×)
  - `$2000–$2007` — PPU registers
  - `$4000–$401F` — APU and I/O registers (including controller input)
  - `$8000–$FFFF` — cartridge ROM/RAM
- **PPU bus** — video memory (see `ppu` module).
- **OAM** (Object Attribute Memory) — 256 bytes of sprite data
- **Controllers** — two ports (Player 1 / Player 2) polled through
  `input::HostInput`

**Execution model**:
`NesConsole::run_frame(&mut host_input)` executes one complete frame:
- Scanlines 0–239: visible rendering area
- Scanline 240: post-render (VBLANK starts)
- Scanlines 241–260: vertical blank period (no PPU rendering)
- Scanline 261: pre-render (sprite evaluation, flag clearing)

Internally the CPU executes ~114 cycles per scanline; the PPU runs at 3× CPU
clock so each CPU cycle produces three PPU cycles.

**API**:
```rust
impl NesConsole {
    pub fn new() -> Self;
    pub fn insert_cartridge(&mut self, cart: Box<dyn Cartridge>);   // panics if already inserted
    pub fn switch_on(&mut self);                                  // power on (resets CPU + PPU)
    pub fn switch_off(&mut self);
    pub fn reset(&mut self);                                      // CPU + PPU + cycle counter reset
    pub fn remove_cartridge(&mut self);                           // panics if currently on
    pub fn cartridge_connected(&self) -> bool;
    pub fn on(&self) -> bool;
    pub fn screen_colors(&self) -> &[Color];

    /// Low-level: one CPU cycle + three PPU cycles.
    pub fn run_console_cycle(&mut self, cart: &mut dyn Cartridge, host: &mut impl HostInput) -> FrameEvent;

    /// Run until the PPU indicates the next frame is ready to present.
    pub fn run_until_present(&mut self, host: &mut impl HostInput);

    /// Run one complete frame (262 scanlines).
    pub fn run_frame(&mut self, host: &mut impl HostInput);
}
```

**Internal module layout**:
- `nes::system``NesConsole` type and frame execution
- `nes::ppu_cart_memory` — PPU address space (CHR ROM, VRAM, palette)
- `nes::dma` — OAM DMA controller for sprite data transfers

### `rom_loader` — iNES parser and mapper dispatch


Given a 16-byte iNES header plus the trailing PRG / CHR bytes, returns a
`Box<dyn Cartridge>` ready for `NesConsole::insert_cartridge`.

**iNES file format**:
```
Offset   Size    Description
------   ----    -----------
0x00     4       Magic: "NES\x1A"
0x04     1       PRG ROM size in 16 KB units
0x05     1       CHR ROM size in 8 KB units (0 means CHR RAM)
0x06     1       Flags 6: mapper low, mirroring, PRG RAM, trainer, four-screen
0x07     1       Flags 7: mapper high, iNES 2.0 marker
0x08+    var     Optional 512-byte trainer (if flag 6 bit 2 is set)
N+       var     PRG ROM data
N+M      var     CHR ROM data
```

**API**:
```rust
pub type LoadRomResult = Result<Box<dyn Cartridge>, RomError>;

pub fn load_rom(rom_reader: &mut impl Read) -> LoadRomResult;

pub enum RomError {
    Io(std::io::Error),
    RomFormat(String),
    UnsupportedMapper(u8),  // known mapper id, not compiled in
}

pub struct HeaderData {
    pub prg_rom_size: u64,
    pub chr_rom_size: u64,
    pub mirroring: Mirroring,
    pub has_persistent_ram: bool,
    pub has_trainer: bool,
    pub mapper: u8,
    pub vs_unisystem: bool,
    pub playchoice: bool,
    pub ines2: bool,
    pub prg_ram_size: u16,
}

pub enum Mirroring { Horizontal, Vertical, FourScreen }
```

**Supported mappers**:

| ID | Name | Features | Status |
|----|------|----------|--------|
| 0  | NROM  | No bank switching; 16 KB or 32 KB PRG; 8 KB CHR ROM or CHR RAM | ✅ Implemented |
| 1  | MMC1  | PRG/CHR bank switching, mirroring, PRG RAM protect | ✅ Implemented |
| 2  | UxROM | 16 KB switchable PRG + fixed last bank; CHR fixed | ✅ Implemented |
| 3  | CNROM | 32 KB fixed PRG; 8 KB switchable CHR ROM | ✅ Implemented |
| 4  | MMC3  | PRG/CHR bank switching, scanline IRQ counter | ✅ Implemented |
| Others ||| ❌ Not implemented (returns `RomError::UnsupportedMapper(mapper_id)`) |

NROM, UxROM, and CNROM share `rom_loader::ines::mappers::basic_mapper`. Bus
conflicts that real UxROM / CNROM boards exhibit on writes to
`$8000–$FFFF` are intentionally not emulated.

**Adding new mappers**: implement `Cartridge`, create
`src/rom_loader/ines/mappers/mapperNNN.rs` with `pub fn load(&HeaderData, &mut impl Read) -> LoadRomResult`,
add a match arm in `rom_loader::load_rom`, and add unit tests.

**Known issues**:
- **Trainer block not skipped.** When header[6] bit 2 is set, the 512-byte
  trainer area is read as if it were PRG ROM.
- **NES 2.0 largely ignored.** The `ines2` flag is not decoded; extended
  fields in header[8..15] are dropped. NES 2.0 ROMs parse as iNES 1.0.
- **No board-quirk handling** on mappers 1 and 4 beyond the basics.

### `ines_header_inspector` binary


A debug tool that scans `test_assets/` for `.nes` files and writes
`header_info.txt` summarising each ROM's header:

```bash
cargo run --bin ines_header_inspector
```

Run from the repo root so it can find the `test_assets/` directory.

## Development notes


- **Architecture philosophy**:
  - Each top-level module (`cartridge`, `input`, `ppu`, `nes`, `rom_loader`)
    has a single responsibility and a narrow public surface
  - Memory built from trait-based device composition via `devices6502`,
    with no allocations in the hot path
  - Testable: per-module tests verify behavior in isolation
  - Performance-conscious: cycle accuracy without overhead; bit-accurate
    pixel rendering

- **When modifying the PPU**:
  - PPU rendering is bit-accurate; small changes to pixel math can break
    test ROM behavior
  - Add focused unit tests for sprite priority, palette selection, and
    flipping
  - Test against ROM test suites (e.g. `nestest`) if available
  - VRAM read buffering is a hardware quirk: the first read after an
    address write returns the buffered value, not the new value

- **When modifying cartridge / mapper logic**:
  - Mappers handle bank switching and address translation; errors affect
    every CPU / PPU memory access
  - Add mapper-specific tests for bank switching sequences and ROM paging
  - Use the `mapper_debug_log` feature to trace bank-switching

- **Performance tuning** (from `Cargo.toml`):
  - `[profile.dev.package."*"] opt-level = 3` — fast dependency builds
  - `[profile.dev] opt-level = 1` — reasonable debug iteration
  - `[profile.release] lto = "fat"` — full link-time optimization
  - Profile CPU-heavy paths with `perf` or `flamegraph`

## License


Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE or
  <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT]LICENSE-MIT or
  <http://opensource.org/licenses/MIT>)

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.