{"id":27139510,"url":"https://github.com/ddrcode/teensy_6502_bridge","last_synced_at":"2025-10-24T05:18:46.488Z","repository":{"id":210597106,"uuid":"726951467","full_name":"ddrcode/teensy_6502_bridge","owner":"ddrcode","description":"COM-W65C02 CPU bridge on Teensy 4.1","archived":false,"fork":false,"pushed_at":"2024-03-06T15:50:02.000Z","size":2517,"stargazers_count":0,"open_issues_count":8,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-03-06T17:28:05.931Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ddrcode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-12-03T21:28:48.000Z","updated_at":"2024-02-05T16:19:30.000Z","dependencies_parsed_at":"2023-12-17T20:46:27.240Z","dependency_job_id":"b4c4adef-3da6-43f7-8a65-c4439f959cc9","html_url":"https://github.com/ddrcode/teensy_6502_bridge","commit_stats":null,"previous_names":["ddrcode/teensy_6502_com_bridge","ddrcode/teensy_6502_bridge"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fteensy_6502_bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fteensy_6502_bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fteensy_6502_bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fteensy_6502_bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ddrcode","download_url":"https://codeload.github.com/ddrcode/teensy_6502_bridge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247779698,"owners_count":20994573,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-08T04:58:25.700Z","updated_at":"2025-10-24T05:18:41.431Z","avatar_url":"https://github.com/ddrcode.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# W65C02-Serial port bridge for Teensy 4.1\n\nThis project is a simple bridge between [W65C02 CPU](https://westerndesigncenter.com/wdc/documentation/w65c02s.pdf)\nand a serial port, implemented for [Teensy 4.1](https://www.pjrc.com/store/teensy41.html) development board.\n\nThe main purpose of this project is to provide cycle-by-cycle debugging capabilities to W65C02, but it can serve\nother purpose too, i.e.:\n\n- __Comparative debugging for emulators__:\n  You can run your 6502-family emulator in parallel with an actual CPU to compare the results\n  in order to measure the accuracy of your emulator.\n- __Emulated 8-bit computers with a physical processor__:\n  You can implement an emulator of an 8-bit, 6502-based computer (i.e. C64), that will work\n  (via this bridge) with a real CPU, while other components of the system (like RAM, ROM,\n  video chip) remain emulated\n  (something like [Neo 6502](https://www.olimex.com/Products/Retro-Computers/Neo6502/open-source-hardware)).\n\n## Content of this repo\n\n- C++ code for Teensy 4.1 that enables full control over W65C02 CPU via serial port.\n- [Examples](./examples/) (in C++ and Rust) demonstrating how to use the Bridge from a program running on a\n  computer.\n- Explanation how to wire Teensy with W65C02 on a breadboard.\n- Complete [PCB design and schematics](./pcb/) (with some extra features).\n\n## Wiring\n\nThe W65C02 CPU is a 40-pin chip, with 37 data pins (2 pins are for\npower/ground and pin 35 is unused). As Teensy board is equipped with 41 digital input/output pins,\nand allows for serial connection via USB, it seems to be a perfect match for this project.\nIt is very simple to create this bridge on a breadboard.\n\nBelow there is a pinout diagram of the W65C02 and Teensy, as well\nas mapping between the CPU and Teensy pins.\n\n```text\n                                                        Teensy 4.1\n                                                     +--------------+\n                W65C02                           --- | GND      VIN | ---\n            +------------+                   VP/ --- |  0       GND | ---\n    VP/ \u003c-- |  1      40 | \u003c-- RES/              --- |  1      3.3V | ---\n    RDY \u003c-\u003e |  2      39 | --\u003e PHI2O       PHI1O --- |  2        23 | --- RES/\n  PHI1O \u003c-- |  3      38 | \u003c-- SO/          IRQ/ --- |  3        22 | --- PHI2O\n   IRQ/ --\u003e |  4      37 | \u003c-- PHI2          ML/ --- |  4        21 | --- RDY\n    ML/ \u003c-- |  5     @36 | \u003c-- BE           NMI/ --- |  5        20 | --- SO/\n   NMI/ --\u003e |  6      35 | --- NC           SYNC --- |  6        19 | --- BE\n   SYNC \u003c-- |  7     *34 | --\u003e RW/            A0 --- |  7        18 | ---\n    VDD --\u003e |  8     *33 | \u003c-\u003e D0             A1 --- |  8        17 | --- RW/\n     A0 \u003c-- |  9*    *32 | \u003c-\u003e D1             A2 --- |  9        16 | --- D0\n     A1 \u003c-- | 10*    *31 | \u003c-\u003e D2                --- | 10        15 | --- D1\n     A2 \u003c-- | 11*    *30 | \u003c-\u003e D3             A3 --- | 11        14 | --- D2\n     A3 \u003c-- | 12*    *29 | \u003c-\u003e D4             A4 --- | 12        13 | --- PHI2\n     A4 \u003c-- | 13*    *28 | \u003c-\u003e D5            VDD --- | 3.3V     GND | --- GND\n     A5 \u003c-- | 14*    *27 | \u003c-\u003e D6             A5 --- | 24        41 | --- D3\n     A6 \u003c-- | 15*    *26 | \u003c-\u003e D7             A6 --- | 25        40 | --- D4\n     A7 \u003c-- | 16*    *25 | --\u003e A15               --- | 26        39 | --- D5\n     A8 \u003c-- | 17*    *24 | --\u003e A14               --- | 27        38 | --- D6\n     A9 \u003c-- | 18*    *23 | --\u003e A13            A7 --- | 28        37 | --- D7\n    A10 \u003c-- | 19*    *22 | --\u003e A12            A8 --- | 29        36 | --- A15\n    A11 \u003c-- | 20*     21 | --\u003e GND            A9 --- | 30        35 | --- A14\n            +------------+                   A10 --- | 31        34 | --- A13\n                                             A11 --- | 32        33 | --- A12\n    * - tri-state pin,                               +--------------+\n    @ - async,\n    / - active on low\n```\n\n### Default configuration\n\nThe table below (and pinouts above) illustrates the default configuration of the project.\nIn order to adjust the pin mapping, modify the `PINS_MAP` macro definition in the\n[configuration file](./configuration.h).\n\nThe pin assignment is organized the way, that it leaves one SPI interface\navailable (pins 1, 10, 26 and 27), that can be used for additional device (i.e. the [PCB](./pcb/)\ngives the option to connect ILI9341 screen).\n\n| Teensy pin | CPU pin | CPU pin name | ←  → | CPU Pin name  | CPU pin | Teensy pin |\n| ---------- | ------- | ------------ | ---- | ------------- | ------- | ---------- |\n| 0          | 1       | Vector pull  |      | Reset         | 40      | 23         |\n| 21         | 2       | Ready        |      | PHI2O         | 39      | 22         |\n| 2          | 3       | PHI1O        |      | Set overflow  | 38      | 20         |\n| 3          | 4       | IRQ          |      | PHI2          | 37      | 13         |\n| 4          | 5       | Memory lock  |      | Bus enable    | 36      | 19         |\n| 5          | 6       | NMI          |      | No connection | 35      |            |\n| 6          | 7       | SYNC         |      | Read/Write    | 34      | 17         |\n| 3.3V       | 8       | VDD          |      | D0            | 33      | 16         |\n| 7          | 9       | A0           |      | D1            | 32      | 15         |\n| 8          | 10      | A1           |      | D2            | 31      | 14         |\n| 9          | 11      | A2           |      | D3            | 30      | 41         |\n| 11         | 12      | A3           |      | D4            | 29      | 40         |\n| 12         | 13      | A4           |      | D5            | 28      | 39         |\n| 24         | 14      | A5           |      | D6            | 27      | 38         |\n| 25         | 15      | A6           |      | D7            | 26      | 37         |\n| 28         | 16      | A7           |      | A15           | 25      | 36         |\n| 29         | 17      | A8           |      | A14           | 24      | 35         |\n| 30         | 18      | A9           |      | A13           | 23      | 34         |\n| 31         | 19      | A10          |      | A12           | 22      | 33         |\n| 32         | 20      | A11          |      | Ground        | 21      | GND        |\n\n### Minimal configuration\n\nIn the minimalistic configuration the following CPU pins must be connected to the board:\n\n- `A0-A15` - address bus (inputs)\n- `D0-D7` - data bus (input or output)\n- `RW` - read/write input; it informs whether the data bus is in read (high) or write (low) state\n- `PHI2` - clock signal input\n- `GND` - ground, should be connected to Teensy's ground pin\n- `VDD` - power; should be connected to Teensy's 3.3V pin\n\nIn such configuration `RES`, `IRQ`, `NMI` and `BE` and `RDY` pins must be also connected to 3.3V,\nideally via 1k resistor.\n\nPlease note that the `RES` (pin 40) must be kept low for at least two cycles on power on.\nThe bridge handles it programmatically, but if your intention is to not connect RST pin to\nTeensy or to have a physical control over the reset state (i.e. in case of power failure),\nthen the best option is to connect the pin via\n[DS1818 Econo Reset](https://www.mouser.co.uk/datasheet/2/609/DS1818-3122611.pdf)\n\n### Example\n\nThe photo shows wiring on solderable prototype board (a regular breadbord could be used instead).\nAll CPU pins are connected to Teensy, apart the pin 35 (NC).\nThe CPU pin 8 (VDD) is connected via [decoupling capacitor](https://en.wikipedia.org/wiki/Decoupling_capacitor).\nAdditionally, there are some LEDs to indicate some of the signals.\n\n![Example wiring](./assets/board.jpg)\n\n### Schematic and extended configuration\n\nThe schematic below illustrates the connectivity between Teensy and the CPU, but also\nprovides some optional configuration:\n- reset button\n- indication LEDs\n- SPI connectivity with ILI9341 LCD.\n![Schematic](./pcb/schematics/teensy-bridge-v1.png)\n\n### Warning\n\nIncorrect connection may damage the CPU or the development board. Please take extra attention to the\ncorrectness of your wiring, and double-check it before powering up your board.\n\n## Compilation and execution\n\n### With Arduino IDE and Teensyduino\n\n1. Install [Arduino IDE](https://www.arduino.cc/en/software)\n1. Install and configure [Teensyduino](https://www.pjrc.com/teensy/teensyduino.html)\n1. Clone this project into your Arduino sketches folder (usually `~/Arduino/sketches`)\n1. Click menu `Sketch / Compile` to compile\n1. Click menu `Sketch / Upload` tu upload to Teensy board\n\n### With Arduino CLI\n\nFollow [this post](https://forum.pjrc.com/index.php?threads/arduino-cli-and-ide-now-released-teensy-supported.53548/page-5#post-299430) to see how to configure Arduino CLI with Teensy.\n\n- to compile: `make build`\n- to upload `make upload`\n\nMake sure that the port name in `Makefile` is the correct one. To find the port name execute\n`arduino-cli board list`. You must have Teensy connected to your computer to have the port visible.\n\n##### Install Arduino CLI and dependencies with Nix and Direnv\n\nIf you are not using Nix packages, then you don't know how much you miss in terms of convenience and reproducible dev environments - [start](https://nixos.org/download) today :-)\n\n1. Clone the repo and cd project's directory\n1. If you use [Direnv](https://direnv.net/), just execute `direnv allow`, otherwise type `nix-shell`\n1. Wait until all dependencies download and configure\n1. You can now use `make build` and `make upload`\n1. I've added [`treefmt`](https://github.com/numtide/treefmt) to dependencies, so you can format the\n   code after changes by executing `treefmt`\n\n## Example\n\nThere is complete example program in the [examples folder](./examples), that demonstrates\nhow to execute 6502 binary with the bridge and RAM emulated on the host machine\n(not on Teensy). See the [Readme file](.examples/README.md) for details.\n\n## Data structure and message protocol\n\nThe communication \"protocol\" is very simple - every message sent to and going from serial port contains the status of all 40 CPU pins (one per bit).\nImagine that the CPU pins representaion is a 40-bit number, with Pin 1 representing the least significant bit (bit 0) and Pin 40 - the most significant\nbit. In practice that number is being transferred via serial port as buffer of 5 bytes in [big-endian](https://en.wikipedia.org/wiki/Endianness) format,\nso byte 0 contains the status of pins 40 to 33 (reading bits left-to-right), etc. The table below illustrates the exact structure of a message.\n\n| Byte | Bit 7  | Bit 6  | Bit 5  | Bit 4  | Bit 3  | Bit 2  | Bit 1  | Bit 0  |\n|----- | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ |\n| 0    | Pin 40\u003cbr\u003e`RES/` | Pin 39\u003cbr\u003e`PHI2O`| Pin 38\u003cbr\u003e`SO/` | Pin 37\u003cbr\u003e`PHI2`| Pin 36\u003cbr\u003e`BE`  | Pin 35\u003cbr\u003e`NC`  | Pin 34\u003cbr\u003e`RW/` | Pin 33\u003cbr\u003e`D0`  |\n| 1    | Pin 32\u003cbr\u003e`D1`   | Pin 31\u003cbr\u003e`D2`   | Pin 30\u003cbr\u003e`D3`  | Pin 29\u003cbr\u003e`D4`  | Pin 28\u003cbr\u003e`D5`  | Pin 27\u003cbr\u003e`D6`  | Pin 26\u003cbr\u003e`D7`  | Pin 25\u003cbr\u003e`A15` |\n| 2    | Pin 24\u003cbr\u003e`A14`  | Pin 23\u003cbr\u003e`A13`  | Pin 22\u003cbr\u003e`A12` | Pin 21\u003cbr\u003e`VSS` | Pin 20\u003cbr\u003e`A11` | Pin 19\u003cbr\u003e`A10` | Pin 18\u003cbr\u003e`A9`  | Pin 17\u003cbr\u003e`A8`  |\n| 3    | Pin 16\u003cbr\u003e`A7`   | Pin 15\u003cbr\u003e`A6`   | Pin 14\u003cbr\u003e`A5`  | Pin 13\u003cbr\u003e`A4`  | Pin 12\u003cbr\u003e`A3`  | Pin 11\u003cbr\u003e`A2`  | Pin 10\u003cbr\u003e`A1`  | Pin  9\u003cbr\u003e`A0`  |\n| 4    | Pin  8\u003cbr\u003e`VDD`  | Pin  7\u003cbr\u003e`SYNC` | Pin  6\u003cbr\u003e`NMI/`| Pin  5\u003cbr\u003e`ML/` | Pin  4\u003cbr\u003e`IRQ/`| Pin 3\u003cbr\u003e`PHI1O`| Pin  2\u003cbr\u003e`RDY` | Pin  1\u003cbr\u003e`VP/` |\n\n### Messaging order\nAs the Teensy Bridge doesn't implement a clock, the communication must start on the host side - that means the host is responsible for\nsending `PHI2` values (pin 37), and - in order to make the CPU to _tick_ - the value must be inverted for every data package being sent from\nthe host.\n\nEvery write to the Teensy Bridge must be followed by a read, even if we are not planning to use the data from the Bridge\n(typical request-response approach).\nThat can be understood as follows: every single half-cycle (the cpu phase), consist of write to serial port followed by a read.\nA full CPU cycle will consit of write-read-write-read operations, with pin 37 being set to 0 for the first time and 1 for second.\n\nWhen the CPU is in the first half-cycle, it executes internal operations, resulting in setting address bus and the `RW\\` pin.\nThe 2nd half-cycle is a _memory cycle_, when the CPU writes or reads its data pins. The algorithm below demonstrates the\nthe typical interaction with the CPU, respecting both CPU phases.\n\n1. First half-cycle\n    1. Set `PHI2` pin to LOW (0)\n    2. Write buffer to serial port\n    3. Read buffer from serial port\n    4. Extract address and read/write state (pin 34) from the buffer\n2. Second half-cycle\n    1. Set `PHI2` pin to HIGH (1)\n    2. In case of read operation (pin 34 is high) - read the value from the memory and set the data pins.\n    3. Write buffer to serial port\n    4. Read buffer from serial port\n    5. In case of write operation (pin 34 was low in the first half-cycle) - read the value from data pins and save in the memory.\n\n\n## Working with other CPUs from the 6502 family\n\nThis project is meant to work specifically with W65C02 CPU. The main reason\nis that W65C02 is static, that means it can be easily step-by-step cycled at any speed,\nthat is very useful in case of debugging. Some other CPUs, like C64's MOS6510, have\nlimitations of a minimum speed (~100kHz).\n\nThe project should work fine with other CPUs from WDC family, especially with\n65C802, due to pin layout compatibility. Although untested, the 65C802 should work\nas is without any changes in the current code.\n\nThere is plan to make the project fully compatible with W65C816, however that\nrequiresd some changed in handling 24-bit address bus (there is already a\n[ticket](https://github.com/ddrcode/teensy_6502_bridge/issues/3) for that).\n\nThis bridge may still work with some CPUs from the 6502 family, but some adjustments\nmay need to be required, as the other processor may have different pin layout. Also,\nthe minimum speed requirement may be a blocker here, due to limited speed of the bridge itself\n(see the section below).\n\n## Speed limitations\n\nThe speed of the bridge is limited by the speed of the serial port. The bridge sends 40-bits of data\nboth directions for every single half-cycle (clock phase). In order to obtain the CPU speed of\n1MHz, it would require the serial port operating at 160Mbit/s. Although the Teensy 4.1 is equipped\nwith high-speed USB 2.0 port, that - in theory - allows for 480Mbit/s, in reality the serial port\nemulation uses a single USB channel only and there is an overhead related\nto creation of USB packages. The current speed I've achieved in my tests is around\n0.5Mbit/s. This is quite sufficient for any form of debugging and execution of test programs,\nbut definitely too slow in case of running any real-time applications.\nIn example starting the C64 Kernal via the bridge takes around 300 seconds.\n\nI've created a [ticket](#8) for this issue. Contributors are welcome!\n\n## References\n\n- [W65C02 datasheet](https://westerndesigncenter.com/wdc/documentation/w65c02s.pdf)\n- [Teensy 4.1](https://www.pjrc.com/store/teensy41.html)\n- [6502 Primer: What do I do with the \"mystery\" pins](https://wilsonminesco.com/6502primer/MysteryPins.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddrcode%2Fteensy_6502_bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fddrcode%2Fteensy_6502_bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddrcode%2Fteensy_6502_bridge/lists"}