3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-06-18

はじめに

Ben Eater の 6502 computer を、キットを買って組み立てて、コンピューターの基礎を学びました。

image.png

6502 は1975年に発表されたCPUで、Apple IIに搭載されたり、互換CPUがファミコンで利用されたり、、、していたらしいです。

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

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

まずは 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
38 SOB + Set Overflow
40 RESB + ボタンでGND接続

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

クロック

動作確認

次に、動作の挙動が確認できるように、Arduino Mega 2560 を利用して簡単なロジックアナライザを自作します。

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

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

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

How do CPUs read machine code? — 6502 part 2

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 で埋めて、動作確認をしました。

スタートアドレス

fffc: 00
fffd: 80

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

プログラミング

LDA #$42
STA $6000

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

a9 42 8d 00 60

VIA の追加

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

Assembly language vs. machine code — 6502 part 3

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

Connecting an LCD to our computer — 6502 part 4

HD44780 という LCD ディスプレイを 6522 から操作します。

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

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

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

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

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

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

RAM and bus timing — 6502 part 6

62256 という RAM をブレッドボードに追加し、アドレスバスとデータバスの接続を行います。

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

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

Subroutine calls, now with RAM — 6502 part 7

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

Why build an entire computer on breadboards?

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

ブレッドボード比較して、ちゃんと いいもの を使うように促しています。

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

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

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

How assembly language loops work

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

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

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

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

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

Hardware interrupts

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

Interrupt handling

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

So how does a PS/2 keyboard interface work?

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

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

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

Keyboard interface hardware

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

突然 LCD が4bitモードに変更されていますが、深く考えずに先に進みましょう。
(詳細は パトロン になることで見ることができます)

Keyboard interface software

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

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

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

SPI: The serial peripheral interface

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

倒せませんでした。

How do hardware timers work?

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

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

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

The RS-232 protocol

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

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

Let's build a voltage multiplier!

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

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

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

6502 serial interface

image.png

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

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

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

RS232 interface with the 6551 UART

W65C51 という IC を利用して UART 通信を簡単に行うことにします。

配線

pin 名称 接続 備考
10 TxD MAX232のTX TX
12 RxD MAX232のRX RX
6 XTLI 水晶振動子/1MΩ/- 3つつなぐ
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)

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

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 のステータスレジスタの送信が常に0を返すため、その情報は利用できず、自前の待ち処理で対応しました。

Running Apple 1 software on a breadboard computer (Wozmon)

自作の 6502 コンピューターで、Wozmon を動かしてみました。

  .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

Adapting WozMon for the breadboard 6502

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

A simple BIOS for my breadboard computer

msbasic を動かすために ca65 という 6502 向けのコンパイラを導入します。

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

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

Running MSBASIC on my breadboard 6502 computer

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

おわりに

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

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

キットは こちら から購入可能です。

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

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?