# PET 2001 Emulator ## About This App A retro computing emulator that faithfully recreates the Commodore PET 2001 with authentic tape drive, keyboard matrix, and save-to-tape recording ## Specification # Commodore PET 2001 Emulator — With Authentic PIA Keyboard Matrix Scanning, Photo-Accurate Chiclet Keyboard, Hardware-Accurate Datasette Emulation, Universal File-to-TAP Conversion & Faithful SAVE-to-Tape Recording Create a faithful, retro-styled Commodore PET 2001 emulator as a single-page HTML/CSS/JS web application (no React). This is a full 6502 CPU emulator with ROM loading, keyboard input, screen rendering, and authentic Datasette tape emulation — all wrapped in a nostalgic green-phosphor CRT aesthetic. **Key Enhancement #1 — PIA-Based Keyboard**: This version implements a **fully authentic PIA-based keyboard matrix scanning system**, faithfully replicating how the real Commodore PET hardware scans its 10×8 keyboard matrix through the PIA chips, 74LS145 row decoder, and IRQ-driven VSYNC scanning routine — rather than injecting characters directly into the keyboard buffer. **Key Enhancement #2 — Hardware-Accurate Datasette Emulation (READ)**: This version implements a **faithful emulation of the PET 2001's integrated Datasette drive** using the actual memory-mapped I/O hardware: the **6520 PIA ($E810–$E813)** for motor control and sense line, and the **6522 VIA ($E840–$E84F)** for precision pulse timing of tape data. It supports the **.TAP file format** with proper pulse-level emulation, matching the original hardware's short/medium/long pulse encoding scheme. **Key Enhancement #3 — Universal File-to-TAP Converter (ANY FILE → Datasette Tape)**: This version adds a **built-in file converter** that can take **ANY uploaded file of ANY extension** — .PRG, .BIN, .TXT, raw binary, or anything else — and dynamically convert it into a proper .TAP pulse stream that the PET's ROM tape routines can read natively through the emulated hardware. This means users can simply drag-and-drop any compatible binary onto the Datasette, and the emulator will: 1. Wrap the raw file data in a proper **192-byte Commodore cassette header block** (file type, start address, end address, filename) 2. Generate the correct **leader tone** (~10 seconds of short pulses for synchronization) 3. Encode a **sync countdown sequence** ($09 down to $01) 4. Encode the header block as proper PWM pulses (short=0, medium=1, long=sync marker) 5. Write the header **twice** for redundancy (matching PET hardware behavior) 6. Generate an inter-block gap 7. Encode the **data block** with proper sync, countdown, data bytes, and XOR checksum 8. Write the data block **twice** for redundancy 9. Feed the resulting pulse stream through the authentic VIA Timer 1 / PIA CA1 hardware path This creates a seamless experience: upload any file → it appears as a tape → type `LOAD` on the PET → the ROM's native tape routines read it through the actual emulated hardware. **Key Enhancement #4 — FAITHFUL SAVE-TO-TAPE RECORDING (WRITE TO DATASETTE)**: This is the major new enhancement. The emulator now implements **authentic SAVE functionality** that faithfully replicates the PET 2001's process of writing data from the computer to the Datasette cassette recorder. This is not a shortcut or hack — it follows the exact hardware data flow of the real machine: ### SAVE-to-Tape Data Flow (Matching Real Hardware) **Step 1 — Cassette Buffer at $033A–$03FF**: When the user types `SAVE "FILENAME"` (or `SAVE "FILENAME",01` for the internal unit), the PET's Kernal ROM routines use the **cassette buffer starting at $033A** (extending to $03FF) as a staging area. The emulator monitors this buffer region and participates in the Kernal's native save routine rather than bypassing it. **Step 2 — Motor Control via 6520 PIA**: The PET's Kernal turns on the Datasette motor by writing to the **6520 PIA at $E810–$E813**. The emulator's PIA emulation detects the motor-on signal and transitions the Datasette widget into **RECORD mode**. The visual Datasette widget shows: - The **RECORD indicator** (a red dot 🔴) illuminating next to the tape reels - Both tape reels **spinning in the forward direction** (left reel shrinks, right reel grows) - The tape counter advancing during the recording process - A "WRITING..." status message in the Datasette panel **Step 3 — Sense Line & RECORD+PLAY Button Detection**: On real hardware, the user must press **RECORD and PLAY simultaneously** on the Datasette before the SAVE can proceed. The PET's Kernal checks the **sense line** (PIA CA1 at $E810) to detect whether the buttons are pressed. The emulator implements this by: - Displaying a prominent **"PRESS RECORD & PLAY ON TAPE"** prompt (matching the real PET's screen message) - Showing a clickable **"⏺+▶ RECORD & PLAY"** button on the Datasette widget that the user must click to simulate pressing both mechanical buttons - Setting the appropriate sense line bit in the PIA when the user clicks, allowing the Kernal to proceed - If the user doesn't click within a reasonable time, the PET's ROM will display its standard error, just like real hardware **Step 4 — Dual-Pass Pulse Encoding at ~1500 Baud**: The PET's Kernal converts each byte of program data into **timed audio pulses** using the same short/medium/long pulse scheme used for reading. The emulator captures these output pulses by monitoring the **VIA ($E840–$E84F) Timer 1** and the **tape write line** (PIA Port A bit for write output). The encoding follows the real hardware behavior: - **Short pulse** (~176μs) = bit value 0 - **Medium pulse** (~256μs) = bit value 1 - **Long pulse** (~352μs) = sync/marker byte - Data is recorded at approximately **1500 baud** raw - **ALL data is written TWICE** (dual-recording) for redundancy, resulting in an effective data rate of ~750 baud — the emulator faithfully captures both passes **Step 5 — Complete Tape Structure Written**: The Kernal's SAVE routine writes a complete tape file structure: 1. **Leader tone** (~10 seconds of short pulses for tape synchronization) 2. **Sync countdown** ($09 down to $01) 3. **Header block** (192 bytes) containing: file type byte, start address (low/high), end address (low/high), filename (up to 187 characters padded with spaces), and XOR checksum 4. **Header block repeated** (second copy for redundancy) 5. **Inter-block gap** (short leader tone) 6. **Data block** containing: the actual program bytes from memory (from start address to end address), plus XOR checksum 7. **Data block repeated** (second copy for redundancy) **Step 6 — Capture and Storage of Recorded Data**: As pulses are written by the Kernal through the VIA/PIA hardware path, the emulator's Datasette emulation captures them in real-time and: - Accumulates the pulse data into a **virtual tape buffer** in proper .TAP format - Displays a **live byte counter** showing how many bytes have been recorded - Shows a **progress indicator** based on estimated data size - Updates the visual tape reel animation throughout the recording **Step 7 — Post-SAVE Options**: Once the SAVE operation completes (motor turns off via PIA), the emulator provides: - A **"💾 DOWNLOAD .TAP"** button that lets the user download the recorded tape as a standard **.TAP file** — this file is fully compatible with other PET emulators (VICE, etc.) and can be re-loaded into this emulator - A **"🔄 VERIFY"** option that rewinds the virtual tape and allows the user to type `VERIFY` to check the recording against memory (matching real PET workflow) - The recorded tape **remains in the virtual Datasette** and can be rewound and re-played (LOADed back) without downloading - A **tape library panel** that shows all recorded/loaded tapes in the current session with filenames, sizes, and timestamps **Step 8 — Multiple Files on One Tape**: Just like a real cassette, the virtual tape supports **multiple files recorded sequentially**. If the user SAVEs multiple programs without ejecting the tape, they are appended in sequence. The user can then LOAD specific files by name or use `LOAD ""` to load the next file on tape. The tape counter helps the user find specific files, just like on real hardware. ### SAVE Implementation Technical Details The SAVE-to-tape system intercepts the hardware path at the correct points: - **PIA Port A ($E810) Bit 3** — Cassette Write Line: The emulator monitors writes to this bit. When the Kernal's tape write routine toggles this line to generate pulses, the Datasette emulation captures the timing between transitions using the CPU cycle counter. - **PIA Port A ($E810) Bit 4** — Cassette Motor Control: Writing 0 = motor ON, writing 1 = motor OFF. The emulator uses this to detect when recording starts and stops. - **PIA CA1 ($E810/$E811)** — Sense Line: The emulator sets this based on whether the user has clicked the RECORD+PLAY button on the virtual Datasette. - **VIA Timer 1 ($E844/$E845)** — Precision Timing: The Kernal uses VIA Timer 1 for precise pulse width timing during writes. The emulator's VIA emulation provides accurate countdown behavior so the Kernal's timing loops work correctly. - **Cassette Buffer ($033A–$03FF)** — The emulator does NOT directly read this buffer to cheat the save process. Instead, it lets the Kernal ROM naturally read program data from memory, encode it through its native routines, and output it through the hardware write line. The emulator simply captures what comes out of the write line, exactly as a real Datasette would record it. ### Recording State Machine The Datasette emulation maintains a recording state machine: ``` IDLE → RECORD_WAITING (user typed SAVE, motor on, waiting for RECORD+PLAY) → RECORDING (sense line active, capturing pulses) → RECORD_COMPLETE (motor off, tape data available for download/playback) ``` Visual indicators in the Datasette widget reflect each state: - **IDLE**: Reels stationary, no indicator lit - **RECORD_WAITING**: Red "PRESS RECORD & PLAY" message flashing, record button pulsing - **RECORDING**: Red record dot lit, reels spinning, byte counter incrementing, "WRITING..." status - **RECORD_COMPLETE**: Green "RECORDING COMPLETE" message, download button appears, tape counter shows final position **HISTORICAL ACCURACY NOTE — NO FLOPPY DISK SUPPORT**: The original 1977 Commodore PET 2001 with the chiclet keyboard did **NOT** ship with floppy disk support. The IEEE-488 bus and external floppy drives (CBM 2040, 8050, D9060) were later peripherals for updated PET models. This emulator faithfully recreates the **original 1977 PET 2001 experience** with only the built-in Datasette as the storage device, plus a convenience PRG loader for development/testing purposes. **CRITICAL VISUAL ENHANCEMENT — PHOTO-ACCURATE CHICLET KEYBOARD WITH CORRECT LAYOUT**: This version features a meticulously recreated on-screen keyboard that visually matches the **actual Commodore PET 2001 chiclet keyboard** as seen in the provided reference photograph. The keyboard must replicate the distinctive look AND exact layout/alignment of the original hardware. **CRITICAL FIX — KEY GEOMETRY AND SQUARENESS**: This corrected version addresses three specific visual bugs from the previous iteration: 1. **ALL keys that span 2 lines of text content MUST remain perfectly SQUARE (1:1 aspect ratio)**: Keys like `CLR HOME`, `INST DEL`, `CRSR ↓↑`, `CRSR →←`, `RUN STOP`, `OFF RVS`, and any other key with two lines of label text must NOT be taller than they are wide. These are standard square chiclet keys (same width and height as single-label keys, approximately 42×42px). The two lines of text simply use a smaller font size to fit within the square key. Do NOT let multi-line text content stretch the key height. Use `overflow: hidden`, fixed `width` and `height`, and reduced `font-size` or `line-height` to ensure the text fits within the square dimensions. Every key in the grid (except RETURN and SPACE) should be exactly the same square size. 2. **SHIFT keys must be perfectly SQUARE**: Both left and right SHIFT keys must be the same 42×42px square as all other standard keys. They are NOT wider or taller. They are cherry red with white "SHIFT" text, but geometrically identical to every other standard key. Do NOT make SHIFT keys wider like on a modern keyboard — the PET chiclet keyboard has uniform square keys throughout. 3. **RETURN key must be visually CONNECTED as one continuous double-height key**: The RETURN key spans two rows vertically. It must render as a **single, seamless, connected rectangular key** — one DOM element with double the height (approximately 42px wide × 88-90px tall, accounting for the inter-row gap it spans). It must NOT appear as two separate disconnected square keys stacked on top of each other. Use a single `