Skip to main content
Glama
simen

VICE C64 Emulator MCP Server

by simen

readColorRam

Reads color RAM to retrieve foreground color values for each character on the Commodore 64 screen. Returns a 25x40 grid of color data with names and usage summary for debugging C64 programs.

Instructions

Read color RAM ($D800-$DBE7) and return color values with names.

Color RAM determines the foreground color of each character on screen.

Returns:

  • 25x40 grid of color values (0-15) with names

  • Summary of colors used

Related tools: readScreen, readVicState

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
summaryNoReturn only color usage summary, not full grid (default: false)

Implementation Reference

  • Handler function that reads color RAM memory from $D800 for 1000 bytes, extracts 4-bit color values, computes usage statistics, optionally builds a 25x40 color grid, and formats the response using getColorInfo for color names.
    async (args) => { try { // Color RAM is always at $D800 const colorData = await client.readMemory(0xd800, 0xd800 + 999); // Count color usage const colorCounts = new Map<number, number>(); for (const byte of colorData) { const color = byte & 0x0f; colorCounts.set(color, (colorCounts.get(color) || 0) + 1); } // Sort by frequency const colorUsage = Array.from(colorCounts.entries()) .sort((a, b) => b[1] - a[1]) .map(([color, count]) => ({ color: getColorInfo(color), count, percentage: Math.round((count / 1000) * 100), })); const response: Record<string, unknown> = { address: { value: 0xd800, hex: "$D800" }, summary: { uniqueColors: colorUsage.length, dominantColor: colorUsage[0]?.color || null, usage: colorUsage, }, }; if (!args.summary) { // Convert to 25 lines of 40 color values const colorLines: Array<Array<{ value: number; name: string }>> = []; for (let row = 0; row < 25; row++) { const line: Array<{ value: number; name: string }> = []; for (let col = 0; col < 40; col++) { const offset = row * 40 + col; line.push(getColorInfo(colorData[offset])); } colorLines.push(line); } response.grid = colorLines; } response.hint = colorUsage.length === 1 ? `Entire screen uses ${colorUsage[0].color.name} (${colorUsage[0].color.value})` : `${colorUsage.length} colors used. Dominant: ${colorUsage[0]?.color.name} (${colorUsage[0]?.percentage}%)`; return formatResponse(response); } catch (error) { return formatError(error as ViceError); } }
  • Input schema defining optional 'summary' boolean parameter to control whether full color grid or just summary is returned.
    inputSchema: z.object({ summary: z .boolean() .optional() .describe("Return only color usage summary, not full grid (default: false)"), }),
  • src/index.ts:1164-1237 (registration)
    MCP tool registration for 'readColorRam', including description, input schema, and handler reference.
    server.registerTool( "readColorRam", { description: `Read color RAM ($D800-$DBE7) and return color values with names. Color RAM determines the foreground color of each character on screen. Returns: - 25x40 grid of color values (0-15) with names - Summary of colors used Related tools: readScreen, readVicState`, inputSchema: z.object({ summary: z .boolean() .optional() .describe("Return only color usage summary, not full grid (default: false)"), }), }, async (args) => { try { // Color RAM is always at $D800 const colorData = await client.readMemory(0xd800, 0xd800 + 999); // Count color usage const colorCounts = new Map<number, number>(); for (const byte of colorData) { const color = byte & 0x0f; colorCounts.set(color, (colorCounts.get(color) || 0) + 1); } // Sort by frequency const colorUsage = Array.from(colorCounts.entries()) .sort((a, b) => b[1] - a[1]) .map(([color, count]) => ({ color: getColorInfo(color), count, percentage: Math.round((count / 1000) * 100), })); const response: Record<string, unknown> = { address: { value: 0xd800, hex: "$D800" }, summary: { uniqueColors: colorUsage.length, dominantColor: colorUsage[0]?.color || null, usage: colorUsage, }, }; if (!args.summary) { // Convert to 25 lines of 40 color values const colorLines: Array<Array<{ value: number; name: string }>> = []; for (let row = 0; row < 25; row++) { const line: Array<{ value: number; name: string }> = []; for (let col = 0; col < 40; col++) { const offset = row * 40 + col; line.push(getColorInfo(colorData[offset])); } colorLines.push(line); } response.grid = colorLines; } response.hint = colorUsage.length === 1 ? `Entire screen uses ${colorUsage[0].color.name} (${colorUsage[0].color.value})` : `${colorUsage.length} colors used. Dominant: ${colorUsage[0]?.color.name} (${colorUsage[0]?.percentage}%)`; return formatResponse(response); } catch (error) { return formatError(error as ViceError); } } );
  • Color palette constants and helper functions getColorName/getColorInfo used to interpret raw color RAM bytes into named C64 colors (0-15). Called multiple times in the handler.
    export const C64_COLORS = [ "black", "white", "red", "cyan", "purple", "green", "blue", "yellow", "orange", "brown", "light red", "dark gray", "gray", "light green", "light blue", "light gray", ] as const; export type C64Color = (typeof C64_COLORS)[number]; export function getColorName(value: number): C64Color { return C64_COLORS[value & 0x0f]; } export function getColorInfo(value: number): { value: number; name: C64Color } { return { value: value & 0x0f, name: getColorName(value), }; }

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