Leap キーボードの製作

先日某所で Canon Cat に触れる機会がありました。

概要はいつものごとく Wikipedia を参照いただきたいのですが、Canon Cat は Macintosh プロジェクトの半ばでアップル社を離脱した Jef Raskin 氏が独自に設計したキャラクタインターフェイスのコンピュータ(注 1)です。CPU が 68000 という共通点はあるものの Macintosh シリーズとは全く異なるアーキテクチャを持っています。

まず目につくのはスタイリッシュかつコンパクトな筐体デザインです。オリジナル Mac(128k Mac)と同様に 9 インチモノクロモニタと 3.5 インチFDD 1 基を搭載しており、おおよそのサイズ感も同等です。ただし Canon Cat はモニタと FDD を横並びにしたレイアウトで、またキーボード一体型なので、全体のシルエットは本邦の名機 Sharp MZ-80B ないし MZ-2000 を思わせるものがあります。ボディカラーは Mac に似た白味の強いベージュで、シンプルながらも個性的な存在感があります。

機能面の特徴は完全なキャラクタベースのシステムという点で、マウスどころかカーソルキーまでも撤廃した入力インターフェイスを持ちます。画面のカーソル移動は 2 つの Leap キーを使って行います。これは一種のインクリメンタルサーチ機能で、若干 Emacs を思い起こさせる動作です。

Canon Cat のシステムソフトウェアは t-Forth で実装されており、隠しコマンドを入れることで Forth のコンソールに入ることができます。詳しい情報を事前に知らなかったので Forth モードで Canon Cat をいじることはできなかったのですが、通常のワープロモードで少し操作しました。128k Mac とはまた違うコンパクトなマシンの操作感はなかなか魅力的で「できれば手に入れたい!」と思わせるものでした。しかし調べたところ当時の販売期間が極端に短かかったこともあって台数はさほど出なかったらしく、今となっては入手困難なようです。

Leap キーボード製作工程

次善の策としてエミュレータを探すことにしました。Mame バージョン 0.219 あたりで動くコードがあるようです(注 2)。Mac 上で動かしてしばらく使っていましたが、実物のキーボードを触った経験からすると Leap キーはスペースバーの下に配置して、親指で操作するのがしっくり来るようです(注 3、注 4)。

そこで Canon Cat のキー配列をできるだけ再現した Leap キーボードを作ることにしました。まずは Keyboard Layout Editor ツールを使ってキーボードレイアウトのデザインから始めます。

http://www.keyboard-layout-editor.com/

Leap、Use Front、Document の各キーが標準キーボードとは異なる配列になります。 Keyboard Layout Editor ツールが出力する JSON ファイルを元にして、次のツールを利用してキーボードの PCB データを生成します。

https://adamws.github.io/keyboard-tools/guide

これらのツールを使用した場合、基板データの作成は通常と異なりレイアウトデータのインポート -> PCB データ生成 -> 回路図作成 -> MCU 回路追加 -> PCB データ更新、という流れになります(これが原因で後でエラーを出しました)。

変則的な作業手順でしたが比較的すんなりと基板発注データを JLCPCB へ提出できました。発注後一週間も待たずに基板が到着します。いつもながら早い。

JLCPCB で全長 30cm 超の基板を少量注文するとニコイチ箱に入って届きます
このサイズの基板 5 枚で $20 はやはり安い。しかし最近は送料がかさみます。今回は PCBA 料金も加算

今回はダイオードと抵抗のチップ部品のみ PCBA で部品調達と組み立てを依頼しました。約 60 箇所の手はんだ工程が省けるのと、一般的な SMT 部品は JLCPCB 内部調達で安価に購入できる点が長所で、 PCBA は趣味の基板製作でも便利に利用できます。

SOD323 という極小パッケージのダイオードが安価だったので採用しました。手はんだ困難なサイズ

ぜいたくを言うならキースイッチ用のミルマックスソケットも PCBA で調達したかったのですが、実装方法が特殊な部品なのでさすがに対応してないんじゃないかと思います。

ミルマックスソケットはキースイッチ数x2 必要なので合計 118 個を手はんだしました

しかし同形式の部品を使った安価なホットスワップキーボードも各社から販売されているので、もしかしたらミルマックスソケットは PCBA 可能かもしれません。

スイッチプレートは自宅 3D プリンタで作成しました。幅 30cm を超えるサイズなので 3 分割でプリントしています。基板に直接乗せるタイプのプレートなので分割しても剛性的な問題はあまりありません(基板自体を安定にサポートする必要があります)。

基板配線チェックの際にエラッタが判明しました。キーボードレイアウトから設計を開始するワークフローでろくに回路図を検証しなかったのが原因かと思われます。ドロナワ式で回路図修正->基板ジャンパ配線の順に進めます。

MCU には Raspberry Pi Pico を使用します。基板が完成したあとでコントローラのファームウェア設定を始めるのもどうかと思いますが、qmk に慣れてしまうとこの程度のキーボード設定はかなり容易です。

試作品テスト

以上で最低限動作する Leap キーボードができました。この時点での試作品と位置づけて操作テストを行います。

キーキャップは当面の間に合せでいつもの XVX プロファイルを使用

Leap キーボードの操作方法は Canon Cat クイックリファレンスガイドに説明されています。試作段階で気づいた改善点は次のようになります。

  • 市販キーキャップが適合しない。Leap キーや他の特殊キー用に 2u x4、1.5u x1 が必要
  • Leap キーのキーキャップはスペースバーより低いプロファイルのものを選んだほうがいい
  • ISO Enter キーは L 字型の左端が足りず余白ができるがこのままでも差し支えなさそう
  • スタビライザがスムーズに動くようプレートを再設計する
  • ちゃんとしたケースに入れたい
  • キーボード中央部分はネジ穴がないが基板下から支柱でサポートする
  • 基板と支柱間にクッション材を挿入する
なんだかんだいってすぐ Forth モードに入ってしまいます

以後はケースと基板サポート構造の設計、エミュレータ詳細設定、エミュ用コンピュータの選定の順で進める予定です。

参考:

注:

  1. 販売元は「ワークプロセッサ」と銘打ちワープロ主体の使い方を全面に打ち出していましたが、簡単なキーワードで Forth モードに入れるため「コンピュータ」製品と称して差し支えないでしょう。
  2. Internet Archive のオンライン Cat エミュレータは Mame 0.239 ベースです。個人的には Mame リリース 0.219 をローカルにインストールして使っており、こちらの方が比較的安定動作する(?)ようです。ただし、どのリリースでもまったくクラッシュしないわけではありません。
  3. とはいえ Canon Cat のプロトタイプとなった Swyft システムは Apple IIe 上で動いており、IIe キーボードではスペースバー左右の Open/Close Apple キーが Leap forward/back に割り当てられています。
  4. Swyft(Swift 言語とは別物)は本来 Apple IIe に専用 ROM カードを差すことで起動するのですが、近年完全ソフトウェアエミュレーション化されています(https://github.com/erangell/kfest2021)。
  5. この番組「High Tech Heroes」はアップル本社にほど近い Foothill College で撮影・製作されています。
  6. Canon Cat と同系統のデバイス Information Appliance(Jef Raksin 氏の会社名でもあるがここではデバイス名)プロトタイプのキーボードがこのページで確認できます。これによると最終的に Leap キーはスペースバーの左右へ移動されているようです(最下段のキーは左から Use Front, Leap, Space, Leap, Use Front, Phone)。

6800FLEX Basic と ASCIIART

Lambda-2022 マシンで FLEX 2.0 が動くようになりました。6800 / 6809 用 DOS である FLEX はマイコン DOS としては比較的初期の製品になりますが、アセンブラをはじめ各種言語が利用できて便利です。

「DOS」といっても物理フロッピーディスクならびにドライブをいまさら使う必要は感じられないので、シリアルサーバ Apple2pi のコードを改変して FLEX ディスクドライブをエミュレートしました。Apple ProDOS が 1 ブロック 512 バイトなのに対して FLEX は 1 セクタ 256 バイトが読み書きの最小単位であるため、Apple2pi のディスクエミュレーションコードの該当部分を変更します。FLEX のセクタ数とトラック数はディスク TOC に書かれたパラメータに応じて可変となるので、その処理も追加しました。6800 用クライアントコードは 6502 クライアントコード(a2pi.s)を参考にして新規に書きました。

FLEX ブートディスクイメージの作り方については Old68fun 様のページの情報が大変参考になりました。

FLEXシステムの作成方法(その3)・・・6800FLEXの場合

さて DOS が起動するとまず BASIC を使いたいところです。最初の手習いは当然、ASCIIART.BAS です。FLEX Basic で ASCIIART 画面をきれいに出力するには FLEX の TTYSET コマンドを使って 80 桁改行、ページャなしに設定する必要があります。

1 MHz 6802 / Flex 2.0 Basic:05:05.37

同じ 1 MHz 6800(6802)で動く Altair680 Basic と比較すると若干高速であることに気が付きます。

1 MHz 6802 / Altair68 Basic:07:35

同一プロセッサ、同一クロック数でこの処理速度の違いは?思って調べると、FLEX Basic はどうも浮動小数点の精度が低めなようです。試しに 10 進での有効桁数を確認しました。

1.00001 が表現できて 1.000001 が表現できないということは10進数有効桁数は7未満です。FLEX Basic マニュアルには「10 進 6 桁精度 (“six decimal digit accuracy”)」と記載されています [1]。

2^16 < 10^5 <  2^17  < 2^19 < 10^6 < 2^20

という比較が成り立つので、FLEX Basic 浮動小数点数の仮数部は 17-19 ビットの範囲のいずれかであるようです。例の「ケチビット」を勘定に入れると仮数部は 16-18 ビットで表現可能でしょう。また指数部については検証を怠りましたがマニュアルによると +/-38(10進)ですから 2 進換算で 7 ビット、これに加えて符号 1 ビットが必要となります。

FLEX Basic はおそらく 24 ビット精度の浮動小数点を使用していると推測されます。Aitair680 Basic その他初期 MS Basic は多くの場合 40 ビットまたは 32 ビット浮動小数点です。24 ビット浮動小数点の FLEX Basic で ASCIIART を実行した場合、計算量が少ない分速くなると言えそうです。

[補注]よく数えたら仮数部が 16-18 ビット、指数部(指数符号を含むオフセット表現)が 8 ビット、符号が 1 ビットとなり全体では最低でも 25 ビット必要になります。8 ビットマシンにしては半端なビット幅なので何か計算違いしている可能性があります。

参照

  • 1. TSC Basic Users Manual, 1979, Technical System Consultants, Inc.

8 ビットコンピュータ用シリアルサーバの構成

「シリアルサーバ」というのは今考えた言葉で、世間で通用する用語ではありません。どういう意味かというと、8 ビットレトロコンピュータに装備されている最低限の通信機能を利用して最近のハイエンドマイコンボード(Raspberry Pi、ESP32 等)ないし PC へ接続し、端末接続のみならず様々なサービス、例えばディスクエミュレーションやネットワーク機能などをレトロコンピュータに提供しようというアイディアです(注 1)。

レトロコンピュータ界隈ではこのアイディアに基づいて次のデバイスが発表されています。

レトロパソコンは数多く存在するのでこれ以外にも同様のデバイスがあるかもしれませんが、私が多少なりとも調べてみたことがあるのは上記二つだけです(注 2)。

ポイントは、レトロコンピュータ側は処理能力の限界上あまり複雑なプロトコルを喋れないということです。AppleTalk などの汎用ネットワークプロトコルは 16/32 ビットプロセッサの時代になってようやくサポートされました(注 3)。

そういう理由で、シリアルサーバでは接続相手のレトロコンピュータがなるべく少ないオーバーヘッドでサービス呼び出し処理を行えるように配慮されています。レトロコンピュータに搭載されている既存のモニタプログラムにはそのような機能がないので、専用のクライアントソフトウェア(ドライバ)を新たに書く必要があります。なお文字端末機能をシリアルサーバに載せる場合は基本的に 1 文字送受信を 1 パケットに対応させるため、元の回線速度の数分の 1 程度に遅くなることは避けられません。

***

と、ここまでさも全容を知っているかのように説明してきましたが、実のところ各システムの細部について十分理解できていません。そこで本稿では Apple2Pi のソースを読み、どのような構成になっているのかを調べていこうと思います。要は学習ノートです。あわよくば自分でも同様のサーバやクライアントプログラムを書こうという考えです。

Apple2Pi ソースコード

Apple2Pi ソースコードは Github で公開されています。docs ディレクトリに格納されているドキュメントを参照したところ、システム構成について簡単な説明がみつかりました。さしあたって次のソースコードが参考になりそうです。

  • src/a2pid.c: Raspberry Pi 上で実行されるデーモン(サーバプログラム)
  • src/a2lib.c、src/a2pidcmd.c、src/a2term.c: Linux 側から a2pid を利用するためのライブラリとクライアントプログラム群。
  • client/BUILD/a2pi.s: Apple II(ProDOS)側から a2pid を利用するための、シリアル回線経由のクライアントプログラム。マウス・ジョイスティックイベントをサポートしているため単純な文字入出力処理に比べると結構複雑になる。

注意することは、Apple2pi では Apple II の入出力ストリームを Linux のデバイスに接続するだけではなく、Apple II のキーボードとジョイスティックないしマウスを Linux の入力デバイスとして利用するという、いわば双方向のサービスが提供されている点です。今回想定している目的では後者の機能に深入りする必要がないので、興味のない部分はなるべく読み飛ばすようにします。

src/a2pid.c

シリアルサーバのサービスを提供するコードです。とりあえず main() から読んで概要を把握します。と思ったらファイルの半分超、約 550 行が main() 内のコードでした。くじけず読んでいくと次のような流れがわかります。

  • ポート 6551 で TCP ソケットをオープン(#653-)
  • Apple II に接続されたシリアル回線もソケット管理に含める(#676)
  • Apple II とサーバのコンソール入出力、ブロックデータ転送、仮想ディスクファイルの読み書き、IIe/IIc のマウスイベントなどの情報を通信する(#682-)

いったんソケットがオープンされると a2pid は TCP ソケットのクライアントおよびシリアル回線を監視し(#691 select())、パケットを受信するとコマンドを解釈して適切な応答を返すか関連するクライアントに転送します。a2pid プログラムの実行中はこのパケット処理ループから脱出しません。

なおソケット通信でやり取りされるデータはパケットと称されますが、パケット管理のためのフィールドは必要最低限に抑えられています。Apple II シリアル回線上での通信の場合パケット長は最小 3 バイトで、1 バイト目は必ずコマンド値です。主要なコマンドについて、将来流用できそうなものを中心として以下のように読んでいきます。

a. afd(Apple II シリアル回線)でやり取りされるコマンド

  • 0x80 sync:Apple II からの同期チェック。Apple II への応答(ack)は 0x81。
  • 0x82 a2d keybd:Apple II から送られるキーボードイベント。2 バイト目がモディファイアコード、3 バイト目がキーコード。
  • (マウス関連は割愛)
  • 0x90 ack read bytes, 0x92 ack write bytes: Apple II と a2pid 間のバイナリデータ転送
  • 0x96 send input char:Apple II へキーボード入力文字を送信する。Apple II からの ack コマンドは 0x97
  • 0x98 get output char:Apple II から文字出力を受信して印字担当クライアントへ転送する。
  • 0xa0、0xa2 drive 1/2 status call:ドライブ 1、ドライブ 2 のステータスを Apple II へ送信する。
  • 0xa4、0xa6 dirve 1/2 read call:ドライブ 1、ドライブ 2 の指定ブロックを読み出して Apple II へブロック転送(512 バイト)する。その後結果コード 1 バイトを Apple II へ送信する。
  • 0xa8、0xaa drive 1/2 write call:Apple II からのブロック転送(512 バイト)をドライブ 1、ドライブ 2 の指定ブロック位置へ書き込む。その後結果コード 1 バイトを Apple II へ送り返す。

b. srvfd(Linux TCP ソケット)でやり取りされるコマンド

Linux 上のクライアントプログラムと通信します。TCP ソケットなので a2pid が走っているマシンとは別のマシンでもクライアントを実行できます。

  • 0x90 read bytes:Apple II のメモリ内容を読み出す。addreq() を用いて転送リクエストをキューに追加する。
  • 0x92 write bytes:Apple II へデータを書き込む。addreq() を用いて転送リクエストをキューに追加する。
  • 0x94, 0x9a call:指定されたアドレスの Apple II ルーチンを実行する
  • 0x96 send input char:Apple II のキーボード入力へ 1 文字送る
  • 0x98 get output char:Apple II の 1 文字印字を取得する
  • 0xc0 reconnect vdrvs:仮想ドライブを再起動する(仮想ドライブファイルのクローズと再オープン)
  • 0xc2 disconnect vdrvs:仮想ドライブを閉じる
  • 0xff close:クライアントをクローズする

***

a2pid.c には仮想ドライブファイル読み書きのためのインターフェイス(vdrvopen(), vdrvclose(), vdrvread(), vdrvwrite() 等)が用意されています。ここは少し詳しく関数単位で見ていきます。

int vdrvopen(char *path)

Apple2Pi の仮想ドライブ 1/2 をオープンします。仮想ドライブのディスクイメージファイル名は “A2VD1.PO” および “A2VD2.PO” で固定ですが、これらのファイルがあるディレクトリパスは引数 path で指定できます。双方のファイルのオープンに失敗すると 0 を返し、それ以外の場合は 0 以外の値を返します。

void vdrvclose(void)

Apple2Pi の仮想ドライブ 1/2 をクローズします。

int vdrvtime(int afd)

ProDOS 形式の日付時刻データ(4 バイト)をファイルディスクリプタ afd に書き込みます。現在使用されていません。

int vdrvstat(int afd, int drive)

ドライブ drive に割り当てられているディスクイメージファイルのブロック数(=ファイルサイズ/512、2 バイト値)をファイルディスクリプタ afd へ書き出します。

int vdrvread(int afd, int drive, int block)

仮想ドライブ drive に対応するディスクイメージファイルのブロック番号 block の位置から 512 バイト(ProDOS 1 ブロック分のデータ)を読み出して、ファイルディスクリプタ afd へ書き出します。成功すると 0、失敗すると 0x27 を返します。

int vdrvwrite(int afd, int drive, int block)

ファイルディスクリプタ afd から 512 バイト(ProDOS 1 ブロック分のデータ)を読み取り、仮想ドライブ drive のブロック番号 block へ書き込みます。 完了後 fsync() で Linux ファイルシステムへの書き込みを確定します。成功すると 0 を返します。ファイルシステムへの書き込みに失敗すると 0x27 を、それ以外の失敗では 0x28 を返します。

***

Apple2Pi には vdrv* 系の読み書きインターフェイスとは別に Linux ファイルシステムから ProDOS ボリュームを操作するための fusea2pi.c も含まれますが、この部分は今回は読みません。

a2pid.c 以外のソースは簡単に眺めるだけに止めます。

src/a2lib.c

Linux クライアントから a2pid への接続と通信を扱うライブラリです。ソケットのオープン/クローズ、データブロック送受信、Apple II 内ルーチン(ProDOS システムコール等)の呼び出しが行なえます。

src/a2term.c

Linux コンソールを Apple II の入出力に接続するためのプログラムです。以下のコマンドを送受信します。

  • 0x96 を送信: Apple II へのキーボード入力送信。
  • 0x98 を受信:Apple II からの印字リクエスト。MSBをマスクする、改行文字を変更するなどの変換を行ってから Linux 端末上に表示する。
  • 0x9E を受信:キーボード入力を Apple II が受信したことの確認応答。a2term から送信した文字と一致しなかった場合はエラーとみなし、再同期はせずそのまま a2term 終了。

src/a2setvd, src/a2pidcmd.c

a2pidcmd は a2pid 経由で Apple II シリアル回線へ任意のコマンドパケットを送信し、応答として返される ack 値を受信します。なお Apple II が受け取った 1 バイト(かならず偶数値)のコマンドに対して返す ack 値は、コマンド値+1(奇数値)となります。

a2setvd は仮想ドライブにディスクイメージファイルを割り当てるスクリプトです。a2picmd を経由して a2pid へコマンド 0xc0(マウント)、0xc2(アンマウント)を送ります。アンマウント→イメージファイルを再リンク→マウントの順に実行することで指定のファイルを割り当てます。

利用する機能

以上、Apple2Pi のコードをかなり恣意的に拾い読みしてきました。ピックアップした内容を押さえることで次の処理が可能になります。

  1. シリアル回線を経由した文字の送受信
  2. シリアル回線を経由したディスクイメージファイルへのブロック単位の読み書き

つまり単一のシリアル回線を利用してコンソールとディスクエミュレータの 2 つの機能を実行できます。今後この機能を実装していきます。

注:

  1. 1980 年代の ASCII 誌で「Planet」という汎用パソコン間ネットワークのシステムが紹介されていました。京大 KMC のプロジェクトだったと思います。シリアル回線経由かどうかはわかりません。
  2. 2023 年 6 月時点で Raspberry Pi Pico ベースの 8 ビットサーバといえるシステムが複数発表されています。Raspberry Pi Pico には高速 PIO 機能があり、レトロマシンとの接続は必ずしもシリアルに限定する必要はありません。PIO は 1〜4 MHz クロックの 8 ビットシステムバスに接続するのにぴったりだな、と考えていたら RP2040 データシートそのものに用途例として “8080 and 6800 parallel bus” と書いてありました。
  3. 実際のところ Apple IIe 用の AppleTalk アダプタが発売されていたようです(参考)。また 1990 年代に入ってから Ethernet カードなども発売されていました。

自作 ASCII キーボードを Apple II Plus に接続する

ここしばらく作成している ASCII キーボード LMD-3420 の基本機能は 7 ビット ASCII キーコードをパラレル出力することです。またこれとは別に、キー押し下げイベントを信号立ち上げ(または立ち下げ)として表す 1 ビットの Strobe 出力があります。

Lmbda-3420 キーボード。ALPS AKB-3420(ほぼ)互換

このようなキーボードをコンピュータに接続する場合、CPU からは 7 ビット ASCII コードは通常の I/O ポート入力として読み出せばいいのですが、Strobe 信号は立ち上がりでフラグがセットされるフリップフロップとして保持し、CPU から任意の時点で読みに行けるようにしておく機構が必要です。また、ASCII コードを読み出したときにフラグが自動的にクリアされると便利です。Λ-1 コンピュータでは MC6821 PIA の CA 入力を使用することでキーボードからの Strobe 出力に即した設定が可能です。ASCII コードの 7 ビット出力は PIA の 8 ビットポート PA に接続します[注 1]。

実は Apple II のキーボードインターフェイスも全く同じ構成です[注 2]。違いは ASCII コードが負論理(Λ-1)か、正論理(Apple II)かという点だけです。したがって LMD-3420 のファームウェア(qmk_firmware のカスタムコード)で ASCII コードのビット反転操作を無効にすれば Apple II へ接続できます [注 3]。

Apple II のキーボード端子は 16 ピンの IC ソケットで、下図のピン配列になっています。これにあわせて LMD-3420 キーボード基板にも同様の配列の 16 ピンソケットを用意しました。上の画像でいうと基板上端の右寄りのソケット(ケーブルが取り付けられていない)です。

※ 実際には 1/16 ピン側が Apple II 基板手前となる(出典:
http://www.1000bit.it/support/manuali/apple/R023PINOUTS.TXT)

Apple II キーボード端子と LMD-3420 キーボードを 16 pin DIP 端子のフラットケーブルで接続します。DIP ソケット対応のコネクタ端子は最近あまり見かけませんが、当時はそれなりに使われていました。

DIP ソケットのフラットケーブル

このケーブルは DigiKey で注文するとワンオフで受注生産してくれます。特に高価なものではありません。

キーボードケースとキーキャップ

これまで LMD-3420 キーボードは基板むき出しの状態で使用していましたが、完成を目指してケースと下部構造を追加します。下部構造として、適切な傾斜のついた桁構造で基板とスイッチプレートを下から支持します。左右に 2 本と、中央に小さな桁を 3 個組み合わせた構造を使用しています。左右の 2 本はケース上面の左右端を支える構造にもなります。これらの桁構造は自宅の 3 D プリンタで出力しました。インサートナットを取り付けてねじ止めが容易になるようにします。

桁構造部品の 3D デザイン

また ABS 板を切り出してキーボード底板を作りました。ここへ 3 本の桁構造をねじ止めします。ねじのうち 5 本はゴム足を取り付けるためにも利用します。

キーキャップは SA プロファイルの Aifei 製 Fishing 互換セットに変更しました。LMD-3420 キーボードは一部キー配列が変則的なので既製品のキーキャップセットでは形状・キーレジェンドが完全に適合するものが見つからない部分もありますが、今回使用したキーキャップセットでは少なくともサイズと行が合致するキーキャップがひと通り揃っています。

唯一不足するのは 8u のスペースバーです。こんな長いスペースバーを現代のキーキャップセットに期待するほうが間違っているので、あきらめて自作することにしました。ところが、スタビライザも 8u サイズのものは簡単に見つかりません。そこで 7u スタビライザが使えるように自作スペースバーのスタビライザ用ステムの位置をずらしました。スイッチプレート上のスタビライザ取り付け穴も 7u 相当の形状で設置しています。

スペースバーの 3D デザイン。スタビライザ取り付け位置は 7u 相当

このキーボードの上にケースを取り付けます。ケースは長方形の板からキースイッチ部分の穴をくり抜き、前部・後部を折り曲げた形状になっています。上面は桁構造で決まる斜面に沿って前傾しています。オリジナルの AKB-3420 ケースは金属板を折り曲げ加工しているのだと思いますが、実物を触ったことがないのでよくわかりません。今回は加工後の形状のケース部品を、8u スペースバー部品とともに 3D プリントサービスへ発注しました。ケースには 128×32 ドットの小型 OLED ディスプレイと Raspberry Pi Pico の USB コネクタ部分を外に出すための穴を開けてあります。

ケースの 3D デザイン

ケースのサイズは 36cm x 18cm で主要部分の厚さは 2mm です。サイズに比べて厚みが足りないようで、発注先 JLCPCB の 3D プリント部門から「細い部分でひずみが出るかも」というデザインレビュー警告が来ましたが「とりあえずこのままプリントしてください」と回答しました。到着した実物を確認すると案の定、ケース手前の一番細い部分が若干歪んでおりキーボードスペースバーと干渉しかねない形状になっています。ヒートガンで温風を当てつつ手で逆方向へ曲げることで問題ない程度まで修正しました。

ケース(ひずみ修正済み)と 8u スペースバーを取り付けた状態

レジン製のケースは乳白色の地味な色合いなので、この上にステッカー式のカスタム印刷デザインシートを貼付しました。以前 Λ-1 のフロントパネルに貼ったものと同じ製品です。色もフロントパネルに合わせてあります。

貼付け前のデザインシート。これは同時注文した色違い版
デザインシートを貼ったケース。OLED と USB コネクタの部分に 3D プリント製のカバーを追加しています

Apple II plus キーボード端子に接続

Apple II plus 純正キーボードは小文字入力モードがなくデフォルトで英大文字が入力されます [注 4]。また一部の記号キーが足りず Shift-P で ‘@’、Shift-N で ‘^’、Shift-M で ‘]’ を入力するようになっています。LMD-3420 キーボードではこれらの記号にもそれぞれ ANSI のキーが割り当てられているので、Shift-英文字キーによる記号入力機能は割愛しました。

前述のフラットケーブルで LMD-3420 と Apple II plus キーボードコネクタを接続し、電源投入して問題なく入力できることを確認しました。

なお上の画像では LMD-3420 キーボードが Apple II の幅より大きく見えますがこれは遠近法による錯覚で、実際には LMD-3420 も Apple II ケースも横幅はほぼ同じで 14 インチ(36 センチ)内外です。

ファームウェアの設定を変えると Λ-1 に対応

[1] 8 ビット幅の PIA A ポートに 7 ビット ASCII パラレル信号を接続するので、MSB 1 ビットが余ります。Λ-1 および Chick-Bug の設計ではこの MSB に VDG の書き込み禁止 FS 信号を入力しています。ということは、画面書き換え処理を行うたびに CA1 のキーボードストローブがクリアされるという動作になります。どうやらこれは意図的な設計らしく、Λ-1 システムでは画面書き換えが完了するまで次の文字は入力できないという挙動になります。

[2] オリジナル Apple II と Apple II plus の本体基板だけがパラレルキーボードインターフェイス対応のコネクタを備えています。Apple IIe 以降ではエンコード回路も本体基板に内蔵されるため、キーボードコネクタはもっとピン数の多いものになっています。

[3] ただし実際には左矢印キーの扱いなど一部例外的な処理があるので、キーコードマッピング自体も変更してあります。

[4] 後期リビジョンの Apple II plus では小文字入力も可能です。

Λ-1/Lambda-2022 の拡張(10)ASCII キーボード基板

表題の「ASCII キーボード」は以前の投稿で「パラレルインターフェイスキーボード」と呼んでいたものと同じです。これをプリント基板化しました。リファレンスモデルである ALPS AKB-3420 にあやかって “LMD-3420” と名付けました。完全にまねっこですが、本家の方は廃版になって久しいのでまあいいんじゃないかとうやむやにしておきます。

基板化にあたってシフトロックインジケータと i2c OLED ディスプレイを追加しています。

既存の XVX キーキャップを使用

LMD-3420 のキー物理レイアウトは AKB-3420 に準拠します。右端列の 5 個の独立スイッチを再現したかったためです。独立スイッチはキーマトリクスに含まれずそれぞれ個別に GND に落ちる配線になっており、システム割込みやリセットに使用します。右から二列目の 5 個のキーにもリレジェンダブルキーキャップが使われており外見は右端列キーと同一ですが、この列は通常のキーマトリクスに含まれます。AKB-3420 ではコントロールコードが割り当てられていますが、LMD-3420 では現代のキーボードで標準装備されているバッククォート、バックスラッシュなどの文字種を割り当てています。このあたりは qmk ファームウェアで自由に設定できます。

物理レイアウトこそ AKB-3420 に一致させたものの、論理レイアウトは ANSI 配列に変更してあります。

上:AKB-3420 キー配列、下:LMD-3420 キー配列

AKB-3420 と異なる点がもう一つあり、今回使用したキースイッチは Cherry MX 互換品です。AKB-3420 で使用されていた ALPS SKCC キースイッチは現在ほぼ入手不可なため今回の基板では採用しませんでした。ALPS SKCC は Apple IIe や Apple III にも使われており個人的には親しみ深いスイッチなのですが、まさかこれらのビンテージ機種から部品取りするわけにもいきません。このため今回の基板設計では現役で流通している Cherry MX 対応としました。

qmk ファームウェアに追加したカスタムコードで GPIO Expander PCF8574A を制御し、パラレルインターフェイス端子へ ASCII コードと Strobe 信号を出力します。インターフェイスとして AKB-3420、Λ-1、Apple II、および USB の 4 種類を備えており、それぞれ仕様が異なります。Apple II と AKB-3420/Λ-1 では文字コードの正論理・負論理が異なるのですが、これはファームウェアを書き換えることで切り替えます。なお Apple II インターフェイスは未テストですので、万が一この基板で動かなければリビジョンアップということになります。

LMD-3420 基板。上に乗せてあるのは比較用の SBC サイズ(80mm x 100mm)基板

基板の製造と組み立て

基板製造は JLCPCB に発注しました。このサイズの基板が 5 枚で $26、同時にスイッチプレートも設計して発注しました。しかしフルキーのスイッチプレートをガラスエポキシ板で作ると特殊形状の穴加工が多すぎて追加料金が発生します。さらにこのサイズの基板やプレートだと重さに比例して送料が増え、トータルコストは $90 弱まで膨れ上がりました。まとめて注文するメリットはそれほどないようです。それでもたとえばアクリル製スイッチプレートを別業者に頼んだ場合よりも総額では安価になります。

この基板とキースイッチ、スイッチプレート、MCU(RPi Pico)などを組み合わせてキーボードとして機能するところまで組み立てました。AKB-3420 の元設計だと角度を付けた 2 本の金属製の脚を基板の左右両端に取り付けるのですが、今回の設計では基板、スイッチプレートともガラエポ製であるため左右 2 本の脚だけでは剛性的に不十分で、中央付近に補助的な脚を追加する必要があると思われます(基板上にネジ穴だけは用意してあります)。これら取り付け脚やケースについては 3D プリントで作る予定ですがその作業は次回以降に先送りすることにして、まずは通電テストを目指し、基板むき出しのまま最低限の組み立てを完了させます。

スイッチプレートもほぼ同サイズ

キーキャップ問題

キーキャップの選択は悩みどころで、現在は XVX プロファイルの既製品を流用しているのですが随所にサイズの合わない部分が発生します。特に AKB-3420 のキー配列を再現する場合は 1.5u のキーキャップがすべての行で必要になる点、また当時の常でスペースバーが 8u の長いものである点が問題となります。実用上は現状でもあまり困らないのですが、将来ケースを取り付けた際にきれいに長方形に収まるようにしたいと考えています。以下のキーが不足します。

  • Backspace (R4, 1.5u)
  • ’Q’ (R3, 1.5u)
  • LF (R3, 1u)
  • Ctrl (R2, 1u)
  • Tab (R1, 1.5u)
  • Caps (R1, 1u)
  • Space (conv., 8u)
  • Esc (R1, 1u)

なお行名は下から R1, R1, R2, R3, R4 としてあります。キーキャップの行名は上から R1, R2, … と数える流儀と下から数える流儀があって統一されていないようです。

不足キーキャップはいずれ 3D プリントでカスタム設計することにしますが、上に挙げたキーキャップをすべて作り直すと R1〜R4 までまんべんなく設計する必要があり、作業量を考えると二の足を踏みます。しかし、実は同行・同サイズのキーはキーキャップセット内を探せば割とあるのです。レジェンド刻印のみ異なるキーキャップを再利用すればいいわけです。こういったキーキャップの再利用を検討に含めるとカスタムメイドが必要なキーキャップの数を減らせます。

同行同サイズのキーキャップを代用して可能な限り外形を合わせた状態

レジェンド刻印を無視すれば次のキーが不足するということになります。

  • Backspace: R4, 1.5u
  • R-Shift: R1, 2u(キーキャップセットによる)
  • Space: Convex, 8u

また今回は XVX プロファイルのキーキャップセットを使いましたが、これは必要となる 1.5u、2u のキーキャップがそれなりに揃っているという理由で選択したものです。最終的にいくつかのキーキャップを自作して補充するのであればよりクラシックな SA プロファイルのキーキャップセットを選択するという手もあり、このあたりはまだ決めかねています。

リファレンスモデル ALPS AKB-3420

Λ-1 接続テスト

組み上がったキーボードを 16 ピンフラットケーブル(IDC 端子)を使ってΛ-1 コンピュータに接続します。qmk ファームウェアの設定は以前プログラムしたものがほぼそのまま流用できたのでこの接続テストはハードウェア、ソフトウェアとも大きな修正なく完了しました。キーボード右上端の独立スイッチを使った ATTN キー(インタラプトキー)によるモニタプロンプト呼び出しもできます。なお Λ-1 コンソールは小文字非対応なので CAPS LOCK を常にオンにする必要があります。

Λ-1 に接続してテスト。右の大きなディスプレイは使っていません

今後の計画

基本的な動作を確認できたので、完成に向けて次の作業を進めようと思います。

  • 底面と支柱の設計・プリント
  • 不足キーキャップの設計・検証と発注
  • ケースの設計と発注(外装シートも含む)
  • Apple II 接続テスト

Λ-1/Lambda-2022 の拡張(9)カセットインターフェイス

マイコンシステム Λ-1 は外部記憶装置としてカセットテープレコーダを使います。データ保存にテープレコーダを利用する手法は特に珍しいものではなく、1980 年前後の 8 ビットパソコンでは標準的な記録方式でした。コモドール 8 ビットマシン(PET/CBM/VIC/C64)や MZ-80 のようにデジタル信号専用インターフェイスのカセットデッキもあったようですが、多くは普通の音楽録再用デッキ(ラジカセ含む)の入出力端子に接続することを前提とした設計でした。Λ-1 も市販のカセットデッキを外付けする仕組みです。

カセットデッキ回路は音声信号処理を目的とするので 15 kHz 以上の成分の録音・再生は保証されません。接続方式によっては出力の位相が反転する可能性もあります。これらの条件に適した信号を変調・復調できるインターフェイス回路が必要です。

出力信号のソースは PIA の出力ポートなので 5Vp-p の矩形波です。パルス幅の違いでビット値 0 と 1 を表し、0 が 1400us、1 が 700us のパルス幅となります。なお実際には 1 ビットのデータあたり 1 周期分を出力するのでビット値 0 の出力に 2800us、ビット値 1 の出力に 1400us を要します。

「マイコン手づくり塾」記載の回路を参考にインターフェイスを組みました。回路図は次のとおりです。

PIA に接続する他の回路、つまりキーボード入力ポートや K68-VDG の垂直同期信号入力ポートなどもこの回路図に含まれます。図の中央付近がテープレコーダ録音再生のための変復調回路ですが、面白いのはカセットテープから再生された音声信号をパルス幅測定に適した波形に加工する復調回路です。入力音声信号をクランプ回路でクリップしたあとでコンパレータを通して矩形波に整形するのですが、Λ-1 の設計ではコンパレータとしてオーディオレベルメータ用 IC の三洋 LB1405 を使います。レベルメータ LED を見ながら入力レベルを調節できます。

Lambda-2022 筐体に組み込んだ回路基板は次の画像のとおりです(筐体奥のユニバーサル基板)。

筐体背面パネルにカセットデッキ入出力用の端子を追加しました。次の画像のとおりです。

“DOUT” “DIN” “RMT” というラベルのある部分がカセットデッキ用端子です。通常はポータブルテープレコーダに接続するための 3.5mm/2.5mm 端子(ラベルよりも右側の部分)があれば十分ですが、左側に RCA 端子による入出力も追加してあります。今回はコンポ型のカセットデッキを使うのでこの RCA 端子を使って接続します。ステレオデッキの L チャンネルのみ使用します。

カセットデッキは SONY のエントリレベルモデルを中古で入手しました。今となっては 3 ヘッドの中堅機あたりでも中古価格は大差ないのでしょうが[注 1]、メカや回路がシンプルな方がメンテナンスが容易になり、またデータ信号波形に余計な色がつかなくて良いのではと考えました。当然ドルビー NR は無効にして録音します。

カセットデッキを使うのは久しぶりです。メンテを済ませてから手持ちの新旧テープを聴くなどしばらく寄り道をした後、デッキの録音レベルを調節しながらセーブ・ロードのテストを繰り返しました。ところがエラーがなかなか解消されません。

どうしたものかと書籍「マイコン手づくり塾」を読み返すと、カセットインターフェイス製作の章ではなく別のページの読者 Q&A コラムの中でトラブルシューティングとして説明されていたのが、回路図中 R5 の両端に 0.1uF 程度のスピードアップコンデンサを追加するという手法です。これが効果的だったようで、インターフェイスからデッキへの出力信号が正確なパルス幅を含む波形になりました。

それでもまだロード時にエラーが出るので、今度はデータ入力側にオシロスコープを繋いで設定を詰めていきました。最終的にまあまあ安定したと思われる設定での波形が下図のものです(CH1: デッキからのオーディオ出力信号、CH2: 復調後のデジタル信号波形)。

念のため録音後 24 時間ほどおいて再度テストしましたが同様にパスしました[注 2]。

当時オシロは高価な機器だったため、専門家以外が簡単に使えるものではありませんでした。こういったアナログ絡みの回路をオシロの助けなしに上手く動作させるには長時間のカットアンドトライが必要になったはずです。Λ-1 のカセットインターフェイス回路に LED レベルメータが組み込まれているのも、調整作業が少しでも簡単になるようにという配慮があってのことと思われます。

注:

1. eBay を覗いたらそんな事はありませんでした。3 ヘッドデッキはエントリレベルデッキに比べて少なくとも 3〜4 倍の値付けです。

2. セーブ・ロードの対象はダンプの前半 32 バイトのみなので、後半 32 バイトが変化していても問題はありません。

Λ-1/Lambda-2022 の拡張(8)パラレルインターフェイスキーボードの作成

「マイコン手づくり塾」で Λ-1 に接続して使用するキーボードは ALPS AKB-3420 というパラレルインターフェイス仕様の機種です。7 ビット ASCII コードと Strobe 信号を送出します。

ALPS AKB-3420 はとうの昔に廃番になっており、それどころか現在ではパラレルインターフェイスキーボードというカテゴリ全体が、オークションサイトで探してもたまに見かける程度という希少品種です。

さいわい昨今はキーボード自作部品やツールキットが潤沢に供給されています。自作キーボードの手法でパラレルインターフェイスキーボードを作成しようと思います。

自作キーボードと言いながら、既製品を改造して近道しておりますが…

パラレルインターフェイスキーボードの構成

自作キーボードのファームウェアといえば qmk が定番ですが、qmk ではコンピュータとのインターフェイスとしてサポートされるのは USB のみです。Λ-1 および Chick-Bug モニタで使うキーボードは PIA に接続するパラレルインターフェイスが必要になるので、qmk をそのまま使用することは残念ながら不可能です。

かといってファームウェアをいちから書くのは結構な仕事です。どうしたものかと考えながら qmk_firmware ドキュメントを眺めていたら i2c 経由でパラレルインターフェイスを追加できるかも、と思いつきました。qmk_firmware にはキーマクロ用の C 言語インターフェイスがあるので、キー押し下げイベントに応じて ASCII コードをパラレルポートへ送信する処理が追加できそうです。この方向で試してみます。

また、今回は MCU として Raspberry Pi pico を使います。そもそもパラレル出力などという風変わりなことを試すのに、その上 qmk 標準から外れた RPi pico を使うのでいろいろ予想外の困難が出るかもしれません。段階的に確かめながら作っていくことにします。

RPi pico 版 qmk_firmware のビルドとインストール

Mac Parallels VM の上に Ubuntu 20.04 を新規インストールして専用開発環境を構築します。ホスト Mac 側から VSCode Remote Development で接続して編集、ビルドを行います。

おおよそ次のステップで新しい Ubuntu VM 上に環境を作りました。

  1. Raspberry Pi pico 公式ドキュメントなどを参考に SDK とサンプルコードをセットアップします。サンプルコード pico-examples の blink プログラム(L チカ)が動くところまで確認しておきます(参考)。
  2. qmk 公式ドキュメントを参考に qmk_firmware 環境をセットアップします。
  3. qmk new-keyboard コマンドで新しいキーボードを定義します。mcu として RP2040 を指定します(参考)。

注:2022 年 11 月現在で qmk_firmware の RP2040 対応コードは master ブランチにマージ済みなので、ブランチの切り替えは不要です。

テスト用に作るキーボードはワンキーでも標準的なフルキーでもいいのですが、ともかくキーアサイン定義を RPi pico GPIO に即して書き換えます。GPIO ピン名として “GPnn” という書き方ができます。

キーボードの設定にあたって主に書き換えるのは次のファイルです(キーボード名を ‘lambda2022a’ とした場合)。

  • keyboards/lambda2022a/info.json
  • keyboards/lambda2022a/keymaps/default/keymap.c
  • keyboards/lambda2022a/config.h

この時点で簡単なキー入力テストを行います。実際につなぐのは 1 キー分のスイッチなのでブレッドボード配線で十分です。キーマトリクスの 1 交点を選んでスイッチを GPIO ピンに配線し(1 キーなのでダイオードは不要)、スイッチ押し下げのテストを行います。

使い回しの部品ばかりでリードがよじれていますが動作には問題ありません

下の画面では主に ‘c’ キー交点にスイッチを配線して入力テストを行なっています。途中でブレッドボードの GPIO 配線を差し替えて他のキーに対応する交点にスイッチを組み替えた状態でもテストしています。qmk_firmware を使うとチャタリング回避の調整が不要になるのでその点は安定して使えます(画面中 ‘c’ や ‘b’ が何度も連続して表示されるのはそれだけキーを押したからで、チャタリングではありません)。

せっかくなので C 言語のサンプルキーマクロも動かしました。特定のキーコードの押し下げイベントに対応してカスタム出力が可能であることを確認しました。

GPIO Expander パラレル出力

ここで qmk のほうはいったん置き、GPIO Expander PCF8574 モジュールを RPi pico の i2c ピンにつないでパラレルポートのテストを行います。最も単純な接続方式として PCF8574 を RPi の i2c ピンに直接接続し、電源も RPi 3V3 出力に接続します。こうすると GPIO Expander の出力は RPi pico 動作電圧と同じ 3.3V レベルになります。

デフォルトの i2c SDA/SCL ピン(pin6 と pin7)を使い、pico-examples/i2c/lcd_1602_i2c/ をお手本にして PCF8574 へ 8 ビットデータを出力しました。オシロを見ながら書き込みデータを変えてみると任意の信号を出力できそうなので、ASCII コード(AKB-3420 の仕様に従い負論理 [注 1])を b0-b6 に書いてから b7 ストローブ信号を立ち下げる動作をさせます。これを Λ-1 の PIA に入力します。

上の画像は Λ-1 モニタプログラムのキーボード入力ソースを ACIA シリアルから PIA パラレルインターフェイスキーボードに切り替えた後で文字 ’Q’ を 1.6 秒間隔で受信している状態です。

これで、RPi pico + PCF8574 から 3.3V 出力の ASCII データを送信できることがわかりました。当初は信号が減衰してデータエラーが出る可能性を考慮して I2C 回線に 3.3V – 5V レベルシフタを挟むことも検討しましたが、3.3V レベルのままで 60cm 長のフラットケーブルを介して PIA 入力に信号が届くようなのでレベルシフタは割愛します。

RPi pico + PCF8574 でパラレル出力実験

qmk C マクロでパラレル出力ドライバを書く

前節の実験は pico-examples のサンプルコードを流用して書いたものですが、これを qmk 環境に移植します。

前述の qmk 設定に加え i2c 関連機能を有効にするために、次の各ファイルを変更します。qmk 公式ドキュメントの「Raspberry Pi RP2040」および「I2C Master Driver」のページを参考にしました。

  • keyboards/lambda2022a/halconf.h
  • keyboards/lambda2022a/mcuconf.h
  • keyboards/lambda2022a/rules.mk

qmk_firmware には i2c 制御のライブラリが用意されているはずですが、RP2040 ターゲットのビルドを行ったところライブラリのリンクや i2c の初期化がなかなかうまく行きません。ソースツリーの keyboards/ 以下にある他のキーボード用の i2c コードも参照しましたが、RP2040+OLED の組み合わせによる作例ばかりで i2c バス直接制御の使用例はないようです。

この i2c バス直接制御のための設定方法がわからず数日間悩みましたが、結局 rules.mk ファイルに次の 1 行を追加することで RP2040 ビルドでの i2c 関連のリンク、初期化ともほぼ自動的に行われるようです。

OLED_ENABLE = yes

前節と同じブレッドボード上のワンキー構成キーボードを使って、’q’ キーを押すと Λ-1 のパラレルキーボードインターフェイスに ASCII コード ‘Q’ が送られることを確認しました。同時に USB ポートへも ‘q’ キーの押し下げイベントが送信されています。

最後に、qmk のキーコード(KC_xx)とモディファイアキー状態 get_mods() から ASCII コードへの変換を行います。AKB-3420 相当のパラレル出力データとして必要なのは 7 ビット ASCII コードなのでチェックすべきキーコードの数も限られ、比較的シンプルな変換処理になります。

以上でコントローラの設定とプログラムはあらかたできました。次に物理的なキーボードの選定について検討します。

キーレイアウトと機種の選定

Λ-1 で使用された ALPS AKB-3420 の写真を見ると全キーが長方形に収まっておりなかなか端正なデザインです。今で言う 68 キー配列に近いものがあります。そこで既存の 68 キー配列キーボードをベースにしてパラレルインターフェイスキーボードを作ろうと思います。

(AKB-3420 画像出典:Deskthority WIKI)

最初は基板を起こして AKB-3420 のレイアウトを再現しようと思ったのですが、現代のキーキャップで構成するには若干無理があるため断念しました。パラメトリックキーキャップ設計ツールに習熟すればあるいは可能かもしれません。

当面は一般的な 68 キー配列の市販キーボードのケースや基板を流用します。AKB-3420 は ISO 配列ですが、個人的な好みにより ANSI 配列に変更します。AKB-3420 の最大の特徴は右側 2 列のリレジェンダブルキーキャップなのでここはなるべく再現したいところですが、現代の 68 キー配列ではこの部分は 1 列しかありません。ただ、Λ-1 ではこのエリアを多用しないので 1 列のままでもしのげるのではないかと思います(ATN キー、LF キー、ESC キーが最低限必要。LF、ESC はスペースバー横に配置しても可。ATN キーはマトリクスではなく直接配線が必要)。

68 キー配列ではカーソルキーが右端列の下端にはみ出しますがこれはそのまま残します。といっても Λ-1 / Chick-Bug ではカーソルキーはほぼ無用ですので、将来の拡張用といった位置付けになります。

既製品キーボードから流用するのはケース、スイッチプレートとマトリクス配線です。もともと基板に実装されていた MCU などは配線から除外します。今回 MCU として使用する RPi pico、PCF8574 などを足し合わせると既製品ケースには収まりきらないようなので当面はマトリクス配線をケース外に引っ張り出して結線します。いずれにしてもケースは再設計が必要なようです。

組み立てとテスト

既製品キーボードのマトリクスを調べたところ 15×5 でした。RPi pico ではマトリクスに GPIO ピンを 20 個割り当ててもまだ 6 個余るので、そこへ i2c を結線します。キーボード右上端のキーはパターンカットしてマトリクスから除外し、PIA の CA1(NMI 入力) に接続してプルアップ抵抗も付けます。これが Chick-Bug の Atn キーとして機能します。

この既製品キーボードをベースにしたテスト構成では、RPi pico と PCF8574 はブレッドボードに組んだまま使用しています。改造したキーボード基板から多数の線が出ていますが、この状態で実運用予定の構成と同じ配線になっています。

以前に作成したシリアルコンソールと独立して、パラレルインターフェイスキーボードと K68-VDG によるコンソールが動くようになりました。

***

以上で、パラレルインターフェイス方式の ASCII 出力キーボードを作成しました。ところで、よく考えたらこれは Apple II Plus にそのままつながりそうです。今後折を見て、キーレイアウトをカスタマイズしてテストしようと思います。

  1. AKB-3420 単体での出力は正論理ですが、Λ-1 では AKB-3420 にバッファ回路が追加されその部分でインバータを使用しているため Λ-1 への入力信号は負論理となります。

Λ-1/Lambda-2022 の拡張(7)K68-VDG の接続

Λ-1 システムの目玉機能のひとつは MC6847 VDG を使用したディスプレイインターフェイスです。なにしろカラーモニタディスプレイは当時の市販マイコン(今で言うパソコン)のアイデンティティで、標準的な自作ボードマイコンとは一線を画する機能でした。これさえあればビデオゲームが作れる!まあそう簡単に行くものではなかったのですが、表現力という点でカラーディスプレイは飛躍的な差が付くデバイスでした。

そんな MC6847 VDG ですが Λ-1 でサポートされるのは 32×16 テキストモードだけです。このモードでは VRAM は 512 バイトしか必要としませんし、同じメモリ量で使えるセミグラフィックモードでは 64×32 のまがりなりにもカラーグラフィックスが出力できたのです。グラフィックスといってもテキストモードに間借りして 2×2 ボックスパターンキャラクタを表示させるものですが、8 色カラー出力が可能でした。

当時はメモリのコスト面を考慮するとこのような低解像度の画面設定がベストだったわけですが、現在では大容量 SRAM がたやすく手に入ります。MC6847 がサポートする 8KB VRAM を最大限に使いたいところです。

ここまで考えると Kuni-Net 様設計の K68-VDG が選択肢に入ってきます。K68-VDG は今回作っている Λ-1 マシン(Lambda-2022)で採用した SBC-Bus に対応していますし、MC6847 の各種モードをサポートして VRAM は 8KB x 4 プレーンが利用できます。またビデオ出力が高画質であることも K68-VDG の利点です。

K68-VDG を Λ-1(Lambda-2022)で使うには VRAM アドレスと VDG 設定レジスタ(LS573 8 ビットラッチ)のアドレスを K68-VDG のオリジナル設計から変更する必要があります。

デバイス旧アドレス新アドレス
VRAM$A000-$BFFF$C000-$DFFF
VDG CTL$81xx$F048

このアドレスに沿って動作するためには K68-VDG のデコード回路に手を入れる必要があります。次のように設定します(元回路は K68-VDG リポジトリのこちらを参照)。

  • VRAM アドレス変更:SBC Bus ピン 35 に $C000-$DFFF デコード信号(VRAM#)が出るようにする(I/O 基板で実装済み)
  • VDG CTL アドレス変更:SBC Bus ピン 37 に VRAMCTL# が出るようにする(I/O 基板で実装済み)。また、K68-VDG の LS02(U5)のピン5, 6 と A8 の間の配線をカットし、代わりに A3 を U5 ピン 5, 6 に接続する
  • K68-VDG のアドレスデコーダ(U10、LS138)は実装しない

K68-VDG 基板上の実作業としては U3 のピン 5, 6 部分のみパターンカットとジャンパが必要になります(トレースの都合上 U9 へ A8 を再配線する必要があります)。それから、現在 NC となっている MC6847 の FS#(ピン 37)を I/O 基板上の PIA0 PA7に接続します。これはバスを介さずに行います。

このように若干手を入れる必要がありますが、MC6847 の VRAM をフル実装した K68-VDG の機能が簡単に利用できるのは大きな利点です。これも SBC-Bus を基盤として SBC ユーザの皆様の設計が標準化されているおかげです。

Chick-Bug の再改造

これでハードウェア的には K68-VDG を Lambda-2022 に接続できましたが、Chick-Bug モニタプログラムは現状ではシリアルコンソール専用に改造されています。これを VDG/シリアル両対応に再改造します。方針としては次のようになります。

  1. 以前シリアル用に書き換えたルーチン(disp1c, kbread, etc.)を一旦オリジナルコードに戻す。
  2. シリアル対応版ルーチンを別名(disp1csr、ineee)に変えて移動する。
  3. Chick-Bug ワークエリアに入出力ルーチンスイッチ変数 eeesw を設ける。bit7 … シリアル出力オンオフ、bit6 … VDG 出力オンオフ、bit0 … シリアル入力 (1) / パラレルキーボード入力(0) 切り替え、という設定にする。デフォルトは 0xc1
  4. disp1c ルーチンで eeesw bit7, bit6 をみて、それに応じて出力ルーチンの呼び出しをオンオフする
  5. kbread ルーチンで eeesw bit0 をみて、それに応じて入力ルーチンを切り替える

下の画像に示すように、シリアルコンソールと並行して VDG テキスト画面が表示されました。FS# を監視して VRAM への書き込みを同期することにより、画面のちらつきを抑えることが可能です。ただし FS# 同期、シリアルポートとの並行出力、ソフトウェアスクロールなど処理が増えるため描画速度は低下します。必要に応じて eeesw の設定を変えれば出力先を限定してある程度高速化できます。

Lambda-2022 の実装

現時点で Lambda-2022 内部の実装状況は次の画像のとおりです。

ついに SBC-Bus スロットがいっぱいになってしまいました。画像奥(筐体前面側)から

  1. DMA カード
  2. CPU/SRAM カード
  3. VDG カード
  4. I/O カード

が装着されています。フル装着時の負荷に備えて、このケースにはスイッチング電源を装備してあります。また SBC-Bus の +5V 供給ラインだけでは心もとないのでフロントパネルと I/O 端子には別のケーブルで電源を供給しています。筐体裏側にはビデオコネクタパネルおよびシリアルコネクタパネルを追加してあります。

Λ-1/Lambda-2022 の拡張(6)Chick-Bug 拡張

Chick-Bug の標準機能がだいたい動くようになりました(カセットテープのロード/セーブは未検証)。予想通りフロントパネルの DMA スイッチ類はほとんど使わなくなってきましたがそこは気にせず、Chick-Bug へ次の機能を追加します。

  • S19 読み込み
  • メモリダンプ
  • MB8861 命令の書き換え

S19 ストリームの読み込み

SBC68 シリーズを使ってわかったことですが、シリアルコンソールが近代的なコンピュータに接続されている場合、ユーザプログラムをロードするには S19 ファイルの内容をコンソール画面にコピペする方法が最も手軽です。この機能、つまり MikBug の L コマンドを Chick-Bug に追加します。

基本的なコードは SBC68 版 MikBug とほぼ同じです。コマンド名には ‘O’ を割り当てます(’L’ はカセットテープロードコマンドですでに使用されているため)。ロードが終わったら待機モードに戻ります(他の Chick-Bug コマンドと同じ終わり方)。

ちなみに元祖 MikBug の L コマンドはテレタイプ端末の紙テープリーダを前提としていたようです。’L’ コマンドをテレタイプ端末のキーボードから入力した後、入力ラインをテープリーダに切り替えてテープを走らせ、内容を読み込みます。すると S19 データストリームが 110 baud でテープリーダからコンピュータへ送られるという仕組みです。一度でいいのでこの紙テープ読み込みを SBC シリーズや Λ-1 でやってみたいですね。

メモリダンプ

Chick-Bug 標準機能の M コマンドは 1 バイトずつの表示・確認となり操作が若干煩雑です。ダンププログラムは 64 バイト単位で表示するので操作が楽になります。「マイコン手づくり塾」の Chick-Bug 開発編にユーザプログラムとして掲載されているものを Chick-Bug 自体に組み込んでしまいます。コマンド名は ‘D’ とします。

***

これらのコマンドを追加することでモニタ内各ルーチンのエントリアドレスが変わってしまいます。今までに作成した、モニタサービスルーチンを使ったユーザプログラムは変更が必要になりますが、そもそも Lambda-2022 ではオリジナル Λ-1 から ROM アドレス自体が変わっているので今回の変更の影響は相対的に小さいものです。

MB8870 命令の書き換え

「マイコン手づくり塾」ではモトローラ MC6802 互換の各種チップを総合的に比較検討した結果、富士通 MB8870 が採用されました。MB8870 の最大の特徴は独自命令が追加されていることです。富士通製 MPU では MC6800 互換の MB8861 の頃から同じ独自命令群が採用されていました。Chick-Bug プログラムの中でもこれらの独自命令が使用されているのですが使用箇所はそれほど多くなく、多少の工夫で置き換えられます。MB8870 と MC6802 はピン互換ですから、MB8870 独自命令がなくなるよう書き換えてしまえば MC6802 チップを Lambda-2022 に装着できます。

以下、MB8870 の独自命令と使用箇所をそれぞれについて検討します。

ADX 命令

6 箇所使われており、そのうち 4 箇所は ADX #$03 という固定値加算で、加算値も小さいので inx を 3 行並べるだけで置き換えられます。

もう一つは ADX #$1F となっています。inx を 31 個も並べるというのも不格好でどうしたものかと思っていたところ、前後をよく読むとこの行での X レジスタの値は不変です。次のように書き換えられます。

変更前:
            ldx #ccrm
            stx xopnd1
            adx #$1f
            stx xopnd2


変更後:
            ldx #ccrm
            stx xopnd1
            ldx #ccrm+$1f
            stx xopnd2

最後の一つは変数へ加算しているため、A/B アキュムレータを使った 2 バイト加算に展開せざるを得ません。前後に A/B アキュムレータの退避・復帰を含むためステップ数が増えます。逆に言うと、これが MB8870 ADX 命令の真価を発揮するケースになります。

変更前:
lf          ldx cpoint
            adx #$20
            stx cpoint

変更後:
lf          psha
            pshb
            ldaa cpoint
            ldab cpoint+1
            addb #$20
            adca #0
            staa cpoint
            stab cpoint+1
            pulb
            pula

NIM、OIM 命令

それぞれ一箇所しか使われていません。どちらもカセットインターフェイス出力処理の中です。

bitout   psha
            pshb
            ldx #pdrb1
nim1    nim $fe00     [8]
            decb             [2]
            bne nim1      [4]
            pulb
oim1    oim $0100    [8]
            decb             [2]
            bne oim1      [4]
            pula
            rts

ここでは PIA の DRB レジスタの PB0(カセットデータ出力)のオフとオンを行い、オーディオ出力を一瞬トグルする効果を求めています。周期は bitout ルーチン呼び出し元で指定する AccB の値で決まります。次のように設定されています。

soutlp  lsra
            bcs markot
            ldab #$64
            bsr bitout
            bra nxtbit
markot ldab #$32
            bsr bitout
nxtbit   dec bitctr
            bne soutlp

つまり出力 ‘1’ の半周期分のループは $32 カウント(50)、出力 ‘0’ の半周期分のループは $64 カウント(100)です。具体的な半周期長はループ処理の命令サイクル数で決まりますが、命令サイクル計算に必要なデータは MB8861 命令シートに書いてあります(「マイコン手づくり塾」に掲載されています)。簡単のためループ外の命令サイクルを除外して計算すると出力 ‘1’ の半周期は 14us x $32 = 700us、出力 ’0’ の半周期は 14us x $64 = 1400us となります。

同等の処理を MC6800 命令で再現するには次のように nim、oim を書き換えます。

bitout   psha
            pshb
            ldx #pdrb1
            ldaa 0,X
            anda #$fe
            staa 0,X
nim1    nop               [2]
            nop               [2]
            nop               [2]
            nop               [2]
            decb             [2]
            bne nim1      [4]
            ldaa 0,X
            oraa #$01
            staa 0,X
            pulb
oim1    nop               [2]
            nop               [2]
            nop               [2]
            nop               [2]
            decb             [2]
            bne oim1      [4]
            pula
            rts

nim ないし oim 命令 1 個の代わりに nop を 4 個挿入することでループ内の合計サイクル数が一致するので、タイミングも同じになります。

以上で MB8870 独自命令使用部分をすべて書き換えることができました。一箇所を除いていずれも独自命令を使用する必然性があまりない処理であったように思えます。特にカセットインターフェイス出力の部分は nim/oim を使用するのであればそもそも AccA を退避する必要はありません。書き換え後のように PIA レジスタ操作に AccA を使うのであれば事前退避が必要になるので、ここはもともと MC6802 互換で書かれていたという可能性も考えられます。

注:

現時点ではまだカセットインターフェイス回路を作っておらず、PIA ポートが未接続のまま放置されています。せっかくなので SAVE コマンド出力信号を実測しました。最初に MB8870 拡張命令を使った処理の場合の出力波形です(D0 チャネル)。

たしかにマーク半周期 ~720us、スペース半周期 ~1410us となっています。

次が MC6802 命令で書き換えた後の SAVE 出力波形です。

こちらもマーク半周期 ~700us、スペース半周期 ~1490us とおよそ一致します。どうもデューティ比が正確に 50% ではないようで、ループ外の命令サイクルの影響だと思われます。

Λ-1/Lambda-2022 の拡張(5)Chick-Bug 移植

オリジナル Chick-Bug は Λ-1 に接続されたパラレルインターフェイスのキーボードと VDG ディスプレイをコンソールとして使用しますが、今回作成する Lambda-2022 システムではコンソールとして ACIA シリアル回線を追加しています。またメモリマップも変更がありますので、これらに対応するため Chick-Bug を次のように変更します。

ワークエリア:オリジナル Chick-Bug ではスタックを含むワークエリアが $F700-$F7FF に設定されていました。他の用途と干渉しない RAM 領域 $A600-$A6FF に変更します。

PIA アドレス:オリジナル Chick-Bug の PIA アドレス $EE00..$EE03 を今回作成した I/O ボードの PIA0 アドレス $F008..$F00B に変更します。

VDG アドレス:オリジナル Chick-Bug の VRAM アドレス $F000-$F1FF を $C000-$C1FF に変更します。またシステム初期化時に VDG コントロールレジスタ($F048)に適切な値を設定します。

ACIA 初期化:SBC68 版 MikBug 同様に ACIA を初期化します。システムリセットルーチン restp 内で ACIA 初期化ルーチンを呼び出します。

PIA、ACIA の接続状況は次のようになります。

Desc.DEVI/O RegisterOrig Addr.New Addr.
K/b asciiPIA0PA0-6pdra1$ee00:b0-6$f008
6847 vsyncPIA0PA7pdra1$ee00:bit7$f008
k/b strobePIA0CA1pcra1$ee01:bit7$f009
k/b ATN NMIPIA0CA2pcra1$ee01:bit6$f009
tape mtrPIA0PB1pdrb1$ee02:bit1$f00A
tape outPIA0PB0pdrb1$ee02:bit0$f00A
tape inPIA0PB7pdrb1$ee02:bit7$f00A
tape inPIA0CB1pcrb1$ee03:bit7$f00B
SerialACIACSaciacs$f028
SerialACIADAaciada$f029

Chick-Bug はプロンプト呼び出しのためにハードウェア割り込みスイッチを使いますが、これは元設計通り PIA0 CA2 へ接続します。当面は、前回 PIA のテストに使用したブレッドボード回路をそのまま流用します。

シリアルコンソール対応

冒頭に述べたように、オリジナル Λ-1 のコンソールに加えて ACIA シリアルコンソールをサポートしようと思います。将来的には VDG/パラレルキーボードとシリアル入出力を切り替えるようにしますが、当面はシリアル入出力のみ対応するコードを書きます。次の要領で 機能を変更します。

入力:オリジナル Chick-Bug では PIA0 PA ポートと CA1 に接続されたパラレルインターフェイスキーボードから入力を読み取っていました。これをシリアル入力に置き替えます。

  1. kbread ルーチンの処理をシリアル入力処理に置き換える。
  2. シリアル入力処理は SBC シリーズ版 MicBug の ineee ルーチンを参考にして記述する。

出力:オリジナル Chick-Bug では文字出力は VDG のテキスト画面へ送られますが、シリアルコンソールに同等の内容を出力できるようにします。VDG への文字出力はおもに disp1c で行われますが、画面クリアやスクロールなどの処理で VDG 直接操作があり、これらすべてをシリアルコンソール対応処理に変更します。次のように改造します。

  1. Chick-bug の中で VDG VRAM 操作に関連するルーチン(cpoint、vramt1 を直接使用する処理)を特定する。
  2. 該当する下記のルーチンをシリアル出力(outeee)対応に書き換える。
clear1, chmark, clrhm1, lf, crlf, bs, disp1c, scrolu

初期化の部分を除くと Chick-Bug モニタの他の部分から表示のために呼び出されるのは disp1c だけのようです。将来的に VDG/シリアル出力を切り替える場合は、disp1c 内で切り替え判定をすれば十分です。

シリアルコンソールの処理では HOME、CLEAR などのエスケープコードを使用します。

改変後の Chick-Bug ソース内のアドレス配置は次のとおりです。

$a600-$a66b   :システムワークエリア
$a6ff                :スタックトップ
$f080-$fa74      : Chick-Bug コード

***

以上の要領で移植作業を進めます。Chick-Bug はもともと Λ-1 ハードウェア用に書かれたモニタプログラムなのでこれを改変することを「移植」と言うのも少し変ですが、ともかく上記の方針に沿って書き換えます。

Chick-Bug 動作確認

書き換えた Chick-Bug はシリアルコンソール上での対話ができるようになっています。各コマンドの動作画面は次のとおりです。

‘M’ コマンドはメモリ内容の表示と書き換えです。

’G’ はユーザプログラム実行、’B’ はブレークポイントの設定/確認/解除です。

カセットテープインターフェイスコマンドの確認

カセットテープのロード/セーブ機能の確認はカセットレコーダインターフェイス回路を作ってから行います。

VDG 上の動作画面

本来の出力である VDG 画面はまだ確認できません。今後 VDG 回路を追加したときに確認します。

なお今回使用する予定の K68-VDG では 6847 の反転文字と通常文字を区別しているので、Chick-Bug が印字する通常の ASCII 文字($20-$5F)は後半のみ反転します。VDG 出力時に表示コードを適宜変換する必要があります。

今後の計画

全部できるかどうかわからない予定を再度並べてみます。

  • K68-VDG の接続
  • カセットテープレコーダ回路の作成とテスト
  • パラレルインターフェイスキーボードの作成