SearchA-ZE › ESP8266

ESP8266

2014 Open source · CC0 On-board only Online

ESP8266 is Espressif's popular low-cost WiFi microcontroller — the chip that launched the hobbyist WiFi boom — emulated in the browser on a from-scratch single-core Tensilica Xtensa LX106 interpreter. The LX106 is a cut-down Xtensa: variable-length 24-bit and 16-bit (density) instructions, a shift-amount register, and PS/EPC1/EXCCAUSE for exceptions, but no register windows — code uses the plain CALL0 ABI (a0-a15, call0/callx0/ret). The core is wired to an ESP8266 SoC model at the real hardware addresses: internal SRAM (IRAM 0x40100000, DRAM 0x3FFE8000), cache-mapped SPI flash (0x40200000), the GPIO block with the on-board LED on GPIO2 (0x60000300), and UART0 (0x60000000). It runs REAL bare-metal Xtensa firmware, including an interactive UART REPL you can type into. Because the whole chip is ordinary JavaScript it plugs into the site's shared debugger (the same from-scratch Xtensa disassembler used by the ESP32): live a0-a15 + PC + the special registers, side-effect-free memory, single-instruction step, execution breakpoints and write-watchpoints. WiFi has no radio here and is an HLE stub, and the module is silent.

ESP8266 at Espressif ↗

Runs on: Web browser

ESP8266 Online Emulator

Play ESP8266 using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
MicroPython v1.23.0ESP8266ESP8266openOpen ⛶
Mini-REPL (UART0)ESP8266ESP8266openOpen ⛶
Blink (on-board LED)ESP8266ESP8266openOpen ⛶
Hello UART (serial)ESP8266ESP8266openOpen ⛶

Chips

Notes

Embedding

The CPU and SoC are written from scratch for this site (public domain / CC0) — nothing here wraps a third-party core. xtensa-esp8266.js shares the site's Xtensa interpreter (the same one that drives the ESP32 LX6) but wires it to an ESP8266 SoC model at the real hardware addresses (from the ESP8266 Technical Reference and the SDK eagle_soc.h). The ESP8266's LX106 is a single-core, CALL0-only Xtensa (no register windows), so the bundled firmware uses the plain CALL0 ABI. It is all inspectable JavaScript state, which is exactly what lets the shared debugger drive it.

var sys = window.ESP8266.createSystem();        // Xtensa LX106 core + ESP8266 SoC
sys.loadImage(bytes, 0x40100000);            // flat app image into IRAM
sys.setEntry(0x40100000);                    // enter at the image start
sys.onUartTx = function(b){ /* UART0 TX -> console */ };
sys.feedRx(byte);                              // keyboard -> UART0 RX FIFO
sys.step();                                    // execute exactly one instruction

We own the run loop so the debugger can drive it. Each frame runs a time-boxed budget of sys.step() (one Xtensa instruction each); breakpoints stop it by PC, write-watchpoints by bus address. Everything is plain JavaScript state:

MemberKindWhat it does
sys.step()methodFetch → decode width (16-bit density vs 24-bit) → execute one instruction, advancing pc. The single-step primitive.
sys.ar(i) / sys.setAr(i,v)methodThe a0-a15 registers. sys.getPc()/setPc() and sys.sr (sar/ps/epc1/exccause) complete the programmer's model — all readable and writable.
sys.mem.sramI / .sramD / .flash / .rtcfieldThe real memories as typed arrays — read side-effect-free for the hex + disassembly views.
sys.read8/16/32, sys.write8/16/32methodThe bus, decoding the ESP8266 memory map. The write path notes watched addresses.
sys.ledOn()methodTrue when GPIO2 is driven high (output enabled) — polled each frame to draw the on-board LED.
sys.feedRx(b)methodSerial RX in — the keyboard drives the mini-REPL through UART0.

Debugger integration

The plug-in (esp8266-debug.js) describes the ESP8266 to the shared debugger and nothing more — the core is not patched:

  • Shared Xtensa decoder. The ESP8266 reuses the site's from-scratch Xtensa disassembler at /debugger/src/cpus/xtensa.js (registered as xtensa, first written for the ESP32), shown here as "Xtensa LX106 (ESP8266)". It decodes the variable-length ISA (24-bit when the low nibble op0 is 0-7, 16-bit density forms when 8-13) — the ALU/shift/load-store/branch set, L32R, the CALL0 calls (CALL0, CALLX0, RET), RSR/WSR/XSR and the narrow forms — and matches the interpreter's encodings exactly.
  • Registers are read live each refresh: a0-a15, the pc, and the special registers SAR, PS, EPC1/EXCCAUSE. Each has a set() that writes straight back through the core. Because the LX106 is CALL0-only, there is no register window or zero-overhead LOOP to surface.
  • Memory is exposed as four chips, all read side-effect-free directly off the typed arrays, each with its real base so the disassembly lines up with PC: IRAM at 0x40100000 (disassembled), DRAM at 0x3FFE8000, XIP flash at 0x40200000 (disassembled), and RTC RAM at 0x60001000. The UART/GPIO peripheral window is deliberately not a chip, because reading the UART FIFO pops a byte — memory reads stay side-effect-free.
  • Single step is one sys.step(). Breakpoints are host-side checks of the PC (in the running code chip's offset space, so a click in the disasm gutter halts) run instruction-by-instruction when any are set. Write-watchpoints are a set the SoC's write path checks, pausing the loop the moment a watched bus address is written.

Architecture

The Espressif ESP8266 (2014) is the low-cost WiFi microcontroller that launched the hobbyist WiFi boom, built on a single 32-bit Tensilica Xtensa LX106:

  • Xtensa LX106 — one 32-bit core at 80 MHz (boostable to 160). The LX106 is a cut-down Xtensa: it has the variable-length 24-bit and 16-bit (density) instructions, a shift-amount register (SAR), and PS / EPC1 / EXCCAUSE for exceptions, but it omits the register-window and zero-overhead-LOOP options — code uses the plain CALL0 ABI (a0-a15, call0/callx0/ret). This emulator implements the LX106 core from scratch — the ALU / SAR shifts / loads-stores / branches, L32R, the 32-bit multiply, RSR/WSR/XSR and a basic trap — sharing the interpreter with the site's ESP32 (LX6) machine.
  • Memory — internal SRAM reachable from the instruction bus at 0x40100000 (IRAM, 32 KiB) and the data bus at 0x3FFE8000 (DRAM, 80 KiB); code also executes in place (XIP) from the external SPI flash cache-mapped at 0x40200000; a small RTC RAM sits at 0x60001000. All are at their real hardware addresses.
  • On-board LED — common modules (ESP-12 / NodeMCU) expose a user LED on GPIO2, driven through the GPIO block at 0x60000300 (GPIO_ENABLE / GPIO_OUT and their W1TS/W1TC set/clear registers).
  • SerialUART0 at 0x60000000 is the console the ROM bootloader and apps log to; its TX FIFO drives the serial panel and its RX FIFO (read through UART_STATUS' RXFIFO_CNT) is fed by the keyboard.

The default is stock, unmodified MicroPython v1.23.0 (the official esp8266 port, MIT) — taken byte-for-byte from micropython.org and booted through the real Espressif NONOS SDK to a live Python >>> REPL. To get there the emulator clean-room HLE-s the ESP8266 mask ROM (from the public eagle.rom.addr.v6.ld addresses — SPIRead/flash, the UART funcs, ets_printf, mem*/str*, the ets_task/ets_post/ets_run os_task loop, the ROM timers), models the eFuse block, the FRC/WDEV timers and the UART0 RX interrupt, and serves flash reads from the image. The boot runs the real chip-ID/eFuse check, flash-config load, RF/PHY calibration, clock bring-up and UART0 bring-up, then the NONOS os_task loop hands off to MicroPython's REPL. It auto-runs print(2+2) and 6*7 on arrival and you can type more; every instruction still single-steps in the debugger. Three original CC0 bare-metal programs (mini-REPL, Blink, Hello UART) remain in the picker.

Honest limits. The from-scratch core runs real Xtensa LX106 and the ROM HLE is clean-room from public addresses, but the WiFi/BLE have no RF — their register windows (and the modem-sleep hooks) are RAM-backed HLE stubs so probing firmware keeps running, but no packets move — and the watchdog is fed so nothing resets. Timing is instruction-approximate, not cycle-exact (the WDEV microsecond timer is driven off the instruction clock); and the bare module has no on-board audio, so this emulator is naturally silent.