Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# OpenAI API Configuration
# Get your API key from: https://platform.openai.com/api-keys
OPENAI_API_KEY=your_openai_api_key_here
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
dist
.env
101 changes: 101 additions & 0 deletions AI_CHAT_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# AI Chat Assistant per Programmi BASIC del C64

## Setup

1. **Get your OpenAI API Key**
- Visit https://platform.openai.com/api-keys
- Create a new API key or use an existing one

2. **Configure the API Key**
- Open the `.env` file in the project root
- Replace `your_openai_api_key_here` with your actual OpenAI API key:
```
OPENAI_API_KEY=sk-your-actual-key-here
```

3. **Start the Development Server**
```bash
npm run start
```

## Usage

1. Open the web emulator at http://localhost:8081
2. Optionally, type a BASIC program in the C64 (or load one)
3. Click the AI chat icon (💬) in the lower tray (next to the joystick and keyboard buttons)
4. Type your request in the chat input
5. The AI will analyze the current BASIC program and respond
6. If the AI proposes changes to the BASIC program, you'll see a confirmation dialog with:
- Description of the change
- The new BASIC program
- "Apply Changes" and "Reject" buttons
7. Click "Apply Changes" to have the program automatically typed into the C64, or "Reject" to dismiss it

## Features

- **BASIC Program Context**: The AI has access to the current BASIC program in the C64's memory
- **Interactive Modifications**: The AI can propose specific changes or write new programs
- **Automatic Entry**: Approved programs are automatically typed into the C64
- **User Confirmation**: All code changes require explicit user approval
- **Conversation History**: The chat maintains context across multiple messages

## How It Works

1. When you send a message, the frontend extracts the current BASIC program from C64 RAM (starting at $0801)
2. The program is detokenized (converted from internal format to readable text)
3. The detokenized program is sent to OpenAI along with your message
4. OpenAI (GPT-4) analyzes your request and responds
5. If the AI wants to modify or create a program, it includes a special JSON block in its response
6. The frontend displays a confirmation dialog showing the new program
7. If you approve, the program is automatically typed into the C64:
- Types `NEW` to clear memory
- Types each line of the program with RETURN
- Ready to RUN!

## Example Requests

- "Write a program that draws a rainbow"
- "Add sound effects to this program"
- "Fix the bug on line 40"
- "Make this program faster"
- "Add a high score system"
- "Convert this to use sprites"
- "Explain what this program does"

## Security Notes

- **Never commit your `.env` file** - it's already in `.gitignore`
- Keep your OpenAI API key secret
- The AI can modify BASIC programs in the C64 when you approve changes
- Always review proposed changes before applying them

## Troubleshooting

**"OpenAI API key not configured"**
- Make sure you've created a `.env` file with a valid API key

**"Failed to communicate with AI"**
- Check your internet connection
- Verify your API key is valid and has sufficient credits
- Check the browser console for detailed error messages

**Changes not being applied**
- Check the browser console for error messages
- Make sure the C64 emulator is running
- Try typing the program manually to test

**Program doesn't extract correctly**
- Make sure you have a BASIC program loaded
- Try typing `LIST` in the C64 to verify the program exists
- The extractor reads from memory address $0801

## Cost Considerations

Using the OpenAI API incurs costs based on:
- The amount of text sent (including your BASIC program as context)
- The model used (GPT-4)
- The number of requests made

Monitor your usage at: https://platform.openai.com/usage

Note: BASIC programs are very small compared to the emulator codebase, so costs should be minimal!
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,52 @@ Viciious brings the outstanding capabilities of the Commodore 64 microcomputer t
- [Run the live demo](https://luxocrates.github.io/viciious/) on a desktop web browser.
- Drag-and-drop `.t64`, `.d64`, `.tap`, `.prg` or `.sid` files into the browser window.
- Your cursor keys and Shift map to a joystick plugged into Control Port 2.
- Whats written on your keyboard is probably what gets typed in.
- What's written on your keyboard is probably what gets typed in.
- Click anywhere on the page to open menus.
- Use the AI Assistant to help write and modify BASIC programs.

The emulator distribution is a single, self-contained HTML file that does not access the Internet.

## AI-Assisted BASIC Programming

Viciious includes an AI assistant that helps you write and modify BASIC programs directly in the C64 environment:

- **AI Chat Dialog**: Click the AI button in the lower tray to open a chat interface with GPT-4
- **Context-Aware**: The AI can see your current BASIC program and suggest modifications
- **Direct Code Injection**: Code changes are automatically tokenized and written to C64 RAM
- **Import/Export**: Use the Import and Export buttons to load and save BASIC programs as text files
- **Export to .PRG / .T64**: From the AI Assistant dialog you can now export the currently loaded BASIC program as a `.prg` file (standard C64 program) or as a minimal `.t64` tape image. Use the `Export .PRG` or `Export .T64` buttons to download a file compatible with VICE and other C64 tools.

Quick notes:
- `.prg` is the simplest and most widely-compatible format: it contains the 2-byte load address followed by the raw program bytes.
- `.t64` produces a minimal tape container wrapping the `.prg` (useful for loaders that expect tape images).

### Live Reload Development Mode

For rapid development, you can edit BASIC code in your favorite text editor:

1. Create a file named `current.basic` in the `dist/web-dev/` directory
2. Write or modify your BASIC program in this file
3. Every time you save the file, the program automatically:
- Reloads into C64 memory
- Executes with the RUN command

This provides a modern development workflow with instant feedback, similar to hot module reloading in web development.

**Example workflow:**
```bash
# Start the dev server
$ npm start

# In another terminal, edit your BASIC program
$ echo "10 PRINT \"HELLO WORLD\"" > dist/web-dev/current.basic
$ echo "20 GOTO 10" >> dist/web-dev/current.basic

# Save the file and watch it auto-run in the emulator!
```

The file watcher checks for changes every second and automatically loads and runs your code.

## Purpose

Viciious was written for tinkering. It does a few things differently from other emulators, with the aim of facilitating playful exploration of the hardware and the software that ran on it. For example...
Expand Down
53 changes: 53 additions & 0 deletions debug-line-number.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Debug what we're actually writing vs what we expect

const program = "10 PRINT \"HELLO\"";

// Parse line
const match = program.match(/^(\d+)\s+(.*)$/);
const lineNum = parseInt(match[1], 10);
const lineText = match[2];

console.log('Line number:', lineNum);
console.log('Line text:', lineText);

// Line number in little-endian
const loByte = lineNum & 0xFF;
const hiByte = (lineNum >> 8) & 0xFF;

console.log('Line number bytes:');
console.log(' Low byte:', loByte, '(0x' + loByte.toString(16) + ')');
console.log(' High byte:', hiByte, '(0x' + hiByte.toString(16) + ')');

// If we write these backwards by mistake:
const wrongNum = hiByte | (loByte << 8);
console.log('If bytes are swapped:', wrongNum);

// Test tokenization
const TOKEN_PRINT = 0x99;
console.log('\nExpected tokenization of PRINT "HELLO":');
console.log('0x99 (PRINT token)');
console.log('0x20 (space)');
console.log('0x22 (quote)');
console.log('0x48 0x45 0x4C 0x4C 0x4F (HELLO)');
console.log('0x22 (quote)');
console.log('0x00 (EOL)');

// What if we're writing the pointer wrong?
const addr = 0x0801;
const lineLength = 2 + 2 + 9 + 1; // ptr + linenum + content + eol = 14
const nextAddr = addr + lineLength; // 0x080f

console.log('\nNext line pointer:');
console.log('Next address: 0x' + nextAddr.toString(16));
console.log('Low byte:', nextAddr & 0xFF, '(0x' + (nextAddr & 0xFF).toString(16) + ')');
console.log('High byte:', (nextAddr >> 8) & 0xFF, '(0x' + ((nextAddr >> 8) & 0xFF).toString(16) + ')');

// Test what 15420 looks like in hex
console.log('\n15420 in hex: 0x' + (15420).toString(16));
console.log('15420 low byte:', 15420 & 0xFF, '(0x' + (15420 & 0xFF).toString(16) + ')');
console.log('15420 high byte:', (15420 >> 8) & 0xFF, '(0x' + ((15420 >> 8) & 0xFF).toString(16) + ')');

// Reverse engineering: what bytes give us 15420?
// 15420 = 0x3C3C
console.log('\n0x3C3C = ', 0x3C3C, '(ASCII: "<<")');
console.log('0x3C in ASCII:', String.fromCharCode(0x3C));
Loading