ブレッドボードで 6502 コンピューターを組み立てて Hello World!

# はじめに

Ben Eater の [6502 computer](https://eater.net/6502) を、キットを買って組み立てて、コンピューターの基礎を学びました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13867/b195319c-ed0d-0bdd-5007-f53d2b4b4da1.png)

[6502](https://ja.wikipedia.org/wiki/MOS_6502) は1975年に発表されたCPUで、Apple IIに搭載されたり、互換CPUがファミコンで利用されたり、、、していたらしいです。

以下のものを組み合わせて基本的なコンピューターを作って遊びました。

- [W65C02](https://eater.net/datasheets/w65c02s.pdf) ... CPU
- [W65C22](https://eater.net/datasheets/w65c22.pdf) ... VIA
- [W65C51](https://eater.net/datasheets/w65c51n.pdf) ... ACIA
- [28C256](https://eater.net/datasheets/28c256.pdf) ... (EEP)ROM
- [62256](https://eater.net/datasheets/hm62256b.pdf) ... (S)RAM

# “Hello, world” from scratch on a 6502 — Part 1

https://www.youtube.com/watch?v=LnzuMJLZRdU

まずは 6502 をブレッドボードに差し、最低限必要な設定である入力を +5V もしくは GND に接続します。

## 配線

|pin|名称|接続|備考|
|--:|---|:-:|---|
|8|VDD|+|Power|
|21|VSS|-|Ground|
|2|RDY|+|Ready|
|4|IRQB|+|Interrupt|
|6|NMIB|+|Non Maskable Interrupt|
|36|BE|+|Bus Enabled|
|37|PHI2|Clock|[Clock Module](https://eater.net/8bit/clock)|
|38|SOB|+|Set Overflow| 
|40|RESB|+|ボタンでGND接続|

これで CPU を動作させます。

## クロック

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">数日ぶり２度目のクロックモジュール <a href="https://t.co/Wv9Ewehxx8">pic.twitter.com/Wv9Ewehxx8</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1792595343181308319?ref_src=twsrc%5Etfw">May 20, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 動作確認

次に、動作の挙動が確認できるように、[Arduino Mega 2560](https://amzn.to/4chBMKi) を利用して簡単なロジックアナライザを自作します。

アドレスが16本、データが8本あるのでつなぐだけでも大変です。

Arduino でクロックごとにアドレスとデータの確認ができるようになったら、
データを 11101010 となるように抵抗でハードコードをして、挙動を観察してみます。

リセット後に、fffc fffd のアドレスから `ea` `ea` を読み込み、 eaea のアドレスにジャンプし、`ea` 命令(=NOP) を実行し続けるようにできました。


# How do CPUs read machine code? — 6502 part 2

https://www.youtube.com/watch?v=yl8vPW5hydQ

32Kバイトのデータを保持できる EEPROM をブレッドボードに追加します。

## 配線

|pin|名称|接続|備考|
|--:|---|:-:|---|
|28|VCC|+|Power|
|14|GND|-|Ground|
|24|/WE|+|Write Enable|
|22|/OE|-|Output Enable|
|20|/CE|※1|Chip Enable|
||A0〜A14|A0〜A14 on 6502|Address line|
||D0〜D7|D0〜D7 on 6502|Data line|

CPU のアドレス空間は16bitで、ROM は15bitなので、8000〜FFFF を ROM に割り当てることにします。

※1: CPU の16bit目を(NAND で反転して) ROM の /CE に接続し、対象のアドレス以外の時にはデータの入出力を行わないようにします。

## EEPROM

EEPROM を `ea` で埋めて、動作確認をしました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">EEPROM のデータ確認した。正しくeaで埋まってる気がするなー。 <a href="https://t.co/cG4wbni92S">pic.twitter.com/cG4wbni92S</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1792745781054906459?ref_src=twsrc%5Etfw">May 21, 2024</a></blockquote>

## スタートアドレス

fffc: `00`
fffd: `80`

とし、8000 にジャンプするようにしました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">EEPROM の先頭から CPU を実行することができたー <a href="https://t.co/VOlO5GNpzV">pic.twitter.com/VOlO5GNpzV</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1792760188614905924?ref_src=twsrc%5Etfw">May 21, 2024</a></blockquote>

## プログラミング

```
LDA #$42
STA $6000
```

を実行してみます。EEPROM に書くバイナリは以下のとおり

`a9 42 8d 00 60`

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">6000番に0x42を保存するコードも動いた。CPU 動かしてる感がすごい。 <a href="https://t.co/dUDX0fmuot">pic.twitter.com/dUDX0fmuot</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1792761683678839127?ref_src=twsrc%5Etfw">May 21, 2024</a></blockquote>

## VIA の追加

[6522](https://ja.wikipedia.org/wiki/MOS_6522) は Versatile Interface Adapter(多用途インタフェースアダプタ)で、以下のような機能を提供しています。

- I/O
- タイマー
- 割り込み

|pin|名称|接続|備考|
|--:|---|:-:|---|
|24|CS1|6502 の A15 と A14| A15 = 0, A4 = 1 で有効に|
|23|CS2B|6502 の A13|A14 = 1 で有効に|
|22|RWB|6502 の RW||
|25|PHI2|Clock|6502 と共通|
|35〜38|RS0〜RS3|6502 の A0〜A3||

6502 のアドレスラインが以下の際に 6522 が有効になるように配線をします。
A15: `0`
A14: `1`
A13: `1`
6000番台を利用していることになります。

6000〜6004 でレジスタの設定ができるようにしています。

```
LDA #$ff
STA $6002
LDA #$55
STA $6000
```

を実行してみます。EEPROM に書くバイナリは以下のとおり

`a9 ff 8d 20 60 a9 55 8d 00 60`

6522 の Bレジスタに、01010101 を出力し（LEDで確認し）ました。

## Lチカ

次に、ループしてみましょう。

```
LDA #$ff
STA $6002
LDA #$55
STA $6000
LDA #$aa
STA $6000
JMP $8005
```

を実行してみます。EEPROM に書くバイナリは以下のとおり

`a9 ff 8d 20 60 a9 55 8d 00 60 a9 aa 8d 00 60 4c 05 80`

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">CPUを利用したLチカ？できたー <a href="https://t.co/N212Ep0PAd">pic.twitter.com/N212Ep0PAd</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793160480401547443?ref_src=twsrc%5Etfw">May 22, 2024</a></blockquote>

# Assembly language vs. machine code — 6502 part 3

https://www.youtube.com/watch?v=oO8_2JJV0B4

バイナリでプログラミングをするのが辛くなってきたので、アセンブリ言語でプログラミングをして、[vasm](http://sun.hasenbraten.de/vasm/) でコンピューターで実行可能な形式に変換することにしました。

# Connecting an LCD to our computer — 6502 part 4

https://www.youtube.com/watch?v=FY3zTUaykVo

[HD44780](https://eater.net/datasheets/HD44780.pdf) という LCD ディスプレイを 6522 から操作します。

Bポートの8bitでデータの操作をし、Aポートの3bitでその他の制御をすることにしました。

様々な初期設定が若干面倒くさいですが、動画を見ながら地道に実装をします。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">Hello World!<br>今まで書いたどんなHello Worldよりも学びが深いなー <a href="https://t.co/at8g4EE5V5">pic.twitter.com/at8g4EE5V5</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793362032101839097?ref_src=twsrc%5Etfw">May 22, 2024</a></blockquote>

# What is a stack and how does it work? — 6502 part 5

https://www.youtube.com/watch?v=xBjQVxVxOxc

雑にコピペでとりあえず動くようにしたソースコードを整理します。

`jsr` と `rts` でサブルーチンの実行をするように変更しました。

すると、謎の挙動で動作しなくなり、それがなぜなのか、スタックとはなんなのかを学習しました。

# RAM and bus timing — 6502 part 6

https://www.youtube.com/watch?v=i_wrxBdXTgM

[62256](https://eater.net/datasheets/hm62256b.pdf) という RAM をブレッドボードに追加し、アドレスバスとデータバスの接続を行います。

RAM はアドレスを指定してからデータが利用可能になるまでに時間がかかるため、念入りにタイミングを確認します。

1MHz 程度では結局大丈夫なのですが、タイミングダイアグラムは読み方が難しい。

# Subroutine calls, now with RAM — 6502 part 7

https://www.youtube.com/watch?v=omI0MrTWiMU

/OE, /CE を接続し、電源を入れると無事に意図したとおりの動作になりました。

# Why build an entire computer on breadboards?

https://www.youtube.com/watch?v=fCbAafKLqC8

ここでちょっと本題からそれて、ブレッドボードの話が挟まります。

ブレッドボード比較して、ちゃんと [いいもの](https://www.mouser.jp/ProductDetail/BusBoard-Prototype-Systems/BB830?qs=VEfmQw3KOauhPeTwYxNCaA%3D%3D) を使うように促しています。

それから、全体の電圧が安定するように、コンデンサをできればICごとに、少なくとも電源ラインごとにつけましょうとのことです。

また、高周波数の電流を流すと、位相が遅れたり、減衰したりします。

様々な可能性を検討し、1MHz のクロックを採用したところ、意図したとおりに動作しなくなり、LCD まわりで追加の対応が必要になりました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ブレッドボードの+と-が繋がってることがあるなんて… <a href="https://t.co/g3zBCrDojn">pic.twitter.com/g3zBCrDojn</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1796030877799207269?ref_src=twsrc%5Etfw">May 30, 2024</a></blockquote>

# How assembly language loops work

https://www.youtube.com/watch?v=ZYJIakkcLYw

LCD パネルの初期化処理を見直して、LCD の busy フラグを確認するようにします。

また、個々に指定していた文字を、文字列形式で扱うように改善しました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">1Mhz で電源オンからちゃんとプログラムが動くようになった。<br>たった188バイトでテキスト出力できてちょっと感動している。 <a href="https://t.co/UA9tVOhFCY">pic.twitter.com/UA9tVOhFCY</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793440417834480079?ref_src=twsrc%5Etfw">May 23, 2024</a></blockquote>

# Binary to decimal can’t be that hard, right?

https://www.youtube.com/watch?v=v3-a-zqKfgA

２進数の割り算について学び、任意の数字を10進数化することができました。

コンピューターはおもしろいですね。

# Hardware interrupts

https://www.youtube.com/watch?v=DlEa8kd7n3Q

ハードウェアの割り込みについてお勉強します。
組み込みでたまに出てくる言葉だけどよく分からないやつですね。。。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">割り込みちょっとだけ理解した <a href="https://t.co/1ZGHDHAS7U">pic.twitter.com/1ZGHDHAS7U</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793462133579305154?ref_src=twsrc%5Etfw">May 23, 2024</a></blockquote>

# Interrupt handling

https://www.youtube.com/watch?v=oOYA-jsWTmc

VIA の割り込み機能を利用して、より本格的な割り込みハンドラの実装を行います。

# So how does a PS/2 keyboard interface work?

https://www.youtube.com/watch?v=7aXbh9VUB3U

PS/2 接続でキーボードに対応してみます。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">ちゃんとしたPS/2のキーボードを入手して、ちゃんと動くことを確認した。 <a href="https://t.co/A4EZnE0pkd">pic.twitter.com/A4EZnE0pkd</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1795982725062484118?ref_src=twsrc%5Etfw">May 30, 2024</a></blockquote>

シリアルデータを 74HC595 でパラレル化して扱います。

キーボードからのデータを可視化する仕組みを作って、基本的な仕組みやキーボードのコードについて学びました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">シフトレジスタでキー押下のシーケンスをパラレルで取れるようにした。 <a href="https://t.co/1lpg2RFvbi">pic.twitter.com/1lpg2RFvbi</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1796041764241125408?ref_src=twsrc%5Etfw">May 30, 2024</a></blockquote>

# Keyboard interface hardware

https://www.youtube.com/watch?v=w1SB9Ry8_Jg

パラレル化したデータを受けとるのに都合のいい信号を、キーボードの信号を工夫して作成します。
RC回路とダイオードと、シュミットトリガーのインバーターでいい感じに作れました。

突然 LCD が4bitモードに変更されていますが、深く考えずに先に進みましょう。
(詳細は [パトロン](https://www.patreon.com/posts/4-bit-lcd-50900073) になることで見ることができます)

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">キーボードからキーコードを取得してLCDに表示できた <a href="https://t.co/slFEbcG037">pic.twitter.com/slFEbcG037</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1796221813707718893?ref_src=twsrc%5Etfw">May 30, 2024</a></blockquote>

# Keyboard interface software

https://www.youtube.com/watch?v=dL0GO9SeBh0

割り込みでスキャンコードを文字に変換し循環バッファにキューイングをして、
通常のループで LCD に表示処理をします。

変換テーブルを内部に用意し、スキャンコードから文字へ変換をします。

キーのプレス／リリースに対応し、シフトにも対応し、キーボードから Hello World! が入力できるようになりました！

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">Hello world!<br><br>たかがこれだけのためにすごい苦労をした。すごい。 <a href="https://t.co/Cz5E7gPaLF">pic.twitter.com/Cz5E7gPaLF</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1796229979925352937?ref_src=twsrc%5Etfw">May 30, 2024</a></blockquote>

# SPI: The serial peripheral interface

https://www.youtube.com/watch?v=MCi7dCBhVpQ

低レイヤーの通信でよく利用される SPI 通信をためします。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">BME220と戦っている <a href="https://t.co/TotI7E00lS">pic.twitter.com/TotI7E00lS</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1794915507877102041?ref_src=twsrc%5Etfw">May 27, 2024</a></blockquote>

倒せませんでした。

# How do hardware timers work?

https://www.youtube.com/watch?v=g_koa00MBLg

まずは手始めに地味な方法で。。。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">nop のループで LED を点滅させた <a href="https://t.co/mxc12EKqDS">pic.twitter.com/mxc12EKqDS</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793932454581190849?ref_src=twsrc%5Etfw">May 24, 2024</a></blockquote>

次に、6522 のタイマー機能を利用してみます。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">250msごとにタイマーでLEDを点滅させるようにできた。 <a href="https://t.co/sWxdH6vO9s">pic.twitter.com/sWxdH6vO9s</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793981601464111303?ref_src=twsrc%5Etfw">May 24, 2024</a></blockquote>

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">割り込みタイマーで10msごとに時刻を更新して、１秒ごとにLCDで数字を表示をするようにしてみた。LCDの制御とLEDで同じIOの別の番地を使ってるので、両方を同時に動かすのは少し頭を使う必要がありそう。 <a href="https://t.co/RcdsmOGSAj">pic.twitter.com/RcdsmOGSAj</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1793990678588031060?ref_src=twsrc%5Etfw">May 24, 2024</a></blockquote>

完璧にはできませんでした。

# The RS-232 protocol

https://www.youtube.com/watch?v=AHYNxpqKqwo

RS-232C でパソコンと通信をできるようにします。

まずは RX を直接つないで nop でタイミングを調整してデータを読んでみました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">RS-232 で Hello World ができた。 <a href="https://t.co/nVxT9R0WgX">pic.twitter.com/nVxT9R0WgX</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1797866802435014723?ref_src=twsrc%5Etfw">June 4, 2024</a></blockquote>

# Let's build a voltage multiplier!

https://www.youtube.com/watch?v=4alV5LzHLE4

RS-232C の通信のために +5V 以上の電圧が必要になりました。

コンデンサを利用して、倍にしてみます。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">コンデンサに充電して、5Vを倍の電圧にしてみた。 <a href="https://t.co/9l62aGJUAh">pic.twitter.com/9l62aGJUAh</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1797870580320043453?ref_src=twsrc%5Etfw">June 4, 2024</a></blockquote>

555 タイマーで高速に切り替えをして（ほぼ）倍の安定した電圧を得ました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">9V弱の電圧になった。不思議というか、なんというか…。 <a href="https://t.co/4xqUQ3VtBd">pic.twitter.com/4xqUQ3VtBd</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1797879372411740195?ref_src=twsrc%5Etfw">June 4, 2024</a></blockquote>

# 6502 serial interface

https://www.youtube.com/watch?v=oLYLnb7kpLg

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13867/47ed6834-a55b-6d83-f2a9-d04198be1002.png)

Serial interface Kit を利用してちゃんと RS-232C に対応していきます。

RS232C 用に MAX232 という IC を利用して電圧を得ます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">MAX232ってICがめちゃくちゃ熱い…のが可視化されててとても良い。めっちゃ熱い🔥 <a href="https://t.co/fxUknNLNvX">pic.twitter.com/fxUknNLNvX</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1797890127236391241?ref_src=twsrc%5Etfw">June 4, 2024</a></blockquote>

動画の通りに接続したらすごく熱くなりました。。。

# RS232 interface with the 6551 UART

https://www.youtube.com/watch?v=zsERDRM1oy8

[W65C51](https://eater.net/datasheets/w65c51n.pdf) という IC を利用して UART 通信を簡単に行うことにします。

## 配線

|pin|名称|接続|備考|
|--:|---|:-:|---|
|10|TxD|MAX232のTX|TX|
|12|RxD|MAX232のRX|RX|
|6|XTLI|水晶振動子/1MΩ/-|３つつなぐ|
|7|XTLO|水晶振動子/1MΩ|1.8432MHz|
|18〜25|D0〜D7|D0〜D7 on 6502|Data line|
|2|CS0|6502|5000〜5003のアドレスになるように|
|3|/CS1|同上|同上|
|13|RS0|同上|同上|
|14|RS1|同上|同上|
|27|PH2|クロック||
|28|/RB|6502のRW||
|4|/RES|リセット||

# Fixing a hardware bug in software (65C51 UART)

https://www.youtube.com/watch?v=CnA8nG3zYHw

密かに配線が追加されています。

|pin|名称|接続|備考|
|--:|---|:-:|---|
|9|/CTS|-|Clear to Send|

> Clear to Send (CTSB)
> The CTSB input pin controls the transmitter operation. The enable state is with CTSB low. The transmitter
> is automatically disabled if CTSB is high.

RS0/RS1 に 5000〜5003 でアクセスして初期設定を済ませます。

データの受信も実装し順調に先に進みますが、データの送信の動作確認に移ったところで問題が発覚します。

6551 のステータスレジスタの送信が常に０を返すため、その情報は利用できず、自前の待ち処理で対応しました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">RS232Cでシリアル通信をして、PC から文字を送れるようになったー <a href="https://t.co/bwzwIiGVxx">pic.twitter.com/bwzwIiGVxx</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1798968400661909536?ref_src=twsrc%5Etfw">June 7, 2024</a></blockquote>

# Running Apple 1 software on a breadboard computer (Wozmon)

https://www.youtube.com/watch?v=HlLCtjJzHVI

自作の 6502 コンピューターで、[Wozmon](https://gist.github.com/beneater/8136c8b7f2fd95ccdd4562a498758217) を動かしてみました。

```
  .org $8000
  .org $ff00

XAML  = $24                            ; Last "opened" location Low
XAMH  = $25                            ; Last "opened" location High
STL   = $26                            ; Store address Low
STH   = $27                            ; Store address High
L     = $28                            ; Hex value parsing Low
H     = $29                            ; Hex value parsing High
YSAV  = $2A                            ; Used to see if hex value is given
MODE  = $2B                            ; $00=XAM, $7F=STOR, $AE=BLOCK XAM

IN    = $0200                          ; Input buffer

ACIA_DATA   = $5000
ACIA_STATUS = $5001
ACIA_CMD    = $5002
ACIA_CTRL   = $5003

RESET:
                LDA     #$1F           ; 8-N-1, 19200 baud.
                STA     ACIA_CTRL
                LDA     #$0B           ; No parity, no echo, no interrupts.
                STA     ACIA_CMD
                LDA     #$1B           ; Begin with escape.

NOTCR:
                CMP     #$08           ; Backspace key?
                BEQ     BACKSPACE      ; Yes.
                CMP     #$1B           ; ESC?
                BEQ     ESCAPE         ; Yes.
                INY                    ; Advance text index.
                BPL     NEXTCHAR       ; Auto ESC if line longer than 127.

ESCAPE:
                LDA     #$5C           ; "\".
                JSR     ECHO           ; Output it.

GETLINE:
                LDA     #$0D           ; Send CR
                JSR     ECHO

                LDY     #$01           ; Initialize text index.
BACKSPACE:      DEY                    ; Back up text index.
                BMI     GETLINE        ; Beyond start of line, reinitialize.

NEXTCHAR:
                LDA     ACIA_STATUS    ; Check status.
                AND     #$08           ; Key ready?
                BEQ     NEXTCHAR       ; Loop until ready.
                LDA     ACIA_DATA      ; Load character. B7 will be '0'.
                STA     IN,Y           ; Add to text buffer.
                JSR     ECHO           ; Display character.
                CMP     #$0D           ; CR?
                BNE     NOTCR          ; No.

                LDY     #$FF           ; Reset text index.
                LDA     #$00           ; For XAM mode.
                TAX                    ; X=0.
SETBLOCK:
                ASL
SETSTOR:
                ASL                    ; Leaves $7B if setting STOR mode.
                STA     MODE           ; $00 = XAM, $74 = STOR, $B8 = BLOK XAM.
BLSKIP:
                INY                    ; Advance text index.
NEXTITEM:
                LDA     IN,Y           ; Get character.
                CMP     #$0D           ; CR?
                BEQ     GETLINE        ; Yes, done this line.
                CMP     #$2E           ; "."?
                BCC     BLSKIP         ; Skip delimiter.
                BEQ     SETBLOCK       ; Set BLOCK XAM mode.
                CMP     #$3A           ; ":"?
                BEQ     SETSTOR        ; Yes, set STOR mode.
                CMP     #$52           ; "R"?
                BEQ     RUN            ; Yes, run user program.
                STX     L              ; $00 -> L.
                STX     H              ;    and H.
                STY     YSAV           ; Save Y for comparison

NEXTHEX:
                LDA     IN,Y           ; Get character for hex test.
                EOR     #$30           ; Map digits to $0-9.
                CMP     #$0A           ; Digit?
                BCC     DIG            ; Yes.
                ADC     #$88           ; Map letter "A"-"F" to $FA-FF.
                CMP     #$FA           ; Hex letter?
                BCC     NOTHEX         ; No, character not hex.
DIG:
                ASL
                ASL                    ; Hex digit to MSD of A.
                ASL
                ASL

                LDX     #$04           ; Shift count.
HEXSHIFT:
                ASL                    ; Hex digit left, MSB to carry.
                ROL     L              ; Rotate into LSD.
                ROL     H              ; Rotate into MSD's.
                DEX                    ; Done 4 shifts?
                BNE     HEXSHIFT       ; No, loop.
                INY                    ; Advance text index.
                BNE     NEXTHEX        ; Always taken. Check next character for hex.

NOTHEX:
                CPY     YSAV           ; Check if L, H empty (no hex digits).
                BEQ     ESCAPE         ; Yes, generate ESC sequence.

                BIT     MODE           ; Test MODE byte.
                BVC     NOTSTOR        ; B6=0 is STOR, 1 is XAM and BLOCK XAM.

                LDA     L              ; LSD's of hex data.
                STA     (STL,X)        ; Store current 'store index'.
                INC     STL            ; Increment store index.
                BNE     NEXTITEM       ; Get next item (no carry).
                INC     STH            ; Add carry to 'store index' high order.
TONEXTITEM:     JMP     NEXTITEM       ; Get next command item.

RUN:
                JMP     (XAML)         ; Run at current XAM index.

NOTSTOR:
                BMI     XAMNEXT        ; B7 = 0 for XAM, 1 for BLOCK XAM.

                LDX     #$02           ; Byte count.
SETADR:         LDA     L-1,X          ; Copy hex data to
                STA     STL-1,X        ;  'store index'.
                STA     XAML-1,X       ; And to 'XAM index'.
                DEX                    ; Next of 2 bytes.
                BNE     SETADR         ; Loop unless X = 0.

NXTPRNT:
                BNE     PRDATA         ; NE means no address to print.
                LDA     #$0D           ; CR.
                JSR     ECHO           ; Output it.
                LDA     XAMH           ; 'Examine index' high-order byte.
                JSR     PRBYTE         ; Output it in hex format.
                LDA     XAML           ; Low-order 'examine index' byte.
                JSR     PRBYTE         ; Output it in hex format.
                LDA     #$3A           ; ":".
                JSR     ECHO           ; Output it.

PRDATA:
                LDA     #$20           ; Blank.
                JSR     ECHO           ; Output it.
                LDA     (XAML,X)       ; Get data byte at 'examine index'.
                JSR     PRBYTE         ; Output it in hex format.
XAMNEXT:        STX     MODE           ; 0 -> MODE (XAM mode).
                LDA     XAML
                CMP     L              ; Compare 'examine index' to hex data.
                LDA     XAMH
                SBC     H
                BCS     TONEXTITEM     ; Not less, so no more data to output.

                INC     XAML
                BNE     MOD8CHK        ; Increment 'examine index'.
                INC     XAMH

MOD8CHK:
                LDA     XAML           ; Check low-order 'examine index' byte
                AND     #$07           ; For MOD 8 = 0
                BPL     NXTPRNT        ; Always taken.

PRBYTE:
                PHA                    ; Save A for LSD.
                LSR
                LSR
                LSR                    ; MSD to LSD position.
                LSR
                JSR     PRHEX          ; Output hex digit.
                PLA                    ; Restore A.

PRHEX:
                AND     #$0F           ; Mask LSD for hex print.
                ORA     #$30           ; Add "0".
                CMP     #$3A           ; Digit?
                BCC     ECHO           ; Yes, output it.
                ADC     #$06           ; Add offset for letter.

ECHO:
                PHA                    ; Save A.
                STA     ACIA_DATA      ; Output character.
                LDA     #$FF           ; Initialize delay loop.
TXDELAY:        DEC                    ; Decrement A.
                BNE     TXDELAY        ; Until A gets to 0.
                PLA                    ; Restore A.
                RTS                    ; Return.

  .org $FFFA

                .word   $0F00          ; NMI vector
                .word   RESET          ; RESET vector
                .word   $0000          ; IRQ vector
```

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">6502で作るコンピュータのキットでWozmon が動いて、Lチカができた！ <a href="https://t.co/j5hBZjdbHz">pic.twitter.com/j5hBZjdbHz</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1799002999563649091?ref_src=twsrc%5Etfw">June 7, 2024</a></blockquote>

# Adapting WozMon for the breadboard 6502

https://www.youtube.com/watch?v=7M8LvMtdcgY

前述の Wozmon の 6502 対応の詳細を見ていきました。

# A simple BIOS for my breadboard computer

https://www.youtube.com/watch?v=0q6Ujn_zNH8

[msbasic](https://github.com/beneater/msbasic) を動かすために [ca65](https://cc65.github.io/) という 6502 向けのコンパイラを導入します。

コンパイラとリンカで作業を分担して最終的なバイナリを作成できるように調整します。

それから、BIOS として、簡単な入出力の機能を関数化しました。

# Running MSBASIC on my breadboard 6502 computer

https://www.youtube.com/watch?v=XlbPnihCM0E

MS Basic を他のボードの実装を参考に自前の対応を行い、動かすことに成功しました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">msbasic も動いた。basic 書いたの…昭和以来かもしれない。 <a href="https://t.co/tT6IzHN8Cz">pic.twitter.com/tT6IzHN8Cz</a></p>&mdash; Tasuku Suzuki (@task_jp) <a href="https://twitter.com/task_jp/status/1799473186100396203?ref_src=twsrc%5Etfw">June 8, 2024</a></blockquote>

# おわりに

6502 という約50年前に誕生した CPU と、その周辺のチップセットを利用し、コンピューターの基礎知識を学びました。

- CPU の基礎原理
- マシンコード
- EEPROM
- VIA
- LCD
- Stack
- RAM
- アセンブリ
- 2/10進数の計算
- 割り込み
- PS/2 キーボードによる操作
- SPI(途中)
- タイマー
- RS-232/ACIA
- UART
- Wozmon
- MS Basic

キットは [こちら](https://eater.net/shop) から購入可能です。

円安で割高感はありますが、それでも３万円くらいで上記がゼロから学べるなんて夢のようです。