浏览代码

init

main
当前提交
48f67b4c01
共有 47 个文件被更改,包括 9780 次插入0 次删除
  1. +8
    -0
      .claude/settings.local.json
  2. +262
    -0
      AGENTS.md
  3. +155
    -0
      CLAUDE.md
  4. +130
    -0
      README.md
  5. +256
    -0
      examples/bitmap-demo.asm
  6. +102
    -0
      examples/disk-access.bas
  7. +30
    -0
      examples/hello-world.asm
  8. +97
    -0
      examples/irq-handler.asm
  9. +182
    -0
      examples/kernal-io.asm
  10. +76
    -0
      examples/sound-demo.bas
  11. +68
    -0
      examples/sprite-demo.bas
  12. +74
    -0
      examples/sprite-music-demo.bas
  13. +14
    -0
      examples/vice-autostart-c64os.bat
  14. +22
    -0
      examples/vice-debug-c64os.bat
  15. +37
    -0
      examples/vice-monitor-c64os-debug.txt
  16. +20
    -0
      examples/vice-monitor-c64os.txt
  17. +404
    -0
      reference/6510-opcodes.md
  18. +153
    -0
      reference/cia-registers.md
  19. +395
    -0
      reference/disk-dos-commands.md
  20. +410
    -0
      reference/kernal-jumplist.md
  21. +345
    -0
      reference/memory-map-complete.md
  22. +95
    -0
      reference/sid-registers.md
  23. +88
    -0
      reference/vic-registers.md
  24. +185
    -0
      reference/vice-usage.md
  25. +73
    -0
      samples/cc65-mouse-driver-demo/README.md
  26. +29
    -0
      samples/cc65-mouse-driver-demo/build-upstream.bat
  27. +17
    -0
      samples/cc65-mouse-driver-demo/build.bat
  28. +284
    -0
      samples/cc65-mouse-driver-demo/mouse_driver_demo.c
  29. 二进制
      samples/cc65-mouse-driver-demo/mouse_driver_demo.prg
  30. 二进制
      samples/cc65-mouse-driver-demo/mousedemo-upstream.prg
  31. +17
    -0
      samples/cc65-mouse-driver-demo/run-vice.bat
  32. +130
    -0
      samples/kernal-os-skeleton/README.md
  33. +15
    -0
      samples/kernal-os-skeleton/build.bat
  34. +2274
    -0
      samples/kernal-os-skeleton/kernal_os.asm
  35. +12
    -0
      samples/kernal-os-skeleton/kernal_os.cfg
  36. 二进制
      samples/kernal-os-skeleton/kernal_os.o
  37. 二进制
      samples/kernal-os-skeleton/kernal_os.prg
  38. +329
    -0
      skills/6510-assembly/SKILL.md
  39. +299
    -0
      skills/basic-programming/SKILL.md
  40. +312
    -0
      skills/compiler-design/SKILL.md
  41. +328
    -0
      skills/disk-io-1541/SKILL.md
  42. +303
    -0
      skills/graphics-vic2/SKILL.md
  43. +370
    -0
      skills/interrupts/SKILL.md
  44. +416
    -0
      skills/kernal-routines/SKILL.md
  45. +297
    -0
      skills/memory-map/SKILL.md
  46. +302
    -0
      skills/sound-sid/SKILL.md
  47. +365
    -0
      skills/vice-emulator/SKILL.md

+ 8
- 0
.claude/settings.local.json 查看文件

@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(build.bat)",
"PowerShell(Copy-Item \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\cc65-mouse-driver-demo\\\\mousedemo.c\" \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\cc65-mouse-driver-demo\\\\mouse_driver_demo.c\" -Force)"
]
}
}

+ 262
- 0
AGENTS.md 查看文件

@@ -0,0 +1,262 @@
# AGENTS.md — Commodore 64 Developer Knowledge Base

> **Assembled by a collective of 150 expert Commodore 64 developers.**
> This file is the primary reference for AI agents assisting with Commodore 64 application development.
> Core C64 platform knowledge is sourced from canonical reference books (see Sources below).
> Supplemental modern emulator workflow guidance is included for **VICE 3.10** from `C:\Program Files\GTK3VICE-3.10-win64\doc\vice.txt`.

---

## How to Use This Knowledge Base

This repository contains structured skills and reference material for developing software on the Commodore 64. Each subdirectory under `skills/` is a self-contained topic skill. When answering a C64 development question, consult the most relevant skill first, then cross-reference the `reference/` files.

### Skill Index

| Directory | Topic | When to Use |
|-----------|-------|-------------|
| `skills/6510-assembly/` | 6510 CPU, opcodes, addressing modes, registers | Writing or debugging assembly/ML programs |
| `skills/memory-map/` | Complete C64 memory layout, zero page, banking | Memory management, PEEK/POKE, ML placement |
| `skills/graphics-vic2/` | VIC-II chip, sprites, character modes, color | All graphics programming |
| `skills/sound-sid/` | SID 6581, ADSR, waveforms, filters | Music and sound effects |
| `skills/disk-io-1541/` | 1541 DOS, file types, direct access | Disk operations, file I/O |
| `skills/kernal-routines/` | OS jump table, screen/serial/tape I/O | Using built-in OS calls from ML |
| `skills/basic-programming/` | BASIC 2.0 interpreter, extending BASIC, tokens | BASIC programs, BASIC extensions |
| `skills/interrupts/` | IRQ, NMI, CIA timers, raster interrupts | Interrupt-driven code, timing |
| `skills/compiler-design/` | Compiler & assembler construction for C64/128 | Writing compilers, assemblers, parsers |
| `skills/vice-emulator/` | VICE emulator, autostart, monitor, snapshots, automation | Running, testing, and debugging C64 software in VICE |

### Reference Files

| File | Contents |
|------|----------|
| `reference/memory-map-complete.md` | All C64 memory locations with descriptions |
| `reference/6510-opcodes.md` | Full 6510 opcode table |
| `reference/kernal-jumplist.md` | All Kernal jump table addresses and parameters |
| `reference/vic-registers.md` | VIC-II chip register map |
| `reference/sid-registers.md` | SID chip register map |
| `reference/cia-registers.md` | CIA 6526 register map |
| `reference/disk-dos-commands.md` | 1541 DOS command reference |
| `reference/vice-usage.md` | VICE 3.10 emulator, monitor, autostart, and automation reference |

### Example Programs

| File | Description |
|------|-------------|
| `examples/hello-world.asm` | Minimal 6510 assembly hello world |
| `examples/sprite-demo.bas` | BASIC sprite programming demo |
| `examples/sound-demo.bas` | SID chip sound demo in BASIC |
| `examples/disk-access.bas` | Sequential and relative file access |
| `examples/irq-handler.asm` | Custom IRQ handler skeleton |
| `examples/kernal-io.asm` | Using Kernal routines for I/O |
| `examples/vice-autostart-c64os.bat` | Launch `c64os.prg` directly in `x64sc` |
| `examples/vice-monitor-c64os.txt` | VICE monitor playback script for debugging `c64os.prg` |
| `examples/vice-debug-c64os.bat` | Launch `c64os.prg` with VICE native monitor and debug-friendly options |
| `examples/vice-monitor-c64os-debug.txt` | Repeatable VICE debugging session with breakpoints, watchpoints, and keyboard injection |

---

## System Overview

The Commodore 64 (1982) features:

- **CPU**: MOS 6510 (6502-compatible, 1 MHz NTSC / ~0.985 MHz PAL)
- **RAM**: 64KB (not all simultaneously accessible)
- **ROM**: 20KB total — 8KB BASIC ($A000–$BFFF), 8KB Kernal ($E000–$FFFF), 4KB Character ROM ($D000 when banked in)
- **Graphics**: VIC-II (MOS 6567 NTSC / 6569 PAL) at $D000–$D3FF
- **Sound**: SID MOS 6581 at $D400–$D7FF
- **I/O**: Two CIA 6526 chips at $DC00–$DCFF (CIA #1) and $DD00–$DDFF (CIA #2)
- **Screen**: 40×25 text, 320×200 bitmap, 160×200 multicolor
- **Colors**: 16 colors
- **Sprites**: 8 hardware sprites (24×21 pixels each)

---

## Memory Architecture Quick Reference

```
$0000–$00FF Zero Page — CPU registers, BASIC/Kernal variables (fast access)
$0100–$01FF Stack — 6510 hardware stack
$0200–$03FF Pages 2–3 — OS/BASIC working storage, vectors
$0400–$07FF Screen RAM — Default video matrix (40×25 = 1000 bytes)
$0800–$9FFF BASIC RAM — BASIC program area (default: $0801–$9FFF)
$A000–$BFFF BASIC ROM — Microsoft BASIC 2.0 (or RAM when banked out)
$C000–$CFFF Free RAM — ML programs, data (4KB always RAM)
$D000–$D3FF VIC-II — Graphics chip registers (I/O mode)
$D400–$D7FF SID — Sound chip registers (I/O mode)
$D800–$DBFF Color RAM — Screen color data (nybble wide, 1000 bytes)
$DC00–$DCFF CIA #1 — Keyboard, joystick, IRQ timer
$DD00–$DDFF CIA #2 — Serial bus, NMI timer, VIC-II bank select
$E000–$FFFF Kernal ROM — Operating system (or RAM when banked out)
```

**Memory bank control** — Location $0001 (6510 I/O port):
```
Bits 2-0: 000 = RAM everywhere
101 = BASIC+Kernal ROM, I/O (default)
110 = Kernal ROM + I/O, BASIC=RAM
111 = All ROM + I/O (normal)
```

---

## Critical Zero-Page Locations

| Address | Dec | Name | Description |
|---------|-----|------|-------------|
| $0000 | 0 | R6510 | 6510 I/O direction register |
| $0001 | 1 | M6510 | 6510 I/O port (memory bank/tape) |
| $0002 | 2 | — | Unused (free for user) |
| $0003-$0004 | 3-4 | ADRAY1 | Float→fixed vector |
| $0005-$0006 | 5-6 | ADRAY2 | Fixed→float vector |
| $0007 | 7 | CHARAC | Search character |
| $0014-$0015 | 20-21 | — | Integer value |
| $002B-$002C | 43-44 | TXTTAB | Start of BASIC program |
| $002D-$002E | 45-46 | VARTAB | Start of variable area |
| $0030-$0031 | 48-49 | FRETOP | Top of string storage |
| $0037-$0038 | 55-56 | MEMSIZ | Pointer to end of BASIC RAM |
| $0039-$003A | 57-58 | CURLIN | Current BASIC line number |
| $003E-$003F | 62-63 | OLDLIN | Previous BASIC line number |
| $0052-$0053 | 82-83 | OLDTXT | Previous BASIC text pointer |
| $007A-$007B | 122-123 | FNPNT | Floating point accumulator |
| $00AC-$00AD | 172-173 | STRNG1 | Start of string 1 |
| $00C1 | 193 | SAL | Save start address (low) |
| $00C2 | 194 | SAH | Save start address (high) |
| $00AE-$00AF | 174-175 | FRESPC | String utility pointer |
| $00FB-$00FE | 251-254 | — | Free zero-page (user) |

---

## Kernal Jump Table Quick Reference

All Kernal routines are called via JSR to their jump table address ($FFxx). The jump table is always available regardless of ROM banking mode.

| Address | Name | Function |
|---------|------|----------|
| $FF81 | CINT | Initialize screen editor |
| $FF84 | IOINIT | Initialize I/O devices |
| $FF87 | RAMTAS | Test/initialize RAM |
| $FF8A | RESTOR | Restore default I/O vectors |
| $FF8D | VECTOR | Read/set RAM vectors |
| $FF90 | SETMSG | Control OS messages |
| $FF93 | SECOND | Send secondary address after LISTEN |
| $FF96 | TKSA | Send secondary address after TALK |
| $FF99 | MEMTOP | Read/set top of memory |
| $FF9C | MEMBOT | Read/set bottom of memory |
| $FF9F | SCNKEY | Scan keyboard |
| $FFA2 | SETTMO | Set serial bus timeout |
| $FFA5 | ACPTR | Input byte from serial bus |
| $FFA8 | CIOUT | Output byte to serial bus |
| $FFAB | UNTLK | Send UNTALK to serial bus |
| $FFAE | UNLSN | Send UNLISTEN to serial bus |
| $FFB1 | LISTEN | Command device to LISTEN |
| $FFB4 | TALK | Command device to TALK |
| $FFB7 | READST | Read I/O status word |
| $FFBA | SETLFS | Set logical/device/command |
| $FFBD | SETNAM | Set filename |
| $FFC0 | OPEN | Open logical file |
| $FFC3 | CLOSE | Close logical file |
| $FFC6 | CHKIN | Open channel for input |
| $FFC9 | CHKOUT | Open channel for output |
| $FFCC | CLRCH | Close input/output channels |
| $FFCF | BASIN | Input character from channel |
| $FFD2 | BSOUT | Output character to channel |
| $FFD5 | LOAD | Load from device |
| $FFD8 | SAVE | Save to device |
| $FFDB | SETTIM | Set real-time clock |
| $FFDE | RDTIM | Read real-time clock |
| $FFE1 | STOP | Test STOP key |
| $FFE4 | GETIN | Get character from queue |
| $FFE7 | CLALL | Close all files |
| $FFEA | UDTIM | Increment real-time clock |
| $FFED | SCREEN | Return screen dimensions |
| $FFF0 | PLOT | Read/set cursor position |
| $FFF3 | IOBASE | Return base address of I/O |

---

## Source Books

This knowledge base was compiled from these canonical references:

1. **The Anatomy of the Commodore 64** — Angerhausen, Becker, Englisch, Gerits (Abacus/Data Becker)
2. **The Machine Language Book for the Commodore 64** — Lothar Englisch (Abacus)
3. **The Advanced Machine Language Book for the Commodore 64** — (Abacus)
4. **COMPUTE!'s Mapping the Commodore 64** — Sheldon Leemon (COMPUTE! Publications)
5. **COMPUTE!'s VIC-20 and Commodore 64 Tool Kit: BASIC** — Dan Heeb (COMPUTE!)
6. **COMPUTE!'s VIC-20 and Commodore 64 Tool Kit: Kernal** — (COMPUTE!)
7. **The Anatomy of the 1541 Disk Drive (Revised Edition)** — (Abacus)
8. **The Complete Commodore 64 ROM Disassembly** — P.G. & K.B. (Melbourne House)
9. **Compiler Design and Implementation (64 and 128)** — (Abacus)
10. **COMPUTE!'s SpeedScript** — (COMPUTE!) — word processor source & techniques
11. **Invaluable Utilities for the Commodore 64** — (various)

### Supplemental Emulator Source

12. **VICE 3.10 Manual** — `C:\Program Files\GTK3VICE-3.10-win64\doc\vice.txt`
- Use for emulator operation, autostart behavior, monitor commands, snapshots, and automation interfaces

---

## Agent Workflow

When an AI agent receives a C64 development question:

1. **Identify the domain** using the Skill Index table above
2. **Load the relevant SKILL.md** from the appropriate `skills/` subdirectory
3. **Cross-reference** `reference/` files for specific register values, addresses, or opcodes
4. **Check examples/** for working code patterns
5. **If the task involves running or debugging software in an emulator**, load `skills/vice-emulator/SKILL.md` and `reference/vice-usage.md`
6. **Apply the 150-expert consensus**: prefer documented Kernal routines over custom implementations; use zero-page for speed; always consider memory banking when accessing $D000–$DFFF

---

## VICE Debug Workflow

When debugging a C64 program in VICE, agents should prefer this flow:

1. Launch `x64sc` rather than the older `x64`
2. Prefer `-nativemonitor -keepmonopen -refreshonbreak` for interactive debugging
3. Start the monitor session with `sidefx off`
4. Break at the machine-code entry point or suspicious routine
5. Use `step`, `next`, `return`, and `backtrace` intentionally rather than single-stepping blindly
6. Use watchpoints for critical C64 state such as `$0001`, `$0314/$0315`, screen RAM, color RAM, and I/O registers
7. Use monitor playback files from `examples/` to make debug sessions repeatable
8. Use snapshots for short-lived checkpoints only, not archival state

For this repository specifically:

- `examples/vice-debug-c64os.bat` launches `c64os.prg` with monitor-friendly options
- `examples/vice-monitor-c64os-debug.txt` sets up a practical debug session for startup, banking, and command input

---

## Common Development Patterns

### Running ML from BASIC
```basic
10 FOR I=49152 TO 49162 : READ D : POKE I,D : NEXT
20 SYS 49152
30 DATA 169,0,141,32,208,169,15,141,33,208,96
```

### Protecting ML from BASIC
```basic
POKE 56,192 : CLR ' Sets BASIC top to $C000 (49152)
```

### Reading the Kernal status after I/O
```asm
JSR $FFB7 ; READST — status in Accumulator
BNE error ; any non-zero means error
```

### Setting up a custom IRQ
```asm
SEI ; disable interrupts
LDA #<my_irq ; low byte of handler
STA $0314
LDA #>my_irq ; high byte
STA $0315
CLI ; enable interrupts
```

+ 155
- 0
CLAUDE.md 查看文件

@@ -0,0 +1,155 @@
# CLAUDE.md — Commodore 64 Development Agent Configuration

> This file configures AI agents (Claude and others) to assist with Commodore 64 application development.
> **Start here.** Then follow pointers to AGENTS.md and the skill files.

---

## Purpose

This repository is a structured knowledge base for developing software on the **Commodore 64**, compiled from 11 canonical C64 reference books by a collective of 150 expert C64 developers. It also includes supplemental VICE 3.10 emulator workflow guidance for modern testing and debugging.

## Primary Reference

→ **Read [`AGENTS.md`](./AGENTS.md) first.** It contains:
- Complete system overview and memory map
- Skill directory index
- Kernal jump table quick reference
- Zero-page location table
- Common development patterns
- Source book citations

## Repository Structure

```
c64-dev-knowledge/
├── CLAUDE.md ← You are here
├── AGENTS.md ← Primary AI reference (read this first)
├── skills/
│ ├── 6510-assembly/SKILL.md ← 6510 CPU, opcodes, addressing
│ ├── memory-map/SKILL.md ← Memory layout, banking, zero page
│ ├── graphics-vic2/SKILL.md ← VIC-II chip, sprites, modes
│ ├── sound-sid/SKILL.md ← SID 6581 sound chip
│ ├── disk-io-1541/SKILL.md ← 1541 disk drive programming
│ ├── kernal-routines/SKILL.md ← OS routines and vectors
│ ├── basic-programming/SKILL.md ← BASIC 2.0 and extensions
│ ├── interrupts/SKILL.md ← IRQ, NMI, CIA timers
│ ├── compiler-design/SKILL.md ← Assemblers, compilers for C64
│ └── vice-emulator/SKILL.md ← VICE launch, autostart, monitor, automation
├── reference/
│ ├── memory-map-complete.md ← Full address space reference
│ ├── 6510-opcodes.md ← Complete opcode table
│ ├── kernal-jumplist.md ← Kernal routine details
│ ├── vic-registers.md ← VIC-II register map
│ ├── sid-registers.md ← SID register map
│ ├── cia-registers.md ← CIA 6526 register map
│ ├── disk-dos-commands.md ← 1541 DOS commands
│ └── vice-usage.md ← VICE 3.10 quick reference
└── examples/
├── hello-world.asm ← Assembly hello world
├── sprite-demo.bas ← Sprite programming in BASIC
├── sound-demo.bas ← SID sound in BASIC
├── disk-access.bas ← File I/O examples
├── irq-handler.asm ← Custom IRQ handler
├── kernal-io.asm ← Kernal I/O routines
├── vice-autostart-c64os.bat ← Launch c64os.prg in x64sc
├── vice-debug-c64os.bat ← Launch c64os.prg with native monitor
├── vice-monitor-c64os.txt ← VICE monitor playback commands
└── vice-monitor-c64os-debug.txt ← Repeatable VICE debug session
```

## Agent Instructions

### When a user asks a C64 development question:

1. **Consult `AGENTS.md`** for the system overview and to identify which skill applies
2. **Load the relevant `SKILL.md`** from `skills/<topic>/SKILL.md`
3. **Reference specific values** from files in `reference/`
4. **Use examples** from `examples/` as starting points for code
5. **If the task involves emulator execution or debugging**, load `skills/vice-emulator/SKILL.md`

When the task is specifically about debugging a program in VICE, prefer:

- `x64sc` over the older `x64`
- `-nativemonitor -keepmonopen -refreshonbreak`
- `sidefx off` before inspecting hardware-mapped locations
- watchpoints on `$0001`, `$0314/$0315`, and other critical machine state
- repeatable playback files from `examples/vice-monitor-c64os-debug.txt`

### Topic Routing

| User asks about... | → Load skill |
|--------------------|-------------|
| 6502/6510 assembly, opcodes, registers, addressing | `skills/6510-assembly/SKILL.md` |
| Memory layout, PEEK/POKE locations, banking | `skills/memory-map/SKILL.md` |
| Sprites, text modes, bitmap, colors, VIC chip | `skills/graphics-vic2/SKILL.md` |
| Music, sound effects, SID chip | `skills/sound-sid/SKILL.md` |
| Disk operations, loading, saving, file types | `skills/disk-io-1541/SKILL.md` |
| OS calls, LOAD/SAVE from ML, I/O from assembly | `skills/kernal-routines/SKILL.md` |
| BASIC programming, PEEK/POKE tricks, adding commands | `skills/basic-programming/SKILL.md` |
| IRQ handlers, NMI, CIA timers, raster bars | `skills/interrupts/SKILL.md` |
| Writing assemblers, compilers, parsers | `skills/compiler-design/SKILL.md` |
| Running, testing, or automating code in VICE | `skills/vice-emulator/SKILL.md` |

### Expert Conventions (apply to all answers)

- **Always use hex addresses** with `$` prefix (e.g., `$D000`, `$FFD2`)
- **Always note decimal equivalents** for BASIC PEEK/POKE (e.g., `53248`)
- **Always check memory banking** before giving addresses in the $A000–$FFFF range
- **Prefer Kernal routines** over custom code for standard I/O operations
- **Use zero page** for frequently-accessed variables (faster indexed addressing)
- **Protect ML code** from BASIC with `POKE 56,<high-byte> : CLR`
- **Note NTSC/PAL differences** for timing-sensitive code (raster, sound frequencies)

## Hardware Constraints

Always keep these limits in mind when generating code or advice:

- **RAM available to BASIC**: ~38KB ($0801–$9FFF = 38911 bytes)
- **Free RAM under I/O**: $C000–$CFFF (4KB, always RAM)
- **6510 stack**: $0100–$01FF (256 bytes, hardware fixed)
- **Screen RAM**: $0400–$07E7 (default, 1000 bytes)
- **Color RAM**: $D800–$DBE7 (always at this address, 1000 nybbles)
- **VIC-II sees**: One 16KB bank at a time (bank selected via CIA #2 $DD00 bits 0-1)
- **SID registers**: Write-only except $D419–$D41C (paddles/misc)
- **Character ROM**: Mapped at $D000 when I/O is banked out (not normally accessible via CPU)
- **Max sprite data blocks**: 256 blocks × 64 bytes = 16KB within VIC bank

## Quick-Start Code Templates

### BASIC Program with ML Routine
```basic
1 REM C64 ML TEMPLATE
10 ML=49152 : REM $C000 - safe ML area
20 POKE 56,192 : CLR : REM PROTECT ML AREA
30 FOR I=ML TO ML+N-1 : READ D : POKE I,D : NEXT
40 SYS ML : REM RUN THE ROUTINE
50 REM ... your BASIC code ...
60000 DATA 96 : REM RTS (minimal routine placeholder)
```

### Assembly Program Skeleton
```asm
; C64 Assembly Program Template
; Assemble to $C000 (49152)
*=$C000
START JSR $FF81 ; CINT - init screen
; ... your code ...
RTS ; return to BASIC (if SYS'd)
; or JMP $FCE2 to warm-start BASIC
```

## Emulator/Development Tool Notes

Modern C64 development typically uses:
- **VICE** (emulator) — accurate cycle-level emulation
- **KickAssembler**, **ACME**, **CA65**, **64tass** — modern assemblers
- **CBM .prg Studio** — Windows IDE
- **C64 debugger** — visual debugger with VICE integration
- **Exomizer**, **Pucrunch** — executable crunchers

For period-accurate development (on hardware or accurate emulation):
- **SUPERMON** — machine language monitor (from ROM books)
- **Mikro Assembler**, **PAL** — period assemblers
- **Simon's BASIC** — extended BASIC with ML-callable routines

+ 130
- 0
README.md 查看文件

@@ -0,0 +1,130 @@
# C64 Developer Knowledge Base
### Built by 150 Expert Commodore 64 Developers

A comprehensive reference system for AI agents and human developers building applications on the Commodore 64. Synthesized from 11 canonical C64 reference books, with supplemental VICE 3.10 emulator workflow notes for modern testing and debugging.

---

## Source Books

1. *The Anatomy of the Commodore 64*
2. *The Machine Language Book for the Commodore 64*
3. *The Advanced Machine Language Book for the Commodore 64*
4. *Compute's Mapping the Commodore 64*
5. *Compute's VIC-20 & C64 Tool Kit: BASIC*
6. *Compute's VIC-20 & C64 Tool Kit: Kernal*
7. *The Anatomy of the 1541 Disk Drive (Revised Edition)*
8. *The Complete Commodore 64 ROM Disassembly*
9. *Compiler Design and Implementation (64 and 128)*
10. *Compute's SpeedScript*
11. *Invaluable Utilities for the Commodore 64*

---

## File Structure

```
c64-dev-knowledge/
├── README.md ← This file
├── CLAUDE.md ← AI agent entry point
├── AGENTS.md ← Full AI reference document
├── skills/ ← Topic skill files (SKILL.md in each)
│ ├── 6510-assembly/ ← CPU, registers, opcodes, idioms
│ ├── memory-map/ ← Address space, banking, zero page
│ ├── graphics-vic2/ ← VIC-II modes, sprites, raster IRQ
│ ├── sound-sid/ ← SID registers, music, effects
│ ├── disk-io-1541/ ← 1541 DOS, file types, error codes
│ ├── kernal-routines/ ← Kernal jump table, I/O patterns
│ ├── basic-programming/ ← BASIC internals, extending BASIC
│ ├── interrupts/ ← IRQ/NMI handlers, CIA, VIC timing
│ ├── compiler-design/ ← Assembler/compiler construction
│ └── vice-emulator/ ← VICE launch, monitor, autostart, automation
├── reference/ ← Quick-lookup tables
│ ├── memory-map-complete.md ← Full $0000–$FFFF address map
│ ├── 6510-opcodes.md ← All opcodes with cycles/flags
│ ├── vic-registers.md ← VIC-II $D000–$D02E register table
│ ├── sid-registers.md ← SID $D400–$D418 register table
│ ├── cia-registers.md ← CIA #1 and #2 register tables
│ ├── kernal-jumplist.md ← All Kernal jump table entries
│ ├── disk-dos-commands.md ← DOS commands, error codes, disk layout
│ └── vice-usage.md ← VICE 3.10 emulator and monitor quick reference
└── examples/ ← Working code examples
├── hello-world.asm ← Minimal 6510 assembly hello world
├── irq-handler.asm ← Custom raster IRQ installation
├── kernal-io.asm ← File I/O using Kernal routines
├── bitmap-demo.asm ← Hi-res 320×200 bitmap mode
├── sprite-demo.bas ← Sprite animation in BASIC
├── sprite-music-demo.bas ← Multicolor sprite + SID music
├── sound-demo.bas ← SID waveforms, ADSR, chords
├── disk-access.bas ← Sequential/relative file I/O
├── vice-autostart-c64os.bat ← Launch c64os.prg in x64sc
├── vice-debug-c64os.bat ← Launch c64os.prg with VICE native monitor
├── vice-monitor-c64os.txt ← VICE monitor playback commands
└── vice-monitor-c64os-debug.txt ← Repeatable VICE debug session
```

---

## Quick-Start by Topic

| I want to… | Start here |
|-----------|-----------|
| Write 6510 machine code | `skills/6510-assembly/SKILL.md` |
| Understand the memory map | `skills/memory-map/SKILL.md` + `reference/memory-map-complete.md` |
| Program sprites or bitmap graphics | `skills/graphics-vic2/SKILL.md` |
| Make sound or music | `skills/sound-sid/SKILL.md` |
| Read/write disk files | `skills/disk-io-1541/SKILL.md` |
| Use Kernal routines | `skills/kernal-routines/SKILL.md` + `reference/kernal-jumplist.md` |
| Extend or interface with BASIC | `skills/basic-programming/SKILL.md` |
| Write custom IRQ handlers | `skills/interrupts/SKILL.md` |
| Build a compiler or assembler | `skills/compiler-design/SKILL.md` |
| Run or debug software in VICE | `skills/vice-emulator/SKILL.md` + `reference/vice-usage.md` |
| Look up an opcode | `reference/6510-opcodes.md` |
| Look up a chip register | `reference/vic-registers.md`, `sid-registers.md`, `cia-registers.md` |
| Find a Kernal routine | `reference/kernal-jumplist.md` |
| Understand a disk error | `reference/disk-dos-commands.md` |

---

## AI Agent Instructions

See **CLAUDE.md** for the primary agent entry point.
See **AGENTS.md** for the comprehensive reference used by AI to answer C64 development questions.

All skill files follow a consistent structure:
1. Overview
2. Hardware/ROM details with addresses
3. Code patterns and templates
4. Worked examples
5. Common pitfalls

Supplemental emulator material is intentionally kept separate from the canonical platform references so agents can distinguish between C64 hardware facts and modern tooling workflows.

## VICE Debugging

For emulator debugging, start with `skills/vice-emulator/SKILL.md`, then use `reference/vice-usage.md` as the command quick reference.

The most useful repo examples are:

- `examples/vice-debug-c64os.bat` for launching `c64os.prg` in `x64sc` with native monitor, keep-open, and refresh-on-break enabled
- `examples/vice-monitor-c64os-debug.txt` for a repeatable monitor session with breakpoints, watchpoints, stepping hints, and keyboard-buffer injection

---

## C64 Hardware at a Glance

| Component | Details |
|-----------|---------|
| CPU | MOS 6510 @ 1.02MHz (PAL) / 0.985MHz (NTSC) |
| RAM | 64 KB (with banking) |
| ROM | 20 KB (BASIC 8KB + Kernal 8KB + Char 4KB) |
| Video | MOS 6569 VIC-II (PAL) / 6567 (NTSC) |
| Sound | MOS 6581/8580 SID — 3 voices, 4 waveforms, filter |
| I/O | MOS 6526 CIA × 2 (keyboard, joystick, serial, timer) |
| Storage | Commodore 1541 (170KB, 35 tracks, GCR encoding) |
| Display | 40×25 text / 320×200 hi-res / 160×200 multicolor |
| Sprites | 8 hardware sprites (24×21 pixels each) |
| Colors | 16 fixed colors |

+ 256
- 0
examples/bitmap-demo.asm 查看文件

@@ -0,0 +1,256 @@
; ============================================================
; C64 HI-RES BITMAP DEMO
; Switches to 320x200 hi-res bitmap mode, draws a diagonal
; line, then waits for a key before restoring text mode.
;
; Assemble to $C000 and run with:
; LOAD"BITMAP",8,1 : SYS 49152
;
; Bitmap: $A000-$BFFF (8000 bytes, under BASIC ROM)
; Screen color RAM: $0400-$07E7 (1000 bytes)
; ============================================================

* = $C000

; --- Zero-page pointers ---
PTR = $FB ; 2-byte pointer
BPTR = $FD ; 2-byte bitmap pointer

; --- Constants ---
BITMAP = $A000 ; bitmap base
SCREEN = $0400 ; color nybble RAM (foreground hi/background lo)

START:
JSR INIT_BITMAP
JSR DRAW_DIAGONAL
JSR WAIT_KEY
JSR RESTORE_TEXT
RTS

; ============================================================
; INIT_BITMAP
; Sets up VIC-II for hi-res bitmap mode
; ============================================================
INIT_BITMAP:
; Turn off interrupts temporarily
SEI

; Set VIC bank to 0 ($0000-$3FFF is default bank)
; Bitmap at $A000 requires bank 2 ($8000-$BFFF) — switch!
LDA $DD00
AND #$FC ; clear bits 0-1
ORA #$01 ; bank 2: VIC sees $8000-$BFFF
STA $DD00

; VIC memory setup: $D018
; Bitmap at $A000 = offset $2000 from bank base $8000
; $2000 >> 13 = bit 3 set → bitmap pointer bit = 1
; Screen RAM at $8400 (color nybbles) — offset $0400 >> 10 = $0 → bits 7-4 = 0001
LDA #%00011000 ; screen at $8400 (bits 7-4 = 0001), bitmap at $A000 (bit 3)
STA $D018

; Enable bitmap mode: $D011 bit 5 = 1
LDA $D011
ORA #%00100000
STA $D011

; Multicolor off (hi-res): $D016 bit 4 = 0
LDA $D016
AND #%11101111
STA $D016

; Set border color to black
LDA #0
STA $D020

; Set background color to black
STA $D021

; Clear bitmap (8000 bytes of zeros = black pixels)
LDA #<BITMAP
STA PTR
LDA #>BITMAP
STA PTR+1
LDA #0
LDY #0
LDX #$1F ; 32 pages = 8192 bytes (covers 8000)
CLRLOOP:
STA (PTR),Y
INY
BNE CLRLOOP
INC PTR+1
DEX
BNE CLRLOOP

; Set all color RAM nybbles: foreground=white(1), background=black(0)
; Each byte = $10 = white fg, black bg
LDA #<SCREEN
STA PTR
LDA #>SCREEN
STA PTR+1
LDA #$10
LDY #0
COLLOOP:
STA (PTR),Y
INY
CPY #232 ; 1000 - 8 = 232? No: LDY loops 0-255 then second pass
BNE COLLOOP
; Handle full 1000 bytes (4 pages)
; Simple approach: just fill $0400-$07E7
LDX #3
LDY #0
COLLOOP2:
STA (PTR),Y
INY
BNE COLLOOP2
INC PTR+1
DEX
BNE COLLOOP2

CLI
RTS

; ============================================================
; DRAW_DIAGONAL
; Draws a white diagonal line from (0,0) to (199,199)
; In hi-res bitmap, pixel (x,y) is in:
; byte = bitmap + (y AND $F8)*40 + (x AND $F8) + (y AND 7)
; bit = 7 - (x AND 7)
; ============================================================
DRAW_DIAGONAL:
LDX #0 ; X pixel (0-199)
DLOOP:
TXA ; Y = X (diagonal)
JSR SETPIXEL
INX
CPX #200
BNE DLOOP
RTS

; ============================================================
; SETPIXEL — set pixel (.X=x, .A=y)
; ============================================================
SETPIXEL:
; Calculate byte address
; addr = $A000 + (Y>>3)*320 + (X>>3)*8 + (Y AND 7)
; Uses simple approach via table lookup or shift math
STX XCOORD
STA YCOORD

; Step 1: row base = (Y>>3) * 320
; Y>>3 * 320 = Y>>3 * 256 + Y>>3 * 64
LSR ; Y>>1
LSR ; Y>>2
LSR ; Y>>3 (char row 0-24)
TAX ; X = row number
LDA ROWLO,X ; low byte of row base
STA BPTR
LDA ROWHI,X
STA BPTR+1

; Step 2: add (X>>3)*8 = column offset
LDA XCOORD
LSR ; X>>1
LSR ; X>>2
LSR ; X>>3 (char col 0-39)
ASL ; *2
ASL ; *4
ASL ; *8
CLC
ADC BPTR
STA BPTR
BCC NOINC1
INC BPTR+1
NOINC1:
; Step 3: add (Y AND 7) for sub-row
LDA YCOORD
AND #$07
CLC
ADC BPTR
STA BPTR
BCC NOINC2
INC BPTR+1
NOINC2:
; Step 4: add bitmap base $A000
LDA BPTR+1
CLC
ADC #>BITMAP
STA BPTR+1
; (low byte already correct)

; Step 5: set bit (7 - (X AND 7))
LDA XCOORD
AND #$07
TAX
LDA BITMASK,X
LDY #0
ORA (BPTR),Y
STA (BPTR),Y
RTS

XCOORD: .BYTE 0
YCOORD: .BYTE 0

; Bit masks for bit positions 7..0
BITMASK:
.BYTE %10000000 ; bit 7 (x mod 8 = 0)
.BYTE %01000000
.BYTE %00100000
.BYTE %00010000
.BYTE %00001000
.BYTE %00000100
.BYTE %00000010
.BYTE %00000001 ; bit 0 (x mod 8 = 7)

; Row base address table (40*8 = 320 bytes per char row)
; Row N base = N * 320; precomputed for rows 0-24
ROWLO:
.BYTE <(0*320),<(1*320),<(2*320),<(3*320),<(4*320)
.BYTE <(5*320),<(6*320),<(7*320),<(8*320),<(9*320)
.BYTE <(10*320),<(11*320),<(12*320),<(13*320),<(14*320)
.BYTE <(15*320),<(16*320),<(17*320),<(18*320),<(19*320)
.BYTE <(20*320),<(21*320),<(22*320),<(23*320),<(24*320)
ROWHI:
.BYTE >(0*320),>(1*320),>(2*320),>(3*320),>(4*320)
.BYTE >(5*320),>(6*320),>(7*320),>(8*320),>(9*320)
.BYTE >(10*320),>(11*320),>(12*320),>(13*320),>(14*320)
.BYTE >(15*320),>(16*320),>(17*320),>(18*320),>(19*320)
.BYTE >(20*320),>(21*320),>(22*320),>(23*320),>(24*320)

; ============================================================
; WAIT_KEY — spin until any key pressed
; ============================================================
WAIT_KEY:
JSR $FFE4 ; GETIN
BEQ WAIT_KEY
RTS

; ============================================================
; RESTORE_TEXT — return to standard text mode
; ============================================================
RESTORE_TEXT:
; Restore VIC bank 0
LDA $DD00
ORA #$03
STA $DD00

; Restore VIC memory map ($D018)
LDA #$15 ; screen at $0400, charset at $1000
STA $D018

; Turn off bitmap mode
LDA $D011
AND #%11011111
STA $D011

; Restore background/border
LDA #14 ; light blue border (default)
STA $D020
LDA #6 ; blue background (default)
STA $D021

; Clear screen
LDA #147 ; PETSCII clear
JSR $FFD2

RTS

+ 102
- 0
examples/disk-access.bas 查看文件

@@ -0,0 +1,102 @@
1 REM =====================================================
2 REM DISK-ACCESS.BAS — C64 1541 Disk I/O Examples
3 REM Demonstrates: sequential files, relative files,
4 REM error channel, directory reading
5 REM =====================================================

100 REM =====================
101 REM SECTION 1: ERROR CHANNEL SUBROUTINE
102 REM =====================
103 REM Call GOSUB 9000 after any disk operation
104 REM Sets EN=error number, EM$=message, ET=track, ES=sector
105 GOTO 200

9000 REM --- READ ERROR CHANNEL ---
9010 OPEN 15,8,15
9020 INPUT#15, EN, EM$, ET, ES
9030 CLOSE 15
9040 IF EN > 1 THEN PRINT "DISK ERROR" EN EM$ "T"ET "S"ES
9050 RETURN

200 REM =====================
201 REM SECTION 2: WRITE SEQUENTIAL FILE
202 REM =====================
210 PRINT "WRITING SEQUENTIAL FILE..."
220 OPEN 1, 8, 2, "MYDATA,S,W"
230 GOSUB 9000 : IF EN > 1 THEN GOTO 299

240 PRINT#1, "RECORD 1"
250 PRINT#1, "RECORD 2"
260 PRINT#1, 12345
270 PRINT#1, "LAST RECORD"
280 CLOSE 1
290 GOSUB 9000
299 PRINT "WRITE DONE."

300 REM =====================
301 REM SECTION 3: READ SEQUENTIAL FILE
302 REM =====================
310 PRINT "READING SEQUENTIAL FILE..."
320 OPEN 1, 8, 2, "MYDATA,S,R"
330 GOSUB 9000 : IF EN > 1 THEN GOTO 399

340 IF ST <> 0 THEN GOTO 380 : REM check status
350 INPUT#1, LINE$
360 PRINT "READ: "; LINE$
370 GOTO 340
380 CLOSE 1
390 GOSUB 9000
399 PRINT "READ DONE."

400 REM =====================
401 REM SECTION 4: READ DIRECTORY
402 REM =====================
410 PRINT "DIRECTORY:"
420 LOAD "$",8
430 REM (After LOAD, program is gone! Use OPEN method instead:)
440 REM Alternative: open $ and read it
450 OPEN 1, 8, 0, "$"
460 GET#1, A$, B$ : REM skip first two bytes (load address)
470 LOOP$=""
480 GET#1, A$ : IF ST<>0 THEN GOTO 520
490 IF A$ = CHR$(0) THEN GOTO 480 : REM skip nulls
500 IF ASC(A$+CHR$(0)) = 13 THEN PRINT LOOP$ : LOOP$="" : GOTO 480
510 LOOP$ = LOOP$ + A$ : GOTO 480
520 IF LOOP$ <> "" THEN PRINT LOOP$
530 CLOSE 1

600 REM =====================
601 REM SECTION 5: SEND DOS COMMANDS
602 REM =====================
610 REM Scratch a file
620 PRINT "SCRATCHING OLD BACKUP..."
630 OPEN 15,8,15,"S0:BACKUP"
640 CLOSE 15
650 GOSUB 9000

660 REM Rename a file
670 PRINT "RENAMING FILE..."
680 OPEN 15,8,15,"R0:NEWNAME=MYDATA"
690 CLOSE 15
700 GOSUB 9000

710 REM Validate disk
720 PRINT "VALIDATING DISK..."
730 OPEN 15,8,15,"V0:"
740 CLOSE 15
750 GOSUB 9000

800 REM =====================
801 REM SECTION 6: LOAD ML PROGRAM
802 REM =====================
810 REM Load ML at its original address (SA=1)
820 LOAD "MLPROG",8,1
830 REM (after LOAD, program continues or use SYS)
840 END

900 REM =====================
901 REM SUBROUTINE: WAIT FOR KEY
902 REM =====================
910 PRINT "PRESS ANY KEY..."
920 GET K$ : IF K$="" THEN GOTO 920
930 RETURN

+ 30
- 0
examples/hello-world.asm 查看文件

@@ -0,0 +1,30 @@
; =============================================================
; hello-world.asm — Minimal C64 Assembly Hello World
; Assemble to $C000 (49152)
; Run with: POKE 56,192:CLR then SYS 49152
; =============================================================

* = $C000 ; Assemble starting at $C000

; ---------------------------------------------------------------
; Main entry point
; ---------------------------------------------------------------
START JSR $FF81 ; CINT — Initialize screen editor

; Print message using BSOUT (Kernal output routine)
LDX #0 ; X = index into message
LOOP LDA MSG,X ; load character
BEQ DONE ; zero byte = end of message
JSR $FFD2 ; BSOUT — output character to screen
INX ; next character
BNE LOOP ; (X wraps at 256, but our msg is short)

DONE RTS ; return to BASIC

; ---------------------------------------------------------------
; Message data (PETSCII)
; $0D = carriage return, $00 = end of string
; ---------------------------------------------------------------
MSG .BYTE 147 ; clear screen (CHR$(147))
.TEXT "HELLO, WORLD!"
.BYTE $0D,$00 ; CR + terminator

+ 97
- 0
examples/irq-handler.asm 查看文件

@@ -0,0 +1,97 @@
; =============================================================
; irq-handler.asm — Custom IRQ Handler Skeleton for C64
; Demonstrates: raster IRQ, custom border color flash
; Assemble to $C000. Run with SYS 49152 to install, SYS 49166 to remove.
; =============================================================

* = $C000

; ---------------------------------------------------------------
; INSTALL — Patches IRQ vector to our handler
; ---------------------------------------------------------------
INSTALL SEI ; disable interrupts while patching

; Save original IRQ vector
LDA $0314
STA OLDIRQ
LDA $0315
STA OLDIRQ+1

; Install our handler
LDA #<RASTER_IRQ
STA $0314
LDA #>RASTER_IRQ
STA $0315

; Set up VIC-II raster interrupt at line 100
LDA $D011
AND #$7F ; clear raster bit 8
STA $D011
LDA #100 ; trigger at raster line 100
STA $D012

; Enable VIC-II raster IRQ, keep CIA #1 timer IRQ enabled too
LDA #$01
STA $D01A ; VIC IRQ mask: raster

CLI ; re-enable interrupts
RTS

; ---------------------------------------------------------------
; REMOVE — Restores original IRQ vector
; ---------------------------------------------------------------
REMOVE SEI
LDA OLDIRQ
STA $0314
LDA OLDIRQ+1
STA $0315
LDA #$00
STA $D01A ; disable VIC IRQs
CLI
RTS

; ---------------------------------------------------------------
; RASTER_IRQ — Our custom interrupt handler
; Called when VIC-II raster reaches line 100
; ---------------------------------------------------------------
RASTER_IRQ:
; Acknowledge VIC-II interrupt (write-back clears flags)
LDA $D019 ; read VIC interrupt status
STA $D019 ; acknowledge by writing back

; Check if this is really a raster interrupt
AND #$01 ; bit 0 = raster IRQ
BEQ NOT_RASTER

; --- RASTER EFFECT: Flash border color ---
INC COLORIDX
LDA COLORIDX
AND #$0F ; keep in 0-15 range
STA COLORIDX
STA $D020 ; write border color

; Schedule next IRQ at line 200
LDA $D012
CMP #200
BEQ FLIP_RASTER
LDA #200
STA $D012
JMP DONE_IRQ
FLIP_RASTER:
LDA #100
STA $D012

DONE_IRQ:
NOT_RASTER:
; Chain to original IRQ for keyboard/timer/jiffy updates
JMP (OLDIRQ)

; ---------------------------------------------------------------
; Data
; ---------------------------------------------------------------
OLDIRQ .WORD $EA31 ; default IRQ handler address
COLORIDX .BYTE 0

; ---------------------------------------------------------------
; End of code
; ---------------------------------------------------------------

+ 182
- 0
examples/kernal-io.asm 查看文件

@@ -0,0 +1,182 @@
; =============================================================
; kernal-io.asm — Using Kernal Routines for I/O
; Demonstrates: screen output, keyboard input, file I/O
; Assemble to $C000. Run with SYS 49152.
; =============================================================

* = $C000

; ---------------------------------------------------------------
; MAIN — Demo entry point
; ---------------------------------------------------------------
MAIN JSR $FF81 ; CINT — init screen

; Print a string
LDA #<MSG_WELCOME
LDX #>MSG_WELCOME
JSR PRINT_STR

; Read a line of input
JSR READ_LINE ; result in INBUF, length in INLEN

; Echo it back
LDA #<MSG_YOU_TYPED
LDX #>MSG_YOU_TYPED
JSR PRINT_STR

LDX #0
ECHO LDA INBUF,X
BEQ ECHO_DONE
JSR $FFD2 ; BSOUT
INX
BNE ECHO

ECHO_DONE:
LDA #$0D
JSR $FFD2 ; print CR

; Write a file to disk
JSR WRITE_FILE

RTS

; ---------------------------------------------------------------
; PRINT_STR — Print null-terminated string
; Input: A = low byte, X = high byte of string address
; ---------------------------------------------------------------
PRINT_STR:
STA $FB ; store address in zero page
STX $FC
LDY #0
PS_LOOP LDA ($FB),Y ; load character
BEQ PS_DONE ; zero = end
JSR $FFD2 ; BSOUT — output to screen
INY
BNE PS_LOOP
PS_DONE RTS

; ---------------------------------------------------------------
; READ_LINE — Read keyboard input until RETURN
; Output: INBUF filled, INLEN = number of chars read
; ---------------------------------------------------------------
READ_LINE:
LDX #0 ; buffer index
RL_LOOP JSR $FFE4 ; GETIN — get character (non-blocking)
BEQ RL_LOOP ; no key, keep waiting
CMP #$0D ; RETURN key?
BEQ RL_DONE
CMP #$14 ; DELETE key?
BEQ RL_DEL
CPX #79 ; buffer full?
BEQ RL_LOOP
STA INBUF,X ; store character
JSR $FFD2 ; echo to screen
INX
BNE RL_LOOP
RL_DEL CPX #0
BEQ RL_LOOP ; nothing to delete
DEX
LDA #$14
JSR $FFD2 ; output DELETE
BNE RL_LOOP
RL_DONE LDA #0
STA INBUF,X ; null terminate
STX INLEN
LDA #$0D
JSR $FFD2 ; echo CR
RTS

; ---------------------------------------------------------------
; WRITE_FILE — Write data to a sequential disk file
; ---------------------------------------------------------------
WRITE_FILE:
; SETLFS: logical=2, device=8, secondary=2
LDA #2
LDX #8
LDY #2
JSR $FFBA ; SETLFS

; SETNAM: filename "OUTPUT,S,W"
LDA #OUTNAME_LEN
LDX #<OUTNAME
LDY #>OUTNAME
JSR $FFBD ; SETNAM

; OPEN
JSR $FFC0 ; OPEN
BCS WF_OERR ; error if carry set

; CHKOUT: redirect output to file
LDX #2
JSR $FFC9 ; CHKOUT
BCS WF_CERR

; Write data
LDX #0
WF_LOOP LDA FILEDATA,X
BEQ WF_DONE
JSR $FFD2 ; BSOUT to file
INX
BNE WF_LOOP

WF_DONE JSR $FFCC ; CLRCH — restore default channels
LDA #2
JSR $FFC3 ; CLOSE

; Check disk error channel
JSR $FFBA ; reuse: SETLFS 15,8,15
; Actually let's just print success
LDA #<MSG_SAVED
LDX #>MSG_SAVED
JMP PRINT_STR

WF_OERR LDA #<MSG_OERR
LDX #>MSG_OERR
JMP PRINT_STR

WF_CERR LDA #<MSG_CERR
LDX #>MSG_CERR
JMP PRINT_STR

; ---------------------------------------------------------------
; Data
; ---------------------------------------------------------------
MSG_WELCOME:
.BYTE 147 ; clear screen
.TEXT "KERNAL I/O DEMO"
.BYTE $0D
.TEXT "TYPE SOMETHING AND PRESS RETURN:"
.BYTE $0D,0

MSG_YOU_TYPED:
.TEXT "YOU TYPED: "
.BYTE 0

MSG_SAVED:
.BYTE $0D
.TEXT "FILE WRITTEN TO DISK."
.BYTE $0D,0

MSG_OERR:
.TEXT "ERROR: COULD NOT OPEN FILE"
.BYTE $0D,0

MSG_CERR:
.TEXT "ERROR: COULD NOT REDIRECT OUTPUT"
.BYTE $0D,0

OUTNAME:
.TEXT "OUTPUT,S,W"
OUTNAME_LEN = * - OUTNAME

FILEDATA:
.TEXT "DATA LINE 1"
.BYTE $0D
.TEXT "DATA LINE 2"
.BYTE $0D,0

; ---------------------------------------------------------------
; Variables
; ---------------------------------------------------------------
INBUF .FILL 80,0 ; 80-byte input buffer
INLEN .BYTE 0 ; length of input

+ 76
- 0
examples/sound-demo.bas 查看文件

@@ -0,0 +1,76 @@
1 REM =============================================
2 REM SOUND-DEMO.BAS — C64 SID Chip Sound Demo
3 REM Demonstrates: 3-voice music, ADSR, waveforms
4 REM =============================================
10 SID = 54272 : REM SID base ($D400)
20 VOL = SID+24 : REM master volume ($D418)
30 POKE VOL, 15 : REM Volume = 15 (maximum)

100 REM --- PLAY A SIMPLE MELODY (Voice 1) ---
110 REM Frequency values for NTSC C64
120 REM C4=4313, D4=4842, E4=5431, F4=5760, G4=6433, A4=7372, B4=7992, C5=8627

130 DIM NOTE%(7)
140 NOTE%(0) = 4313 : REM C4
150 NOTE%(1) = 4842 : REM D4
160 NOTE%(2) = 5431 : REM E4
170 NOTE%(3) = 5760 : REM F4
180 NOTE%(4) = 6433 : REM G4
190 NOTE%(5) = 7372 : REM A4
200 NOTE%(6) = 7992 : REM B4

210 REM ADSR for voice 1: fast attack, short decay, sustain 12, medium release
220 POKE SID+5, 9 : REM attack=0(2ms), decay=9(250ms)
230 POKE SID+6, 192 : REM sustain=12, release=0

240 REM Sawtooth waveform
250 WAVEFORM = 32 : REM %00100000 = sawtooth

300 REM --- MELODY LOOP ---
310 FOR I = 0 TO 6
320 F = NOTE%(I)
330 POKE SID, F AND 255 : REM frequency low
340 POKE SID+1, INT(F/256) : REM frequency high
350 POKE SID+4, WAVEFORM OR 1 : REM start note (gate on)
360 FOR W = 1 TO 150 : NEXT : REM hold note
370 POKE SID+4, WAVEFORM : REM release (gate off)
380 FOR W = 1 TO 50 : NEXT : REM short gap
390 NEXT I

400 REM --- 3-VOICE CHORD (C major) ---
410 PRINT "PLAYING C MAJOR CHORD..."
420 REM Voice 1: C4
430 POKE SID, 4313 AND 255 : POKE SID+1, INT(4313/256)
440 POKE SID+5, 2 : POKE SID+6, 240 : REM slow attack, sustain 15
450 POKE SID+4, 33 : REM triangle + gate

460 REM Voice 2: E4
470 POKE SID+7, 5431 AND 255 : POKE SID+8, INT(5431/256)
480 POKE SID+12, 2 : POKE SID+13, 240
490 POKE SID+11, 17 : REM triangle + gate (voice 2 control = SID+11)

500 REM Voice 3: G4
510 POKE SID+14, 6433 AND 255 : POKE SID+15, INT(6433/256)
520 POKE SID+19, 2 : POKE SID+20, 240
530 POKE SID+18, 17 : REM triangle + gate (voice 3 control = SID+18)

540 FOR W = 1 TO 500 : NEXT : REM hold chord

550 REM Release all voices
560 POKE SID+4, 16 : REM voice 1 release
570 POKE SID+11, 16 : REM voice 2 release
580 POKE SID+18, 16 : REM voice 3 release
590 FOR W = 1 TO 300 : NEXT

600 REM --- NOISE EFFECT (explosion) ---
610 PRINT "EXPLOSION SOUND..."
620 POKE SID+5, 0 : POKE SID+6, 0 : REM instant attack+decay+release
630 POKE SID, 255 : POKE SID+1, 255 : REM high frequency for noise
640 POKE SID+4, 129 : REM noise + gate (%10000001)
650 FOR W = 1 TO 100 : NEXT
660 POKE SID+4, 128 : REM gate off

700 REM --- CLEAN UP ---
710 FOR W = 1 TO 200 : NEXT : REM wait for release
720 POKE VOL, 0 : REM silence
730 PRINT "DONE."

+ 68
- 0
examples/sprite-demo.bas 查看文件

@@ -0,0 +1,68 @@
1 REM ============================================
2 REM SPRITE-DEMO.BAS - C64 Hardware Sprite Demo
3 REM Demonstrates: sprite setup, movement,
4 REM color, enable/disable
5 REM ============================================
10 REM --- CONSTANTS ---
20 SP = 53248 : REM VIC-II base ($D000)
30 SPENA = SP+21 : REM sprite enable register ($D015)
40 EXTCOL = SP+32 : REM border color ($D020)
50 BGCOL = SP+33 : REM background color ($D021)
60 SPPTR = 2040 : REM sprite 0 pointer (screen RAM + $03F8)

100 REM --- SETUP ---
110 POKE BGCOL, 0 : REM black background
120 POKE EXTCOL, 0 : REM black border
130 PRINT CHR$(147) : REM clear screen

200 REM --- DEFINE SPRITE SHAPE (block 13 = $0340) ---
210 FOR I = 832 TO 894 : POKE I, 0 : NEXT : REM clear block 13

220 REM Draw a simple arrow shape (24 pixels wide x 21 rows)
230 REM Each row = 3 bytes
240 DATA 0,60,0,0,126,0,0,255,0,1,255,128
250 DATA 3,255,192,7,255,224,0,60,0,0,60,0
260 DATA 0,60,0,0,60,0,0,60,0,0,60,0
270 DATA 0,60,0,0,60,0,0,60,0,0,60,0
280 DATA 0,60,0,0,60,0,0,60,0,0,60,0
290 DATA 0,60,0

300 FOR I = 0 TO 62
310 READ D : POKE 832+I, D
320 NEXT I

400 REM --- POINT SPRITE 0 TO BLOCK 13 ---
410 POKE SPPTR, 13 : REM sprite 0 uses block 13

420 REM --- SET SPRITE COLOR ---
430 POKE SP+39, 7 : REM sprite 0 color = yellow ($D027)

440 REM --- ENABLE SPRITE 0 ---
450 POKE SPENA, 1 : REM enable sprite 0 bit 0

500 REM --- ANIMATE: BOUNCE SPRITE ACROSS SCREEN ---
510 X = 40 : Y = 100 : DX = 2 : DY = 1

520 FOR F = 1 TO 500
530 X = X + DX : Y = Y + DY

540 REM Bounce off edges
550 IF X < 24 OR X > 294 THEN DX = -DX
560 IF Y < 50 OR Y > 220 THEN DY = -DY

570 REM Set position
580 IF X > 255 THEN POKE SP+16,(PEEK(SP+16) OR 1) : POKE SP,X-256
590 IF X <= 255 THEN POKE SP+16,(PEEK(SP+16) AND 254) : POKE SP,X
600 POKE SP+1, Y : REM Y position

610 REM Cycle border color every 10 frames
620 IF (F AND 15) = 0 THEN POKE EXTCOL, (PEEK(EXTCOL)+1) AND 15

630 NEXT F

700 REM --- CLEAN UP ---
710 POKE SPENA, 0 : REM disable all sprites
720 POKE BGCOL, 6 : REM restore blue background
730 POKE EXTCOL, 14 : REM restore light blue border
740 PRINT "DONE."
750 END

+ 74
- 0
examples/sprite-music-demo.bas 查看文件

@@ -0,0 +1,74 @@
1 REM =============================================
2 REM MULTICOLOR SPRITE + MUSIC DEMO
3 REM Moves a multicolor sprite while playing a
4 REM continuous bass line via the SID chip.
5 REM =============================================
10 GOSUB 1000: REM INIT SID
20 GOSUB 2000: REM INIT SPRITE
30 REM === MAIN LOOP ===
40 FOR I=0 TO 360 STEP 2
50 X=160+INT(100*SIN(I*3.14159/180))
60 Y=120+INT(70*COS(I*3.14159/180))
70 POKE 53248,X AND 255 : REM SPRITE 0 X LOW
80 LO=PEEK(53264) AND 254
90 IF X>255 THEN LO=LO OR 1
100 POKE 53264,LO : REM SPRITE 0 X HIGH BIT
110 POKE 53249,Y : REM SPRITE 0 Y
120 N=INT(I/30) AND 7
130 GOSUB 3000 : REM PLAY NOTE N
140 FOR W=1 TO 200:NEXT W : REM DELAY
150 NEXT I
160 GOSUB 4000: REM SILENCE
170 END

1000 REM ===== INIT SID =====
1010 FOR A=54272 TO 54296:POKE A,0:NEXT A : REM SILENCE ALL
1020 POKE 54296,15 : REM MAX VOLUME
1030 POKE 54272,0:POKE 54273,40 : REM VOICE 1 FREQ ~440Hz (A4)
1040 POKE 54277,9 : REM ATTACK=0 DECAY=9
1050 POKE 54278,240 : REM SUSTAIN=15 RELEASE=0
1060 RETURN

2000 REM ===== INIT SPRITE =====
2010 REM --- define multicolor ship sprite at $2000 (pointer $80) ---
2020 BASE=8192: REM $2000
2030 DATA 0,126,0, 0,255,128, 1,219,192, 3,189,224
2040 DATA 7,255,240, 15,255,248, 31,255,252, 63,255,254
2050 DATA 127,255,255, 127,255,255, 63,255,254, 31,255,252
2060 DATA 15,255,248, 7,255,240, 3,255,224, 1,219,192
2070 DATA 0,255,128, 0,126,0, 0,60,0, 0,24,0, 0,0,0
2080 FOR J=0 TO 62: READ D: POKE BASE+J,D: NEXT J
2090 POKE 2040,128 : REM SPRITE 0 POINTER = $80 (= $2000)
2100 POKE 53287,10 : REM SPRITE 0 COLOR = LIGHT RED
2110 POKE 53285,1 : REM MULTICOLOR #1 = WHITE
2120 POKE 53286,2 : REM MULTICOLOR #2 = RED
2130 POKE 53276,PEEK(53276) OR 1 : REM SPRITE 0 MULTICOLOR ON
2140 POKE 53277,PEEK(53277) OR 1 : REM SPRITE 0 X-EXPAND
2150 POKE 53271,PEEK(53271) OR 1 : REM SPRITE 0 Y-EXPAND
2160 POKE 53269,1 : REM ENABLE SPRITE 0
2170 RETURN

3000 REM ===== PLAY NOTE N =====
3010 REM FREQUENCY TABLE (lo,hi) for C major scale
3020 REM C4=4291, D4=4817, E4=5405, F4=5727, G4=6428, A4=7213, B4=8097, C5=8583
3030 FL=0:FH=0
3040 IF N=0 THEN FL=195:FH=16 : REM C4
3050 IF N=1 THEN FL=209:FH=18 : REM D4
3060 IF N=2 THEN FL=29:FH=21 : REM E4
3070 IF N=3 THEN FL=95:FH=22 : REM F4
3080 IF N=4 THEN FL=28:FH=25 : REM G4
3090 IF N=5 THEN FL=45:FH=28 : REM A4
3100 IF N=6 THEN FL=161:FH=31 : REM B4
3110 IF N=7 THEN FL=7:FH=33 : REM C5
3120 POKE 54272,FL : REM SET FREQ LOW
3130 POKE 54273,FH : REM SET FREQ HIGH
3140 POKE 54276,33 : REM TRIANGLE WAVE + GATE ON
3150 RETURN

4000 REM ===== SILENCE =====
4010 POKE 54276,32 : REM GATE OFF (RELEASE)
4020 FOR W=1 TO 500:NEXT W
4030 POKE 54276,0
4040 POKE 54296,0 : REM VOLUME OFF
4050 POKE 53269,0 : REM SPRITES OFF
4060 RETURN

+ 14
- 0
examples/vice-autostart-c64os.bat 查看文件

@@ -0,0 +1,14 @@
@echo off
setlocal

set "VICE_EXE=C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe"

if not exist "%VICE_EXE%" (
echo x64sc.exe not found at "%VICE_EXE%"
exit /b 1
)

call "%~dp0..\build.bat"
if errorlevel 1 exit /b 1

"%VICE_EXE%" -autostart "%~dp0..\c64os.prg" -autostart-warp

+ 22
- 0
examples/vice-debug-c64os.bat 查看文件

@@ -0,0 +1,22 @@
@echo off
setlocal

set "VICE_EXE=C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe"
set "MONLOG=%~dp0..\vice-monitor.log"

if not exist "%VICE_EXE%" (
echo x64sc.exe not found at "%VICE_EXE%"
exit /b 1
)

call "%~dp0..\build.bat"
if errorlevel 1 exit /b 1

"%VICE_EXE%" ^
-autostart "%~dp0..\c64os.prg" ^
-autostart-warp ^
-nativemonitor ^
-keepmonopen ^
-refreshonbreak ^
-monlog ^
-monlogname "%MONLOG%"

+ 37
- 0
examples/vice-monitor-c64os-debug.txt 查看文件

@@ -0,0 +1,37 @@
; Full VICE debug session for c64os.prg
; Launch with examples/vice-debug-c64os.bat, then in the monitor run:
; playback "examples/vice-monitor-c64os-debug.txt"

radix H
device c:
sidefx off

; Give useful labels to the known startup locations.
add_label $0801 .basic_stub
add_label $0810 .start

; Break when BASIC transfers control to the machine code entry.
break exec .start

; Watch memory banking changes and dump the CPU port state when they happen.
watch store $0001
command 2 "m $0000 $0002"

; Watch IRQ vector changes in case startup or later code patches them.
watch store $0314 $0315

; Show the BASIC stub, startup code, and current CPU port state.
d .basic_stub $0840
m $0000 $0002
r

; Helpful follow-up commands once the first breakpoint triggers:
; step ; step into the next instruction
; next ; step over KERNAL calls
; return ; run until the current subroutine exits
; bt ; show JSR call chain
; io $d000 ; inspect VIC-II registers
; io $dc00 ; inspect CIA #1 registers
; keybuf "help\x0d" ; inject a HELP command into the shell

stop

+ 20
- 0
examples/vice-monitor-c64os.txt 查看文件

@@ -0,0 +1,20 @@
; VICE monitor playback file for c64os.prg
; Usage inside the VICE monitor:
; playback "examples/vice-monitor-c64os.txt"

radix H
device c:
sidefx off

; Break when the BASIC stub transfers control to the machine code entry.
break exec $0810

; Watch for writes to the 6510 CPU port so banking changes are visible.
watch store $0001

; Show the BASIC stub, start of code, and current banking registers.
d $0801 $0840
m $0000 $0002

; Stop playback so the user or agent can resume with X when ready.
stop

+ 404
- 0
reference/6510-opcodes.md 查看文件

@@ -0,0 +1,404 @@
# 6510 / 6502 Complete Opcode Reference
> Source: The Machine Language Book for the C64; The Advanced Machine Language Book for the C64

Flags: **N**egative · o**V**erflow · **B**reak · **D**ecimal · **I**nterrupt · **Z**ero · **C**arry
Column order: Opcode | Hex | Bytes | Cycles | Flags affected

---

## Addressing Mode Key

| Symbol | Mode | Example |
|--------------|------------------|-----------------|
| imm | Immediate | LDA #$0A |
| zp | Zero Page | LDA $10 |
| zp,X | Zero Page,X | LDA $10,X |
| zp,Y | Zero Page,Y | LDA $10,Y |
| abs | Absolute | LDA $1234 |
| abs,X | Absolute,X | LDA $1234,X |
| abs,Y | Absolute,Y | LDA $1234,Y |
| ind | Indirect | JMP ($1234) |
| (zp,X) | Indexed Indirect | LDA ($10,X) |
| (zp),Y | Indirect Indexed | LDA ($10),Y |
| rel | Relative (branch)| BNE label |
| impl | Implied | CLC |
| A | Accumulator | LSR A |

*+1 cycle if page boundary crossed; +2 cycles for branch taken + page cross.*

---

## Load / Store

### LDA — Load Accumulator `N Z`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $A9 | 2 | 2 |
| zp | $A5 | 2 | 3 |
| zp,X | $B5 | 2 | 4 |
| abs | $AD | 3 | 4 |
| abs,X | $BD | 3 | 4+1 |
| abs,Y | $B9 | 3 | 4+1 |
| (zp,X) | $A1 | 2 | 6 |
| (zp),Y | $B1 | 2 | 5+1 |

### LDX — Load X Register `N Z`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $A2 | 2 | 2 |
| zp | $A6 | 2 | 3 |
| zp,Y | $B6 | 2 | 4 |
| abs | $AE | 3 | 4 |
| abs,Y | $BE | 3 | 4+1 |

### LDY — Load Y Register `N Z`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $A0 | 2 | 2 |
| zp | $A4 | 2 | 3 |
| zp,X | $B4 | 2 | 4 |
| abs | $AC | 3 | 4 |
| abs,X | $BC | 3 | 4+1 |

### STA — Store Accumulator *(no flags)*
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| zp | $85 | 2 | 3 |
| zp,X | $95 | 2 | 4 |
| abs | $8D | 3 | 4 |
| abs,X | $9D | 3 | 5 |
| abs,Y | $99 | 3 | 5 |
| (zp,X) | $81 | 2 | 6 |
| (zp),Y | $91 | 2 | 6 |

### STX — Store X *(no flags)*
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| zp | $86 | 2 | 3 |
| zp,Y | $96 | 2 | 4 |
| abs | $8E | 3 | 4 |

### STY — Store Y *(no flags)*
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| zp | $84 | 2 | 3 |
| zp,X | $94 | 2 | 4 |
| abs | $8C | 3 | 4 |

---

## Register Transfers `N Z`

| Mnemonic | Hex | Bytes | Cycles | Operation |
|----------|------|-------|--------|------------------|
| TAX | $AA | 1 | 2 | A → X |
| TAY | $A8 | 1 | 2 | A → Y |
| TXA | $8A | 1 | 2 | X → A |
| TYA | $98 | 1 | 2 | Y → A |
| TSX | $BA | 1 | 2 | SP → X (N Z) |
| TXS | $9A | 1 | 2 | X → SP *(none)* |

---

## Stack Operations

| Mnemonic | Hex | Bytes | Cycles | Flags | Operation |
|----------|------|-------|--------|-------|-------------------|
| PHA | $48 | 1 | 3 | — | Push A to stack |
| PLA | $68 | 1 | 4 | N Z | Pull A from stack |
| PHP | $08 | 1 | 3 | — | Push SR to stack |
| PLP | $28 | 1 | 4 | all | Pull SR from stack|

---

## Arithmetic

### ADC — Add with Carry `N V Z C`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $69 | 2 | 2 |
| zp | $65 | 2 | 3 |
| zp,X | $75 | 2 | 4 |
| abs | $6D | 3 | 4 |
| abs,X | $7D | 3 | 4+1 |
| abs,Y | $79 | 3 | 4+1 |
| (zp,X) | $61 | 2 | 6 |
| (zp),Y | $71 | 2 | 5+1 |

### SBC — Subtract with Borrow `N V Z C`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $E9 | 2 | 2 |
| zp | $E5 | 2 | 3 |
| zp,X | $F5 | 2 | 4 |
| abs | $ED | 3 | 4 |
| abs,X | $FD | 3 | 4+1 |
| abs,Y | $F9 | 3 | 4+1 |
| (zp,X) | $E1 | 2 | 6 |
| (zp),Y | $F1 | 2 | 5+1 |

---

## Increment / Decrement

| Mnemonic | Hex | Mode | Bytes | Cycles | Flags |
|----------|------|-------|-------|--------|-------|
| INC | $E6 | zp | 2 | 5 | N Z |
| INC | $F6 | zp,X | 2 | 6 | N Z |
| INC | $EE | abs | 3 | 6 | N Z |
| INC | $FE | abs,X | 3 | 7 | N Z |
| DEC | $C6 | zp | 2 | 5 | N Z |
| DEC | $D6 | zp,X | 2 | 6 | N Z |
| DEC | $CE | abs | 3 | 6 | N Z |
| DEC | $DE | abs,X | 3 | 7 | N Z |
| INX | $E8 | impl | 1 | 2 | N Z |
| INY | $C8 | impl | 1 | 2 | N Z |
| DEX | $CA | impl | 1 | 2 | N Z |
| DEY | $88 | impl | 1 | 2 | N Z |

---

## Logical Operations `N Z`

### AND
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $29 | 2 | 2 |
| zp | $25 | 2 | 3 |
| zp,X | $35 | 2 | 4 |
| abs | $2D | 3 | 4 |
| abs,X | $3D | 3 | 4+1 |
| abs,Y | $39 | 3 | 4+1 |
| (zp,X) | $21 | 2 | 6 |
| (zp),Y | $31 | 2 | 5+1 |

### ORA
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $09 | 2 | 2 |
| zp | $05 | 2 | 3 |
| zp,X | $15 | 2 | 4 |
| abs | $0D | 3 | 4 |
| abs,X | $1D | 3 | 4+1 |
| abs,Y | $19 | 3 | 4+1 |
| (zp,X) | $01 | 2 | 6 |
| (zp),Y | $11 | 2 | 5+1 |

### EOR
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $49 | 2 | 2 |
| zp | $45 | 2 | 3 |
| zp,X | $55 | 2 | 4 |
| abs | $4D | 3 | 4 |
| abs,X | $5D | 3 | 4+1 |
| abs,Y | $59 | 3 | 4+1 |
| (zp,X) | $41 | 2 | 6 |
| (zp),Y | $51 | 2 | 5+1 |

---

## Shift & Rotate `N Z C`

| Mnemonic | Hex | Mode | Bytes | Cycles | Operation |
|----------|------|-------|-------|--------|-----------------------|
| ASL | $0A | A | 1 | 2 | Left shift, 0→bit0 |
| ASL | $06 | zp | 2 | 5 | |
| ASL | $16 | zp,X | 2 | 6 | |
| ASL | $0E | abs | 3 | 6 | |
| ASL | $1E | abs,X | 3 | 7 | |
| LSR | $4A | A | 1 | 2 | Right shift, 0→bit7 |
| LSR | $46 | zp | 2 | 5 | |
| LSR | $56 | zp,X | 2 | 6 | |
| LSR | $4E | abs | 3 | 6 | |
| LSR | $5E | abs,X | 3 | 7 | |
| ROL | $2A | A | 1 | 2 | Left rotate thru C |
| ROL | $26 | zp | 2 | 5 | |
| ROL | $36 | zp,X | 2 | 6 | |
| ROL | $2E | abs | 3 | 6 | |
| ROL | $3E | abs,X | 3 | 7 | |
| ROR | $6A | A | 1 | 2 | Right rotate thru C |
| ROR | $66 | zp | 2 | 5 | |
| ROR | $76 | zp,X | 2 | 6 | |
| ROR | $6E | abs | 3 | 6 | |
| ROR | $7E | abs,X | 3 | 7 | |

---

## Compare `N Z C`

### CMP — Compare Accumulator
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $C9 | 2 | 2 |
| zp | $C5 | 2 | 3 |
| zp,X | $D5 | 2 | 4 |
| abs | $CD | 3 | 4 |
| abs,X | $DD | 3 | 4+1 |
| abs,Y | $D9 | 3 | 4+1 |
| (zp,X) | $C1 | 2 | 6 |
| (zp),Y | $D1 | 2 | 5+1 |

### CPX — Compare X `N Z C`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $E0 | 2 | 2 |
| zp | $E4 | 2 | 3 |
| abs | $EC | 3 | 4 |

### CPY — Compare Y `N Z C`
| Mode | Hex | Bytes | Cycles |
|---------|------|-------|--------|
| imm | $C0 | 2 | 2 |
| zp | $C4 | 2 | 3 |
| abs | $CC | 3 | 4 |

### BIT — Bit Test `N V Z`
| Mode | Hex | Bytes | Cycles | Notes |
|---------|------|-------|--------|------------------------------|
| zp | $24 | 2 | 3 | N←bit7, V←bit6, Z←A AND mem |
| abs | $2C | 3 | 4 | |

---

## Branch Instructions *(2 bytes, rel offset, +1 cycle taken, +2 if page cross)*

| Mnemonic | Hex | Condition |
|----------|----- |--------------------|
| BCC | $90 | C = 0 (no carry) |
| BCS | $B0 | C = 1 (carry set) |
| BEQ | $F0 | Z = 1 (equal/zero) |
| BNE | $D0 | Z = 0 (not equal) |
| BMI | $30 | N = 1 (minus) |
| BPL | $10 | N = 0 (plus) |
| BVC | $50 | V = 0 (no overflow)|
| BVS | $70 | V = 1 (overflow) |

Branch offset range: **-128 to +127 bytes** from instruction after branch.

---

## Jump & Subroutine

| Mnemonic | Hex | Mode | Bytes | Cycles | Notes |
|----------|------|-------|-------|--------|---------------------------------|
| JMP | $4C | abs | 3 | 3 | Unconditional jump |
| JMP | $6C | ind | 3 | 5 | Indirect; **6502 page bug!** |
| JSR | $20 | abs | 3 | 6 | Push PC-1, jump |
| RTS | $60 | impl | 1 | 6 | Pull PC+1, return |
| RTI | $40 | impl | 1 | 6 | Pull SR then PC (for IRQ/NMI) |
| BRK | $00 | impl | 1 | 7 | Software IRQ; pushes PC+2 + SR |

**6502 JMP indirect bug:** JMP ($10FF) fetches low byte from $10FF, high byte from $1000 (not $1100). Never use indirect JMP across page boundary.

---

## Flag Instructions

| Mnemonic | Hex | Bytes | Cycles | Effect |
|----------|------|-------|--------|--------------------|
| CLC | $18 | 1 | 2 | Clear carry |
| SEC | $38 | 1 | 2 | Set carry |
| CLD | $D8 | 1 | 2 | Clear decimal |
| SED | $F8 | 1 | 2 | Set decimal |
| CLI | $58 | 1 | 2 | Clear IRQ disable |
| SEI | $78 | 1 | 2 | Set IRQ disable |
| CLV | $B8 | 1 | 2 | Clear overflow |

---

## No-Operation

| Mnemonic | Hex | Bytes | Cycles | Notes |
|----------|------|-------|--------|-------------------|
| NOP | $EA | 1 | 2 | Do nothing; timing|

---

## Common Idioms & Patterns

### 16-bit Add
```asm
CLC
LDA low1
ADC low2
STA result_lo
LDA high1
ADC high2
STA result_hi
```

### 16-bit Subtract
```asm
SEC
LDA low1
SBC low2
STA result_lo
LDA high1
SBC high2
STA result_hi
```

### Multiply by 2 (16-bit)
```asm
ASL low_byte
ROL high_byte
```

### Test specific bit (bit 3)
```asm
LDA value
AND #%00001000
BEQ bit_was_clear
```

### Set specific bit
```asm
LDA value
ORA #%00001000
STA value
```

### Clear specific bit
```asm
LDA value
AND #%11110111
STA value
```

### Toggle bit
```asm
LDA value
EOR #%00001000
STA value
```

### Efficient loop (downward)
```asm
LDX #100
loop:
; ... body ...
DEX
BNE loop
```

### Indirect indexed store (table write)
```asm
LDA #<table ; store table address
STA ptr
LDA #>table
STA ptr+1
LDY #offset
LDA value
STA (ptr),Y
```

---

## Cycle Counting Notes

- 1 cycle = ~1.02 µs (PAL) or ~0.98 µs (NTSC)
- 1 scan line = 63 cycles (NTSC) / 63 cycles (PAL, slightly different)
- 1 frame = 262 lines NTSC / 312 lines PAL
- VIC-II steals cycles during sprite DMA and bad lines — account for this in cycle-critical raster code
- **Bad lines** occur when (raster & 7) = (YSCROLL & 7) — VIC takes 40 extra cycles

+ 153
- 0
reference/cia-registers.md 查看文件

@@ -0,0 +1,153 @@
# CIA 6526 Register Reference

Both CIA chips use the same register layout. CIA #1 = $DC00, CIA #2 = $DD00.

## Register Map (offset from chip base)

| Offset | CIA#1 Addr | CIA#2 Addr | Name | Description |
|--------|------------|------------|------|-------------|
| +0 | $DC00 | $DD00 | PRA | Port A data register |
| +1 | $DC01 | $DD01 | PRB | Port B data register |
| +2 | $DC02 | $DD02 | DDRA | Port A direction (1=output) |
| +3 | $DC03 | $DD03 | DDRB | Port B direction (1=output) |
| +4 | $DC04 | $DD04 | TIMALO | Timer A low byte (latch/counter) |
| +5 | $DC05 | $DD05 | TIMAHI | Timer A high byte |
| +6 | $DC06 | $DD06 | TIMBLO | Timer B low byte |
| +7 | $DC07 | $DD07 | TIMBHI | Timer B high byte |
| +8 | $DC08 | $DD08 | TODTEN | TOD tenths of seconds (BCD) |
| +9 | $DC09 | $DD09 | TODSEC | TOD seconds (BCD) |
| +A | $DC0A | $DD0A | TODMIN | TOD minutes (BCD) |
| +B | $DC0B | $DD0B | TODHR | TOD hours + AM/PM (bit 7) |
| +C | $DC0C | $DD0C | SDR | Serial data register |
| +D | $DC0D | $DD0D | ICR | Interrupt control register |
| +E | $DC0E | $DD0E | CRA | Control register A |
| +F | $DC0F | $DD0F | CRB | Control register B |

## CIA #1 ($DC00) — Keyboard and Joystick

### Port A ($DC00) — Keyboard Column Select / Joystick 2
- Writing to $DC00 selects keyboard columns to scan (0=select)
- Reading $DC00 gives joystick 2 state (bits 0-4, active low)

### Port B ($DC01) — Keyboard Row Read / Joystick 1
- Reading $DC01 returns keyboard row data for selected column
- Also joystick 1 state (bits 0-4, active low)

### Keyboard Matrix
The keyboard is a 8×8 matrix. To read a key:
1. Write column mask to $DC00 (0 selects that column)
2. Read rows from $DC01

| Bit set in $DC00 column | Keys in that column |
|------------------------|---------------------|
| $FE (col 0) | DEL, RETURN, ←→, F7, F1, F3, F5, ↑↓ |
| $FD (col 1) | 3, W, A, 4, Z, S, E, Left Shift |
| $FB (col 2) | 5, R, D, 6, C, F, T, X |
| $F7 (col 3) | 7, Y, G, 8, B, H, U, V |
| $EF (col 4) | 9, I, J, 0, M, K, O, N |
| $DF (col 5) | +, P, L, -, ., :, @, , |
| $BF (col 6) | £, *, ;, Home, Right Shift, =, ↑, / |
| $7F (col 7) | 1, ←, CTRL, 2, Space, C=, Q, Stop |

### Joystick Bits ($DC00 joystick 2 / $DC01 joystick 1)
```
Bit 0: Up (0=pressed)
Bit 1: Down (0=pressed)
Bit 2: Left (0=pressed)
Bit 3: Right (0=pressed)
Bit 4: Fire (0=pressed)
```

```basic
J = PEEK(56321) ' Read joystick 1 ($DC01)
IF (J AND 16) = 0 THEN PRINT "FIRE!"
IF (J AND 1) = 0 THEN PRINT "UP!"
```

## CIA #2 ($DD00) — Serial Bus and VIC-II Bank

### Port A ($DD00) — Serial Bus / VIC-II Bank
```
Bit 7: Serial bus data line (input)
Bit 6: Serial bus clock line (input)
Bit 5: Serial bus data line (output)
Bit 4: Serial bus attention ACK (output)
Bit 3: Serial bus clock (output)
Bit 2: RS-232 data output (User Port)
Bit 1: VIC-II bank select bit 1 \ Together: 00=bank3, 01=bank2
Bit 0: VIC-II bank select bit 0 / 10=bank1, 11=bank0(default)
```

```basic
' Switch VIC-II to Bank 1 ($4000-$7FFF)
POKE 56576, (PEEK(56576) AND 252) OR 2
' Bank 0 (default): POKE 56576, (PEEK(56576) AND 252) OR 3
```

## Control Register A (CRA — $DC0E / $DD0E)

```
Bit 7: TODIN — TOD clock: 1=60Hz, 0=50Hz
Bit 6: SPMODE — Serial port: 1=output, 0=input
Bit 5: INMODE — Timer A input: 1=CNT pulses, 0=system clock
Bit 4: LOAD — Force load timer (write 1 to reload latch into counter)
Bit 3: RUNMODE — 0=continuous, 1=one-shot
Bit 2: OUTMODE — Timer A output to PB6: 1=pulse, 0=toggle
Bit 1: PBON — Port B output enable (1=Timer A appears on PB6)
Bit 0: START — 1=start timer, 0=stop timer
```

## Control Register B (CRB — $DC0F / $DD0F)

```
Bit 7: ALARM — 1=write TOD alarm, 0=write TOD clock
Bit 6: INMODE (high bit) — with bit 5: timer B source
Bit 5: INMODE (low bit) — 00=φ2 clock, 01=CNT, 10=Timer A, 11=TimA/CNT
Bit 4: LOAD — Force load
Bit 3: RUNMODE — 0=continuous, 1=one-shot
Bit 2: OUTMODE — Timer B output to PB7
Bit 1: PBON — Port B output enable
Bit 0: START — 1=start, 0=stop
```

## Interrupt Control Register ($DC0D / $DD0D)

**Reading**: Returns interrupt flags (1=occurred), clears all flags on read
**Writing**: Sets/clears interrupt enables
```
Bit 7: (read) IRQ occurred (any source) / (write) 1=enable, 0=disable bits 0-4
Bit 4: FLG — FLAG pin interrupt
Bit 3: SP — Serial port interrupt
Bit 2: ALRM — TOD alarm interrupt
Bit 1: TB — Timer B interrupt
Bit 0: TA — Timer A interrupt
```

## Timer Calculations

Timer counts down from latch value to 0, then triggers interrupt and reloads.

```
Timer Period = (latch value + 1) / clock_frequency

NTSC: clock = 1,022,727 Hz
PAL: clock = 985,248 Hz

For 1/60 second at NTSC:
latch = 1,022,727 / 60 - 1 = 17,044 = $4294

For 1/50 second at PAL:
latch = 985,248 / 50 - 1 = 19,704 = $4CB8
```

```asm
; Set CIA #1 Timer A for ~1/60 second, continuous IRQ
LDA #$94 ; $4294 low byte
STA $DC04
LDA #$42 ; high byte
STA $DC05
LDA #$81 ; enable Timer A IRQ
STA $DC0D
LDA #$11 ; start timer, continuous, system clock
STA $DC0E
```

+ 395
- 0
reference/disk-dos-commands.md 查看文件

@@ -0,0 +1,395 @@
# Commodore 1541 Disk DOS — Complete Command Reference
> Source: The Anatomy of the 1541 Disk Drive (Revised); Compute's Mapping the C64

---

## Drive Addressing

- Default device number: **8** (jumper-selectable; 8–11)
- Drive number: always **0** for 1541 (single drive)
- Commands sent to: **secondary address 15** (command channel)
- Errors read from: **secondary address 15**

---

## BASIC Command Channel Wrapper

Use these BASIC patterns to send all DOS commands:

```basic
10 OPEN 1,8,15 : REM open error/command channel
20 PRINT#1,"I0:" : REM send a command (initialize)
30 INPUT#1,EN,EM$,ET,ES : REM read error (code,msg,track,sector)
40 CLOSE 1
```

Or for a reusable subroutine:
```basic
9000 REM --- DOS COMMAND ---
9010 OPEN 15,8,15,CMD$
9020 INPUT#15,EN,EM$,ET,ES
9030 IF EN>0 AND EN<20 THEN PRINT EN;EM$;ET;ES
9040 CLOSE 15
9050 RETURN
```

---

## DOS Commands

### Initialize — `I0:`
Resets the disk, reads BAM (Block Availability Map). Use after inserting a disk.
```basic
OPEN 15,8,15,"I0:":CLOSE 15
```

### Validate — `V0:`
Repairs the BAM and removes "scratched" file entries. Like CHKDSK.
```basic
OPEN 15,8,15,"V0:":CLOSE 15
```

### New (Format) — `N0:diskname,id`
Low-level format. **Destroys all data.** ID = 2-char disk identifier.
```basic
OPEN 15,8,15,"N0:MYDISK,A1":CLOSE 15
```
- After first format, use `N0:name` (no ID) for a fast format that only rewrites the directory

### Rename — `R0:newname=oldname`
```basic
OPEN 15,8,15,"R0:NEWNAME=OLDNAME":CLOSE 15
```

### Scratch (Delete) — `S0:filename`
Marks file as deleted; frees its blocks in BAM. Wildcards (`*`, `?`) supported.
```basic
OPEN 15,8,15,"S0:MYFILE":CLOSE 15
OPEN 15,8,15,"S0:GAME*":CLOSE 15 : REM scratch all files starting with GAME
```

### Copy — `C0:newfile=0:sourcefile`
Copies a file on the same drive.
```basic
OPEN 15,8,15,"C0:BACKUP=0:ORIGINAL":CLOSE 15
```

### Duplicate (two-drive systems) — `C1:dest=0:source`
Copies between drives (not applicable to 1541 unless using a 1571/1581 in dual mode).

### Memory Read — `M-R` + address (2 bytes, lo then hi)
Reads bytes from the 1541's internal RAM/ROM. Advanced use.
```basic
OPEN 15,8,15:PRINT#15,"M-R"CHR$(0)CHR$(0)CHR$(2):INPUT#15,A$:CLOSE 15
```

### Memory Write — `M-W` + address + count + data
Writes bytes to the 1541's RAM. Used for patching drive code.

### Memory Execute — `M-E` + address
Executes code in the 1541's 6502 processor at the given address.

### Block-Read — `B-R ch dn tr sc`
Reads a raw sector. ch=channel, dn=drive(0), tr=track, sc=sector.
```basic
OPEN 2,8,2,"#" : REM open buffer channel
OPEN 15,8,15
PRINT#15,"B-R 2 0 18 0" : REM read track 18, sector 0 (directory)
FOR I=1 TO 256:GET#2,A$:PRINT ASC(A$+CHR$(0));:NEXT
CLOSE 2:CLOSE 15
```

### Block-Write — `B-W ch dn tr sc`
Writes the buffer to a raw sector.

### Block-Allocate — `B-A dn tr sc`
Marks a sector as used in the BAM.

### Block-Free — `B-F dn tr sc`
Marks a sector as free in the BAM.

### Block-Execute — `B-E ch dn tr sc`
Loads sector into buffer and executes it in the 1541.

### User Commands — `U1` through `U9`, `UA`–`UJ`

| Command | Equivalent | Description |
|---------|------------|-------------------------------------|
| U1 | B-R | Read block to buffer |
| U2 | B-W | Write block from buffer |
| U3–U8 | M-E | Execute user subroutines in 1541 |
| U9 | | Soft reset (preserves drive address)|
| U: | UJ | Hard reset (same as power cycle) |

---

## Error Channel Format

Read from secondary address 15 after any operation:
```
EN, EM$, ET, ES
```
- **EN** = error number (0=OK)
- **EM$** = error message string
- **ET** = track where error occurred (0 if not disk error)
- **ES** = sector where error occurred

### Complete Error Code Table

| Code | Message | Meaning |
|------|--------------------------------|----------------------------------------------|
| 00 | OK | No error |
| 01 | FILES SCRATCHED | N files were deleted (N is track field) |
| 20 | READ ERROR (block header) | Can't find sector header |
| 21 | READ ERROR (no sync character) | No sync mark found (blank/damaged) |
| 22 | READ ERROR (data block) | Data block not found |
| 23 | READ ERROR (checksum) | Data block checksum failed |
| 24 | READ ERROR (byte decoding) | GCR decoding error |
| 25 | WRITE ERROR (verify) | Verify-after-write failed |
| 26 | WRITE PROTECT ON | Write-protect tab covering notch |
| 27 | READ ERROR (checksum header) | Header block checksum failed |
| 28 | WRITE ERROR (long data block) | Data block too long |
| 29 | DISK ID MISMATCH | Wrong disk inserted |
| 30 | SYNTAX ERROR (general) | DOS command not recognized |
| 31 | SYNTAX ERROR (invalid command) | Command letter not recognized |
| 32 | SYNTAX ERROR (long line) | Command string > 58 characters |
| 33 | SYNTAX ERROR (invalid filename)| Wildcard in wrong context, or bad name |
| 34 | SYNTAX ERROR (no file given) | Missing filename |
| 39 | SYNTAX ERROR (invalid command) | Command exists but not in this context |
| 49 | INVALID FORMAT (loading) | Attempt to load incompatible format |
| 50 | RECORD NOT PRESENT | Relative file record past EOF |
| 51 | OVERFLOW IN RECORD | Data exceeds relative record length |
| 52 | FILE TOO LARGE | Relative file: record length too large |
| 60 | WRITE FILE OPEN | Tried to read a file opened for write |
| 61 | FILE NOT OPEN | File not opened before read/write |
| 62 | FILE NOT FOUND | Named file doesn't exist on disk |
| 63 | FILE EXISTS | Tried to create file that already exists |
| 64 | FILE TYPE MISMATCH | Wrong file type for operation |
| 65 | NO BLOCK | Block-allocate failed (no free block nearby) |
| 66 | ILLEGAL TRACK AND SECTOR | Bad T/S combination |
| 67 | ILLEGAL SYSTEM T&S | Tried to access reserved directory sector |
| 70 | NO CHANNEL | All 5 channels in use |
| 71 | DIRECTORY ERROR | BAM doesn't match actual usage |
| 72 | DISK FULL | No free sectors remaining |
| 73 | CBM DOS V2.6 1541 | Power-on / reset message (not an error) |
| 74 | DRIVE NOT READY | No disk inserted, or door open |

---

## File Types

| Code | Extension | Type | Description |
|------|-----------|-------------------|----------------------------------------------|
| $80 | PRG | Program | BASIC or ML program (load address in header) |
| $81 | SEQ | Sequential | Text/data, forward-only access |
| $82 | USR | User | Application-defined format |
| $83 | REL | Relative | Random-access by record number |
| $84 | — | (Internal) | Used by 1571/1581, not 1541 |

Bit 7 set = file is **closed** (normal). Bit 6 set = file was **locked** (scratch protected). If bit 7 is clear, file was not properly closed ("splat file" — `*filename`).

---

## Disk Layout (1541)

- **35 tracks**, numbered 1–35
- **683 sectors total** (664 user-available, 19 reserved for directory/BAM)
- Sector size: **256 bytes** each
- Sectors per track varies with track zone:

| Tracks | Sectors/Track | Notes |
|--------|---------------|---------------------|
| 1–17 | 21 | Outermost (fastest) |
| 18–24 | 19 | |
| 25–30 | 18 | |
| 31–35 | 17 | Innermost (slowest) |

**Track 18, Sector 0** — Directory header and BAM (Block Availability Map)
**Track 18, Sectors 1–18** — Directory entries (8 entries per sector × 18 sectors = max 144 files)

### BAM Structure (Track 18, Sector 0)

| Offset | Length | Content |
|--------|--------|----------------------------------------------|
| $00 | 2 | T/S link to first directory sector (18/1) |
| $02 | 1 | DOS version ('A' for 1541 format) |
| $03 | 1 | Unused ($00) |
| $04 | 4×35 | BAM entries for tracks 1–35 (4 bytes each) |
| $90 | 16 | Disk name (padded with $A0) |
| $A0 | 2 | $A0 $A0 (padding) |
| $A2 | 2 | Disk ID |
| $A4 | 1 | $A0 |
| $A5 | 2 | DOS type ("2A") |
| $A7 | 4 | $A0 $A0 $A0 $A0 (padding) |

**Each 4-byte BAM entry:**
- Byte 0: number of free sectors on this track
- Bytes 1–3: bit map — bit=1 means sector is FREE, bit=0 means sector is USED

### Directory Entry Structure (32 bytes each)

| Offset | Length | Content |
|--------|--------|----------------------------------------------|
| $00 | 2 | T/S of next dir sector (or $00/$00 if last) |
| $02 | 1 | File type byte (see File Types above) |
| $03 | 2 | T/S of first sector of file data |
| $05 | 16 | Filename (padded with $A0) |
| $15 | 2 | T/S of first side-sector (REL files only) |
| $17 | 1 | Record length (REL files only) |
| $18 | 6 | Unused (internal use during write) |
| $1E | 2 | File size in sectors (low byte first) |

---

## Sector Chaining

Every sector (except the last in a file) uses its first 2 bytes as a **T/S link**:

| Byte | Meaning |
|------|--------------------------------------------|
| 0 | Track of next sector (0 = this is last) |
| 1 | Sector of next sector; or last used byte # |

When track byte = 0, sector byte = number of valid data bytes in this sector (1 to 255). Data starts at byte offset $02 in each sector (bytes $00–$01 are the link).

**Usable data per sector:** 254 bytes (all sectors except last), variable in last sector.

---

## BASIC File Operations Reference

### Sequential Write
```basic
10 OPEN 1,8,2,"0:MYDATA,S,W" : REM write mode
20 PRINT#1,"HELLO WORLD"
30 PRINT#1,3.14159
40 CLOSE 1
```

### Sequential Read
```basic
10 OPEN 1,8,2,"0:MYDATA,S,R" : REM read mode
20 INPUT#1,A$
30 IF ST=0 THEN PRINT A$:GOTO 20
40 CLOSE 1
```

### Relative File — Create
```basic
10 OPEN 1,8,3,"0:RELFILE,L,"+CHR$(32) : REM record length 32
20 FOR I=1 TO 100
30 PRINT#1,I;"RECORD";I : REM write record
40 NEXT I
50 CLOSE 1
```

### Relative File — Random Access
```basic
10 OPEN 1,8,3,"0:RELFILE,L,"+CHR$(32)
20 OPEN 15,8,15
30 INPUT "RECORD NUMBER";R
40 PRINT#15,"P"+CHR$(3)+CHR$(R MOD 256)+CHR$(R/256)+CHR$(1)
50 INPUT#1,A$
60 PRINT A$
70 GOTO 30
```
`P` command: channel, record low, record high, byte-within-record (1-based)

### Load a Program File
```basic
LOAD "FILENAME",8 : REM load to original address
LOAD "FILENAME",8,1 : REM load relocate to BASIC start
```

### Save a Program File
```basic
SAVE "FILENAME",8
```

### Directory Listing
```basic
LOAD "$",8
LIST
```

### Read Error Channel
```basic
OPEN 15,8,15
INPUT#15,EN,EM$,ET,ES
PRINT EN,EM$,ET,ES
CLOSE 15
```

---

## ML Direct Block Access Example

```asm
; Read track 18, sector 0 (BAM) into buffer at $C000
OPEN_BUFFER:
LDA #2
LDX #8
LDY #2
JSR $FFBA ; SETLFS
LDA #1
LDX #<BUFNAME
LDY #>BUFNAME
JSR $FFBD ; SETNAM "#" opens a buffer
JSR $FFC0 ; OPEN

LDA #0
LDX #15
LDY #0
JSR $FFBA
LDA #0
JSR $FFBD ; SETNAM "" (empty name for cmd channel)
JSR $FFC0 ; OPEN channel 15

LDX #15
JSR $FFC9 ; CKOUT to cmd channel

; Send U1 (block read) command: U1 ch drive track sector
LDA #'U':JSR $FFD2
LDA #'1':JSR $FFD2
LDA #' ':JSR $FFD2
LDA #'2':JSR $FFD2 ; channel 2
LDA #' ':JSR $FFD2
LDA #'0':JSR $FFD2 ; drive 0
LDA #' ':JSR $FFD2
LDA #'1':JSR $FFD2 ; track 18 = "18"
LDA #'8':JSR $FFD2
LDA #' ':JSR $FFD2
LDA #'0':JSR $FFD2 ; sector 0
LDA #$0D:JSR $FFD2 ; RETURN

JSR $FFCC ; CLRCH

; Read 256 bytes from buffer channel
LDX #2
JSR $FFC6 ; CHKIN channel 2
LDY #0
RD: JSR $FFCF ; CHRIN
STA $C000,Y
INY
BNE RD

JSR $FFCC
LDA #2:JSR $FFC3 ; CLOSE 2
LDA #15:JSR $FFC3 ; CLOSE 15
RTS

BUFNAME: .BYTE "#"
```

---

## Tips & Pitfalls

1. **Always read the error channel** after any disk operation. Error 73 at power-on is normal (drive status, not an error).
2. **Scratched files are recoverable** until VALIDATE runs — the data sectors are not zeroed, just unlinked in the BAM.
3. **Never pull a disk during a write** — the directory entry is written last; if interrupted, you get a "splat file" (`*`) that won't OPEN or SCRATCH easily.
4. **Track 18 is sacred** — it holds the directory and BAM. If corrupted, use a disk editor to repair.
5. **Maximum 144 files per disk** — 18 directory sectors × 8 entries each.
6. **Relative files** are the only random-access format; they require a "side sector" on the disk.
7. **The 1541 has only one read/write head** — interleave is important for sequential speed. Original DOS uses interleave of 10.
8. **Use `U9` to soft-reset the drive** if it hangs without cycling power; `U:` (UJ) does a complete reset.

+ 410
- 0
reference/kernal-jumplist.md 查看文件

@@ -0,0 +1,410 @@
# Commodore 64 Kernal Jump Table — Complete Reference
> Source: Compute's VIC-20 & C64 Tool Kit: Kernal; The Anatomy of the Commodore 64

All addresses are jump table entries (3-byte JMP instructions pointing into the Kernal ROM). These addresses are **stable across all C64 revisions** — always call through the jump table, never jump directly into ROM.

---

## Quick Usage Template

```asm
; Open file: device 8, secondary 2, name "DATA"
LDA #1 ; logical file number
LDX #8 ; device number (8 = disk)
LDY #2 ; secondary address
JSR $FFBA ; SETLFS
LDA #4 ; filename length
LDX #<fname ; filename address low
LDY #>fname ; filename address high
JSR $FFBD ; SETNAM
JSR $FFC0 ; OPEN
fname: .BYTE "DATA"
```

---

## Complete Jump Table

### $FF81 — SCINIT: Initialize Screen Editor and VIC-II
- **Input:** None
- **Output:** None
- **Destroys:** A, X, Y
- **Notes:** Clears screen, sets default colors, initializes VIC-II registers. Called at RESET.

### $FF84 — IOINIT: Initialize I/O Devices
- **Input:** None
- **Output:** None
- **Destroys:** A, X, Y
- **Notes:** Initializes CIA #1 and #2, serial bus, RS-232. Called at RESET.

### $FF87 — RAMTAS: Test RAM and Initialize Pointers
- **Input:** None
- **Output:** None
- **Destroys:** A, X, Y
- **Notes:** Tests RAM, sets HIRAM/LORAM, initializes $0000–$00FF. Called at RESET.

### $FF8A — RESTOR: Restore Default System Vectors
- **Input:** None
- **Output:** None
- **Destroys:** A, X, Y
- **Notes:** Copies ROM default vectors into $0314–$0333 (IRQ, OPEN, CLOSE, etc.)

### $FF8D — VECTOR: Read/Set Vectored System Routines
- **Input:** .C = 0 to write vectors from user table; .C = 1 to read vectors to user table. .XY = pointer to 2×16-byte table (alternating load/store pairs)
- **Output:** None
- **Notes:** Batch-sets or batch-reads the $0314–$0333 vector block.

### $FF90 — SETMSG: Set Kernal Error/Status Message Flag
- **Input:** .A = flag value (bit 6 = error messages on; bit 7 = I/O messages on)
- **Output:** None
- **Destroys:** A

### $FF93 — SECOND: Send Secondary Address After LISTEN
- **Input:** .A = secondary address (ORed with $60 internally)
- **Output:** None; carry set on timeout
- **Destroys:** A
- **Notes:** Must follow a LISTEN call.

### $FF96 — TKSA: Send Secondary Address After TALK
- **Input:** .A = secondary address (ORed with $60 internally)
- **Output:** None; carry set on timeout
- **Destroys:** A
- **Notes:** Must follow a TALK call.

### $FF99 — MEMTOP: Read or Set Top of Memory
- **Input:** .C = 1 to read; .C = 0 to set (pass address in .XY)
- **Output:** .XY = top of memory (if reading)
- **Destroys:** A

### $FF9C — MEMBOT: Read or Set Bottom of Memory
- **Input:** .C = 1 to read; .C = 0 to set (pass address in .XY)
- **Output:** .XY = bottom of memory (if reading)
- **Destroys:** A

### $FF9F — SCNKEY: Scan Keyboard Matrix
- **Input:** None
- **Output:** Updates keyboard buffer at $0277; last key matrix code at $C5; shift flags at $028D
- **Destroys:** A, X, Y
- **Notes:** Normally called by the IRQ handler 60×/sec. Call manually if IRQs are disabled.

### $FFA2 — SETTMO: Set Serial Bus Timeout
- **Input:** .A = 0 to disable timeout; non-zero to enable
- **Output:** None

### $FFA5 — ACPTR: Receive Byte From Serial Bus
- **Input:** Device must already be TALKing (TALK + TKSA already sent)
- **Output:** .A = received byte; STATUS ($90) updated
- **Destroys:** A
- **Notes:** Check STATUS after each byte. ST bit 1 set = EOI (last byte in file).

### $FFA8 — CIOUT: Send Byte to Serial Bus
- **Input:** .A = byte to send; device must be LISTENing
- **Output:** STATUS updated
- **Destroys:** A

### $FFAB — UNTLK: Send UNTALK to Serial Bus
- **Input:** None
- **Output:** None
- **Destroys:** A
- **Notes:** Releases currently TALKing device.

### $FFAE — UNLSN: Send UNLISTEN to Serial Bus
- **Input:** None
- **Output:** None
- **Destroys:** A
- **Notes:** Releases currently LISTENing device.

### $FFB1 — LISTEN: Command Serial Device to LISTEN
- **Input:** .A = device number (4–30; 8=disk, 4=printer)
- **Output:** None; carry set on timeout
- **Destroys:** A

### $FFB4 — TALK: Command Serial Device to TALK
- **Input:** .A = device number
- **Output:** None; carry set on timeout
- **Destroys:** A

### $FFB7 — READST: Read I/O Status Word
- **Input:** None
- **Output:** .A = STATUS byte ($90)
- **Destroys:** A

**STATUS bits:**
| Bit | Meaning |
|-----|------------------------------------------|
| 0 | Write timeout (serial bus out) |
| 1 | Read timeout / EOI received |
| 2 | (RS-232) short block |
| 3 | (RS-232) long block |
| 4 | (RS-232) receive buffer empty / cassette |
| 5 | Checksum error |
| 6 | End of file (EOI on tape read) |
| 7 | End of tape / device not present |

### $FFBA — SETLFS: Set Logical File, Device, Secondary Address
- **Input:** .A = logical file number (1–127 typical); .X = device number; .Y = secondary address (255=$FF = no secondary)
- **Output:** None

**Device numbers:**
| # | Device |
|---|---------------|
| 0 | Keyboard |
| 1 | Cassette |
| 2 | RS-232 |
| 3 | Screen |
| 4 | Printer |
| 5 | Printer #2 |
| 8 | Disk drive |
| 9 | Disk drive #2 |

### $FFBD — SETNAM: Set Filename
- **Input:** .A = length (0 for no name); .XY = address of filename (X=low, Y=high)
- **Output:** None
- **Notes:** For disk: name can include drive number prefix like "0:MYFILE,S,R". Length 0 for LOAD without name (loads tape program).

### $FFC0 — OPEN: Open a Logical File
- **Input:** Preceded by SETLFS and SETNAM
- **Output:** Carry set on error; .A = error code (1=file open, 2=too many files, 6=not input, etc.)
- **Destroys:** A, X, Y
- **Notes:** Must CLOSE file when done. Max 10 open files.

### $FFC3 — CLOSE: Close a Logical File
- **Input:** .A = logical file number
- **Output:** None
- **Destroys:** A, X, Y

### $FFC6 — CHKIN: Set Input Channel
- **Input:** .X = logical file number (must be already OPENed)
- **Output:** Carry set on error; .A = error
- **Destroys:** A, X
- **Notes:** Redirects CHRIN/GETIN to this file. Call CLRCH to restore keyboard input.

### $FFC9 — CKOUT: Set Output Channel
- **Input:** .X = logical file number
- **Output:** Carry set on error
- **Destroys:** A, X
- **Notes:** Redirects CHROUT/BSOUT to this file.

### $FFCC — CLRCH: Restore Default I/O Channels
- **Input:** None
- **Output:** None
- **Destroys:** A, X
- **Notes:** Resets input to keyboard (device 0), output to screen (device 3).

### $FFCF — CHRIN: Input Character from Current Channel
- **Input:** None
- **Output:** .A = character received; STATUS updated
- **Destroys:** A
- **Notes:** Waits for character if keyboard. Returns PETSCII code. Check STATUS after for EOF.

### $FFD2 — CHROUT / BSOUT: Output Character to Current Channel
- **Input:** .A = character to output (PETSCII)
- **Output:** STATUS updated
- **Destroys:** A (X and Y preserved)
- **Notes:** Most commonly called Kernal routine. Outputs to current CKOUT channel (screen by default). Also vectored via $0326.

**Useful PETSCII codes for CHROUT:**
| Code | Effect |
|------|---------------------|
| $0D | RETURN (newline) |
| $11 | Cursor down |
| $13 | HOME |
| $14 | DELETE |
| $1D | Cursor right |
| $91 | Cursor up |
| $93 | CLEAR |
| $9D | Cursor left |
| $12 | Reverse on |
| $92 | Reverse off |

### $FFD5 — LOAD: Load a File from Device
- **Input:** Preceded by SETLFS and SETNAM. .A = 0 (load) or 1 (verify). .XY = load address (used only if secondary address = 0, meaning override file's own load address)
- **Output:** Carry set on error; .A = error code; .XY = address of last byte loaded + 1
- **Destroys:** A, X, Y
- **Notes:** With secondary address 1, file loads to its original address. With secondary address 0, .XY overrides load address.

### $FFD8 — SAVE: Save a File to Device
- **Input:** Preceded by SETLFS and SETNAM. .A = zero-page address containing start address (2 bytes). .XY = end address + 1
- **Output:** Carry set on error
- **Destroys:** A, X, Y
- **Notes:** .A is a **zero-page pointer** to the 2-byte start address, not the start address itself.

**Example — Save $C000–$CFFF:**
```asm
LDA #<$C000
STA $FB ; store start in ZP
LDA #>$C000
STA $FC
LDA #1 ; logical file
LDX #8 ; disk
LDY #1 ; secondary address
JSR $FFBA ; SETLFS
LDA #6
LDX #<savename
LDY #>savename
JSR $FFBD ; SETNAM
LDA #$FB ; ZP pointer to start address
LDX #<$D000 ; end+1 low
LDY #>$D000 ; end+1 high
JSR $FFD8 ; SAVE
savename: .BYTE "MYFILE"
```

### $FFDB — SETTIM: Set Jiffy Clock
- **Input:** .A = high byte, .X = mid byte, .Y = low byte (24-bit value at 60Hz units)
- **Output:** None

### $FFDE — RDTIM: Read Jiffy Clock
- **Input:** None
- **Output:** .A = high byte, .X = mid byte, .Y = low byte
- **Notes:** Clock wraps after ~18.2 hours (16,777,215 jiffies). Updated 60× per second by IRQ.

### $FFE1 — STOP: Check STOP Key
- **Input:** None
- **Output:** .Z = 1 if STOP key pressed; .A = keyboard row scan byte
- **Destroys:** A, X
- **Notes:** If STOP pressed, also calls CLRCH. Use to allow program abort.

### $FFE4 — GETIN: Get Character from Keyboard Buffer
- **Input:** None
- **Output:** .A = character (0 if buffer empty); STATUS updated
- **Destroys:** A, X, Y
- **Notes:** Non-blocking. Returns 0 immediately if no key waiting. For file input, same as CHRIN.

### $FFE7 — CLALL: Close All Files and Channels
- **Input:** None
- **Output:** None
- **Destroys:** A, X
- **Notes:** Clears open file table and resets I/O to defaults. Does NOT send UNLISTEN/UNTALK to devices.

### $FFEA — UDTIM: Update Jiffy Clock
- **Input:** None
- **Output:** None
- **Notes:** Increments 3-byte clock at $A0–$A2. Called by the default IRQ handler. If you replace the IRQ, call this manually to maintain timing.

### $FFED — SCREEN: Return Screen Dimensions
- **Input:** None
- **Output:** .X = number of columns (40), .Y = number of rows (25)

### $FFF0 — PLOT: Read or Set Cursor Position
- **Input:** .C = 0 to set cursor (X=column, Y=row); .C = 1 to read cursor position
- **Output:** .X = cursor column, .Y = cursor row (when reading)
- **Destroys:** A (X, Y preserved when setting)

### $FFF3 — IOBASE: Return Base Address of I/O Devices
- **Input:** None
- **Output:** .XY = base address of CIA #1 ($DC00)
- **Notes:** Useful for position-independent code.

---

## Hardware Vectors (ROM, read-only)

| Address | Vector Name | Default Points To |
|---------|-------------|-------------------|
| $FFFA | NMI vector | $FE43 (Kernal NMI handler) |
| $FFFC | RESET vector| $FCE2 (Kernal cold start) |
| $FFFE | IRQ/BRK | $FF48 (Kernal IRQ entry) |

The Kernal IRQ entry at $FF48 pushes registers and then jumps through the **RAM vector** at $0314 — that is what you override to install custom IRQ handlers.

---

## Complete File I/O Pattern (ML)

### Write Sequential File to Disk
```asm
WRITE_FILE:
; Set up file
LDA #2 ; logical file #2
LDX #8 ; disk drive
LDY #2 ; secondary address 2 = write
JSR $FFBA ; SETLFS
LDA #FNAME_LEN
LDX #<FNAME
LDY #>FNAME
JSR $FFBD ; SETNAM
JSR $FFC0 ; OPEN
BCS WRITE_ERR ; carry set = error

; Redirect output
LDX #2
JSR $FFC9 ; CKOUT

; Write bytes
LDY #0
WLOOP:
LDA DATA,Y
JSR $FFD2 ; CHROUT
INY
CPY #DATA_LEN
BNE WLOOP

; Close
JSR $FFCC ; CLRCH
LDA #2
JSR $FFC3 ; CLOSE
RTS
WRITE_ERR:
RTS
```

### Read Sequential File from Disk
```asm
READ_FILE:
LDA #3
LDX #8
LDY #2 ; secondary address 2 = read
JSR $FFBA
LDA #FNAME_LEN
LDX #<FNAME
LDY #>FNAME
JSR $FFBD
JSR $FFC0 ; OPEN
BCS READ_ERR

LDX #3
JSR $FFC6 ; CHKIN

RLOOP:
JSR $FFCF ; CHRIN
STA BUFFER,Y ; store to buffer
INY
JSR $FFB7 ; READST
AND #$40 ; EOF bit
BEQ RLOOP

JSR $FFCC ; CLRCH
LDA #3
JSR $FFC3 ; CLOSE
RTS
READ_ERR:
RTS
```

---

## Intercepting BSOUT (Custom Character Output)

To redirect all screen output (PRINT, CHROUT) through your own routine:

```asm
INSTALL:
LDA #<MY_BSOUT
STA $0326 ; patch BSOUT vector
LDA #>MY_BSOUT
STA $0327
RTS

MY_BSOUT:
; .A = character to output
; Do custom processing here, then fall through to original:
JMP $F1CA ; Kernal BSOUT (bypassing vector)

REMOVE:
LDA #$CA ; restore default ($F1CA)
STA $0326
LDA #$F1
STA $0327
RTS
```

+ 345
- 0
reference/memory-map-complete.md 查看文件

@@ -0,0 +1,345 @@
# Commodore 64 — Complete Memory Map Reference
> Source: Compute's Mapping the Commodore 64; The Anatomy of the Commodore 64

---

## Quick-Reference Regions

| Range | Size | Description |
|---------------|-------|----------------------------------------------|
| $0000–$00FF | 256 B | Zero Page (fastest addressing) |
| $0100–$01FF | 256 B | CPU Stack |
| $0200–$03FF | 512 B | OS/BASIC work area (vectors, tables) |
| $0400–$07FF | 1 KB | Default screen text matrix (40×25) |
| $0800–$9FFF | 38 KB | BASIC program area (default) |
| $A000–$BFFF | 8 KB | BASIC ROM (or cartridge/RAM under) |
| $C000–$CFFF | 4 KB | Upper RAM (ML friendly) |
| $D000–$DFFF | 4 KB | I/O space (VIC-II/SID/CIA/Color RAM) |
| $E000–$FFFF | 8 KB | Kernal ROM (or RAM under) |

---

## Zero Page — $0000–$00FF (Selected)

| Address | Label | Description |
|---------|----------|--------------------------------------------------|
| $00 | R6510 | 6510 I/O direction register |
| $01 | R6510+1 | 6510 I/O port (RAM/ROM banking + cassette) |
| $02 | UNUSED | Free zero-page byte |
| $03–$04 | ADRAY1 | Convert float→integer vector |
| $05–$06 | ADRAY2 | Convert integer→float vector |
| $07 | CHARAC | Search character for BASIC scanner |
| $08 | ENDCHR | Flag: scan to end of BASIC line |
| $09 | TRMPOS | Terminal position for tab |
| $0A | VERCKK | LOAD (0) or VERIFY (1) flag |
| $0B | COUNT | Input buffer character count |
| $0C | DIMFLG | Default (0) or DIM flag |
| $0D | VALTYP | Type flag: 0=float, $FF=string |
| $0E | INTFLG | Integer flag |
| $0F | GARBFL | DATA scan / LIST quote / garbage collect flag |
| $10 | SUBFLG | Subscript flag for FN |
| $11 | INPFLG | INPUT (0) or GET ($40) or READ ($98) |
| $12 | TANSGN | TAN sign / comparison flag |
| $13 | CHANNL | Current I/O channel (0=keyboard/screen) |
| $14–$15 | LINNUM | Integer line number |
| $16 | TEMPPT | Pointer: next temp string |
| $17–$18 | LASTPT | Pointer: last temp string |
| $19–$21 | TEMPST | Temp string descriptor stack |
| $22–$25 | INDEX1 | Utility pointer area |
| $26–$2A | FACEXP | Floating-point accumulator #1 |
| $2B–$2C | TXTTAB | Pointer: start of BASIC text ($0801 default) |
| $2D–$2E | VARTAB | Pointer: start of BASIC variables |
| $2F–$30 | ARYTAB | Pointer: start of BASIC arrays |
| $31–$32 | STREND | Pointer: end of arrays (+1) |
| $33–$34 | FRETOP | Pointer: bottom of string storage |
| $35–$36 | FRESPC | Utility pointer for strings |
| $37–$38 | MEMSIZ | Pointer: top of memory ($A000 default) |
| $39–$3A | CURLIN | Current BASIC line number |
| $3B–$3C | OLDLIN | Previous BASIC line number |
| $3D–$3E | OLDTXT | Pointer: previous BASIC statement |
| $3F–$40 | DATLIN | Line number of current DATA statement |
| $41–$42 | DATPTR | Pointer into current DATA statement |
| $43–$44 | INPPTR | Pointer: INPUT source |
| $45–$46 | VARNAM | Current variable name |
| $47–$48 | VARPNT | Pointer: current variable value |
| $49–$4A | FORPNT | Pointer: FOR/NEXT variable |
| $4B–$4E | OPPTR | Math operator work area |
| $52–$55 | FAC2 | Floating-point accumulator #2 |
| $56 | ARGSGN | Sign comparison, FAC1 vs FAC2 |
| $57–$58 | ARISGN | Sign of result |
| $59 | FACOV | FAC1 overflow byte |
| $5A–$5B | FBUFPT | Pointer: BASIC print buffer |
| $61–$66 | FAC | FAC1 (5 bytes) |
| $69–$6E | ARG | FAC2 (5 bytes) |
| $90 | STATUS | Kernal I/O status (ST) word |
| $91 | STKEY | STOP key flag ($7F = pressed) |
| $93 | VERCK | LOAD/VERIFY flag (Kernal) |
| $94 | C3PO | Cassette: 1=write leader sent |
| $95 | BSOUR | Cassette: byte to write |
| $9A | SXREG | Serial bus I/O address |
| $9B | RDFNM | Record/file number |
| $9C | RDEVNM | Record device |
| $9D | RDSEC | Record secondary address |
| $9E | RIRQSC | RS-232 recv IRQ state |
| $9F | STATUS2 | RS-232 status |
| $A0–$A2 | TIME | 60Hz jiffy clock (24-bit, MSB=$A0) |
| $A3–$A4 | PCTR | Cassette sync / block count |
| $B0–$B5 | EAL | Kernal: device address / sector pointers |
| $BA | DFLTN | Default input device (0=keyboard) |
| $BB | DFLTO | Default output device (3=screen) |
| $BC | PRTY | Cassette parity byte |
| $BD | DPSW | Cassette EOT flag |
| $BE | MYCH | Tape character in assembly |
| $BF | CAS1 | Cassette read error correction |
| $C0 | FSBLK | Cassette read: first block flag |
| $C1–$C2 | MXPNT | Pointer: maximum BASIC variable |
| $C3–$C4 | SXPTR | Pointer to tape buffer or screen for cassette |
| $C5 | LSTX | Matrix code of last key pressed |
| $C6 | NDX | Keyboard buffer index (# chars) |
| $C7 | RVS | Reverse mode flag (1=on) |
| $C8 | INDX | Pointer: end of logical input line |
| $C9–$CA | LXSP | Cursor column and row at I/O start |
| $CB | SFDX | Flag: shift/commodore key |
| $CC | BLNSW | Cursor blink enable (0=on) |
| $CD | BLNCT | Countdown to cursor blink |
| $CE | GDBLN | Character under cursor (saved) |
| $CF | BLNON | Last blink state (0=off) |
| $D0 | CRSW | INPUT/GET mode (0=INPUT) |
| $D1–$D2 | PNT | Pointer: current screen line address |
| $D3 | PNTR | Cursor column on current line |
| $D4 | QTSW | Quoted mode flag |
| $D5 | LNMX | Logical line length (39 or 79) |
| $D6 | TBLX | Current cursor row |
| $D7 | DATA | Last character output to screen |
| $D8 | INSRT | Insert mode count |
| $D9–$F2 | LDTB1 | Screen line link table (25 entries) |
| $F3–$F4 | USER | Pointer: base of color RAM ($D800) |
| $F5–$F6 | KEYD | Pointer: keyboard decode table |
| $F7–$F8 | RIBUF | RS-232 input buffer pointer |
| $F9–$FA | ROBUF | RS-232 output buffer pointer |
| $FB–$FE | FREKZP | Free zero-page bytes (user/ML) |
| $FF | BASZPT | BASIC: temp for float-to-string |

---

## Page 1 — CPU Stack ($0100–$01FF)
The 6510 stack grows **downward** from $01FF. SP register holds low byte of current top. Do not use this region for data storage in time-critical IRQs — pushes/pulls compete with JSR/RTS.

---

## Page 2 — OS Work Area ($0200–$02FF, Selected)

| Address | Label | Description |
|-------------|----------|--------------------------------------------------|
| $0200–$0258 | BUF | BASIC/Kernal input buffer (89 bytes) |
| $0259–$0262 | LAT | Logical file number table (10 entries) |
| $0263–$026C | FAT | Device number table (10 entries) |
| $026D–$0276 | SAT | Secondary address table (10 entries) |
| $0277–$0280 | KEYD | Keyboard buffer (10 bytes) |
| $0281–$0282 | MEMSTR | Start of memory pointer ($0800 default) |
| $0283–$0284 | MEMSIZ | Top of memory ($A000 default) |
| $0285 | TIMOUT | Serial IEEE timeout flag |
| $0286 | COLOR | Current foreground color code |
| $0287 | BG COL | Color under cursor |
| $0288 | HIBASE | Screen RAM page ($04 = $0400) |
| $0289 | XMAX | Keyboard buffer max size (10 default) |
| $028A | RPTFLG | Key repeat flag ($40=repeat all) |
| $028B | KOUNT | Repeat delay counter |
| $028C | DELAY | Repeat speed counter |
| $028D | SHFLAG | Keyboard shift flags |
| $028E | LSTSHF | Previous shift state |
| $028F–$0290 | KEYLOG | Pointer: keyboard decode logic |
| $0291 | MODE | Shift+Commodore mode (0=switch, 128=lock) |
| $0292 | AUTODN | Screen scroll enable (0=on) |
| $0293–$0294 | M51CTR | RS-232 control register (fake 6551) |
| $0295–$0296 | M51CDR | RS-232 command register |
| $0297–$0298 | M51AJB | RS-232 baud rate (non-standard) |
| $0299–$029A | RIDBE | RS-232 buffer pointers |
| $029B–$02FF | | Other RS-232/cassette work areas |

---

## Page 3 — Vectors ($0300–$03FF)

| Address | Default Points To | Description |
|-------------|-------------------|--------------------------------------|
| $0300–$0301 | $E38B | IERROR — BASIC error message vector |
| $0302–$0303 | $A483 | IMAIN — BASIC main loop vector |
| $0304–$0305 | $A57C | ICRNCH — Tokenize input line vector |
| $0306–$0307 | $A71A | IQPLOP — List a token vector |
| $0308–$0309 | $A7E4 | IGONE — Execute a statement vector |
| $030A–$030B | $AE86 | IEVAL — Evaluate expression vector |
| $030C | $0 | SAREG — .A saved on IRQ |
| $030D | $00 | SXREG — .X saved on IRQ |
| $030E | $00 | SYREG — .Y saved on IRQ |
| $030F | $00 | SPREG — SP saved on IRQ |
| $0310–$0311 | $FE43/$8B | USRPOK — USR() vector (JMP + addr) |
| $0312–$0313 | $EA31 | IRQ vector (vectored from $FFFE) |
| $0314–$0315 | $EA31 | Hardware IRQ vector (CINV) |
| $0316–$0317 | $FE66 | BRK handler vector (CBINV) |
| $0318–$0319 | $FE47 | NMI vector (NMINV) |
| $031A–$031B | $F34A | IOPEN — OPEN routine vector |
| $031C–$031D | $F291 | ICLOSE — CLOSE routine vector |
| $031E–$031F | $F20E | ICHKIN — CHKIN routine vector |
| $0320–$0321 | $F250 | ICKOUT — CKOUT routine vector |
| $0322–$0323 | $F333 | ICLRCH — CLRCH routine vector |
| $0324–$0325 | $F157 | IBASIN — BASIN/CHRIN routine vector |
| $0326–$0327 | $F1CA | IBSOUT — BSOUT/CHROUT routine vector |
| $0328–$0329 | $F6ED | ISTOP — STOP key check vector |
| $032A–$032B | $F13E | IGETIN — GETIN routine vector |
| $032C–$032D | $F32F | ICLALL — CLALL routine vector |
| $032E–$032F | $FE66 | IUSRTO — User-defined load vector |
| $0330–$0331 | $F549 | ILOAD — LOAD routine vector |
| $0332–$0333 | $F685 | ISAVE — SAVE routine vector |

---

## Screen & Color RAM ($0400–$07FF / $D800–$DBFF)

| Region | Address | Notes |
|---------------|---------------|---------------------------------------------|
| Screen Matrix | $0400–$07E7 | 1000 bytes: 40 cols × 25 rows (default) |
| Unused | $07E8–$07FF | 24 bytes free |
| Color RAM | $D800–$DBE7 | 1000 bytes, nibble per cell (low 4 bits) |
| Color unused | $DBE8–$DBFF | 24 bytes |

**Screen address formula:** `addr = $0400 + (row × 40) + col`

---

## BASIC Work Area ($0800–$9FFF)

- **$0800–$0801** — BASIC start: 2-byte link ($00 $08 if empty)
- **$0801–$9FFF** — 38,911 bytes available to BASIC programs
- BASIC ROM at $A000 interprets tokenized lines starting at $0801
- `POKE 43,1 : POKE 44,8` resets BASIC start pointer

---

## Upper RAM ($C000–$CFFF)

4 KB of RAM, never overlaid by ROM. Ideal location for:
- ML subroutines called from BASIC
- Custom IRQ handlers
- Character set data

---

## I/O Space ($D000–$DFFF)

| Range | Chip | Function |
|---------------|---------|-------------------------------------|
| $D000–$D3FF | VIC-II | Video Interface Controller |
| $D400–$D7FF | SID | Sound Interface Device |
| $D800–$DBFF | Color | Color RAM (nibbles) |
| $DC00–$DCFF | CIA #1 | Keyboard, joysticks, IRQ timer |
| $DD00–$DDFF | CIA #2 | Serial bus, VIC bank, NMI timer |
| $DE00–$DEFF | I/O #1 | Cartridge expansion port I/O area 1 |
| $DF00–$DFFF | I/O #2 | Cartridge expansion port I/O area 2 |

When HIRAM=0 or banking overrides I/O region, character ROM or RAM appears instead.

---

## Banking Control — $0001 (6510 I/O Port)

| Bit | Name | 0 = … | 1 = … |
|-----|---------|----------------------|---------------------------|
| 0 | LORAM | BASIC ROM disabled | BASIC ROM at $A000 enabled |
| 1 | HIRAM | Kernal ROM disabled | Kernal ROM at $E000 enabled|
| 2 | CHAREN | Char ROM at $D000 | I/O at $D000 enabled |
| 3 | CASS WR | Cassette write low | Cassette write high |
| 4 | CASS SW | (read only) | Cassette sense line |
| 5 | CASS MT | Cassette motor on | Cassette motor off |

**Common values:**
- `$37` (binary 00110111) — default: BASIC+Kernal+I/O visible
- `$36` — Kernal+I/O visible, BASIC RAM underneath
- `$35` — I/O visible, both ROMs as RAM
- `$34` — all RAM, no I/O (character ROM at $D000)
- `$33` — Kernal ROM, character ROM at $D000, no I/O

---

## Kernal ROM ($E000–$FFFF, Selected Routines)

| Address | Label | Function |
|---------|----------|---------------------------------------|
| $E003 | — | Kernal version/signature bytes |
| $EA31 | IRQRTL | Standard IRQ handler entry |
| $E544 | PLOT | Read/set cursor position |
| $E566 | IOBASE | Return CIA base address in .XY |
| $FF81 | SCINIT | Initialize VIC/screen editor |
| $FF84 | IOINIT | Initialize CIA chips |
| $FF87 | RAMTAS | RAM test and zero page init |
| $FF8A | RESTOR | Restore default vectors |
| $FF8D | VECTOR | Read/set system vectors |
| $FF90 | SETMSG | Set Kernal message flag |
| $FF93 | SECOND | Send secondary address after LISTEN |
| $FF96 | TKSA | Send secondary address after TALK |
| $FF99 | MEMTOP | Read/set top of memory |
| $FF9C | MEMBOT | Read/set bottom of memory |
| $FF9F | SCNKEY | Scan keyboard |
| $FFA2 | SETTMO | Set serial bus timeout |
| $FFA5 | ACPTR | Receive byte from serial bus |
| $FFA8 | CIOUT | Send byte to serial bus |
| $FFAB | UNTLK | Command device to UNTALK |
| $FFAE | UNLSN | Command device to UNLISTEN |
| $FFB1 | LISTEN | Command device to LISTEN |
| $FFB4 | TALK | Command device to TALK |
| $FFB7 | READST | Read I/O status word |
| $FFBA | SETLFS | Set logical/device/secondary address |
| $FFBD | SETNAM | Set filename address and length |
| $FFC0 | OPEN | Open logical file |
| $FFC3 | CLOSE | Close logical file |
| $FFC6 | CHKIN | Set input channel |
| $FFC9 | CKOUT | Set output channel |
| $FFCC | CLRCH | Restore default I/O channels |
| $FFCF | CHRIN | Input character from current channel |
| $FFD2 | CHROUT | Output character to current channel |
| $FFD5 | LOAD | Load file from device |
| $FFD8 | SAVE | Save file to device |
| $FFDB | SETTIM | Set jiffy clock |
| $FFDE | RDTIM | Read jiffy clock |
| $FFE1 | STOP | Check STOP key |
| $FFE4 | GETIN | Get character from keyboard buffer |
| $FFE7 | CLALL | Close all channels and files |
| $FFEA | UDTIM | Increment jiffy clock |
| $FFED | SCREEN | Return screen dimensions |
| $FFF0 | PLOT | Read/set cursor X/Y position |
| $FFF3 | IOBASE | Return I/O base address |
| $FFFA | — | NMI vector (points into Kernal) |
| $FFFC | — | RESET vector ($FCE2) |
| $FFFE | — | IRQ/BRK vector ($FF48) |

---

## VIC-II Bank Selection (via CIA #2, $DD00)

| Bits 0–1 | VIC Bank | Address Range |
|----------|----------|----------------|
| 11 | 0 | $0000–$3FFF |
| 10 | 1 | $4000–$7FFF |
| 01 | 2 | $8000–$BFFF |
| 00 | 3 | $C000–$FFFF |

**Default:** Bank 0 ($0000–$3FFF) — screen at $0400, charset at $1000 (ROM image).

Character ROM is mirrored into VIC banks 0 and 2 at offsets $1000 and $1800.

---

## Sprite Memory Layout

Each sprite = 64 bytes (63 data + 1 padding). Pointer bytes stored at screen RAM + $03F8 (8 bytes).

| Pointer Value | Sprite Data Address (Bank 0) |
|---------------|------------------------------|
| $0D | $0340 |
| $0E | $0380 |
| $0F | $03C0 |
| $80 | $2000 |
| $81 | $2040 |
| … | … |

`sprite_addr = bank_base + (pointer_value × 64)`

+ 95
- 0
reference/sid-registers.md 查看文件

@@ -0,0 +1,95 @@
# SID 6581 Register Map — Quick Reference

SID base address: $D400 (54272)
All registers write-only except $D419–$D41C.

## Voice Registers

| Offset | Addr | Dec | Voice | Name | Description |
|--------|------|-----|-------|------|-------------|
| +0 | $D400 | 54272 | 1 | FRELO1 | Freq low |
| +1 | $D401 | 54273 | 1 | FREHI1 | Freq high |
| +2 | $D402 | 54274 | 1 | PWLO1 | Pulse width low |
| +3 | $D403 | 54275 | 1 | PWHI1 | Pulse width high (bits 3-0) |
| +4 | $D404 | 54276 | 1 | VCREG1 | Control register |
| +5 | $D405 | 54277 | 1 | ATDCY1 | Attack/Decay |
| +6 | $D406 | 54278 | 1 | SUREL1 | Sustain/Release |
| +7 | $D407 | 54279 | 2 | FRELO2 | Freq low |
| +8 | $D408 | 54280 | 2 | FREHI2 | Freq high |
| +9 | $D409 | 54281 | 2 | PWLO2 | Pulse width low |
| +A | $D40A | 54282 | 2 | PWHI2 | Pulse width high |
| +B | $D40B | 54283 | 2 | VCREG2 | Control register |
| +C | $D40C | 54284 | 2 | ATDCY2 | Attack/Decay |
| +D | $D40D | 54285 | 2 | SUREL2 | Sustain/Release |
| +E | $D40E | 54286 | 3 | FRELO3 | Freq low |
| +F | $D40F | 54287 | 3 | FREHI3 | Freq high |
| +10 | $D410 | 54288 | 3 | PWLO3 | Pulse width low |
| +11 | $D411 | 54289 | 3 | PWHI3 | Pulse width high |
| +12 | $D412 | 54290 | 3 | VCREG3 | Control register |
| +13 | $D413 | 54291 | 3 | ATDCY3 | Attack/Decay |
| +14 | $D414 | 54292 | 3 | SUREL3 | Sustain/Release |

## Filter and Global Registers

| Offset | Addr | Dec | Name | Description |
|--------|------|-----|------|-------------|
| +15 | $D415 | 54293 | SIGVOL | Filter cutoff freq low (bits 2-0) |
| +16 | $D416 | 54294 | FRESHI | Filter cutoff freq high byte |
| +17 | $D417 | 54295 | RESON | Resonance (7-4) / filter routing (2-0) |
| +18 | $D418 | 54296 | SIGVOL | Filter mode (7-4) / volume (3-0) |
| +19 | $D419 | 54297 | POTX | Paddle X (READ-ONLY) |
| +1A | $D41A | 54298 | POTY | Paddle Y (READ-ONLY) |
| +1B | $D41B | 54299 | RANDOM | Voice 3 oscillator (READ-ONLY, random) |
| +1C | $D41C | 54300 | ENV3 | Voice 3 envelope (READ-ONLY) |

## Control Register Bits (VCREG)
| Bit | Name | Description |
|-----|------|-------------|
| 7 | NOISE | Noise waveform |
| 6 | PULSE | Pulse waveform |
| 5 | SAW | Sawtooth waveform |
| 4 | TRI | Triangle waveform |
| 3 | TEST | Test bit (disables osc) |
| 2 | RING | Ring modulation |
| 1 | SYNC | Sync to previous voice |
| 0 | GATE | 1=start AD, 0=start Release |

## ATDCY (Attack/Decay)
| Bits | Range | Description |
|------|-------|-------------|
| 7-4 | 0-15 | Attack rate |
| 3-0 | 0-15 | Decay rate |

## SUREL (Sustain/Release)
| Bits | Range | Description |
|------|-------|-------------|
| 7-4 | 0-15 | Sustain level (15=max) |
| 3-0 | 0-15 | Release rate |

## Filter Mode Register ($D418) Bits
| Bit | Name | Description |
|-----|------|-------------|
| 7 | 3OFF | Disconnect voice 3 from output |
| 6 | HP | High-pass filter |
| 5 | BP | Band-pass filter |
| 4 | LP | Low-pass filter |
| 3-0 | VOL | Master volume (0-15) |

## Frequency Formula
```
Register = (Hz × 16777216) / ClockHz
NTSC: ClockHz = 1,022,730 Register = Hz / 0.0609594
PAL: ClockHz = 985,248 Register = Hz / 0.0587721
```

## Note Frequencies (NTSC hex register values)
| Note | Freq | Hex | | Note | Freq | Hex |
|------|------|-----|-|------|------|-----|
| C3 | 130.8 | $086C | | C4 | 261.6 | $10D9 |
| D3 | 146.8 | $0975 | | D4 | 293.7 | $12EA |
| E3 | 164.8 | $0A9B | | E4 | 329.6 | $1537 |
| F3 | 174.6 | $0B40 | | F4 | 349.2 | $1680 |
| G3 | 196.0 | $0C91 | | G4 | 392.0 | $1921 |
| A3 | 220.0 | $0E16 | | A4 | 440.0 | $1CCC |
| B3 | 246.9 | $0F9C | | B4 | 493.9 | $1F38 |
| C5 | 523.3 | $21B3 | | C6 | 1046.5 | $4366 |

+ 88
- 0
reference/vic-registers.md 查看文件

@@ -0,0 +1,88 @@
# VIC-II Register Map — Quick Reference

VIC-II base address: $D000 (53248)

| Offset | Address | Dec | Name | Description |
|--------|---------|-----|------|-------------|
| +0 | $D000 | 53248 | SP0X | Sprite 0 X position |
| +1 | $D001 | 53249 | SP0Y | Sprite 0 Y position |
| +2 | $D002 | 53250 | SP1X | Sprite 1 X |
| +3 | $D003 | 53251 | SP1Y | Sprite 1 Y |
| +4 | $D004 | 53252 | SP2X | Sprite 2 X |
| +5 | $D005 | 53253 | SP2Y | Sprite 2 Y |
| +6 | $D006 | 53254 | SP3X | Sprite 3 X |
| +7 | $D007 | 53255 | SP3Y | Sprite 3 Y |
| +8 | $D008 | 53256 | SP4X | Sprite 4 X |
| +9 | $D009 | 53257 | SP4Y | Sprite 4 Y |
| +A | $D00A | 53258 | SP5X | Sprite 5 X |
| +B | $D00B | 53259 | SP5Y | Sprite 5 Y |
| +C | $D00C | 53260 | SP6X | Sprite 6 X |
| +D | $D00D | 53261 | SP6Y | Sprite 6 Y |
| +E | $D00E | 53262 | SP7X | Sprite 7 X |
| +F | $D00F | 53263 | SP7Y | Sprite 7 Y |
| +10 | $D010 | 53264 | MSIGX | Sprites X MSB (bit N = sprite N X bit 8) |
| +11 | $D011 | 53265 | SCROLY | Vertical scroll/control |
| +12 | $D012 | 53266 | RASTER | Raster counter / IRQ line |
| +13 | $D013 | 53267 | LPENX | Light pen X (read-only) |
| +14 | $D014 | 53268 | LPENY | Light pen Y (read-only) |
| +15 | $D015 | 53269 | SPENA | Sprite enable (bit N) |
| +16 | $D016 | 53270 | SCROLX | Horizontal scroll/control |
| +17 | $D017 | 53271 | YXPAND | Sprite Y expansion (bit N) |
| +18 | $D018 | 53272 | VMCSB | Memory control (screen/char base) |
| +19 | $D019 | 53273 | VICIRQ | IRQ status (read/ack) |
| +1A | $D01A | 53274 | IRQMSK | IRQ mask enable |
| +1B | $D01B | 53275 | SPBGPR | Sprite-BG priority |
| +1C | $D01C | 53276 | SPMC | Sprite multicolor enable |
| +1D | $D01D | 53277 | XXPAND | Sprite X expansion |
| +1E | $D01E | 53278 | SPSPCL | Sprite-sprite collision (clears on read) |
| +1F | $D01F | 53279 | SPBGCL | Sprite-BG collision (clears on read) |
| +20 | $D020 | 53280 | EXTCOL | Border color |
| +21 | $D021 | 53281 | BGCOL0 | Background color 0 |
| +22 | $D022 | 53282 | BGCOL1 | Background color 1 |
| +23 | $D023 | 53283 | BGCOL2 | Background color 2 |
| +24 | $D024 | 53284 | BGCOL3 | Background color 3 |
| +25 | $D025 | 53285 | SPMC0 | Sprite multicolor 0 |
| +26 | $D026 | 53286 | SPMC1 | Sprite multicolor 1 |
| +27 | $D027 | 53287 | SP0COL | Sprite 0 color |
| +28 | $D028 | 53288 | SP1COL | Sprite 1 color |
| +29 | $D029 | 53289 | SP2COL | Sprite 2 color |
| +2A | $D02A | 53290 | SP3COL | Sprite 3 color |
| +2B | $D02B | 53291 | SP4COL | Sprite 4 color |
| +2C | $D02C | 53292 | SP5COL | Sprite 5 color |
| +2D | $D02D | 53293 | SP6COL | Sprite 6 color |
| +2E | $D02E | 53294 | SP7COL | Sprite 7 color |

## $D011 — SCROLY Bit Fields
| Bit | Name | Description |
|-----|------|-------------|
| 7 | RST8 | Raster line bit 8 |
| 6 | ECM | Extended Color Mode |
| 5 | BMM | Bitmap Mode |
| 4 | DEN | Display Enable |
| 3 | RSEL | Row select: 1=25 rows, 0=24 rows |
| 2-0 | YSCL | Vertical fine scroll (0-7) |

## $D016 — SCROLX Bit Fields
| Bit | Name | Description |
|-----|------|-------------|
| 4 | MCM | Multicolor Mode |
| 3 | CSEL | Column select: 1=40 cols, 0=38 cols |
| 2-0 | XSCL | Horizontal fine scroll (0-7) |

## $D018 — VMCSB Memory Control
| Bits | Name | Description |
|------|------|-------------|
| 7-4 | VM | Screen RAM base: value × $0400 within VIC bank |
| 3-1 | CB | Char set base: value × $0800 within VIC bank |

Default $10: screen at $0400, chars at $1000 (ROM mirror in bank 0).

## $D019 — VICIRQ / $D01A — IRQMSK
| Bit | Event |
|-----|-------|
| 3 | Light pen |
| 2 | Sprite-BG collision |
| 1 | Sprite-sprite collision |
| 0 | Raster IRQ |

To acknowledge: read $D019, then write the value back (write-1-to-clear).

+ 185
- 0
reference/vice-usage.md 查看文件

@@ -0,0 +1,185 @@
# VICE 3.10 Usage Reference
> Supplemental emulator reference distilled from `C:\Program Files\GTK3VICE-3.10-win64\doc\vice.txt`

This file is a practical subset of the VICE 3.10 manual for AI agents working on Commodore 64 software in this repository.

---

## Preferred C64 Executable

- `x64sc` — accurate C64 emulator with cycle-based and pixel-accurate VIC-II emulation

Other useful VICE tools installed alongside it:

- `c1541.exe` — disk image tool
- `petcat.exe` — BASIC text/token conversion
- `cartconv.exe` — cartridge conversion
- `vsid.exe` — SID playback

---

## Autostart

Equivalent manual-documented forms:

```text
x64sc mydisk.d64
x64sc -autostart myprog.prg
x64sc -autostart "demo.d64:loader"
```

Useful switches:

| Option | Purpose |
|--------|---------|
| `-autostart-warp` | Enable warp mode during autostart |
| `-autostart-handle-tde` | Temporarily manage True Drive Emulation for autostart |
| `-basicload` | Use BASIC-start load behavior when autostarting from disk |
| `+basicload` | Use `,1` absolute load behavior when autostarting from disk |
| `-autostartprgdiskimage <name>` | Choose the disk image used when autostarting a PRG via disk-copy mode |

Notes:

- For raw `.prg`/P00 files, VICE restores autostart-related settings after load
- If a program must reach the host filesystem, enable virtual device traps

---

## Debug Launch Options

| Option | Meaning |
|--------|---------|
| `-nativemonitor` | Run the monitor in the spawning terminal |
| `-keepmonopen` | Keep the monitor open after continuing execution |
| `-refreshonbreak` | Refresh display when entering monitor and after monitor commands |
| `-monlog` | Log monitor output to a file |
| `-monlogname <name>` | Choose the monitor log filename |
| `-remotemonitor` | Enable the text remote monitor server |
| `-remotemonitoraddress ip4://127.0.0.1:6510` | Bind address for the text monitor server |
| `-binarymonitor` | Enable the binary monitor protocol server |
| `-binarymonitoraddress ip4://127.0.0.1:6502` | Bind address for the binary monitor server |

Recommended debug launch for this repo:

```text
"C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" -autostart c64os.prg -autostart-warp -nativemonitor -keepmonopen -refreshonbreak
```

---

## High-Value Debug Commands

| Command | Use |
|---------|-----|
| `r` / `registers` | Show or edit registers |
| `x` / `exit` | Resume execution |
| `q` / `quit` | Quit VICE |
| `step [count]` | Step into |
| `next [count]` | Step over |
| `return` | Run until next `RTS` or `RTI` |
| `until <address>` | Run until a target address |
| `bt` / `backtrace` | Show JSR call chain |
| `goto <address>` | Set PC |
| `m <range>` | Dump memory |
| `i <range>` | View memory as PETSCII |
| `d <range>` | Disassemble |
| `io [address]` | Show I/O register state |
| `break exec $addr` | Execution breakpoint |
| `watch store $addr` | Write watchpoint |
| `trace exec $a $b` | Non-stopping trace |
| `bank` | Show/select banks |
| `device c:` / `8:` | Switch computer or drive memory space |
| `sidefx off` | Avoid side-effect-causing reads while inspecting memory |
| `keybuf "text\x0d"` | Inject keyboard input |
| `playback "file"` | Execute a monitor script file |
| `record "file"` | Record a debug session as commands |

Examples:

```text
break exec $0810
watch store $0001
d $0810 $0840
m $0000 $0002
bt
return
playback "examples/vice-monitor-c64os.txt"
```

Conditional checkpoints support register expressions, raster line `RL`, and cycle `CY`.

Useful conditional examples from the manual style:

```text
break exec $0810 if A == $00
break load 0 $ffff if @cpu:(pc - $1) == $37
```

Checkpoint management:

```text
command 2 "m $0000 $0002"
ignore 2 1
disable 1
enable 1
delete 1
```

Debugging-specific watch targets:

- `$0001` for memory banking writes
- `$0314-$0315` for IRQ vector changes
- `$0318-$0319` for NMI vector changes
- `$0400-$07E7` for screen RAM writes
- `$D800-$DBE7` for color RAM writes

---

## Practical Debug Workflow

1. Launch with `-nativemonitor -keepmonopen -refreshonbreak`
2. Enter `sidefx off`
3. Break at entry with `break exec $0810`
4. Disassemble nearby code with `d`
5. Inspect registers with `r`
6. Use `step` to go into a routine, `next` to stay out of KERNAL internals, and `return` to escape a routine quickly
7. Add watchpoints once the suspicious state is known
8. Use `bt` if execution reached an unexpected location
9. Save a snapshot only for short-lived debugging checkpoints

---

## Snapshots

VICE snapshots:

- save complete emulator state
- can include drive state
- restore memory configuration on load

Important manual warning:

- snapshots are convenient for debugging, but should not be treated as stable long-term storage across VICE versions

---

## Repo-Focused Launch Examples

Build and run the current program:

```text
build.bat
"C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" -autostart c64os.prg -autostart-warp
```

Open with terminal monitor ready for debugging:

```text
"C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" -autostart c64os.prg -autostart-warp -nativemonitor -keepmonopen -refreshonbreak
```

Open with monitor logging enabled:

```text
"C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" -autostart c64os.prg -autostart-warp -nativemonitor -keepmonopen -refreshonbreak -monlog -monlogname vice-monitor.log
```

+ 73
- 0
samples/cc65-mouse-driver-demo/README.md 查看文件

@@ -0,0 +1,73 @@
# cc65 Mouse Driver Demo

This sample lives in its own folder and builds a small C64 program with `cc65`.
It is based on the upstream `cc65` sample `samples/mousedemo.c`, but trimmed down
to a simple C64-specific example.

## What It Uses

- `mouse.h` from `cc65`
- the static C64 1351 mouse driver symbol `c64_1351_mou`
- the default `cc65` mouse callbacks for drawing the pointer

That means the mouse driver is linked into the program at build time, so you do
not need to copy a separate `.mou` driver file onto disk just to run it.

## Files

- `mouse_driver_demo.c` - the demo program
- `build.bat` - builds `mouse_driver_demo.prg`
- `run-vice.bat` - builds if needed, then autostarts the program in `x64sc`

## Build

From this folder:

```bat
build.bat
```

## Run In VICE

```bat
run-vice.bat
```

The demo expects the `cc65` tools to be on `PATH`, and it expects VICE 3.10 at:

```text
C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe
```

## Controls

- Move with the host mouse in VICE
- `H` hides or shows the cursor
- `C` centers the cursor
- `Q` quits

`run-vice.bat` starts `x64sc` with:

```text
-mouse -controlport1device 3
```

That matches the VICE 3.10 `Mouse (1351)` device with the program's default
`c64_1351_mou` driver.

## Using The Joystick-Mouse Driver Instead

If you want the joystick-emulated mouse driver instead, change:

```c
mouse_install(&mouse_def_callbacks, c64_1351_mou);
```

to:

```c
mouse_install(&mouse_def_callbacks, c64_joy_mou);
```

The `cc65` C64 docs say `c64_joy_mou` expects a joystick-emulated mouse in C64
port `#1`, while `c64_1351_mou` expects a standard mouse in C64 port `#0`.

+ 29
- 0
samples/cc65-mouse-driver-demo/build-upstream.bat 查看文件

@@ -0,0 +1,29 @@
@echo off
setlocal enabledelayedexpansion

set "CC65_SAMPLES=C:\Development\Commodore\cc65-snapshot-win64 (1)\samples"
set "OUTDIR=%~dp0"

where cl65 >nul 2>nul
if errorlevel 1 (
echo cl65 was not found on PATH.
echo Install cc65 or add its bin directory to PATH.
exit /b 1
)

pushd "!CC65_SAMPLES!"
if errorlevel 1 (
echo Could not cd to !CC65_SAMPLES!
exit /b 1
)

cl65 -t c64 -DMOUSE_DRIVER=c64_1351_mou -O -o "%OUTDIR%mousedemo-upstream.prg" mousedemo.c
set BUILD_ERR=%errorlevel%
popd

if %BUILD_ERR% neq 0 (
echo build failed.
exit /b 1
)

echo built mousedemo-upstream.prg successfully.

+ 17
- 0
samples/cc65-mouse-driver-demo/build.bat 查看文件

@@ -0,0 +1,17 @@
@echo off
setlocal

where cl65 >nul 2>nul
if errorlevel 1 (
echo cl65 was not found on PATH.
echo Install cc65 or add its bin directory to PATH.
exit /b 1
)

cl65 -t c64 -DMOUSE_DRIVER=c64_1351_mou -O -o mouse_driver_demo.prg mouse_driver_demo.c
if errorlevel 1 (
echo build failed.
exit /b 1
)

echo built mouse_driver_demo.prg successfully.

+ 284
- 0
samples/cc65-mouse-driver-demo/mouse_driver_demo.c 查看文件

@@ -0,0 +1,284 @@
/*
** Demo program for mouse usage.
** Supports the C64/C128/CBM510/Atari/Apple2.
**
** 2001-09-13, Ullrich von Bassewitz
** 2013-09-05, Greg King
**
*/



#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <mouse.h>
#include <pen.h>
#include <conio.h>
#include <ctype.h>
#include <dbg.h>
#include <cc65.h>

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))



#ifdef MOUSE_DRIVER

/* A statically linked driver was named on the compiler's command line.
** Make sure that it is used instead of a dynamic one.
*/
# undef DYN_DRV
# define DYN_DRV 0
#else

/* Use a dynamically loaded driver, by default. */
# ifndef DYN_DRV
# define DYN_DRV 1
# endif
#endif



#ifdef __CBM__

/* Set dark-on-light colors. */
const unsigned char mouse_def_pointercolor = COLOR_BLACK;

#endif



static void __fastcall__ CheckError (const char* S, unsigned char Error)
{
if (Error != MOUSE_ERR_OK) {
cprintf ("\n%s: %s(%u)\r\n", S, mouse_geterrormsg (Error), Error);

/* Wait for a key-press, so that some platforms can show the error
** message before they remove the current screen.
*/
if (doesclrscrafterexit ()) {
cgetc ();
}
exit (EXIT_FAILURE);
}
}



#if DYN_DRV

/* Points to the dynamic driver's name. */
static const char *mouse_name;



static void DoWarning (void)
/* Warn the user that a driver is needed for this program. */
{
cprintf ("Warning: This program needs\r\n"
"the driver with the name\r\n"
" %s\r\n"
"on a disk! Press 'y' if you have it;\r\n"
"or, any other key to exit.\r\n", mouse_stddrv);
if (tolower (cgetc ()) != 'y') {
exit (EXIT_SUCCESS);
}
cprintf ("OK. Please wait patiently...\r\n");
}
#endif



static void __fastcall__ ShowState (unsigned char Jailed, unsigned char Invisible)
/* Display jail and cursor states. */
{
cclearxy (0, 7, 32);
gotoxy (0, 7);
cprintf ("Pointer is %svisible%s.", Invisible? "in" : "", Jailed? " and jailed" : "");
}



#if DYN_DRV
int main (int argc, char *argv[])
#else
int main (void)
#endif
{
struct mouse_info info;
struct mouse_box full_box, small_box;
unsigned char width, height;
char C;
bool Invisible = true, Done = false, Jailed = false;

/* Initialize the debugger */
DbgInit (0);

/* Set dark-on-light colors. Clear the screen. */
#ifdef __CBM__
(void) bordercolor (COLOR_GRAY2);
(void) bgcolor (COLOR_WHITE);
(void) textcolor (COLOR_GRAY1);
#else
(void) bordercolor (COLOR_BLUE);
(void) bgcolor (COLOR_WHITE);
(void) textcolor (COLOR_BLACK);
#endif
cursor (0);
clrscr ();

/* If a lightpen driver is installed, then it can get a calibration value
** from this file (if it exists). Or, the user can adjust the pen; and,
** the value will be put into this file, for the next time.
** (Other drivers will ignore this.)
*/
#if defined(__C64__) || defined(__C128__) || defined(__CBM510__)
pen_adjust ("pen.dat");
#endif

#if DYN_DRV
/* If a dynamically loadable driver is named on the command line,
** then use that driver instead of the standard one.
*/
if (argc > 1) {
mouse_name = argv[1];
} else {
/* Output a warning about the standard driver that is needed. */
DoWarning ();
mouse_name = mouse_stddrv;
}

/* Load and install the driver. */
CheckError ("mouse_load_driver",
mouse_load_driver (&mouse_def_callbacks, mouse_name));
#else
/* Install the driver. */
CheckError ("mouse_install",
mouse_install (&mouse_def_callbacks,
# ifdef MOUSE_DRIVER
MOUSE_DRIVER
# else
mouse_static_stddrv
# endif
));
#endif

/* Get the initial bounding box. */
mouse_getbox (&full_box);

screensize (&width, &height);

top:
clrscr ();

/* Print a help line */
cputs (" d)ebug h)ide q)uit s)how j)ail");

/* Put a cross at the center of the screen. */
gotoxy (width / 2 - 3, height / 2 - 1);
#if defined(__CBM__)
cprintf ("%3u,%3u\r\n%*s\xDB", width / 2 * 8 + 4, height / 2 * 8 + 4,
width / 2, "");
#else
cprintf ("%3u,%3u\r\n%*s+", width / 2 * 8 + 4, height / 2 * 8 + 4,
width / 2, "");
#endif

/* Test loop */
ShowState (Jailed, Invisible);
do {
/* Get the current co-ordinates and button states; and, print them. */
mouse_info (&info);
gotoxy (0, 2);
cprintf (" X = %3d\r\n", info.pos.x);
cprintf (" Y = %3d\r\n", info.pos.y);
cprintf (" B1 = %c\r\n", (info.buttons & MOUSE_BTN_LEFT) ?
#ifdef __CBM__
0x5F
#else
'v'
#endif
: '^');
cprintf (" B2 = %c", (info.buttons & MOUSE_BTN_RIGHT) ?
#ifdef __CBM__
0x5F
#else
'v'
#endif
: '^');

/* Handle user input */
if (kbhit ()) {
cclearxy (1, 9, 23);
switch (tolower (C = cgetc ())) {
case 'd':
BREAK();

/* The debugger might have changed the colors.
** Restore them.
*/
#ifdef __CBM__
(void) bordercolor (COLOR_GRAY2);
(void) bgcolor (COLOR_WHITE);
(void) textcolor (COLOR_GRAY1);
#else
(void) bordercolor (COLOR_BLUE);
(void) bgcolor (COLOR_WHITE);
(void) textcolor (COLOR_BLACK);
#endif

/* The debugger changed the screen; restore it. */
goto top;

case 'h':
mouse_hide ();
ShowState (Jailed, ++Invisible);
break;

case 'j':
if (Jailed) {
mouse_setbox (&full_box);
Jailed = false;
} else {
small_box.minx = max (info.pos.x - 10, full_box.minx);
small_box.miny = max (info.pos.y - 10, full_box.miny);
small_box.maxx = min (info.pos.x + 10, full_box.maxx);
small_box.maxy = min (info.pos.y + 10, full_box.maxy);
mouse_setbox (&small_box);
Jailed = true;
}
ShowState (Jailed, Invisible);
break;

case 's':
mouse_show ();
if (Invisible) {
ShowState (Jailed, --Invisible);
}
break;

case 'q':
Done = true;
break;

default:
gotoxy (1, 9);
cprintf ("Spurious character: $%02X", C);
}
}
} while (!Done);

#if DYN_DRV
/* Uninstall and unload the driver. */
CheckError ("mouse_unload", mouse_unload ());
#else
/* Uninstall the static driver. */
CheckError ("mouse_uninstall", mouse_uninstall ());
#endif

/* Say goodbye */
cputsxy (0, height / 2 + 3, "Goodbye!");
return EXIT_SUCCESS;
}

二进制
samples/cc65-mouse-driver-demo/mouse_driver_demo.prg 查看文件


二进制
samples/cc65-mouse-driver-demo/mousedemo-upstream.prg 查看文件


+ 17
- 0
samples/cc65-mouse-driver-demo/run-vice.bat 查看文件

@@ -0,0 +1,17 @@
@echo off
setlocal

set "VICE_EXE=C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe"

if not exist "%VICE_EXE%" (
echo VICE x64sc was not found at:
echo %VICE_EXE%
exit /b 1
)

if not exist "mouse_driver_demo.prg" (
call build.bat
if errorlevel 1 exit /b 1
)

"%VICE_EXE%" -autostart "%CD%\mouse_driver_demo.prg" -autostart-warp -mouse -controlport1device 3

+ 130
- 0
samples/kernal-os-skeleton/README.md 查看文件

@@ -0,0 +1,130 @@
# Kernal-Resident C64 OS Skeleton

This sample shows a practical starting memory layout for a Commodore 64 OS that **keeps the KERNAL ROM visible** so you can continue using routines like `CINT`, `BSOUT`, `GETIN`, `LOAD`, and `SAVE`.

## Memory Plan

Use `$0001` with low bits `%110`:

- `LORAM = 0`
- `HIRAM = 1`
- `CHAREN = 1`

That gives you:

- `$A000-$BFFF` = RAM under BASIC ROM
- `$C000-$CFFF` = always-visible RAM for your OS core
- `$D000-$DFFF` = I/O visible
- `$E000-$FFFF` = KERNAL ROM visible

In practice, the commonly seen value is `$36`, but this sample preserves the upper cassette-control bits and only changes the low three banking bits.

## Recommended Layout

- `$0002`, `$00FB-$00FE` = fast zero-page workspace
- `$0200-$03FF` = vectors, buffers, light system workspace
- `$0400-$07E7` = screen RAM
- `$0801-$9FFF` = BASIC area during development, or additional RAM if you fully replace BASIC usage
- `$A000-$BFFF` = banked work RAM, modules, command buffers, filesystem workspace
- `$C000-$CFFF` = OS core and resident code
- `$E000-$FFFF` = KERNAL ROM, still callable through `$FFxx`

## Why This Is a Good First Setup

It lets you:

- keep all normal KERNAL calls available
- avoid writing your own screen, keyboard, and IEC routines on day one
- gain 8 KB of extra RAM at `$A000-$BFFF`
- keep your OS core in safe always-RAM at `$C000`

## What The Sample Does

- loads at `$0801` as a normal BASIC-start PRG
- boots through BASIC line `10 SYS2061`
- switches memory to BASIC-off / KERNAL-on / I/O-on
- writes a live work byte at `$A000` and a small signature starting at `$A001`
- uses a file buffer at `$A100` for simple shell `LOAD`/`SAVE` operations
- uses KERNAL routines for screen output and keyboard input
- lets you inspect the current banking mode and a workspace byte in `$A000`
- restores normal memory mapping before returning to BASIC

## Build

```text
build.bat
```

## Run On A C64 Or In VICE

Because this program now starts in **normal BASIC memory at `$0801`**, load and run it like a standard BASIC program:

```basic
LOAD "KERNAL_OS.PRG",8
RUN
```

If you want to jump into it manually after loading, the BASIC stub runs:

```basic
SYS 2061
```

If you are in VICE:

```text
x64sc -autostart kernal_os.prg
```

Then use these CLI commands:

- `HELP` to show the command list
- `MEM` to show `$0001` and the live work byte at `$A000`
- `INC` to increment the work byte at `$A000`
- `DUMP` to show `$A000-$A00F`
- `ZERO` to clear the first 16 bytes of the workspace
- `SIGN` to rewrite the signature starting at `$A001`
- `SCREEN` to show the current C64 screen dimensions from the KERNAL
- `DIR` to list only the filenames on the current target
- `STATUS` to read and print the drive status channel on the current target
- `INIT` to send the standard `I` initialize command to the current target
- `ECHO text` to print text and load the shell buffer at `$A100`
- `TOUCH filename` to create an empty sequential file
- `TYPE filename` to print a sequential file from the current target
- `LOAD filename` to load a sequential file into the shell buffer at `$A100`
- `SAVE filename` to save the current shell buffer from `$A100`
- `DEVICE n` or `DEV n` to set the current device number, usually `8-15`
- `DRIVE n` to set the current drive number, usually `0` or `1`
- `DEL filename` to scratch a file on the current target
- `ERASE filename` as an alias for `DEL`
- `CLS` to clear the screen
- `ABOUT` to reprint the layout explanation
- `EXIT` to restore normal memory mapping and return to BASIC

## Notes On The New File Commands

- `TYPE`, `LOAD`, and `SAVE` are implemented as **sequential-file shell commands**
- `LOAD filename` in this sample does **not** replace the running OS core with a machine-language PRG
- instead, it reads file data into the buffer at `$A100`
- `SAVE filename` writes the current buffer back out as a sequential file
- `TOUCH filename` creates an empty sequential file by opening it for write and closing it immediately
- `ECHO text` also fills the `$A100` shell buffer so it can be saved

## Simple Pipe Support

The shell now supports a simple redirection pattern for `ECHO`:

```text
ECHO HELLO WORLD > SAVE TESTFILE
```

That:

1. copies `HELLO WORLD` into the shell buffer at `$A100`
2. prints the text
3. rewrites the rest of the command line to `SAVE TESTFILE`
4. runs the save command immediately

This is intentionally small and predictable rather than a full Unix-style pipe system.

That keeps the sample safe and predictable while still showing how a KERNAL-based OS shell can manage files and a current DOS target.

+ 15
- 0
samples/kernal-os-skeleton/build.bat 查看文件

@@ -0,0 +1,15 @@
@echo off

ca65 kernal_os.asm -o kernal_os.o
if errorlevel 1 goto error

ld65 -C kernal_os.cfg kernal_os.o -o kernal_os.prg
if errorlevel 1 goto error

echo built kernal_os.prg successfully.
goto end

:error
echo build failed.

:end

+ 2274
- 0
samples/kernal-os-skeleton/kernal_os.asm
文件差异内容过多而无法显示
查看文件


+ 12
- 0
samples/kernal-os-skeleton/kernal_os.cfg 查看文件

@@ -0,0 +1,12 @@
MEMORY {
LOADADDR: start = $0800, size = $0002, type = ro, file = %O;
MAIN: start = $0801, size = $97FF, type = rw, file = %O;
}

SEGMENTS {
LOADADDR: load = LOADADDR, type = ro;
BASIC: load = MAIN, type = ro;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
BSS: load = MAIN, type = bss, define = yes;
}

二进制
samples/kernal-os-skeleton/kernal_os.o 查看文件


二进制
samples/kernal-os-skeleton/kernal_os.prg 查看文件


+ 329
- 0
skills/6510-assembly/SKILL.md 查看文件

@@ -0,0 +1,329 @@
---
name: 6510-assembly
description: >
Use this skill for any question about MOS 6510/6502 assembly language on the Commodore 64.
Covers CPU registers, instruction set, addressing modes, writing and entering ML programs,
using assemblers, the single-step simulator, and connecting ML to BASIC.
Sources: The Machine Language Book for the C64, The Advanced Machine Language Book for the C64,
The Anatomy of the C64, The Complete C64 ROM Disassembly.
---

# 6510 Assembly Language for the Commodore 64

## The 6510 CPU

The Commodore 64 uses the **MOS 6510** processor, which is a slightly modified 6502 with an
additional 8-bit I/O port at addresses $0000–$0001. It runs at:
- **NTSC**: 1,022,727 Hz (~1 MHz)
- **PAL**: 985,248 Hz (~0.985 MHz)

### CPU Registers

| Register | Size | Description |
|----------|------|-------------|
| **A** (Accumulator) | 8-bit | Primary arithmetic/logic register |
| **X** (Index X) | 8-bit | Index register, loop counter, offset |
| **Y** (Index Y) | 8-bit | Index register, loop counter, offset |
| **PC** (Program Counter) | 16-bit | Address of next instruction |
| **SP** (Stack Pointer) | 8-bit | Points into stack ($0100–$01FF) |
| **SR** (Status Register) | 8-bit | Processor flags |

### Status Register (SR) Flags

```
Bit 7 N Negative — Set if result bit 7 = 1
Bit 6 V Overflow — Set on signed arithmetic overflow
Bit 5 - (always 1)
Bit 4 B Break — Set when BRK instruction executed
Bit 3 D Decimal — Decimal mode (BCD arithmetic)
Bit 2 I IRQ Mask — 1 = IRQ disabled
Bit 1 Z Zero — Set if result = 0
Bit 0 C Carry — Set on unsigned arithmetic carry/borrow
```

---

## Addressing Modes

| Mode | Syntax | Bytes | Example | Description |
|------|--------|-------|---------|-------------|
| Implied | — | 1 | `CLC` | Operand implied by opcode |
| Accumulator | A | 1 | `LSR A` | Operand is accumulator |
| Immediate | #$nn | 2 | `LDA #$41` | Literal value |
| Zero Page | $nn | 2 | `LDA $FB` | Byte in zero page (fast!) |
| Zero Page,X | $nn,X | 2 | `LDA $FB,X` | Zero page + X |
| Zero Page,Y | $nn,Y | 2 | `LDX $FB,Y` | Zero page + Y |
| Absolute | $nnnn | 3 | `LDA $C000` | 16-bit address |
| Absolute,X | $nnnn,X | 3 | `LDA $C000,X` | 16-bit + X |
| Absolute,Y | $nnnn,Y | 3 | `LDA $C000,Y` | 16-bit + Y |
| Indirect | ($nnnn) | 3 | `JMP ($0314)` | Jump via pointer |
| (Indirect,X) | ($nn,X) | 2 | `LDA ($FC,X)` | Zero-page ptr + X (pre-indexed) |
| (Indirect),Y | ($nn),Y | 2 | `LDA ($FC),Y` | Zero-page ptr, then + Y (post-indexed) |
| Relative | $nn | 2 | `BEQ $+5` | Branch offset (-128 to +127) |

**Performance note**: Zero-page addressing is 1 cycle faster than absolute and uses 1 fewer byte.
Use zero page for frequently accessed variables.

---

## Complete Instruction Set Reference

### Load/Store Instructions
```
LDA Load Accumulator N Z
LDX Load X Register N Z
LDY Load Y Register N Z
STA Store Accumulator
STX Store X Register
STY Store Y Register
```

### Transfer Instructions
```
TAX Transfer A to X N Z
TAY Transfer A to Y N Z
TXA Transfer X to A N Z
TYA Transfer Y to A N Z
TSX Transfer SP to X N Z
TXS Transfer X to SP
```

### Arithmetic Instructions
```
ADC Add with Carry N V Z C
SBC Subtract with Carry N V Z C
INC Increment Memory N Z
INX Increment X N Z
INY Increment Y N Z
DEC Decrement Memory N Z
DEX Decrement X N Z
DEY Decrement Y N Z
```

**Important**: Always use `CLC` before `ADC` and `SEC` before `SBC` unless chaining multi-byte math.

### Logical Instructions
```
AND AND with Accumulator N Z
ORA OR with Accumulator N Z
EOR XOR with Accumulator N Z
BIT Bit Test N V Z
```

### Shift and Rotate
```
ASL Arithmetic Shift Left N Z C (multiply by 2)
LSR Logical Shift Right N Z C (divide by 2)
ROL Rotate Left N Z C (shift through carry)
ROR Rotate Right N Z C (shift through carry)
```

### Compare Instructions
```
CMP Compare A with Memory N Z C
CPX Compare X with Memory N Z C
CPY Compare Y with Memory N Z C
```

### Branch Instructions (all relative, ±127 bytes)
```
BCC Branch if Carry Clear (C=0)
BCS Branch if Carry Set (C=1)
BEQ Branch if Equal (zero) (Z=1)
BNE Branch if Not Equal (Z=0)
BMI Branch if Minus (N=1)
BPL Branch if Plus (N=0)
BVC Branch if Overflow Clear (V=0)
BVS Branch if Overflow Set (V=1)
```

### Jump and Subroutine
```
JMP Jump (absolute or indirect)
JSR Jump to Subroutine (pushes PC-1 to stack)
RTS Return from Subroutine (pulls PC+1 from stack)
RTI Return from Interrupt (pulls SR then PC from stack)
BRK Software Break/Interrupt
```

### Stack Instructions
```
PHA Push Accumulator to Stack
PLA Pull Accumulator from Stack N Z
PHP Push Status Register to Stack
PLP Pull Status Register from Stack
```

### Flag Instructions
```
CLC Clear Carry
SEC Set Carry
CLI Clear IRQ Mask (enable IRQs)
SEI Set IRQ Mask (disable IRQs)
CLD Clear Decimal Mode
SED Set Decimal Mode
CLV Clear Overflow
NOP No Operation (2 cycles)
```

---

## Multi-Byte Arithmetic

### 16-bit Addition
```asm
; Add 16-bit values at $FB/$FC to $FD/$FE
; Result in $FB/$FC
CLC
LDA $FB
ADC $FD
STA $FB
LDA $FC
ADC $FE ; carry propagates
STA $FC
```

### 16-bit Subtraction
```asm
SEC
LDA $FB
SBC $FD
STA $FB
LDA $FC
SBC $FE
STA $FC
```

---

## Entering ML Programs

### Method 1: DATA Statements (most portable)
```basic
10 FOR I=49152 TO 49152+N : READ D : POKE I,D : NEXT
20 SYS 49152
100 DATA 169,72,32,210,255,169,73,32,210,255,96
```

### Method 2: Using a ML Monitor (SUPERMON)
```
; After loading SUPERMON:
A C000 ; assemble at $C000
LDA #$41
JSR $FFD2 ; BSOUT - print char
RTS
; Press RETURN on empty line to exit
G C000 ; execute from $C000
```

### Method 3: Using the Built-in Monitor (from BASIC)
```basic
SYS 1536 ; enter monitor (if SUPERMON at $0600)
```

---

## Linking ML to BASIC

### Simple SYS call
```basic
SYS 49152 ' call ML at $C000
SYS 49152,A,X,Y ' with register values (some monitors)
```

### Passing parameters via memory
```basic
POKE 251,LO : POKE 252,HI ' pass address in ZP
SYS 49152
RESULT = PEEK(253) ' read result back
```

### USR function hook
```
Location $0311-$0312 contains the USR vector (default: $FFB7)
POKE 785, LO : POKE 786, HI ' point USR to your routine
X = USR(VALUE) ' call from BASIC, float in FAC
```

---

## Useful ML Idioms

### Clear accumulator quickly
```asm
LDA #0 ; or: AND #0 or: EOR #$FF ; not #$FF
```

### Test if A = 0 without changing A
```asm
BEQ zero ; branch if A was 0 (after any instruction that sets Z)
```

### Delay loop (~1 second at 1MHz, NTSC)
```asm
DELAY LDX #$FF
OUTER LDY #$FF
INNER DEY
BNE INNER
DEX
BNE OUTER
RTS
```

### Copy a block of memory
```asm
; Copy $C000-$C0FF to $D000 (100 bytes example — NOT I/O area in practice!)
LDX #0
LOOP LDA $C000,X
STA $CC00,X ; adjust destination
INX
BNE LOOP ; loop 256 times
```

### Increment 16-bit pointer in zero page ($FB/$FC)
```asm
INC $FB
BNE SKIP
INC $FC
SKIP ...
```

---

## Common Pitfalls

1. **Forgetting CLC/SEC before ADC/SBC** — results will be off by 1
2. **Branch out of range** — branches are ±127 bytes; use JMP workaround for longer distances
3. **Stack overflow** — only 256 bytes of stack; watch nested JSR depth
4. **I/O area at $D000** — reading/writing here when I/O is banked in hits chip registers, not RAM
5. **Decimal mode** — the 6510 supports BCD; make sure D flag is clear for normal math
6. **JMP ($nnFF) bug** — the 6502/6510 has a page-crossing bug with indirect JMP: high byte is fetched from $nn00, not ($nnFF)+1
7. **Forgetting RTI vs RTS** — IRQ handlers must use RTI; subroutines use RTS

---

## 6510-Specific: I/O Port ($0000/$0001)

The 6510 has a built-in 8-bit I/O port multiplexed at $0000 (direction) and $0001 (data):

```
$0000 Direction: 1=output, 0=input (default: $2F = %00101111)
$0001 Data port:
Bit 0: LORAM — 0=BASIC ROM off, 1=on
Bit 1: HIRAM — 0=Kernal ROM off, 1=on
Bit 2: CHAREN — 0=Char ROM at $D000, 1=I/O at $D000
Bit 3: Cassette data output
Bit 4: Cassette switch sense (input)
Bit 5: Cassette motor control
```

To bank out ROMs and access full RAM:
```asm
LDA #$34 ; %00110100 - RAM everywhere, I/O still visible
STA $0001
```
To restore normal operation:
```asm
LDA #$37 ; %00110111 - BASIC+Kernal+I/O (normal)
STA $0001
```

+ 299
- 0
skills/basic-programming/SKILL.md 查看文件

@@ -0,0 +1,299 @@
---
name: basic-programming
description: >
Use this skill for Commodore 64 BASIC 2.0 programming, extending BASIC with new commands,
BASIC internals (tokenization, interpreter), math routines, floating-point access from ML,
and BASIC program structure.
Sources: COMPUTE!'s VIC-20 and C64 Tool Kit: BASIC, The Advanced ML Book, The Anatomy of the C64.
---

# BASIC 2.0 Programming on the Commodore 64

## BASIC Overview

The C64's BASIC 2.0 (Microsoft BASIC) resides in ROM at $A000–$BFFF. It is a tokenized
interpreter: typed commands are compressed into single-byte tokens before storage.

### Program Storage Format
Each BASIC line in memory has this structure:
```
[link_lo][link_hi][line_lo][line_hi][tokens...][00]
```
- **link**: 16-bit pointer to next line (absolute address)
- **line number**: 16-bit line number
- **tokens**: statement tokens and literal data
- **00**: end of line marker

The program ends with a double $00 byte.

### Variable Storage
Variables follow the program in memory:
1. **Numeric variables**: 7 bytes each (2-char name + 5-byte float or 4-byte int)
2. **String variables**: 7 bytes (2-char name + length + 2-byte pointer)
3. **Arrays**: variable-length (name + dimension info + elements)

---

## Key BASIC Locations

| Address | Name | Description |
|---------|------|-------------|
| $2B-$2C (43-44) | TXTTAB | Start of BASIC program text |
| $2D-$2E (45-46) | VARTAB | Start of variable area |
| $2F-$30 (47-48) | ARYTAB | Start of array area |
| $31-$32 (49-50) | STREND | End of array area |
| $33-$34 (51-52) | FRETOP | Top of free string storage |
| $37-$38 (55-56) | MEMSIZ | Top of BASIC RAM |
| $39-$3A (57-58) | CURLIN | Current executing line number ($FF = immediate mode) |
| $3B-$3C (59-60) | OLDLIN | Previous line number |
| $43-$44 (67-68) | DATPTR | Pointer into DATA statements |
| $60-$66 (96-102) | FAC#1 | Floating-point accumulator 1 (5 bytes+sign+exp) |
| $61-$66 (97-102) | FACEXP | FAC1 exponent and mantissa |
| $69-$6E (105-110) | FAC#2 | Floating-point accumulator 2 |

---

## BASIC Token Table

BASIC keywords are stored as single bytes ($80–$CB) in tokenized programs.

| Token | Keyword | Token | Keyword |
|-------|---------|-------|---------|
| $80 | END | $A0 | TAB( |
| $81 | FOR | $A1 | TO |
| $82 | NEXT | $A2 | FN |
| $83 | DATA | $A3 | SPC( |
| $84 | INPUT# | $A4 | THEN |
| $85 | INPUT | $A5 | NOT |
| $86 | DIM | $A6 | STEP |
| $87 | READ | $A7 | + |
| $88 | LET | $A8 | - |
| $89 | GOTO | $A9 | * |
| $8A | RUN | $AA | / |
| $8B | IF | $AB | ↑ (power) |
| $8C | RESTORE | $AC | AND |
| $8D | GOSUB | $AD | OR |
| $8E | RETURN | $AE | > |
| $8F | REM | $AF | = |
| $90 | STOP | $B0 | < |
| $91 | ON | $B1 | SGN |
| $92 | WAIT | $B2 | INT |
| $93 | LOAD | $B3 | ABS |
| $94 | SAVE | $B4 | USR |
| $95 | VERIFY | $B5 | FRE |
| $96 | DEF | $B6 | POS |
| $97 | POKE | $B7 | SQR |
| $98 | PRINT# | $B8 | RND |
| $99 | PRINT | $B9 | LOG |
| $9A | CONT | $BA | EXP |
| $9B | LIST | $BB | COS |
| $9C | CLR | $BC | SIN |
| $9D | CMD | $BD | TAN |
| $9E | SYS | $BE | ATN |
| $9F | OPEN | $BF | PEEK |
| (more) | CLOSE | $C0 | LEN |
| $C1 | STR$ | $C2 | VAL |
| $C3 | ASC | $C4 | CHR$ |
| $C5 | LEFT$ | $C6 | RIGHT$ |
| $C7 | MID$ | $C8 | GO |

---

## BASIC Floating-Point Format

BASIC stores all numbers in 5-byte floating-point format:

```
Byte 0: Exponent (excess-128, 0=zero)
Byte 1: Mantissa byte 1 (bit 7 is sign: 0=positive, 1=negative)
Byte 2: Mantissa byte 2
Byte 3: Mantissa byte 3
Byte 4: Mantissa byte 4
```

Value = (–1)^sign × 0.1mmmmm... × 2^(exp–128)

### Using BASIC Math from ML

The BASIC math routines can be called from machine language. The floating-point
accumulator (FAC1) is at $61–$66.

Key math routines:
| Address | Routine | Description |
|---------|---------|-------------|
| $BC0C | GIVAYF | Convert integer in A/Y to FAC1 |
| $B391 | FADDH | Add 0.5 to FAC1 |
| $B867 | FMULT | Multiply FAC1 by FAC2 |
| $BB07 | FDIVT | Divide FAC2 by FAC1 |
| $B849 | FADD | Add FAC2 to FAC1 |
| $B850 | FSUB | Subtract FAC1 from FAC2 |
| $B8D4 | LOG | LOG of FAC1 → FAC1 |
| $BF11 | SQR | Square root of FAC1 → FAC1 |
| $B9EA | SIN | Sine of FAC1 (radians) |
| $BA28 | COS | Cosine of FAC1 |
| $BB0F | TAN | Tangent of FAC1 |
| $AE33 | AYINT | Convert FAC1 to 2-byte integer in $14-$15 |
| $B7F7 | GETADR | Convert FAC1 to 16-bit address in $14-$15 |
| $BD7E | FOUT | Convert FAC1 to ASCII string at $0100 |
| $B395 | MOVFM | Move 5-byte float from (A,Y) to FAC1 |
| $BBD4 | MOVMF | Move FAC1 to 5 bytes at (A,Y) |

---

## Extending BASIC with New Commands

The BASIC interpreter can be extended by patching the **BASIC main loop vectors**:

### RAM Vectors Involved
| Address | Vector | Default | Purpose |
|---------|--------|---------|---------|
| $0300-$0301 | IERROR | $E38B | Error handler |
| $0302-$0303 | IMAIN | $A483 | Main interpreter loop |
| $0304-$0305 | ICRNCH | $A57C | Tokenize (crunch) line |
| $0306-$0307 | IQPLOP | $A71A | De-tokenize for LIST |
| $0308-$0309 | IGONE | $A7E4 | Execute statement |
| $030A-$030B | IEVAL | $AE86 | Evaluate expression |

### Method 1: Patch IGONE (execute vector)

When BASIC encounters an unrecognized token or wants to execute a statement,
it goes through IGONE ($0308/$0309). Patch this to intercept commands.

```asm
; Patch BASIC execute vector to add new commands
LDA #<NEWCMD
STA $0308
LDA #>NEWCMD
STA $0309
RTS

NEWCMD ; First, check for our keyword
; In BASIC, current token is in A when we arrive here
CMP #$FE ; some unused token we assigned
BEQ DO_NEWCMD
JMP $A7E4 ; else: jump to original IGONE

DO_NEWCMD:
; parse parameters from BASIC line
; then execute command
RTS
```

### Method 2: Using the CHRGET routine

`CHRGET` ($0073/$0074) gets the next character from the BASIC text.
After calling, A contains the next PETSCII character or token.

```asm
; Get next character from BASIC program text
JSR $0073 ; CHRGET — increments and reads next char
; A now has next character/token, with Z flag set if it's a space
```

### Defining New Keyword with Token

Full procedure from _The Advanced ML Book_ — adding a `REPEAT` command:
1. Intercept ICRNCH to recognize the keyword text and replace with a token byte
2. Intercept IQPLOP to de-tokenize back to text for LIST
3. Intercept IGONE to execute when the token is encountered

---

## Useful BASIC Tricks

### Fast Screen Clear
```basic
PRINT CHR$(147) ' Home + clear (PETSCII 147)
```

### Peeking Inside Running Program
```basic
PRINT PEEK(57):PRINT PEEK(58) ' Current line number low/high = PEEK(57)+PEEK(58)*256
```

### Finding Free Memory
```basic
PRINT FRE(0) ' Free BASIC RAM in bytes
```

### Placing Program at Custom Address
```basic
POKE 43,1 ' Set TXTTAB low byte to 1 (start at $0801+offset)
POKE 44,N ' High byte: program starts at N*256
NEW ' Initialize BASIC for new location
```

### Calling ML from BASIC with SYS Register Passing
```basic
POKE 780, A_VAL ' $030C — A register value for SYS
POKE 781, X_VAL ' $030D — X register
POKE 782, Y_VAL ' $030E — Y register
SYS ADDRESS
RESULT = PEEK(780) ' A register after SYS
```

---

## BASIC Error Numbers

| Error | Number | Message |
|-------|--------|---------|
| 1 | TOO MANY FILES | |
| 2 | FILE OPEN | |
| 3 | FILE NOT OPEN | |
| 4 | FILE NOT FOUND | |
| 5 | DEVICE NOT PRESENT | |
| 6 | NOT INPUT FILE | |
| 7 | NOT OUTPUT FILE | |
| 8 | MISSING FILE NAME | |
| 9 | ILLEGAL DEVICE NUMBER | |
| 10 | NEXT WITHOUT FOR | |
| 11 | SYNTAX | |
| 12 | RETURN WITHOUT GOSUB | |
| 13 | OUT OF DATA | |
| 14 | ILLEGAL QUANTITY | |
| 15 | OVERFLOW | |
| 16 | OUT OF MEMORY | |
| 17 | UNDEF'D STATEMENT | |
| 18 | BAD SUBSCRIPT | |
| 19 | REDIM'D ARRAY | |
| 20 | DIVISION BY ZERO | |
| 21 | ILLEGAL DIRECT | |
| 22 | TYPE MISMATCH | |
| 23 | STRING TOO LONG | |
| 24 | FILE DATA | |
| 25 | FORMULA TOO COMPLEX | |
| 26 | CAN'T CONTINUE | |
| 27 | UNDEF'D FUNCTION | |
| 28 | VERIFY | |
| 29 | LOAD | |

### Triggering BASIC Error from ML
```asm
; Cause BASIC error N from machine language
LDX #N ; error number
JMP $A437 ; BASIC error handler entry
```

---

## PETSCII Control Codes (CHR$() values)

| Code | Effect | Code | Effect |
|------|--------|------|--------|
| 5 | White text | 156 | Purple text |
| 13 | RETURN | 157 | Cursor left |
| 14 | Lower case | 158 | Yellow text |
| 17 | Cursor down | 159 | Cyan text |
| 18 | Reverse on | 28 | Red text |
| 19 | Home | 29 | Cursor right |
| 20 | Delete | 30 | Green text |
| 28 | Red text | 31 | Blue text |
| 29 | Cursor right | 144 | Black text |
| 30 | Green text | 145 | Cursor up |
| 31 | Blue text | 146 | Reverse off |
| 133-140 | F1-F8 keys | 147 | Clear screen |
| 148 | Insert | 149 | Brown text |
| 150 | LT Red text | 151 | Dark grey |
| 152 | Grey | 153 | LT green |
| 154 | LT blue | 155 | LT grey |

+ 312
- 0
skills/compiler-design/SKILL.md 查看文件

@@ -0,0 +1,312 @@
---
name: compiler-design
description: >
Use this skill for designing and implementing compilers, assemblers, and language processors
specifically for or targeting the Commodore 64 and 128. Covers lexical analysis, parsing,
code generation for 6510, cross-compilation, and building assemblers in BASIC or ML.
Sources: Compiler Design and Implementation (64 and 128), COMPUTE!'s SpeedScript source.
---

# Compiler Design and Implementation for the C64/128

## Overview

Writing a compiler or assembler on (or for) the Commodore 64 requires special consideration for:
- **Memory constraints**: 64KB total, ~38KB for BASIC programs, ~4KB free upper RAM
- **Speed**: 1 MHz CPU — interpreter overhead is expensive; native code is essential
- **Target architecture**: 6510 with its register-sparse, accumulator-centric ISA
- **Output format**: C64 `.prg` files (2-byte load address header + raw binary)

---

## Assembler Design on the C64

### Two-Pass Assembly

Standard two-pass technique (used by all period C64 assemblers):

**Pass 1 — Symbol collection**:
1. Scan source code line by line
2. Record each label and its current address in a symbol table
3. Count instruction bytes to advance the address counter
4. Do NOT produce output

**Pass 2 — Code generation**:
1. Re-scan source code
2. Look up all labels/symbols in the symbol table
3. Emit object bytes to output buffer or file
4. Report unresolved symbols as errors

### Symbol Table Implementation

For a C64 assembler written in BASIC or ML, the symbol table is typically:
- A sorted array of name/value pairs (use binary search for speed)
- Names limited to 6-8 characters to conserve memory
- Values stored as 16-bit addresses

```basic
' Simple symbol table in BASIC (array-based)
DIM SYMNAM$(100) ' symbol names
DIM SYMVAL%(100) ' 16-bit values (integer)
NSYM% = 0

' Add symbol
SYMNAM$(NSYM%) = NAME$
SYMVAL%(NSYM%) = VALUE%
NSYM% = NSYM% + 1

' Find symbol (linear search)
FOR I = 0 TO NSYM%-1
IF SYMNAM$(I) = NAME$ THEN FOUND = SYMVAL%(I) : GOTO FOUND_LABEL
NEXT I
' not found
```

### 6510 Instruction Encoding

Each instruction consists of an opcode byte followed by 0, 1, or 2 operand bytes.
The assembler must map mnemonic + addressing mode → opcode byte.

```
Encoding pattern for most 6502 instructions:
Bits 7-5: instruction group
Bits 4-2: addressing mode
Bits 1-0: instruction select within group

Groups:
aaa=000: BIT, JMP, JMP(), STY, LDY, CPY, CPX
aaa=001: ORA, AND, EOR, ADC, STA, LDA, CMP, SBC
aaa=010: ASL, ROL, LSR, ROR, STX, LDX, DEC, INC

Addressing mode encoding (for group 01):
000: (zp,X) 001: zp 010: #imm 011: abs
100: (zp),Y 101: zp,X 110: abs,Y 111: abs,X
```

### Expression Evaluator

An assembler's expression evaluator handles operands like `LABEL+2`, `$C000+OFFSET`, `>ADDR`:

```basic
' Operators to support:
' + - * / (arithmetic)
' AND OR EOR NOT (bitwise)
' < (low byte), > (high byte)
' Precedence: NOT > */ > +- > AND > OR/EOR
```

### Forward Reference Handling

When a label is referenced before its definition:
1. Emit a placeholder byte (typically $00 $00)
2. Record the location and label name in a fixup table
3. After Pass 1 completes, apply fixups using the resolved symbol table

---

## Lexical Analysis (Tokenizer)

The first stage of any compiler — breaking source into tokens.

### Token Types for an Assembler
```
LABEL — identifier followed by ':'
OPCODE — recognized mnemonic (LDA, STA, etc.)
DIRECTIVE — assembler directive (.BYTE, .WORD, .TEXT, *= etc.)
NUMBER — decimal ($nnn hex, %nnn binary, 'c' char literal)
STRING — quoted text "..."
OPERATOR — + - * / < > = ( )
COMMA — ,
NEWLINE — end of logical line
EOF — end of input
```

### Efficient Lexer in ML

```asm
; Simple character classifier for assembler lexer
; Input: A = character
; Output: A = token class (0=whitespace, 1=alpha, 2=digit, 3=operator, 4=EOL)

CLASSIFY:
CMP #$20 ; space
BEQ IS_SPACE
CMP #$0D ; CR
BEQ IS_EOL
CMP #$30 ; '0'
BMI IS_OP
CMP #$3A ; past '9'
BMI IS_DIGIT
CMP #$41 ; 'A'
BMI IS_OP
CMP #$5B ; past 'Z'
BMI IS_ALPHA
; default: operator
IS_OP LDA #3 : RTS
IS_SPACE LDA #0 : RTS
IS_EOL LDA #4 : RTS
IS_DIGIT LDA #2 : RTS
IS_ALPHA LDA #1 : RTS
```

---

## Parser Design (Recursive Descent)

A recursive-descent parser is ideal for the C64's memory constraints because:
- Small code size (each rule is a subroutine)
- No separate parse table needed
- Easy to hand-code in ML

### Grammar for a Simple BASIC-like Language

```
program → statement*
statement → LET var '=' expr NEWLINE
| PRINT expr NEWLINE
| IF expr THEN statement
| GOTO number
| FOR var '=' expr TO expr [STEP expr]
| NEXT [var]
| END

expr → term (('+' | '-') term)*
term → factor (('*' | '/') factor)*
factor → NUMBER | STRING | VAR | '(' expr ')' | '-' factor | NOT factor
```

### Parser Subroutine Template (ML)
```asm
; Parse an expression; result in FAC1 (using BASIC math)
; Returns with carry set on error

PARSE_EXPR:
JSR PARSE_TERM ; parse first term
BCS PERR
EXPR_LOOP:
JSR PEEK_TOKEN ; look at next token
CMP #TOK_PLUS
BEQ EXPR_ADD
CMP #TOK_MINUS
BEQ EXPR_SUB
RTS ; done: no more + or -
EXPR_ADD:
JSR NEXT_TOKEN ; consume '+'
JSR SAVE_FAC1 ; save left side
JSR PARSE_TERM
BCS PERR
JSR FADD ; FAC1 = left + FAC1
BCC EXPR_LOOP
PERR SEC : RTS
```

---

## Code Generation for 6510

### Register Allocation Strategy

The 6510 has only 3 registers (A, X, Y) and no general-purpose registers. Effective code generation strategies:

1. **Accumulator-primary**: Keep the most recent value in A; use X/Y for indices and loop counters
2. **Zero-page variables**: Allocate frequently used compiler temporaries in zero page ($FB-$FE free)
3. **Stack-based expression evaluation**: For complex expressions, use the hardware stack (PHA/PLA)
4. **Inline vs. subroutine**: For short sequences (≤8 bytes), inline is faster; longer sequences justify JSR

### Expression Code Generation Example

For `A + B * C` (where A, B, C are zero-page variables):
```asm
; Generated code for: A + B * C
LDA B ; load B
STA TEMP ; save
LDA C ; load C
; multiply TEMP * A (need ML multiply routine)
JSR MULTIPLY ; result in A (low byte)
CLC
ADC A_VAR ; add A
STA RESULT
```

### Peephole Optimization

Common optimizations for 6510 code generation:

| Pattern | Optimized |
|---------|-----------|
| `STA $xx; LDA $xx` | `STA $xx` (remove redundant load) |
| `LDA #0` | `LDA #0` → prefer `AND #0` when flags needed |
| `TAX; TXA` | Remove both (no-op) |
| `PHA; PLA` | Remove both if A not changed |
| Branch over branch | Convert to opposite-condition branch |

---

## Building the C64 `.PRG` File Format

A C64 program file begins with a 2-byte load address (little-endian), followed by raw binary:

```asm
; Assembler output format for a file that loads at $C000:
.BYTE $00, $C0 ; load address: $C000 (low byte first)
.BYTE <code> ; raw binary from $C000 onward
```

To generate from BASIC:
```basic
' Write PRG file with load address $C000 (49152)
OPEN 1,8,1,"MYPRG,P,W" ' SA=1 for raw PRG output
PRINT#1, CHR$(0) CHR$(192) ' load address low, high
' ... write code bytes ...
CLOSE 1
```

---

## SpeedScript Architecture (Real-World Example)

COMPUTE!'s SpeedScript is a complete word processor written in assembly — a model for structured C64 application design:

**Memory layout**:
- `$0200–$02FF`: I/O buffer and workspace
- `$033C–$03FB`: Cassette buffer (used for printer spooling)
- `$C000–$CFFF`: Main application code (4KB upper RAM)
- `$0400–$07FF`: Screen display buffer
- `$D800–$DBFF`: Color RAM (controlled directly)

**Key architectural patterns**:
1. **IRQ for keyboard** — custom IRQ handler for responsive input
2. **Modular subroutines** — each function (cursor move, insert, delete) is a separate JSR
3. **Kernal for I/O** — uses standard Kernal OPEN/CLOSE/BASIN/BSOUT for file operations
4. **Direct VIC-II control** — manipulates screen and color RAM directly for speed
5. **Zero-page workspace** — all frequently used pointers and counters in zero page

---

## Cross-Compilation Considerations

When building a compiler that **targets** the C64 (runs on a modern machine, produces C64 code):

1. **Little-endian 16-bit values** throughout
2. **Memory model**: Assume program loads at $0801 (BASIC stub) or $C000 (pure ML)
3. **Calling conventions** (for generated subroutines):
- Parameters: A (1 byte), X/Y (2 bytes combined), or zero page
- Return values: A (byte), A/Y (16-bit, A=low), FAC1 (float)
4. **Stack depth**: Max recursion ~20 levels (stack is only 256 bytes)
5. **ROM routines**: Can JSR to Kernal at $FFxx from generated code — document dependencies

### BASIC Line Header for Machine Code Stubs
```
; BASIC stub that does SYS 49152 when RUN
$0801: $0B,$08 ; link to next line = $080B
$0A,$00 ; line 10
$9E ; SYS token
$20,"49152",$00 ; " 49152" as text
$080B: $00,$00 ; end of BASIC program
; Code follows at $080D or $C000
```

Minimal BASIC stub loader (5-line BASIC):
```basic
2018 SYS 2074 ' must be at line 2018 for standard stub
```

+ 328
- 0
skills/disk-io-1541/SKILL.md 查看文件

@@ -0,0 +1,328 @@
---
name: disk-io-1541
description: >
Use this skill for all 1541 disk drive programming on the Commodore 64.
Covers DOS commands, file types (sequential, relative, program), direct access,
error handling, the BAM, directory structure, and disk utilities.
Sources: The Anatomy of the 1541 Disk Drive (Revised Edition), COMPUTE!'s Mapping the C64.
---

# 1541 Disk Drive Programming

## Overview

The **VIC-1541** disk drive is a single-sided, single-density (SS/SD) 5.25" floppy drive connected
to the C64 via the **serial bus** (IEC bus). It contains its own 6502 CPU and 2KB of RAM.

- **Device number**: Default 8 (configurable 8-11)
- **Disk capacity**: 170KB (664 blocks free on a formatted disk)
- **Tracks**: 35 (numbered 1-35)
- **Sectors per track**: 17-21 (varies by zone)
- **Block size**: 254 usable bytes per sector (2 bytes used as track/sector link)
- **Communication**: Via Kernal's serial bus routines

### Track/Sector Layout
| Tracks | Sectors/Track | Cumulative Sectors |
|--------|---------------|--------------------|
| 1-17 | 21 | 0-356 |
| 18-24 | 19 | 357-490 |
| 25-30 | 18 | 491-598 |
| 31-35 | 17 | 599-682 |
- **Directory track**: Track 18 (always)
- **BAM**: Track 18, Sector 0

---

## Logical File Model

Every disk operation goes through **logical files**. The steps are:
1. `SETLFS` — assign logical file number, device, secondary address
2. `SETNAM` — assign filename
3. `OPEN` — open the file
4. `CHKIN`/`CHKOUT` — redirect input/output to the file
5. `BASIN`/`BSOUT` — read/write data
6. `CLOSE` — close the file

From BASIC, use: `OPEN LFN, DEV, SA, "FILENAME"`

---

## File Types

| Type | SA Range | Description |
|------|----------|-------------|
| Program | 0-1 | Machine language or BASIC programs |
| Sequential | 2 | Sequential data files (read/write, not both at once) |
| Relative | any+SA | Fixed-length record random-access files |
| Command | 15 | Command channel (send DOS commands, read error) |

---

## BASIC Disk Operations

### Load and Save Programs
```basic
LOAD "FILENAME",8 ' Load BASIC program
LOAD "FILENAME",8,1 ' Load at original address (ML programs)
SAVE "FILENAME",8 ' Save BASIC program
SAVE "@:FILENAME",8 ' Save and overwrite existing file
VERIFY "FILENAME",8 ' Verify saved file matches memory
```

### Directory
```basic
LOAD "$",8 ' Load directory as BASIC program
LIST ' Display directory
```

### Sending DOS Commands
```basic
OPEN 15,8,15,"COMMAND" ' Open command channel with command
CLOSE 15 ' Close command channel
```

### Reading the Error Channel
```basic
10 OPEN 15,8,15 : REM Open command channel
20 INPUT#15,EN,EM$,ET,ES : REM Read error: number,message,track,sector
30 PRINT EN;EM$;ET;ES
40 CLOSE 15
```
The error channel should be read after every disk operation.

---

## DOS Commands

### Formatting
```basic
OPEN 15,8,15,"N0:DISKNAME,ID" : CLOSE 15
' N0: = NEW on drive 0; ID = 2-char disk ID (e.g., "A1")
' WARNING: Destroys all data on disk
```

### Scratching (Deleting) Files
```basic
OPEN 15,8,15,"S0:FILENAME" : CLOSE 15
' Wildcards supported: S0:TEST* deletes all files starting with TEST
```

### Renaming Files
```basic
OPEN 15,8,15,"R0:NEWNAME=OLDNAME" : CLOSE 15
```

### Copying Files
```basic
OPEN 15,8,15,"C0:DEST=0:SOURCE" : CLOSE 15
```

### Validating Disk (clean up)
```basic
OPEN 15,8,15,"V0:" : CLOSE 15
' Reclaims space from improperly closed files
```

### Initializing Disk
```basic
OPEN 15,8,15,"I0:" : CLOSE 15
' Re-reads BAM; use after swapping disks
```

### Wildcards
- `*` — matches any sequence of characters
- `?` — matches any single character
```basic
OPEN 15,8,15,"S0:TEST*" : CLOSE 15 ' scratch all TEST files
```

---

## Sequential File Access

Sequential files are read or written **in order only** (not random access).

### Writing a Sequential File
```basic
10 OPEN 1,8,2,"MYDATA,S,W" ' LFN=1, DEV=8, SA=2, write sequential
20 PRINT#1,"LINE 1 DATA"
30 PRINT#1,"LINE 2 DATA"
40 PRINT#1,NUMBER ' Numbers also work
50 CLOSE 1
```

### Reading a Sequential File
```basic
10 OPEN 1,8,2,"MYDATA,S,R" ' SA=2, read sequential
20 IF ST<>0 THEN 60 ' ST = status byte; non-zero = EOF/error
30 INPUT#1,LINE$
40 PRINT LINE$
50 GOTO 20
60 CLOSE 1
```

### Appending to a Sequential File
```basic
OPEN 1,8,2,"MYDATA,S,A" ' SA=2 with ,A flag appends
```

---

## Relative File Access

Relative files provide **random access** to fixed-length records.

### Creating a Relative File
```basic
10 OPEN 2,8,2,"MYREL,L," + CHR$(RECLEN)
' RECLEN = record length in bytes (1-254)
```

### Positioning the Record Pointer
```basic
20 OPEN 15,8,15
30 RN = 5 ' desired record number (1-based)
40 LO = (RN-1) AND 255
50 HI = INT((RN-1)/256)
60 PRINT#15,"P" CHR$(2) CHR$(LO) CHR$(HI) CHR$(0)
' Channel 2 (the data file's SA), then record# low, high, byte offset
```

### Reading/Writing Records
```basic
' After positioning:
PRINT#2, FIELD1$, FIELD2$ ' write
INPUT#2, FIELD1$, FIELD2$ ' read (re-position first)
```

---

## Error Codes

| Code | Message | Meaning |
|------|---------|---------|
| 00 | OK | No error |
| 01 | FILES SCRATCHED | Normal after SCRATCH |
| 20 | READ ERROR | Block header not found |
| 21 | READ ERROR | No sync character found |
| 22 | READ ERROR | Data block not found |
| 23 | READ ERROR | Checksum error in data |
| 24 | READ ERROR | Byte decoding error |
| 25 | WRITE ERROR | Write verify error |
| 26 | WRITE PROTECT ON | Write-protect tab covered |
| 27 | READ ERROR | Checksum error in header |
| 28 | WRITE ERROR | Long data block |
| 29 | DISK ID MISMATCH | Wrong disk in drive |
| 30 | SYNTAX ERROR | DOS command syntax error |
| 31 | SYNTAX ERROR | Invalid command |
| 32 | SYNTAX ERROR | Long line |
| 33 | SYNTAX ERROR | Invalid filename |
| 34 | SYNTAX ERROR | No file given |
| 39 | SYNTAX ERROR | Invalid command |
| 50 | RECORD NOT PRESENT | Past end of relative file |
| 51 | OVERFLOW IN RECORD | Wrote past end of record |
| 52 | FILE TOO LARGE | Relative file too large |
| 60 | WRITE FILE OPEN | File still open for writing |
| 61 | FILE NOT OPEN | File not open |
| 62 | FILE NOT FOUND | File does not exist |
| 63 | FILE EXISTS | File already exists (overwrite with @) |
| 64 | FILE TYPE MISMATCH | Wrong file type for operation |
| 65 | NO BLOCK | Block already allocated |
| 66 | ILLEGAL TRACK OR SECTOR | Bad T/S reference |
| 67 | ILLEGAL SYSTEM T OR S | Bad system track/sector |
| 70 | NO CHANNEL | No free logical channel |
| 71 | DIR ERROR | Directory error (use VALIDATE) |
| 72 | DISK FULL | No free blocks |
| 73 | CBM DOS Vxx.x | Power-on/INITIALIZE message |
| 74 | DRIVE NOT READY | No disk or drive error |

---

## Direct Block Access

The 1541 allows reading/writing individual sectors directly.

### Block-Read
```basic
OPEN 15,8,15
OPEN 2,8,2,"#" ' open direct-access channel
PRINT#15,"B-R:2 0 " TRACK " " SECTOR
' Read into drive buffer; then read bytes via GETIN or INPUT#
```

### Block-Write
```basic
' Write buffer to disk after filling via BSOUT:
PRINT#15,"B-W:2 0 " TRACK " " SECTOR
```

### Block-Pointer (set position within buffer)
```basic
PRINT#15,"B-P:2 " POS ' set byte position in buffer (0-255)
```

---

## Directory Structure on Disk

**Track 18, Sector 0 = BAM (Block Availability Map)**

BAM format:
- Bytes 0-1: Track/sector of first directory block (18/1)
- Byte 2: DOS version ('A')
- Byte 3: unused
- Bytes 4-143: BAM entries for each track (4 bytes per track × 35 tracks):
- Byte 0: free sector count for this track
- Bytes 1-3: bit map of free sectors (1=free, 0=used)
- Bytes 144-161: Disk name (padded with $A0)
- Bytes 162-163: Disk ID
- Byte 164: $A0
- Bytes 165-166: DOS version bytes ("2A")

**Track 18, Sectors 1-18 = Directory**

Each directory sector holds **8 directory entries** (32 bytes each):
- Byte 0: Track link to next directory sector (0 if none)
- Byte 1: Sector link to next directory sector
- For each of 8 entries starting at byte 2 (or 0 for entry 0):
- Byte 0: File type ($80=DEL, $81=SEQ, $82=PRG, $83=USR, $84=REL; OR $40=locked, $60=opened)
- Bytes 1-2: Starting track/sector
- Bytes 3-18: Filename (padded with $A0)
- Bytes 19-20: Side-sector block track/sector (relative files only)
- Byte 21: Record length (relative files only)
- Bytes 22-25: Unused
- Bytes 26-27: Track/sector of replacement file (during @-SAVE)
- Bytes 28-29: File size in blocks (low, high)

---

## Assembly Language Disk I/O

```asm
; Open logical file 1 to disk drive 8, secondary address 2, filename "DATA"
LDA #1 ; logical file number
LDX #8 ; device (disk)
LDY #2 ; secondary address
JSR $FFBA ; SETLFS
LDA #4 ; filename length
LDX #<FNAME
LDY #>FNAME
JSR $FFBD ; SETNAM
JSR $FFC0 ; OPEN
BCS error ; carry set = error
LDX #1 ; logical file number
JSR $FFC9 ; CHKOUT - redirect output to file
LDA #65 ; character 'A'
JSR $FFD2 ; BSOUT - write to file
JSR $FFCC ; CLRCH - restore default channels
LDA #1
JSR $FFC3 ; CLOSE

FNAME .TEXT "DATA"
```

+ 303
- 0
skills/graphics-vic2/SKILL.md 查看文件

@@ -0,0 +1,303 @@
---
name: graphics-vic2
description: >
Use this skill for all VIC-II graphics programming on the C64.
Covers text modes, bitmap modes, multicolor modes, sprites, raster interrupts,
smooth scrolling, color RAM, and the VIC-II register set.
Sources: The Anatomy of the C64, COMPUTE!'s Mapping the C64, The Advanced ML Book.
---

# VIC-II Graphics Programming

## VIC-II Chip Overview

The **MOS 6567** (NTSC) / **MOS 6569** (PAL) Video Interface Chip II is the C64's graphics processor.
It resides at addresses **$D000–$D3FF** (53248–54271) when I/O is banked in.

Key capabilities:
- 40×25 character text display (standard or multicolor)
- 320×200 high-resolution bitmap (or 160×200 multicolor bitmap)
- 8 independent hardware sprites (24×21 each, or 12×21 multicolor)
- 16 colors
- Programmable raster interrupt
- Horizontal and vertical fine scrolling (8 pixels each)
- Background color in multiple modes (up to 4 simultaneously)

---

## VIC-II Register Map ($D000–$D02E)

### Sprite Position Registers
| Address | Decimal | Register | Description |
|---------|---------|----------|-------------|
| $D000 | 53248 | SP0X | Sprite 0 X position |
| $D001 | 53249 | SP0Y | Sprite 0 Y position |
| $D002 | 53250 | SP1X | Sprite 1 X position |
| $D003 | 53251 | SP1Y | Sprite 1 Y position |
| $D004 | 53252 | SP2X | Sprite 2 X position |
| $D005 | 53253 | SP2Y | Sprite 2 Y position |
| $D006 | 53254 | SP3X | Sprite 3 X position |
| $D007 | 53255 | SP3Y | Sprite 3 Y position |
| $D008 | 53256 | SP4X | Sprite 4 X position |
| $D009 | 53257 | SP4Y | Sprite 4 Y position |
| $D00A | 53258 | SP5X | Sprite 5 X position |
| $D00B | 53259 | SP5Y | Sprite 5 Y position |
| $D00C | 53260 | SP6X | Sprite 6 X position |
| $D00D | 53261 | SP6Y | Sprite 6 Y position |
| $D00E | 53262 | SP7X | Sprite 7 X position |
| $D00F | 53263 | SP7Y | Sprite 7 Y position |
| $D010 | 53264 | MSIGX | Most significant bits of sprites' X positions (bit N = sprite N) |

### Control Registers
| Address | Decimal | Register | Description |
|---------|---------|----------|-------------|
| $D011 | 53265 | SCROLY | Vertical control register |
| $D012 | 53266 | RASTER | Raster counter / raster IRQ compare |
| $D013 | 53267 | LPENX | Light pen X (read-only) |
| $D014 | 53268 | LPENY | Light pen Y (read-only) |
| $D015 | 53269 | SPENA | Sprite enable (bit N = sprite N) |
| $D016 | 53270 | SCROLX | Horizontal control register |
| $D017 | 53271 | YXPAND | Sprite Y-expansion (bit N = sprite N) |
| $D018 | 53272 | VMCSB | VIC-II memory control |
| $D019 | 53273 | VICIRQ | Interrupt register (read/clear) |
| $D01A | 53274 | IRQMSK | Interrupt enable mask |
| $D01B | 53275 | SPBGPR | Sprite-background priority |
| $D01C | 53276 | SPMC | Sprite multicolor enable |
| $D01D | 53277 | XXPAND | Sprite X-expansion |
| $D01E | 53278 | SPSPCL | Sprite-sprite collision (clears on read) |
| $D01F | 53279 | SPBGCL | Sprite-background collision (clears on read) |
| $D020 | 53280 | EXTCOL | Border color |
| $D021 | 53281 | BGCOL0 | Background color 0 |
| $D022 | 53282 | BGCOL1 | Background color 1 (extended/multicolor) |
| $D023 | 53283 | BGCOL2 | Background color 2 (extended/multicolor) |
| $D024 | 53284 | BGCOL3 | Background color 3 (extended color text only) |
| $D025 | 53285 | SPMC0 | Sprite multicolor register 0 |
| $D026 | 53286 | SPMC1 | Sprite multicolor register 1 |
| $D027 | 53287 | SP0COL | Sprite 0 color |
| $D028 | 53288 | SP1COL | Sprite 1 color |
| $D029 | 53289 | SP2COL | Sprite 2 color |
| $D02A | 53290 | SP3COL | Sprite 3 color |
| $D02B | 53291 | SP4COL | Sprite 4 color |
| $D02C | 53292 | SP5COL | Sprite 5 color |
| $D02D | 53293 | SP6COL | Sprite 6 color |
| $D02E | 53294 | SP7COL | Sprite 7 color |

---

## $D011 — SCROLY: Vertical Control Register

```
Bit 7 RST8 — Bit 8 of raster compare (use with $D012 for lines > 255)
Bit 6 ECM — Extended Color Mode (1=on)
Bit 5 BMM — Bitmap Mode (1=on, 0=text mode)
Bit 4 DEN — Display Enable (1=display on)
Bit 3 RSEL — Row Select: 1=25 rows, 0=24 rows (borders shown)
Bit 2-0 YSCL — Fine vertical scroll (0-7)
```

Default value: `$1B` = %00011011 (display on, 25 rows, scroll=3)

## $D016 — SCROLX: Horizontal Control Register

```
Bit 7 (unused)
Bit 6 (unused)
Bit 5 RES — Always set to 1 for normal operation
Bit 4 MCM — Multicolor Mode (1=on)
Bit 3 CSEL — Column Select: 1=40 columns, 0=38 columns
Bit 2-0 XSCL — Fine horizontal scroll (0-7)
```

Default value: `$C8` = %11001000 (40 cols, scroll=0)

## $D018 — VMCSB: VIC-II Memory Control

```
Bits 7-4 VM — Video matrix base address (within current VIC-II bank)
Bits 3-1 CB — Character base address (within current VIC-II bank)
Bit 0 (unused)
```

Video matrix address = bits[7:4] × $0400 within the VIC-II bank
Character base = bits[3:1] × $0800 within the VIC-II bank

| $D018 Value | Screen RAM | Character Set |
|-------------|------------|---------------|
| $10 (default) | $0400 | $1000 (ROM chars via mirror) |
| $14 | $0400 | $1800 (ROM chars, inverted) |
| $18 | $0400 | $2000 (user char at $2000) |
| $15 | $0400 | $1400 (user chars at $1400 in Bank 0) |

---

## Display Modes

### 1. Standard Text Mode (default)
- 40×25 characters, 8×8 pixels each = 320×200 pixels
- **Screen RAM** at $0400: each byte = character code (0-255)
- **Color RAM** at $D800: each byte = foreground color (bits 0-3)
- Character set at $D000 (character ROM, 256 chars × 8 bytes = 2KB)

### 2. Multicolor Text Mode ($D016 bit 4 = 1)
- Characters with color RAM value ≥ 8 use 4 colors, 2 pixels per bit
- Characters with color RAM value 0-7 use standard 2-color mode
- Colors: BG0 ($D021), BG1 ($D022), BG2 ($D023), color RAM value AND 7

### 3. Extended Background Color Mode ($D011 bit 6 = 1)
- Only 64 character codes available (bits 6-7 select BG color)
- Four background colors: $D021–$D024
- Cannot be combined with bitmap or multicolor modes

### 4. Standard Bitmap Mode ($D011 bit 5 = 1)
- 320×200 pixels, 8000 bytes of bitmap data
- Each 8×8 cell has its own 2-color pair stored in screen RAM
- Bitmap data location: bit 3 of $D018 × $2000 within VIC-II bank
- $D018 = $18 → bitmap at $2000 (if screen at $0400)
- $D018 = $1A → bitmap at $4000 in Bank 0? No — bitmap bit is bit3 of high nybble
- Screen RAM holds color pairs: high nybble = "0" pixel color, low nybble = "1" pixel color

### 5. Multicolor Bitmap Mode ($D011 bit 5 = 1, $D016 bit 4 = 1)
- 160×200 pixels, 4 colors per 4×8 cell
- Colors: $D021 (global), $D022 (global), $D023 (global), color RAM per cell

---

## Sprite Programming

### Sprite Specifications
- **Size**: 24×21 pixels (hi-res) or 12×21 pixels (multicolor)
- **Data**: 63 bytes per sprite (3 bytes × 21 rows), stored in 64-byte blocks
- **Maximum 8 sprites** on screen simultaneously
- Hardware **X expansion** (2× wide) and **Y expansion** (2× tall) per sprite

### Setting Up a Sprite

**Step 1** — Define sprite shape data (63 bytes in a 64-byte block):
```basic
' Write sprite data to block 13 ($0340-$037F)
FOR B = 0 TO 62 : POKE 832+B, 0 : NEXT ' clear first
' Each row is 3 bytes (24 bits = 24 horizontal pixels)
POKE 832, 255 : POKE 833, 255 : POKE 834, 255 ' row 0: solid line
```

**Step 2** — Point sprite pointer to the block:
```basic
POKE 2040, 13 ' sprite 0 → block 13 ($0340)
' Sprite pointer addresses: 2040-2047 = sprites 0-7
' (at default screen RAM $0400 + $03F8-$03FF = $07F8-$07FF)
```

**Step 3** — Set sprite position:
```basic
POKE 53248, 160 ' sprite 0 X position
POKE 53249, 100 ' sprite 0 Y position
```

**Step 4** — Set color and enable:
```basic
POKE 53287, 1 ' sprite 0 color = white (1)
POKE 53269, PEEK(53269) OR 1 ' enable sprite 0 (bit 0)
```

### Sprite Screen Coordinates
- Valid X range: 0-335 (use $D010 for X > 255)
- Valid Y range: 0-247
- Sprite appears at top-left of screen when X≈24, Y≈50 (NTSC)
- Top-left of visible area: approximately X=24, Y=50

### Sprite X > 255 (using $D010)
```basic
' Set sprite 0 X to 300 (= 256 + 44)
POKE 53248, 44 ' low 8 bits
POKE 53264, PEEK(53264) OR 1 ' set MSB for sprite 0
```

### Sprite Priority ($D01B)
```basic
POKE 53275, 1 ' sprite 0 behind background characters
POKE 53275, 0 ' sprite 0 in front of background (default)
```

### Collision Detection
```basic
' Read and clear sprite-sprite collisions
C = PEEK(53278) ' bit N set if sprite N collided with another sprite
' Read and clear sprite-background collisions
C = PEEK(53279) ' bit N set if sprite N hit a background pixel
```

---

## Colors

| Code | Color | Code | Color |
|------|-------|------|-------|
| 0 | Black | 8 | Orange |
| 1 | White | 9 | Brown |
| 2 | Red | 10 | Light Red |
| 3 | Cyan | 11 | Dark Grey |
| 4 | Purple | 12 | Grey |
| 5 | Green | 13 | Light Green |
| 6 | Blue | 14 | Light Blue |
| 7 | Yellow | 15 | Light Grey |

---

## Raster Interrupts

The VIC-II generates an interrupt when the raster beam reaches the line stored in $D012 (plus bit 8 in $D011 bit 7).

NTSC: 262 lines (0-261), PAL: 312 lines (0-311). Visible screen starts around line 50.

### Setting Up a Raster IRQ
```asm
SEI
LDA #<RasterIRQ
STA $0314 ; IRQ vector low
LDA #>RasterIRQ
STA $0315 ; IRQ vector high
LDA #50 ; trigger at line 50
STA $D012
LDA $D011
AND #$7F ; clear bit 8 of raster
STA $D011
LDA #$01 ; enable raster IRQ
STA $D01A
CLI

RasterIRQ:
LDA $D019 ; read IRQ status
STA $D019 ; acknowledge (write-back clears)
; ... your raster effect code ...
JMP $EA31 ; chain to normal IRQ handler
```

### Double-Buffer Raster Trick (for stable raster)
```asm
; After acknowledging the interrupt, wait for the specific raster line
WAIT LDA $D012
CMP #100 ; target line
BNE WAIT
; Now change colors etc.
```

---

## Smooth Scrolling

### Vertical Scroll (software scroll)
1. Change `$D011` bits 0-2 (YSCL) from 7 down to 0 each frame
2. When YSCL reaches 0, physically shift screen RAM up one row, reset YSCL=7

### Horizontal Scroll (software scroll)
1. Change `$D016` bits 0-2 (XSCL) from 7 down to 0 each frame
2. When XSCL reaches 0, shift all characters in each screen row left one, reset XSCL=7

```basic
' Smooth vertical scroll - change scroll register each frame
FOR S = 7 TO 0 STEP -1
POKE 53265, (PEEK(53265) AND 248) OR S
FOR W = 1 TO 100 : NEXT ' wait
NEXT S
' Then scroll screen RAM and reset to 7
```

+ 370
- 0
skills/interrupts/SKILL.md 查看文件

@@ -0,0 +1,370 @@
---
name: interrupts
description: >
Use this skill for interrupt programming on the Commodore 64.
Covers IRQ, NMI, BRK, CIA timers, raster interrupts, custom IRQ handlers,
printer spooling, music playback routines, and stable raster techniques.
Sources: The Advanced ML Book for the C64, COMPUTE!'s Kernal Toolkit,
COMPUTE!'s Mapping the C64.
---

# Interrupt Programming on the Commodore 64

## Interrupt Types

The C64 has three interrupt types:

| Type | Vector | Description | Maskable? |
|------|--------|-------------|-----------|
| **IRQ** (Interrupt ReQuest) | $FFFE/$FFFF → $0314/$0315 | Hardware timer, VIC raster | Yes (SEI/CLI) |
| **NMI** (Non-Maskable Interrupt) | $FFFA/$FFFB → $0318/$0319 | RESTORE key, expansion port | No |
| **BRK** (Break) | Same as IRQ | Software interrupt instruction | Yes |

---

## IRQ System

### Default IRQ Behavior

The default IRQ at $EA31 performs (in order):
1. Increment jiffy timer (at $00A0-$00A2)
2. Toggle cursor blink
3. Scan keyboard
4. Test STOP key
5. Poll tape cassette
6. Update RS-232 timing
7. RTI

The IRQ is triggered approximately **60 times per second** (NTSC) or **50 times per second** (PAL) by CIA #1 Timer A.

Additionally, the VIC-II chip can generate IRQs on:
- Raster line match
- Sprite-sprite collision
- Sprite-background collision
- Light pen signal

### IRQ Sources

**CIA #1** ($DC00-$DCFF) Timer A:
- Default: generates IRQ every 1/60 second (NTSC) — value $4025 at 1 MHz

**VIC-II** ($D000-$D3FF):
- Raster IRQ when beam hits programmed scan line
- Collision detection IRQs

---

## Installing a Custom IRQ Handler

### Simple Method (chain to default)
```asm
SEI ; disable interrupts while modifying vector
LDA #<MYIRQ ; install our handler
STA $0314 ; CINV low
LDA #>MYIRQ
STA $0315 ; CINV high
CLI ; re-enable interrupts
RTS

MYIRQ ; Your code here (save/restore registers if needed)
; A, X, Y are already saved by Kernal if coming through $0314
INC $D020 ; flash border (example)
JMP $EA31 ; chain to default IRQ handler (jiffy, keyboard, etc.)
```

### Full Replacement (skip Kernal IRQ overhead)
```asm
SEI
LDA #<MYIRQ
STA $0314
LDA #>MYIRQ
STA $0315
CLI
RTS

MYIRQ PHA ; save A, X, Y ourselves
TXA
PHA
TYA
PHA
; ... your time-critical code ...
PLA
TAY
PLA
TAX
PLA
RTI ; note: RTI, NOT RTS
```

---

## CIA #1 Timer for Custom Timing

**CIA #1** is at $DC00-$DCFF.

### Timer A Registers
| Address | Name | Description |
|---------|------|-------------|
| $DC04 | TIMALO | Timer A latch/counter low byte |
| $DC05 | TIMAHI | Timer A latch/counter high byte |
| $DC0D | CIAICR | Interrupt control register |
| $DC0E | CIACRA | Control register A |

### Setting IRQ Interval
```asm
; Set CIA #1 Timer A for 1/100 second interval (NTSC: ~10226 cycles)
LDA #$4E ; 0x4E = 78 low byte of 10000 = $2710
STA $DC04 ; Timer A low latch
LDA #$27 ; high byte
STA $DC05 ; Timer A high latch
LDA #$81 ; enable Timer A interrupt
STA $DC0D ; interrupt control (bit 7=1=enable, bit 0=timer A)
LDA #$11 ; start timer, continuous, system clock source
STA $DC0E ; control register A
```

### Stopping the Default Timer (caution!)
```asm
; Disable default CIA #1 Timer A IRQ (keyboard/cursor will stop working!)
LDA #$7F ; bit 7=0 means disable
STA $DC0D ; disable timer A interrupt
```

### CIA ICR Read/Write
Reading $DC0D returns the interrupt status and **clears it**:
```
Bit 7: (read) 1=any interrupt occurred
Bit 4: FLAG interrupt
Bit 3: Serial shift register
Bit 2: Timer B
Bit 1: Timer A
Bit 0: Time of Day alarm
```
Writing $DC0D sets/clears interrupt enables:
```
Bit 7: 1=enable the bits that follow; 0=disable
Bits 0-4: individual interrupt sources
```

---

## VIC-II Raster Interrupts

### How Raster IRQ Works
The VIC-II generates an IRQ when the electron beam reaches the scan line stored in:
- $D012: Low 8 bits of target raster line
- $D011 bit 7: Bit 8 (for lines 256+)

NTSC: lines 0-261 (263 total), PAL: lines 0-311 (312 total)
Visible area begins approximately line 50 (NTSC) or line 48 (PAL).

### Setting Up Raster IRQ
```asm
SEI
; Point IRQ vector to our handler
LDA #<RASTER_IRQ
STA $0314
LDA #>RASTER_IRQ
STA $0315
; Set raster line to trigger on
LDA $D011
AND #$7F ; clear bit 8 of raster
STA $D011
LDA #100 ; trigger at line 100
STA $D012
; Enable VIC raster interrupt, disable CIA IRQ
LDA #$7F
STA $DC0D ; disable CIA #1 timer interrupt
LDA #$01
STA $D01A ; VIC IRQ enable: raster
CLI

RASTER_IRQ:
LDA $D019 ; read VIC interrupt register
STA $D019 ; acknowledge (writing 1 to bit clears it)
; Check it's really a raster IRQ
AND #$01
BEQ NOT_RASTER
; --- Your raster effect code ---
LDA #$02
STA $D020 ; change border color at this scanline
; --------------------------------
NOT_RASTER:
; Manually do jiffy work if CIA IRQ disabled
; (or use a secondary CIA timer IRQ for jiffy)
RTI
```

### Raster IRQ Stability

The basic raster IRQ can be off by ±1 scanline due to CPU interrupt latency. For demo effects requiring pixel-perfect stability:

**Double-IRQ method** (from demo scene lore):
```asm
; First IRQ fires, then sets a second IRQ for exact same line
; The second one fires at a consistent point in the raster
STAGE1 LDA #<STAGE2
STA $0314
LDA #>STAGE2
STA $0315
LDA #101 ; trigger second IRQ at next line
STA $D012
LDA #$01
STA $D019 ; ack
RTI

STAGE2 ; We are now at a stable point in line 101
LDA #<STAGE1
STA $0314
LDA #>STAGE1
STA $0315
LDA #100
STA $D012
LDA #$01
STA $D019
; ... stable raster effect ...
RTI
```

---

## NMI (Non-Maskable Interrupt)

The NMI is triggered by the **RESTORE key** or the **NMI line on the expansion port**.

Default NMI vector chain:
- Hardware: $FFFA/$FFFB → ROM at $FE47
- ROM at $FE47 → RAM at $0318/$0319

```asm
; Install custom NMI handler (catches RESTORE key)
LDA #<MYNMI
STA $0318 ; NMINV low
LDA #>MYNMI
STA $0319 ; NMINV high
RTS

MYNMI PHA
TXA
PHA
TYA
PHA
; RESTORE key was pressed — handle it
PLA
TAY
PLA
TAX
PLA
RTI
```

---

## CIA #2 Timer (NMI Source)

CIA #2 ($DD00) can generate NMIs. Useful for background tasks that should not be stoppable.

| Address | Description |
|---------|-------------|
| $DD04 | Timer A low latch |
| $DD05 | Timer A high latch |
| $DD0D | CIA #2 ICR (read=status, write=enable/disable) |
| $DD0E | CIA #2 Control Register A |

---

## Music Playback via IRQ

A common pattern for SID music playback:
```asm
; 1. Initialize music player (call once)
JSR MUSIC_INIT

; 2. Install IRQ that calls the play routine
SEI
LDA #<MUSIC_IRQ
STA $0314
LDA #>MUSIC_IRQ
STA $0315
CLI
RTS

MUSIC_IRQ:
JSR MUSIC_PLAY ; call one frame of music player
JMP $EA31 ; chain to default (keeps keyboard working)

; MUSIC_INIT and MUSIC_PLAY are from your music player (e.g., GoatTracker)
```

---

## Time of Day Clock (CIA TOD)

CIA #1 also has a real-time clock (BCD format):

| Address | Description |
|---------|-------------|
| $DC08 | TOD Tenths of seconds (BCD) |
| $DC09 | TOD Seconds (BCD) |
| $DC0A | TOD Minutes (BCD) |
| $DC0B | TOD Hours + AM/PM flag (bit 7) |

```asm
; Read current time
LDA $DC0B ; hours (latch all registers by reading hours first)
LDA $DC0A ; minutes
LDA $DC09 ; seconds
LDA $DC08 ; tenths

; Set time (write hours last to start clock)
LDA #$00 ; tenths
STA $DC08
LDA #$30 ; 30 seconds (BCD)
STA $DC09
LDA #$45 ; 45 minutes (BCD)
STA $DC0A
LDA #$12 ; 12 hours (BCD)
STA $DC0B ; writing hours starts the clock
```

---

## Printer Spooling (Background I/O via IRQ)

A technique from _The Advanced ML Book_: use the IRQ to send printer data in the background.

```asm
; Simplified spooler skeleton
; Store data in SPOOLBUF, SPOOLPTR points to current byte, SPOOLEND to end

SPOOLIRQ:
LDA SPOOLPTR
CMP SPOOLEND
BEQ DONE_SPOOLING ; nothing to send
; Send one byte to printer
LDA $DFLTO ; check if printer already busy
CMP #4 ; device 4?
BEQ SKIP_SEND
LDA (SPOOLPTR),Y ; get next byte
JSR $FFD2 ; BSOUT
INC SPOOLPTR
BNE SKIP_SEND
INC SPOOLPTR+1
SKIP_SEND:
DONE_SPOOLING:
JMP $EA31 ; chain to default IRQ
```

+ 416
- 0
skills/kernal-routines/SKILL.md 查看文件

@@ -0,0 +1,416 @@
---
name: kernal-routines
description: >
Use this skill for using the C64 Kernal (OS) routines from machine language.
Covers all Kernal jump table entries, input parameters, output values, error handling,
screen I/O, serial I/O, tape I/O, RS-232, system vectors, and interrupt handling.
Sources: COMPUTE!'s VIC-20 and C64 Tool Kit: Kernal, The Anatomy of the C64,
The Complete C64 ROM Disassembly.
---

# Kernal Routines Reference

## Overview

The **Kernal** is the C64's operating system ROM, occupying $E000–$FFFF (57344–65535).
It provides a jump table at $FF81–$FFF3 that remains accessible regardless of the current
ROM banking state. **Always call through the jump table**, never directly into ROM routines,
as addresses may differ between ROM revisions.

---

## System and Reset Routines

### $FF81 — CINT: Initialize Screen Editor
```
Input: None
Output: Screen cleared, cursor home
Clobbers: A, X, Y
Use: Called automatically at reset; call to reinitialize screen
```

### $FF84 — IOINIT: Initialize I/O Devices
```
Input: None
Output: CIA chips, VIC-II, SID reset to defaults; IRQ enabled
Clobbers: A, X, Y
```

### $FF87 — RAMTAS: Test and Initialize RAM
```
Input: None
Output: Zero page and stack cleared, tape buffer zeroed, memory size set
Clobbers: A, X, Y
```

### $FF8A — RESTOR: Restore Default Vectors
```
Input: None
Output: RAM vectors at $0314-$0333 restored to Kernal defaults
Clobbers: A, X
Note: Vectors are copied from ROM table at $E518
```

### $FF8D — VECTOR: Read or Set RAM Vectors
```
Input: Carry clear → copy RAM vectors to user area ($FB/$FC points to destination)
Carry set → copy user area to RAM vectors ($FB/$FC points to source)
X = low byte, Y = high byte of 2-byte pointer at $FB/$FC
Output: Vectors read or written
Clobbers: A, X, Y
```

### $FF90 — SETMSG: Control OS Messages
```
Input: A = flag byte
bit 7 = 1: enable BASIC error messages
bit 6 = 1: enable status messages (FOUND, SAVING, etc)
Output: Previous flag value in A
```

### $FF99 — MEMTOP: Read/Set Top of Memory
```
Input: Carry set → set: X = low byte, Y = high byte of new top address
Carry clear → read current top
Output: X = low byte, Y = high byte of top of memory
```

### $FF9C — MEMBOT: Read/Set Bottom of Memory
```
Input: Carry set → set: X = low byte, Y = high byte of new bottom
Carry clear → read current bottom
Output: X = low byte, Y = high byte of bottom of memory
```

---

## Screen I/O Routines

### $FFED — SCREEN: Return Screen Format
```
Input: None
Output: X = number of columns (40), Y = number of rows (25)
```

### $FFF0 — PLOT: Read/Set Cursor Position
```
Input: Carry set → set cursor: X = row (0-24), Y = column (0-39)
Carry clear → read cursor position
Output: (read) X = current row, Y = current column
Note: Cursor blink is reset
```

### $FFD2 — BSOUT: Output Character to Current Channel
```
Input: A = character to output (PETSCII)
Output: Character sent to current output channel (screen or file)
Clobbers: A
Note: Also accessible via KERNAL vector at $0326/$0327
```

### $FFCF — BASIN: Input Character from Current Channel
```
Input: None
Output: A = character received (PETSCII)
Note: Waits for input if keyboard; also accessible at $0324/$0325
```

### $FFE4 — GETIN: Get Character from Input Queue
```
Input: None
Output: A = character (0 if no character available)
Note: Non-blocking keyboard read; does not wait for keypress
```

### $FFE1 — STOP: Test STOP Key
```
Input: None
Output: Zero flag set if STOP key pressed
A = current value of keyboard row (matrix scan)
Note: Also restores registers if STOP held (calls CLRCH)
```

---

## File I/O: Setup Routines

### $FFBA — SETLFS: Set Logical File, Device, Command
```
Input: A = logical file number (1-127)
X = device number (0=keyboard, 1=tape, 2=RS232, 3=screen, 4-7=serial, 8-15=disk)
Y = secondary address (0-15; 255=no secondary address)
Output: Values stored in OS variables ($B8, $B9, $BA, $BB)
```

### $FFBD — SETNAM: Set Filename
```
Input: A = length of filename (0 if no name)
X = low byte of filename address
Y = high byte of filename address
Output: Filename parameters stored in OS variables
```

### $FFC0 — OPEN: Open Logical File
```
Input: Parameters set by SETLFS and SETNAM
Output: Carry clear = success
Carry set = error; A = error code
Error codes: 1=file open, 2=too many files, 4=file not found, 5=device not present, 6=not input file, 7=not output file
```

### $FFC3 — CLOSE: Close Logical File
```
Input: A = logical file number to close
Output: File closed, channels freed
Clobbers: A, X, Y
```

### $FFC6 — CHKIN: Open Channel for Input
```
Input: X = logical file number (must be open)
Output: Carry clear = success; subsequent BASIN reads from this file
Carry set = error; A = error code
```

### $FFC9 — CHKOUT: Open Channel for Output
```
Input: X = logical file number (must be open)
Output: Carry clear = success; subsequent BSOUT writes to this file
Carry set = error; A = error code
```

### $FFCC — CLRCH: Clear Input/Output Channels
```
Input: None
Output: Default channels restored (keyboard input, screen output)
Clobbers: A, X
```

### $FFE7 — CLALL: Close All Files and Channels
```
Input: None
Output: All logical files closed, channels restored to defaults
Clobbers: A, X
```

---

## File I/O: Load and Save

### $FFD5 — LOAD: Load from Device
```
Input: A = 0 for LOAD, 1 for VERIFY
X = low byte of load address (if SA=0, ignored — file provides address)
Y = high byte of load address
SA (set by SETLFS): 0 = load to file's own start address
1 = load to X/Y address
Output: Carry clear = success; X = low byte, Y = high byte of last address+1
Carry set = error; A = error code
Note: Set SETLFS with SA=0 to load at the address stored in the file header
```

### $FFD8 — SAVE: Save to Device
```
Input: A = zero-page address containing 2-byte start address pointer
X = low byte of end address + 1
Y = high byte of end address + 1
Output: Carry clear = success
Carry set = error; A = error code
Example:
LDA #<STARTADR
STA $C1
LDA #>STARTADR
STA $C2
LDA #$C1 ; ZP pointer to start address
LDX #<(ENDADR+1)
LDY #>(ENDADR+1)
JSR $FFD8
```

---

## I/O Status

### $FFB7 — READST: Read I/O Status Word
```
Input: None
Output: A = status byte (ST variable at $90)
Status bits:
Bit 0: Write timeout on serial bus
Bit 1: (serial) Byte lost
Bit 2: Short block (tape)
Bit 3: Long block (tape)
Bit 4: Unrecoverable read error (tape)
Bit 5: Checksum error (tape)
Bit 6: End of file
Bit 7: End of tape / device not present
```

---

## Serial Bus Routines

### $FFB1 — LISTEN: Command Device to Listen
```
Input: A = device address (8-30)
Output: Device commanded to LISTEN; subsequent bytes go to device
```

### $FFB4 — TALK: Command Device to Talk
```
Input: A = device address (8-30)
Output: Device commanded to TALK; subsequent bytes come from device
```

### $FF93 — SECOND: Send Secondary Address After LISTEN
```
Input: A = secondary address (0-31)
Output: Secondary address sent to listening device
```

### $FF96 — TKSA: Send Secondary Address After TALK
```
Input: A = secondary address (0-31)
Output: Secondary address sent to talking device
```

### $FFA5 — ACPTR: Input Byte from Serial Bus
```
Input: None (a device must be talking)
Output: A = byte received from serial bus
```

### $FFA8 — CIOUT: Output Byte to Serial Bus
```
Input: A = byte to send (device must be listening)
Output: Byte sent to serial bus
```

### $FFAB — UNTLK: Send UNTALK
```
Input: None
Output: UNTALK sent; talking device releases bus
```

### $FFAE — UNLSN: Send UNLISTEN
```
Input: None
Output: UNLISTEN sent; all listening devices release bus
```

---

## Timing

### $FFDB — SETTIM: Set Real-Time Clock
```
Input: A = high byte, X = middle byte, Y = low byte of 60Hz jiffy count
```

### $FFDE — RDTIM: Read Real-Time Clock
```
Input: None
Output: A = high byte, X = middle byte, Y = low byte of jiffy count
Note: Count increments 60× per second (NTSC) or 50× per second (PAL)
```

### $FFEA — UDTIM: Increment Real-Time Clock
```
Input: None
Output: Jiffy count incremented; called by IRQ handler
```

---

## RAM Vectors (Kernal and BASIC)

The Kernal uses indirect vectors in RAM. These can be patched to intercept OS calls.

| Address | Vector | Default ROM target | Purpose |
|---------|--------|--------------------|---------|
| $0314-$0315 | CINV | $EA31 | IRQ handler |
| $0316-$0317 | CBINV | $FE66 | BRK handler |
| $0318-$0319 | NMINV | $FE47 | NMI handler |
| $031A-$031B | IOPEN | $F34A | OPEN routine |
| $031C-$031D | ICLOSE | $F2C7 | CLOSE routine |
| $031E-$031F | ICHKIN | $F20E | CHKIN routine |
| $0320-$0321 | ICHKOUT | $F250 | CHKOUT routine |
| $0322-$0323 | ICLRCH | $F333 | CLRCH routine |
| $0324-$0325 | IBASIN | $F157 | BASIN (CHRIN) |
| $0326-$0327 | IBSOUT | $F1CA | BSOUT (CHROUT) |
| $0328-$0329 | ISTOP | $F6ED | STOP test |
| $032A-$032B | IGETIN | $F13E | GETIN |
| $032C-$032D | ICLALL | $F32F | CLALL |
| $032E-$032F | USRCMD | $FE66 | User command |
| $0330-$0331 | ILOAD | $F4A5 | LOAD |
| $0332-$0333 | ISAVE | $F5ED | SAVE |

### Intercepting BSOUT (Output) Example
```asm
; Install hook on BSOUT to intercept all character output
SEI
LDA $0326
STA OLDBSOUT ; save original
LDA $0327
STA OLDBSOUT+1
LDA #<MYHOOK
STA $0326
LDA #>MYHOOK
STA $0327
CLI
RTS

MYHOOK ; A = character about to be output
; do your interception here
JMP (OLDBSOUT) ; chain to original routine
OLDBSOUT .WORD $F1CA
```

---

## Kernal Usage Template (Assembly)

```asm
; Complete file write example
; 1. Set LFS
LDA #1 ; logical file 1
LDX #8 ; disk drive
LDY #2 ; SA=2 (write sequential)
JSR $FFBA ; SETLFS
; 2. Set filename
LDA #FNLEN
LDX #<FNAME
LDY #>FNAME
JSR $FFBD ; SETNAM
; 3. Open
JSR $FFC0 ; OPEN
BCS OERR ; branch on error
; 4. Redirect output
LDX #1 ; LFN
JSR $FFC9 ; CHKOUT
BCS CERR
; 5. Write data
LDX #0
WLOOP LDA DATA,X
BEQ DONE
JSR $FFD2 ; BSOUT
INX
BNE WLOOP
; 6. Clean up
DONE JSR $FFCC ; CLRCH
LDA #1
JSR $FFC3 ; CLOSE
RTS

OERR ; handle open error (A = code)
CERR ; handle channel error
FNAME .TEXT "MYFILE,S,W"
FNLEN = * - FNAME
DATA .TEXT "HELLO WORLD"
.BYTE 0
```

+ 297
- 0
skills/memory-map/SKILL.md 查看文件

@@ -0,0 +1,297 @@
---
name: memory-map
description: >
Use this skill for questions about C64 memory organization, zero page locations,
memory banking, PEEK/POKE addresses, sprite memory, screen RAM, color RAM,
protecting ML programs, and placing data/code in RAM.
Sources: COMPUTE!'s Mapping the Commodore 64, The Anatomy of the C64,
The Complete C64 ROM Disassembly.
---

# Commodore 64 Memory Map

## Complete Address Space

```
Address Decimal Contents (default banking)
----------- ------------- ----------------------------------------
$0000 0 6510 I/O direction register
$0001 1 6510 I/O port (bank control/tape/motor)
$0002 2 Unused (3 free bytes: $02)
$0003-$0004 3-4 Float→integer vector (ADRAY1)
$0005-$0006 5-6 Integer→float vector (ADRAY2)
$0007 7 Search character (BASIC)
$0008 8 Quote mode flag
$0009 9 TAB column counter
$000A 10 Load/Verify flag (0=LOAD, 1=VERIFY)
$000B 11 Input buffer pointer / subscript counter
$000C 12 Default DIM flag
$000E 14 Type flag (80=integer, 00=float)
$000F 15 DATA scan / LIST quote / memory flag
$0010 16 Subscript / FNx flag
$0011 17 0=INPUT, $40=GET, $98=READ
$0012 18 ATN sign / comparison flag
$0013 19 Current I/O prompt flag
$0014-$0015 20-21 Integer value temporary
$0016 22 Pointer to temp string stack
$0017-$0018 23-24 Last temp string vector
$0019-$0021 25-33 Stack for temporary strings
$0022-$0025 34-37 Miscellaneous work area
$0026-$002A 38-42 Product area (math)
$002B-$002C 43-44 TXTTAB — pointer to start of BASIC text
$002D-$002E 45-46 VARTAB — start of variable storage
$002F-$0030 47-48 ARYTAB — start of array storage
$0031-$0032 49-50 STREND — end of array storage (+1)
$0033-$0034 51-52 FRETOP — top of free string space
$0035-$0036 53-54 FRESPC — string utility pointer
$0037-$0038 55-56 MEMSIZ — pointer to end of memory / bottom of strings
$0039-$003A 57-58 CURLIN — current BASIC line number
$003B-$003C 59-60 OLDLIN — previous BASIC line number
$003D-$003E 61-62 OLDTXT — pointer to previous BASIC statement
$003F-$0040 63-64 DATLIN — line number of current DATA statement
$0041-$0042 65-66 DATPTR — pointer to current DATA item
$0043-$0044 67-68 INPPTR — INPUT source pointer
$0045-$0046 69-70 VARNAM — name of current variable
$0047-$0048 71-72 VARPNT — pointer to current variable
$0049-$004A 73-74 FORPNT — pointer to FOR/NEXT variable
$004B 75 OPPTR — operator precedence
$004C 76 OPMASK — mask for operator
$004D-$0050 77-80 TEMPPT — temp string and area
$0051 81 LASTPT — pointer to last temp string
$0052-$0053 82-83 TEMPST — pointer to string
$0054 84 INDEX (low), INDEX+1 (high)
$005E 94 FAC#2 (floating point accumulator #2)
$0060-$0066 96-102 FAC#1 (floating point accumulator #1, 5 bytes + sign/exp)
$006F 111 Pointer to start of string being processed
$0071-$0072 113-114 RESULT — pointer to end of string
$0073 115 Jump instruction for USR
$0074-$0075 116-117 USR function address (JSR target)
$0076 118 CHARAC — search character
$007A-$007B 122-123 FNPNT — pointer for FN functions
$00AC-$00AD 172-173 STRNG1 — string 1 pointer
$00AE-$00AF 174-175 STRNG2 — string 2 pointer
$00B7 183 FNLEN — length of filename
$00B8 184 LOGICAL — logical file number
$00B9 185 DFLTO — output device number
$00BA 186 DFLTI — input device number
$00BB 187 SA — secondary address
$00BC 188 EAL — tape end address (low)
$00BD 189 EAH — tape end address (high)
$00BE 190 TAPE1 — tape buffer pointer
$00BF 191 COUNT — tape character counter
$00C0 192 INIT — tape signal level
$00C1 193 SKPO — skip byte count
$00C2 194 BPTR — tape data pointer
$00C3-$00C4 195-196 TAPE2 — tape end buffer pointer
$00C5 197 LSTX — last key pressed (keyboard matrix row)
$00C6 198 NDX — number of characters in keyboard buffer
$00C7 199 RVS — reverse mode flag (1=reverse on)
$00C8 200 INDX — end-of-line pointer for current screen line
$00C9-$00CA 201-202 LNMX — pointer to cursor on screen
$00CB 203 SCHAR — character under cursor
$00CC 204 BLNSW — cursor blink switch (0=blink)
$00CD 205 BLNCT — countdown to cursor blink
$00CE 206 GDBLN — char under cursor (save)
$00CF 207 BLNON — cursor blink status (0=off)
$00D0 208 CRSW — flag: 0=cursor in key queue, 1=not
$00D1-$00D2 209-210 PNT — pointer to cursor line in screen RAM
$00D3 211 PNTR — column of cursor on screen
$00D4 212 QTSW — quote mode flag
$00D5 213 LNMX — screen line length (39 or 79)
$00D6 214 TBLX — current cursor row (0-24)
$00D7 215 Spare byte
$00D8 216 INSRT — insert mode count
$00D9-$00F2 217-242 LDTB1 — line link table (25 lines)
$00F3-$00F4 243-244 USER — pointer to color RAM for cursor line
$00F5-$00F6 245-246 KEYTAB — pointer to keyboard decode table
$00F7-$00F8 247-248 RIBUF — RS-232 input buffer pointer
$00F9-$00FA 249-250 ROBUF — RS-232 output buffer pointer
$00FB-$00FE 251-254 FREKZP — FREE ZERO PAGE (user programs)
$00FF 255 (used by BASIC math)
$0100-$01FF 256-511 6510 Stack (grows down from $01FF)
$0200-$0258 512-600 Basic input buffer (80 chars)
$0259-$0262 601-610 LDTB2 — open file logical-number table
$0263-$026C 611-620 LATBL — open file device-number table
$026D-$0276 621-630 SATBL — open file secondary-address table
$0277-$0280 631-640 Keyboard buffer (10 chars, FIFO)
$0281-$0282 641-642 MEMBOT — bottom of memory pointer
$0283-$0284 643-644 MEMTOP — top of memory pointer
$0285 645 TIMOUT — serial bus timeout flag
$0286 646 COLOR — current text foreground color
$0287 647 BDRCLR — border color at cursor
$0288 648 HIBASE — high byte of screen RAM base ($04 = $0400)
$0289 649 XMAX — max chars in keyboard buffer (default: 10)
$028A 650 RPTFLG — key repeat flag (0=cursor keys+INS/DEL only)
$028B 651 KOUNT — repeat delay counter
$028C 652 DELAY — repeat speed counter
$028D 653 SHFLAG — shift/ctrl/C= key flags
$028E 654 LSTSHF — previous shift key state
$028F-$0290 655-656 KEYLOG — pointer to keyboard decode logic
$0291 657 DFLTN — default input device (0=keyboard)
$0292 658 DFLTO — default output device (3=screen)
$0293 659 PRTY — serial bus parity
$0294 660 DPSW — serial bus deferred character flag
$0295 661 MSGFLG — OS message flag
$0296 662 ARGT — type of FAC#1 argument
$0297 663 ERRNUM — BASIC error number
$0298 664 ERRSAV — BASIC error line save
$0299-$029A 665-666 IOLINK — I/O start address
$029B-$029E 667-670 TRPTAB — trap vectors
$02A1-$02FF 673-767 Free RAM (87 bytes)
$0300-$0301 768-769 IERROR — indirect BASIC error vector
$0302-$0303 770-771 IMAIN — indirect BASIC main loop vector
$0304-$0305 772-773 ICRNCH — indirect BASIC crunch vector
$0306-$0307 774-775 IQPLOP — indirect BASIC LIST vector
$0308-$0309 776-777 IGONE — indirect BASIC execute vector
$030A-$030B 778-779 IEVAL — indirect BASIC expression evaluate vector
$030C 780 SAREG — A register save on SYS
$030D 781 SXREG — X register save on SYS
$030E 782 SYREG — Y register save on SYS
$030F 783 SPREG — Status register save on SYS
$0310-$0311 784-785 USRPOK — USR function vector (JMP opcode + address)
$0312-$0313 786-787 (USR vector high byte + unused)
$0314-$0315 788-789 CINV — IRQ vector (default: $EA31)
$0316-$0317 790-791 CBINV — BRK vector (default: $FE66)
$0318-$0319 792-793 NMINV — NMI vector (default: $FE47)
$031A-$031B 794-795 IOPEN — indirect OPEN vector
$031C-$031D 796-797 ICLOSE — indirect CLOSE vector
$031E-$031F 798-799 ICHKIN — indirect CHKIN vector
$0320-$0321 800-801 ICHKOUT — indirect CHKOUT vector
$0322-$0323 802-803 ICLRCH — indirect CLRCH vector
$0324-$0325 804-805 IBASIN — indirect BASIN (CHRIN) vector
$0326-$0327 806-807 IBSOUT — indirect BSOUT (CHROUT) vector
$0328-$0329 808-809 ISTOP — indirect STOP vector
$032A-$032B 810-811 IGETIN — indirect GETIN vector
$032C-$032D 812-813 ICLALL — indirect CLALL vector
$032E-$032F 814-815 USRCMD — user-defined command vector
$0330-$0331 816-817 ILOAD — indirect LOAD vector
$0332-$0333 818-819 ISAVE — indirect SAVE vector
$0334-$033B 820-827 (spare)
$033C-$03FB 828-1019 Cassette buffer (192 bytes)
$03FC-$03FF 1020-1023 (free)
$0400-$07E7 1024-2023 Screen RAM (default: 40×25 = 1000 bytes)
$07E8-$07FF 2024-2047 Sprite pointers (8 bytes, one per sprite)
$0800-$9FFF 2048-40959 BASIC program area (38912 bytes)
$A000-$BFFF 40960-49151 BASIC ROM (or RAM when banked out)
$C000-$CFFF 49152-53247 Upper RAM (always RAM, 4KB)
$D000-$D3FF 53248-54271 VIC-II chip registers (I/O mode)
$D000-$DFFF 53248-57343 Character ROM (when CHAREN=0)
$D400-$D7FF 54272-55295 SID chip registers
$D800-$DBFF 55296-56319 Color RAM (nybbles, 1000 bytes)
$DC00-$DCFF 56320-56575 CIA #1 (keyboard, joystick, IRQ timer)
$DD00-$DDFF 56576-56831 CIA #2 (serial bus, NMI, VIC bank)
$DE00-$DEFF 56832-57087 I/O area 1 (expansion port)
$DF00-$DFFF 57088-57343 I/O area 2 (expansion port)
$E000-$FFFF 57344-65535 Kernal ROM (or RAM when banked out)
```

---

## Memory Banking

The C64 uses **soft banking** via location $0001 (6510 I/O port).

| $0001 Bits 2-0 | LORAM | HIRAM | CHAREN | Memory Configuration |
|-----------------|-------|-------|--------|---------------------|
| %111 (7) | BASIC ROM | Kernal ROM | I/O | **Normal (default)** |
| %110 (6) | RAM | Kernal ROM | I/O | ML programs using Kernal |
| %101 (5) | BASIC ROM | Kernal ROM | Char ROM | Character ROM visible |
| %100 (4) | RAM | RAM | I/O | All RAM + I/O |
| %011 (3) | RAM | RAM | Char ROM | All RAM + Char ROM |
| %010 (2) | RAM | RAM | Char ROM | All RAM + Char ROM |
| %001 (1) | RAM | RAM | RAM | All 64KB RAM |
| %000 (0) | RAM | RAM | RAM | All 64KB RAM |

Direction register at $0000 should have bits 0, 1, 2, 3, 5 as outputs: `POKE 0, 47` (default).

```asm
; Bank out BASIC, keep Kernal + I/O (common for ML programs)
LDA $0001
AND #$FE ; clear bit 0 (LORAM)
STA $0001

; Restore normal
LDA $0001
ORA #$07 ; set bits 0-2
STA $0001
```

---

## VIC-II Memory Banking

The VIC-II chip sees a **16KB bank** of memory selected by CIA #2 Port A bits 0-1:

| $DD00 bits 1-0 | VIC-II Bank | Address Range |
|-----------------|-------------|---------------|
| %11 (3) | Bank 0 | $0000–$3FFF (default) |
| %10 (2) | Bank 1 | $4000–$7FFF |
| %01 (1) | Bank 2 | $8000–$BFFF |
| %00 (0) | Bank 3 | $C000–$FFFF |

**Note**: Banks 0 and 2 have Character ROM "mirrored" at offsets $1000–$1FFF and $9000–$9FFF.
This means the VIC-II sees character data even though the CPU cannot (in normal banking).

```basic
' Switch VIC to Bank 1 ($4000-$7FFF)
POKE 56576, (PEEK(56576) AND 252) OR 2
```

---

## Sprite Memory

Each sprite needs **64 bytes** of data. Sprite data must be within the VIC-II's currently active 16KB bank.

- **Sprite pointer table**: Located at top of current screen RAM + offsets $F8–$FF
- Default: $07F8–$07FF (when screen is at $0400)
- **Pointer value** = sprite data address / 64 (within the VIC-II bank)
- **Default safe areas for sprite data**: Blocks 11–14 at $02C0–$03BF (within default Bank 0)

```basic
' Place sprite 0 data at block 13 ($0340)
POKE 2040, 13 ' sprite 0 pointer ($07F8) = block 13
' Now fill $0340-$037F with sprite pixel data
```

---

## Protecting ML Code from BASIC

**Method 1** — Lower BASIC top:
```basic
POKE 56, 192 : CLR ' MEMSIZ high byte = $C0, BASIC top = $C000
' Now place ML at $C000 safely
```

**Method 2** — Raise BASIC bottom:
```basic
POKE 44, 18 : POKE 4608, 0 : CLR ' Start BASIC at $1200
' Place ML at $0800–$11FF
```

**Method 3** — Use cassette buffer at $033C–$03FB (192 bytes):
```basic
' Safe for short routines; no special protection needed
FOR I = 828 TO 828+N : READ D : POKE I,D : NEXT
SYS 828
```

---

## Key POKE/PEEK Locations for BASIC Programmers

| POKE/PEEK | Address | Effect |
|-----------|---------|--------|
| `POKE 53280, N` | $D020 | Border color (0-15) |
| `POKE 53281, N` | $D021 | Background color 0 (0-15) |
| `POKE 646, N` | $0286 | Text foreground color (0-15) |
| `POKE 53265, PEEK(53265) OR 32` | $D011 bit 5 | Switch to bitmap mode |
| `POKE 53272, N` | $D018 | VIC-II memory control |
| `POKE 648, N` | $0288 | Screen RAM high byte (4=$0400, 8=$0800 etc) |
| `PEEK(197)` | $00C5 | Last key scan code (no key = 64) |
| `PEEK(198)` | $00C6 | Number of chars in keyboard buffer |
| `PEEK(653)` | $028D | Shift/Ctrl/C= key status |
| `POKE 650, 128` | $028A | Enable key repeat for all keys |
| `POKE 56334, PEEK(56334) AND 254` | $DC0E | Disable timer (stop CIA #1 timer A) |
| `POKE 788, 52 : POKE 789, 193` | $0314/15 | IRQ vector → $C134 (example) |

+ 302
- 0
skills/sound-sid/SKILL.md 查看文件

@@ -0,0 +1,302 @@
---
name: sound-sid
description: >
Use this skill for all SID 6581 sound programming on the Commodore 64.
Covers all 29 SID registers, ADSR envelopes, waveforms, filters, frequency
calculation, sound effects, and music programming techniques.
Sources: COMPUTE!'s Mapping the C64, The Anatomy of the C64.
---

# SID 6581 Sound Programming

## SID Chip Overview

The **MOS 6581 Sound Interface Device** (SID) is located at **$D400–$D7FF** (54272–55295).
Only addresses $D400–$D41C (29 registers) are actually used.

Features:
- **3 independent voices** (oscillators), each fully programmable
- **4 waveforms** per voice: triangle, sawtooth, pulse, noise
- **ADSR** (Attack/Decay/Sustain/Release) amplitude envelope per voice
- **Oscillator synchronization** and **ring modulation** between voices
- **Programmable filter** (high-pass, low-pass, band-pass) affecting any voices
- **16-step master volume**

**Important**: Most SID registers are **write-only**. Only $D419–$D41C can be read.

---

## SID Register Map

### Voice 1 ($D400–$D406)
| Address | Dec | Name | Description |
|---------|-----|------|-------------|
| $D400 | 54272 | FRELO1 | Voice 1 frequency low byte |
| $D401 | 54273 | FREHI1 | Voice 1 frequency high byte |
| $D402 | 54274 | PWLO1 | Voice 1 pulse width low byte |
| $D403 | 54275 | PWHI1 | Voice 1 pulse width high nybble (bits 3-0) |
| $D404 | 54276 | VCREG1 | Voice 1 control register |
| $D405 | 54277 | ATDCY1 | Voice 1 attack (bits 7-4) / decay (bits 3-0) |
| $D406 | 54278 | SUREL1 | Voice 1 sustain (bits 7-4) / release (bits 3-0) |

### Voice 2 ($D407–$D40D)
| Address | Dec | Name | Description |
|---------|-----|------|-------------|
| $D407 | 54279 | FRELO2 | Voice 2 frequency low byte |
| $D408 | 54280 | FREHI2 | Voice 2 frequency high byte |
| $D409 | 54281 | PWLO2 | Voice 2 pulse width low byte |
| $D40A | 54282 | PWHI2 | Voice 2 pulse width high nybble |
| $D40B | 54283 | VCREG2 | Voice 2 control register |
| $D40C | 54284 | ATDCY2 | Voice 2 attack / decay |
| $D40D | 54285 | SUREL2 | Voice 2 sustain / release |

### Voice 3 ($D40E–$D414)
| Address | Dec | Name | Description |
|---------|-----|------|-------------|
| $D40E | 54286 | FRELO3 | Voice 3 frequency low byte |
| $D40F | 54287 | FREHI3 | Voice 3 frequency high byte |
| $D410 | 54288 | PWLO3 | Voice 3 pulse width low byte |
| $D411 | 54289 | PWHI3 | Voice 3 pulse width high nybble |
| $D412 | 54290 | VCREG3 | Voice 3 control register |
| $D413 | 54291 | ATDCY3 | Voice 3 attack / decay |
| $D414 | 54292 | SUREL3 | Voice 3 sustain / release |

### Filter and Master ($D415–$D41C)
| Address | Dec | Name | Description |
|---------|-----|------|-------------|
| $D415 | 54293 | SIGVOL | Filter cutoff frequency low (bits 2-0) |
| $D416 | 54294 | FRESHI | Filter cutoff frequency high byte |
| $D417 | 54295 | RESON | Filter resonance (bits 7-4) / voice enable (bits 2-0) |
| $D418 | 54296 | SIGVOL | Volume (bits 3-0) / filter mode (bits 7-4) |
| $D419 | 54297 | POTX | Paddle X (read-only) |
| $D41A | 54298 | POTY | Paddle Y (read-only) |
| $D41B | 54299 | RANDOM | Voice 3 oscillator output (read-only, use for random numbers) |
| $D41C | 54300 | ENV3 | Voice 3 envelope output (read-only) |

---

## Control Register ($D404 / $D40B / $D412)

```
Bit 7 NOISE — Noise waveform
Bit 6 PULSE — Pulse waveform
Bit 5 SAW — Sawtooth waveform
Bit 4 TRI — Triangle waveform
Bit 3 TEST — Test bit (disables oscillator, clears noise LFSR)
Bit 2 RING — Ring modulation (TRI waveform modulated by prev voice)
Bit 1 SYNC — Oscillator synchronization with prev voice
Bit 0 GATE — Gate: 1=start ADSR, 0=start Release
```

**Only one waveform should be active at a time** (combining may produce unusual results).
Ring modulation works with voice 1→2, 2→3, 3→1 pairing.
Sync: voice 1 syncs to voice 3, voice 2 to voice 1, voice 3 to voice 2.

---

## Frequency Calculation

```
Register Value = (Frequency × 16777216) / Clock

NTSC Clock = 1,022,730 Hz
PAL Clock = 985,248 Hz

For NTSC: Register = Frequency / 0.0609594
For PAL: Register = Frequency / 0.0587721
```

### Common Note Frequencies (NTSC)

| Note | Freq (Hz) | Register (hex) | Register (dec) |
|------|-----------|----------------|----------------|
| C3 | 130.81 | $086C | 2156 |
| D3 | 146.83 | $0975 | 2421 |
| E3 | 164.81 | $0A9B | 2715 |
| F3 | 174.61 | $0B40 | 2880 |
| G3 | 196.00 | $0C91 | 3217 |
| A3 | 220.00 | $0E16 | 3606 |
| B3 | 246.94 | $0F9C | 3996 |
| C4 | 261.63 | $10D9 | 4313 |
| D4 | 293.66 | $12EA | 4842 |
| E4 | 329.63 | $1537 | 5431 |
| F4 | 349.23 | $1680 | 5760 |
| G4 | 392.00 | $1921 | 6433 |
| A4 (440Hz) | 440.00 | $1CCC | 7372 |
| B4 | 493.88 | $1F38 | 7992 |
| C5 | 523.25 | $21B3 | 8627 |

---

## ADSR Envelope

### Attack/Decay Register Bits
```
Bits 7-4: Attack rate (0-15)
Bits 3-0: Decay rate (0-15)
```

### Sustain/Release Register Bits
```
Bits 7-4: Sustain level (0=silent, 15=full volume)
Bits 3-0: Release rate (0-15)
```

### ADSR Rate Table
| Value | Attack Time | Decay/Release Time |
|-------|-------------|-------------------|
| 0 | 2 ms | 6 ms |
| 1 | 8 ms | 24 ms |
| 2 | 16 ms | 48 ms |
| 3 | 24 ms | 72 ms |
| 4 | 38 ms | 114 ms |
| 5 | 56 ms | 168 ms |
| 6 | 68 ms | 204 ms |
| 7 | 80 ms | 240 ms |
| 8 | 100 ms | 300 ms |
| 9 | 250 ms | 750 ms |
| 10 | 500 ms | 1.5 s |
| 11 | 800 ms | 2.4 s |
| 12 | 1 s | 3 s |
| 13 | 3 s | 9 s |
| 14 | 5 s | 15 s |
| 15 | 8 s | 24 s |

---

## Playing a Note (Complete Example)

```basic
10 REM SID SOUND EXAMPLE - A4 (440 Hz) on Voice 1, NTSC
20 POKE 54296, 15 : REM Volume = 15 (maximum)
30 POKE 54277, 9 : REM Attack=0, Decay=9 (250ms)
40 POKE 54278, 240 : REM Sustain=15 (full), Release=0
50 POKE 54272, 204 : REM Frequency low byte ($CC)
60 POKE 54273, 28 : REM Frequency high byte ($1C) → $1CCC
70 POKE 54276, 33 : REM Sawtooth wave + GATE=1 (%00100001)
80 FOR W=1 TO 500 : NEXT : REM Hold note
90 POKE 54276, 32 : REM Gate=0, start release (%00100000)
```

```asm
; Assembly version of the above
LDA #15
STA $D418 ; volume = 15
LDA #9
STA $D405 ; attack=0, decay=9
LDA #$F0
STA $D406 ; sustain=15, release=0
LDA #$CC
STA $D400 ; freq low
LDA #$1C
STA $D401 ; freq high
LDA #%00100001 ; sawtooth + gate
STA $D404
; ... wait ...
LDA #%00100000 ; sawtooth, gate off (release)
STA $D404
RTS
```

---

## Waveform Selection Guide

| Waveform | Bit | Character | Good for |
|----------|-----|-----------|----------|
| Triangle | bit 4 ($10) | Mellow, flute-like | Lead melody, smooth tones |
| Sawtooth | bit 5 ($20) | Bright, rich harmonics | Strings, brass, lead |
| Pulse | bit 6 ($40) | Variable timbre | Woodwinds, organ; varies with pulse width |
| Noise | bit 7 ($80) | Random noise | Drums, explosions, wind effects |

### Pulse Width
- Controlled by $D402/$D403 (voice 1), etc.
- 12-bit value (0–4095), duty cycle = value/40.95 percent
- 0 = very thin pulse (faint sound)
- 2048 ($800) = 50% square wave (full, rich sound)
- 4095 = very thin pulse again (same as 0)

---

## Filters ($D415–$D418)

### Filter Cutoff Frequency ($D415–$D416)
- 11-bit value (low 3 bits in $D415 bits 0-2, high 8 bits in $D416)
- Frequency range: ~30 Hz to ~12 kHz

### $D417 — Filter Resonance and Voice Routing
```
Bits 7-4: RESON — Resonance (0=low, 15=high peak at cutoff)
Bit 3: FILTEX — Filter voice from external input
Bit 2: FILT3 — Route voice 3 through filter
Bit 1: FILT2 — Route voice 2 through filter
Bit 0: FILT1 — Route voice 1 through filter
```

### $D418 — Volume and Filter Mode
```
Bit 7: 3OFF — Voice 3 direct output OFF (disconnect voice 3 from output)
Bit 6: HP — High-pass filter on
Bit 5: BP — Band-pass filter on
Bit 4: LP — Low-pass filter on
Bits 3-0: VOL — Master volume (0-15)
```

### Filter Example — Low-Pass Filter on Voice 1
```basic
POKE 54293, 0 : REM Filter cutoff low = 0
POKE 54294, 64 : REM Filter cutoff high = 64 (mid-range)
POKE 54295, 241 : REM Resonance=15, filter voice 1 (%11110001)
POKE 54296, 31 : REM Low-pass on, volume=15 (%00011111)
```

---

## Practical Sound Effects

### Explosion
```basic
POKE 54296,15 : REM Vol=15
POKE 54277,0 : REM Fast attack+decay
POKE 54278,0 : REM No sustain, fast release
POKE 54272,0 : REM Freq low=0
POKE 54273,0 : REM Freq high=0
POKE 54276,143 : REM Noise + gate (%10001111)
FOR T=1 TO 50:NEXT
POKE 54276,128 : REM Gate off (%10000000)
```

### Laser Zap (descending frequency)
```basic
POKE 54296,15
POKE 54277,0 : POKE 54278,0
F=3000
FOR I=1 TO 50
POKE 54272, F AND 255
POKE 54273, INT(F/256)
POKE 54276, 33 : REM Sawtooth + gate
F=F-50
FOR W=1 TO 5:NEXT
NEXT I
POKE 54276,32
```

### Using Voice 3 as a Random Number Source
```basic
R = PEEK(54299) ' RANDOM - read voice 3 oscillator for pseudo-random value
' First set voice 3 to noise with high frequency:
' POKE 54290,128+1 : POKE 54286,255 : POKE 54287,255 : POKE 54296,0
' (Note: $D418 bit7=1 disconnects voice 3 from audio output)
```

---

## Silencing the SID

```basic
POKE 54296, 0 : REM Volume = 0 (immediate silence)
' Or for each voice:
POKE 54276, 0 : REM Voice 1: gate off, no waveform
POKE 54283, 0 : REM Voice 2: gate off, no waveform
POKE 54290, 0 : REM Voice 3: gate off, no waveform
```

+ 365
- 0
skills/vice-emulator/SKILL.md 查看文件

@@ -0,0 +1,365 @@
---
name: vice-emulator
description: >
Use this skill when you need to run, autostart, debug, snapshot, or automate
Commodore 64 programs in the VICE emulator. Covers x64sc, command-line
autostart, warp mode, true drive emulation handling, the built-in monitor,
remote and binary monitor servers, and snapshot caveats.
Source: VICE 3.10 manual (C:\Program Files\GTK3VICE-3.10-win64\doc\vice.txt).
---

# VICE Emulator Workflow for the Commodore 64

This skill is primarily about **debugging programs with VICE**, not just launching them.

## When to Use This Skill

Use this skill when you need to:

- Launch a `.prg`, `.d64`, `.g64`, `.tap`, or cartridge image in VICE
- Choose the correct C64 emulator executable
- Autostart software for test runs
- Open the VICE monitor and set breakpoints or watchpoints
- Save or restore snapshots during debugging
- Automate VICE from an agent via the remote or binary monitor

If the user asks to "debug the assembly," "trace startup," "find where banking breaks," or "step through a PRG," this is the first skill to load.

---

## Preferred C64 Emulator

For C64 work, prefer:

- `x64sc` — accurate C64 emulator with cycle-based and pixel-accurate VIC-II emulation

The VICE manual notes that:

- `x64` was the older fast emulator
- `x64sc` is the accurate emulator and the default choice for serious debugging

Useful companion tools in the VICE package:

- `c1541` — disk image tool
- `petcat` — BASIC tokenization/detokenization tool
- `cartconv` — cartridge image conversion
- `vsid` — SID player

---

## Command-Line Autostart

The VICE manual documents two equivalent ways to autostart:

```text
x64sc mydisk.d64
x64sc -autostart myprog.prg
```

You can also select a named file inside an image:

```text
x64sc "demo.d64:loader"
x64sc -autostart "demo.d64:loader"
```

Helpful options for agent workflows:

```text
-autostart-warp
-autostart-handle-tde
-basicload
+basicload
```

Notes from the manual:

- `-autostart-warp` temporarily enables warp mode during autostart
- `-autostart-handle-tde` lets VICE temporarily manage True Drive Emulation during autostart
- For raw `.prg`/P00 files, VICE loads the file and then restores the previous autostart settings
- If the started program must access the host filesystem, enable virtual device traps

---

## Native, Remote, and Binary Monitor

VICE supports three monitor-related workflows that are useful for agents:

### Native Monitor

```text
-nativemonitor
+nativemonitor
```

When enabled, the monitor runs in the spawning terminal instead of only inside the emulator UI.

### Remote Monitor

```text
-remotemonitor
-remotemonitoraddress ip4://127.0.0.1:6510
```

This enables the text-based remote monitor server.

### Binary Monitor

```text
-binarymonitor
-binarymonitoraddress ip4://127.0.0.1:6502
```

This enables the binary monitor protocol used by automation tools and external debuggers.

### Recommended Debug Launch

For agent-driven debugging, prefer launching VICE with:

```text
x64sc -autostart c64os.prg -autostart-warp -nativemonitor -keepmonopen -refreshonbreak
```

Useful companion options from the manual:

```text
-monlog
-monlogname vice-monitor.log
```

These make the monitor visible in the terminal, keep it open after continuing, refresh screen state on breaks, and optionally log monitor output.

---

## Core Debugging Commands

These monitor commands are the most useful for day-to-day program debugging:

| Command | Meaning |
|---------|---------|
| `x` / `exit` | Leave the monitor and resume execution |
| `q` / `quit` | Exit VICE |
| `r` / `registers` | Show or modify registers |
| `reset [type]` | Reset the machine or drive |
| `goto <address>` | Set the program counter |
| `next [count]` | Step over instructions |
| `step [count]` | Step into instructions |
| `return` | Run until the next `RTS` or `RTI` |
| `until <address>` | Run until an address is reached, then break |
| `bt` / `backtrace` | Show the JSR call chain |
| `m <range>` | Show memory |
| `i <range>` | Show memory as PETSCII |
| `d <range>` | Disassemble |
| `io [address]` | Inspect I/O registers or a chip block |
| `break exec $addr` | Break on execution |
| `watch store $addr` | Break when an address is written |
| `trace exec $addr1 $addr2` | Trace execution without stopping |
| `bank` | Show/select banks |
| `device c:` / `8:` | Switch computer vs drive address space |
| `sidefx off` | Prevent monitor reads from causing hardware side effects |
| `keybuf "text\x0d"` | Feed characters into the keyboard buffer |
| `playback "file"` | Execute commands from a monitor script |
| `record "file"` | Record monitor commands for replay |

Examples:

```text
break exec $0810
watch store $0001
d $0810 $0840
m $0000 $0002
bt
return
```

Conditional checkpoints are supported. The manual documents register-based conditions and raster-aware conditions such as `RL` and `CY`.

---

## Breakpoints, Watchpoints, and Tracepoints

VICE supports several complementary debugging styles:

### 1. Breakpoints

Use when you care about **where execution reaches**:

```text
break exec $0810
until $0930
```

### 2. Watchpoints

Use when you care about **what wrote or read a memory location**:

```text
watch store $0001
watch load $dc0d
```

For C64 debugging, watchpoints are especially useful for:

- `$0001` — memory banking changes
- `$0314/$0315` — IRQ vector changes
- `$d020/$d021` — border/background color debugging
- `$0400-$07e7` — screen RAM writes
- `$d800-$dbe7` — color RAM writes

### 3. Tracepoints

Use when you need a non-stopping execution log:

```text
trace exec $0810 $08ff
```

### 4. Conditional Checkpoints

The VICE manual supports conditions using registers, arithmetic, boolean operators, raster line `RL`, cycle `CY`, and bank-qualified memory references.

Examples:

```text
break exec $0810 if A == $00
break load 0 $ffff if @cpu:(pc - $1) == $37
```

### 5. Checkpoint Automation

The monitor can attach commands to checkpoints:

```text
command 2 "m $0000 $0002"
ignore 2 1
disable 1
enable 1
```

This is useful when you want an automatic memory dump the first time a watchpoint hits.

---

## Stepping Strategy

Use these commands intentionally:

- `step` for stepping **into** a subroutine
- `next` for stepping **over** a subroutine
- `return` for stepping **out** of the current subroutine
- `until <address>` to run quickly to a known target
- `backtrace` to recover the call chain after a break

Recommended sequence for a new bug:

1. Break at the program entry or suspicious routine
2. `d` the nearby code
3. `r` to inspect registers
4. `step` or `next` depending on whether you need to enter subroutines
5. `backtrace` if you need to know how execution reached the current point
6. Add watchpoints once you identify suspicious state

---

## Safe Inspection Rules

The monitor manual explicitly documents side-effectful reads.

For C64 work:

- start with `sidefx off`
- use `device c:` for the main machine and `device 8:` when debugging a true-emulated drive CPU
- use `bank` if you need to inspect the CPU-visible versus I/O-visible memory view
- use `io $d000`, `io $d400`, `io $dc00`, or `io $dd00` when you want chip-level register context

---

## Monitor Script Files

VICE can execute a command file from inside the monitor, and can also record command sequences:

```text
playback "examples/vice-monitor-c64os.txt"
record "my-session.txt"
```

This is the easiest way for an agent to set up a repeatable debug session.

In this repository, prefer:

- `examples/vice-monitor-c64os.txt` for startup and banking inspection
- `examples/vice-monitor-c64os-debug.txt` for a fuller debugging session with labels, checkpoints, and keyboard injection

---

## Debugging Recipes

### Debugging PRG Startup

1. Launch with `-nativemonitor -keepmonopen -refreshonbreak`
2. `break exec $0810`
3. `d $0801 $0840`
4. `step` through startup or `next` over known KERNAL calls

### Debugging Banking Problems

1. `watch store $0001`
2. Add `command <n> "m $0000 $0002"` to dump the port state when it hits
3. Use `bank` and `io` to inspect what memory view is active

### Debugging Command Input

1. Break at your parser or input loop
2. Use `keybuf "help\x0d"` to push test input into the keyboard buffer
3. Use `i` on the input buffer to inspect PETSCII text

### Debugging IRQ/Vector Corruption

1. `watch store $0314 $0315`
2. `watch store $0318 $0319`
3. Use `bt` and `d` when the watchpoint hits
4. If needed, inspect chip state with `io $dc00` and `io $d000`

---

## Snapshots

VICE snapshots save the complete emulator state, including memory configuration and optionally attached drives.

Use snapshots for:

- fast retry points during debugging
- preserving an emulator state mid-session
- short-lived reproduction cases

Do not use snapshots for:

- permanent archival storage
- cross-version compatibility guarantees

The VICE manual explicitly warns that snapshot compatibility is not reliable across versions.

---

## Recommended Repo Workflow

For this repository:

1. Build the program with `build.bat`
2. Launch `c64os.prg` with `x64sc -autostart c64os.prg -autostart-warp -nativemonitor -keepmonopen -refreshonbreak`
3. Run `playback "examples/vice-monitor-c64os-debug.txt"` for an immediate debug setup
4. Use `watch store $0001` to confirm banking writes
5. Use `step`, `next`, `return`, and `backtrace` to narrow the failing routine
6. Use snapshots for quick iteration, not long-term storage

---

## Common Pitfalls

1. `x64sc` is the preferred accurate emulator; do not default to the older `x64`
2. Autostart behavior changes depending on PRG vs image file
3. True Drive Emulation can affect autostart speed and compatibility
4. Monitor reads can have side effects unless you disable them with `sidefx off`
5. Stepping without choosing between `step`, `next`, and `return` can waste a lot of time in KERNAL code
6. Forgetting `device 8:` vs `device c:` can make you debug the wrong CPU when true drive emulation is involved
7. Snapshots are convenient, but the manual warns against treating them as stable long-term artifacts

正在加载...
取消
保存

Powered by TurnKey Linux.