Amstrad CPC Debug — Emulator Interface Specification (inspired from Lone’s work for SugarBOX)

The reference implementation is DebugServer.cpp in SugarboxV2.


Using WEB API with ACE-DL, new command line options

enable WEB API with command line ( -enable_webapi ) port may be changed via command line ( -web_port port) disable main window decoration ( -borderless )

Transport

  • Protocol: TCP, plain text
  • Default port: 6128 (configurable via command line with ACE-DL -web_portport`)
  • Direction: bidirectional
  • Extension → Emulator: commands (one at a time, serialised)
  • Emulator → Extension: responses (one per command)
  • There is no events at this time

The extension serialises all commands through an internal queue: only one command is in-flight at a time. The emulator must reply to each command before the next one is sent. Commands are processed each 1/50s or as fast as possible when Emulator is paused

Command format

{ "cmd": "<command_name>", ... }
 

Fields beyond cmd are command-specific.

Response format

A response is any JSON object that does not have "type": "event". The extension reads it as the answer to the currently in-flight command.

Error response (optional convention): json { "error": "<human-readable message>" }

Event format

{ "type": "event", "event": "<event_name>", "body": { ... } }
 

Events can be sent at any time, independently of the command queue. The extension routes them to the appropriate handler without blocking pending commands.


Commands

Core commands

getStatus

Returns all Z80 registers as a flat object. Used to populate the Variables panel.

Request: json { "cmd": "getStatus" }

Response: ```json { “running”:true, “z80”: { “registers”: { “AF”:11564 ,“BC”:57246 ,“DE”:62977 ,“HL”:64504 ,“AF'”:0 ,“BC'”:65526 ,“DE'”:25612 ,“HL'”:41422, “PC'”:64504 ,“SP'”:49146, “IX”:48203 ,“IY”:48932, “I”:184 ,“R”:245,“WZ”:64504 }, “states”: { “IM”:2 ,“cycleCount”:4 , “nmiIssued”:false } }, “crtc”: { “type”:3, “registers”: { “regSelect”:13,“R0”:63, “R1”:49, “R2”:51, “R3”:140, “R4”:38, “R5”:0, “R6”:32, “R7”:34, “R8”:0, “R9”:7, “R10”:0, “R11”:0, “R12”:19, “R13”:155,“R14”:0,“R15”:0 }, “states”: { “C0”:1, “C4”:19, “C9”:4, “C5”:0, “C3H”:0, “C3V”:0, “hSync”:false, “vSync”:false } }, “gateArray”: { “version”:2, “penSelect”:0, “ink0”:25, “ink1”:25, “ink2”:25, “ink3”:25, “ink4”:25, “ink5”:25, “ink6”:25, “ink7”:25, “ink8”:25, “ink9”:25, “ink10”:25, “ink11”:25, “ink12”:25, “ink13”:25, “ink14”:25, “ink15”:25, “ink16”:25, “currentMode”:0, “requestedMode”:0,“blanking”:false, “vSync”:false,“hSync”:false, “intLineCount”:38, “intPending”:0 }, “ay”: { “registers”: { “regSelect”:14,“R0”:113, “R1”:0, “R2”:56, “R3”:2, “R4”:142, “R5”:0, “R6”:20, “R7”:56, “R8”:11, “R9”:14, “R10”:11, “R11”:0, “R12”:0, “R13”:0,“R14”:0,“R15”:0 }, “ppi”: { “printerPortData”:0, “printerPortStrobe”:true, “ppiControl”:130, “ppiPortA”:14, “ppiPortB”:0, “ppiPortC”:74, “keyboardLine”:10, “ppiCurrentAccess”:0 } }


And an optional "asic" block with all registers, status, sprites info and content

Keys are case-insensitive (the extension lowercases them internally).

#### `getEmulatorSettings`

Returns all settings available for the emulator

**Request:**
 

{ “cmd”: “getEmulatorSettings” } ```

Response: ```json { “status”:“ok”, “settings”:[ {“HspHighlight”:1}, {“IntHighlight”:0}, {“RealTimeGrid”:1}, {“DebugGrid”:1}, {“PlasticCover”:1} ] }

Obviously there will be more settings available than this documentation

---

#### `setEmulatorSettings`

Returns all settings available for the emulator

**Request:**
 

{ “cmd”:“setEmulatorSetting”,“setting”:“RealTimeGrid”, “value”:1 } ```

Response: ```json { “status”:“ok”, }

Obviously there will be more settings available than this documentation

---

#### `readRegisters`

Returns all Z80 registers as a flat object. Used to populate the Variables panel.

**Request:**
 

{ “cmd”: “readRegisters” } ```

Response: json { "AF": 4660, "BC": 1, "DE": 2, "HL": 3, "AF'": 0, "BC'": 0, "DE'": 0, "HL'": 0, "PC": 49152, "SP": 65408, "IX": 0, "IY": 0, "I": 0, "R": 0 }


setRegisters

Writes one or more register values.

Request: json { "cmd": "setRegisters", "hl": 16384, "pc": 49152 }

Register names are lowercase. Any subset of registers may be included. Supported registers are a,af,bc,de,hl,a',af',bc',de',hl',ix,iy,pc,sp,i,r.

Response: json { "status": "ok" }


readMemory

Reads a contiguous block of memory.

Request: json { "cmd": "readMemory", "address": 16384, "size": 256, "memType": "ram", "bank": -1 }

Field

Type

Default

Description

address

number

—

Start address (0–65535)

size

number

—

Number of bytes to read

memType

string

"read"

Memory source: "ram", "rom", "cart"

bank

number

-1

Bank index (−1 = currently mapped bank)

Response: json { "bytes": [0, 1, 2, 3, ...] }

bytes is an array of unsigned integers (0–255), length = size.


writeMemory

Writes a contiguous block of memory.

Request: json { "cmd": "writeMemory", "address": 16384, "bytes": [0, 1, 2, 3] "memType": "ram", "bank": -1 } - when writing to ram, you can use absolute address and more than 64K of data

  • optional memType must be ram or rom (default:ram)
  • bank must be set if memType is rom (-1:lower 0-255:upper)

Response: json { "status": "ok" }


setBreakpoints

Replaces the entire breakpoint list on the emulator. Called after every change to any breakpoint (add, remove, re-apply on session start).

Request: json { "cmd": "setBreakpoints", "breakpoints": [ { "address": 49152 }, { "address": 48000 }, { "FDC": "calibration" }, { "Winape": 1 }, { "Maxam": 1 }, { "badVBL": 1 }, { "int": 1 }, ] }

FDC breaks available are version, specify, sensedrive, calibration,seek,senseint,getid,readdata,readdiag,write,format,scandata,status,timeout,resultread,motorchange and weak Other breakpoints available are Winape, Maxam, VBL, BadVBL, badHBL, INT (set to 1)

Response: json { "status": "ok" }


setBreakpoint

Add a full ACE breakpoint. Existing breakpoints wont be removed, you can call setBreakpoints command with an empty array to reset everything

Request: json { "cmd": "setBreakpoint", "breakpointType": "exec", "runMode": "stopper", "address": 12345, "condition": "IX = 0x100" }

Request: json { "cmd": "setBreakpoint", "breakpointType": "memory", "runMode": "watcher", "accessMode" : "RW", "value": 123, "valueMask": 255, "address": 12345, "addressMask": 65535, "size": 123, "condition": "IX = 0x100" }

Request: json { "cmd": "setBreakpoint", "breakpointType": "IO", "runMode": "stopper", "accessMode" : "W", "value": 123, "valueMask": 255, "address": 12345, "addressMask": 65535, "condition": "IX = 0x100" }

ACE breakpoints definition : - breakpointType : exec, memory, io

Response: json { "status": "ok" }


continue

Resume CPU execution.

Request: { "cmd": "continue" }

Response: { "status": "ok" }

The emulator sends a stopped event when execution halts again (breakpoint, error, etc.).

Note: continue is also called by the extension on disconnectRequest before closing the TCP connection, to leave the emulator running after the debug session ends.


halt

Pause CPU execution immediately.

Request: { "cmd": "halt" }

Response: { "status": "ok" }


step or stepOver

Execute one instruction (Step Over). Handles CALL, RST, and block instructions without stepping into them.

Request: { "cmd": "step" }

Response: { "status": "ok" }


stepIn

Execute one instruction (Step Into). Enters CALL and RST.

Request: { "cmd": "stepIn" }

Response: { "status": "ok" }


stepOut

Run until the current subroutine returns. Typically implemented by placing a temporary breakpoint on the return address read from the stack.

Request: { "cmd": "stepOut" }

Response: { "status": "ok" }


reset or resetHard

Reset the CPU and machine to their initial state.

Request: { "cmd": "reset" }

Response: { "status": "ok" }


resetSoft

Soft reset the CPU, not memory and some hardware

Request: { "cmd": "reset" }

Response: { "status": "ok" }


loadFile

Request: json { "cmd": "loadFile", "filename": "path to the file" }

Response (success): json { "status": "ok" }


getMemBanks @@TODO

Returns the list of addressable memory sources, used to let the user choose which bank to disassemble.

Request: { "cmd": "getMemBanks" }

Response: json { "sources": [ { "type": "read", "bank": -1, "label": "READ (mapped)" }, { "type": "ram", "bank": 0, "label": "RAM bank 0" }, { "type": "ram", "bank": 1, "label": "RAM bank 1" }, { "type": "rom", "bank": 0, "label": "ROM bank 0 (BASIC)" }, { "type": "cart", "bank": 0, "label": "Cartridge bank 0" } ] }

If the command is not supported, return { "error": "unknown command" }. The extension falls back to memType="read", bank=-1 silently.


getScreen @@TODO

Returns a single snapshot of the currently displayed CPC screen, cropped to the visible window and de-interlaced (i.e. what a real monitor would show).

Request: { "cmd": "getScreen" }

Response: json { "format": "png", "width": 768, "height": 544, "data": "<base64-encoded PNG bytes>" }

If capture is unsupported or unavailable, return { "error": "screen capture unavailable" }.


Unknown commands

Return { "error": "unknown command" } for any unrecognised cmd. The extension checks for result?.error and handles it without throwing.

Concurrency

The extension sends one command at a time. The emulator does not need to handle concurrent requests. However, events (stopped, mediaChanged) may arrive while a command response is being written — this is safe because the extension dispatches events independently from the command queue.