Skip to main content
Glama
simen

VICE C64 Emulator MCP Server

by simen

step

Execute individual Commodore 64 assembly instructions to analyze code flow and debug 6502 programs in the VICE emulator.

Instructions

Execute one or more instructions, then stop.

Single-stepping is essential for understanding code flow and debugging.

Options:

  • count: Number of instructions to execute (default: 1)

  • stepOver: If true, treat JSR as single instruction (don't step into subroutines)

After stepping, use getRegisters to see the new CPU state.

Related tools: getRegisters, continue, setBreakpoint, status

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
countNoNumber of instructions to step (default: 1)
stepOverNoStep over JSR calls instead of into them (default: false)

Implementation Reference

  • MCP tool handler for 'step': executes client.step(), fetches updated registers to extract and report new PC
    async (args) => {
      try {
        await client.step(args.count || 1, args.stepOver || false);
    
        // Get registers after step
        const regResponse = await client.getRegisters();
        const count = regResponse.body.readUInt16LE(0);
        let offset = 2;
        let pc = 0;
    
        for (let i = 0; i < count && offset < regResponse.body.length; i++) {
          const id = regResponse.body[offset];
          const size = regResponse.body[offset + 1];
          offset += 2;
          if (id === 3 && size === 2) {
            // PC
            pc = regResponse.body.readUInt16LE(offset);
          }
          offset += size;
        }
    
        return formatResponse({
          stepped: true,
          count: args.count || 1,
          stepOver: args.stepOver || false,
          pc: {
            value: pc,
            hex: `$${pc.toString(16).padStart(4, "0")}`,
          },
          message: `Stepped ${args.count || 1} instruction(s)`,
          hint: "Use getRegisters() for full CPU state, or readMemory at PC for next instruction",
        });
      } catch (error) {
        return formatError(error as ViceError);
      }
    }
  • Zod input schema defining parameters: count (optional number >=1, default 1), stepOver (optional boolean, default false)
    inputSchema: z.object({
      count: z.number().min(1).optional().describe("Number of instructions to step (default: 1)"),
      stepOver: z.boolean().optional().describe("Step over JSR calls instead of into them (default: false)"),
    }),
  • src/index.ts:473-528 (registration)
    Registers the 'step' MCP tool with server.registerTool including full description, input schema, and handler reference
    server.registerTool(
      "step",
      {
        description: `Execute one or more instructions, then stop.
    
    Single-stepping is essential for understanding code flow and debugging.
    
    Options:
    - count: Number of instructions to execute (default: 1)
    - stepOver: If true, treat JSR as single instruction (don't step into subroutines)
    
    After stepping, use getRegisters to see the new CPU state.
    
    Related tools: getRegisters, continue, setBreakpoint, status`,
        inputSchema: z.object({
          count: z.number().min(1).optional().describe("Number of instructions to step (default: 1)"),
          stepOver: z.boolean().optional().describe("Step over JSR calls instead of into them (default: false)"),
        }),
      },
      async (args) => {
        try {
          await client.step(args.count || 1, args.stepOver || false);
    
          // Get registers after step
          const regResponse = await client.getRegisters();
          const count = regResponse.body.readUInt16LE(0);
          let offset = 2;
          let pc = 0;
    
          for (let i = 0; i < count && offset < regResponse.body.length; i++) {
            const id = regResponse.body[offset];
            const size = regResponse.body[offset + 1];
            offset += 2;
            if (id === 3 && size === 2) {
              // PC
              pc = regResponse.body.readUInt16LE(offset);
            }
            offset += size;
          }
    
          return formatResponse({
            stepped: true,
            count: args.count || 1,
            stepOver: args.stepOver || false,
            pc: {
              value: pc,
              hex: `$${pc.toString(16).padStart(4, "0")}`,
            },
            message: `Stepped ${args.count || 1} instruction(s)`,
            hint: "Use getRegisters() for full CPU state, or readMemory at PC for next instruction",
          });
        } catch (error) {
          return formatError(error as ViceError);
        }
      }
    );
  • ViceClient.step(): core implementation sending VICE binary monitor AdvanceInstructions (0x71) command with stepOver flag and count
    async step(count = 1, stepOver = false): Promise<ViceResponse> {
      // Uses AdvanceInstructions (0x71) - there is no separate Step command in VICE
      const body = Buffer.alloc(3);
      body[0] = stepOver ? 1 : 0;
      body.writeUInt16LE(count, 1);
      const response = await this.sendCommand(Command.AdvanceInstructions, body);
      this.state.running = false;
      return response;
    }
  • advanceInstructions(): alias for step() method in ViceClient for backward compatibility
    async advanceInstructions(count: number, stepOver = false): Promise<ViceResponse> {
      // Alias for step() - kept for API compatibility
      const body = Buffer.alloc(3);
      body[0] = stepOver ? 1 : 0;
      body.writeUInt16LE(count, 1);
      const response = await this.sendCommand(Command.AdvanceInstructions, body);
      return response;
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and does well by explaining key behavioral traits: it's a controlled execution tool that stops after stepping, requires follow-up with 'getRegisters' to see results, and has specific stepping behavior with JSR calls. However, it doesn't mention potential side effects like memory changes or performance characteristics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections: purpose statement, importance context, parameter options, and related tools. Every sentence earns its place, though the 'Single-stepping is essential...' sentence could be integrated more tightly with the purpose statement.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a debugging tool with no annotations and no output schema, the description does well by explaining the tool's purpose, usage context, parameters, and follow-up actions. It could be more complete by describing what 'execute instructions' actually means in this context or what happens with invalid instructions, but it covers the essential debugging workflow adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents both parameters. The description adds minimal value beyond the schema by mentioning the parameters in the 'Options' section but doesn't provide additional semantic context about when to use stepOver vs. not, or implications of different count values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('execute one or more instructions, then stop') and distinguishes it from siblings by explaining its role in debugging and code flow analysis. It explicitly differentiates from 'continue' (continuous execution) and 'getRegisters' (state inspection).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool ('essential for understanding code flow and debugging'), when not to use it (use 'continue' for continuous execution), and names specific alternatives ('getRegisters' for state inspection, 'continue' for resuming execution, 'setBreakpoint' for breakpoints). The 'Related tools' section reinforces this context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/simen/vice-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server