Kageki (TD4互換機) で16ワードを超えるプログラムを扱う (ROMに工夫、本体無改造)

通常、TD4、およびその命令を実行できる自作CPUの [Kageki](https://elchika.com/article/9ac9fd67-86c5-4b1b-a355-4f662e8af794/) では、プログラムカウンタが4ビットなので、16ワードまでのプログラムしか扱えない。
しかし、この長さでは、複雑なプログラムを組もうとすると容量オーバーになってしまうことが多い。
そこで、今回は、本体には手を加えず、ROM部分の回路に工夫をすることで16ワードを超えるプログラムを扱う方法を考えた。

# 作戦

TD4やKagekiで使うROMは、4ビットのアドレスを入力すると対応する8ビットのデータが出力されるものである。
本体のクロックやリセットは供給されない。
出力しているデータは、ROM側から参照できる。
また、TD4やKagekiには、命令としては未定義のビット列が存在する。

そこで、

* 現在のデータの上位4ビットが(未定義の命令から選んだ)キーのビット列になっていれば
* ROMにアクセスする時のアドレスの上位4ビットをデータの下位ビットに設定する

という処理を行い、ROMのデータを格納する部分に8ビットでアクセスするようにすることにした。
未定義のビット列は4種類あるので、理論上最大10ビットのアドレスを用いることができるが、8ビットにすることで既存のROMインターフェースを再利用しやすくなる。
この処理により、アドレスの上位ビットを設定するデータの所にアクセスしようとすると、次のクロックを待たずに切り替え先にかわりにアクセスすることになる。
なお、本体のリセットに対応するため、ROMの16の倍数のアドレス (0番地を除く) はアドレスの上位4ビットを0にするデータにしておくべきである。

ハードウェアとしては、以下のような構造を想定する。
本体のクロックよりは十分高速だが、クロックとクロックの間でアドレスやデータの変化が完了する程度には低速のクロックを用いる。
例えば1MHz程度にするといいだろう。
変化途中のデータに誤って反応しないよう、クロック2回分のデータが一致した時にのみ切り替えを行う。

![拡張ROMの構造](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/a152a659-eeaa-09b7-f481-e8a7ce9debf8.png)

# プログラム例

先日の[ガッテンRのようなもの](https://qiita.com/mikecat_mixc/items/76aca5705a14063ae071)を拡張し、入力ポートのビット2 (0-origin) に1を入力すると「R」のかわりに「FB」を出力するようにした。
上位4ビットが `1010` のときにアドレスの上位ビットを切り替え、これを `BNK` と表す。

```
位置 機械語   アセンブリ言語
--------------------------------
00   01111000        MOV B, 1000
01   00100000 KEY0:  IN  A
02   00001000        ADD A, 1000
03   11100001        JNC KEY0
04   00100000 KEY1:  IN  A
05   00001000        ADD A, 1000
06   11101000        JNC SEL
07   11110100        JMP KEY1
08   00000100 SEL:   ADD A, 0100
09   11101011        JNC PLAYR
0A   10100010        BNK 0010
0B   10100001 PLAYR: BNK 0001
0C   xxxxxxxx
0D   xxxxxxxx
0E   xxxxxxxx
0F   xxxxxxxx

10   10100000        BNK 0000
11   10100000 RRET:  BNK 0000
12   10010000 R0:    OUT B
13   10110000        OUT 0000
14   10010000        OUT B
15   00000000        ADD A, 0000
16   00000000        ADD A, 0000
17   10110000        OUT 0000
18   10010000        OUT B
19   10110000        OUT 0000
1A   11110001        JMP RRET
1B   11110010        JMP R0
1C   xxxxxxxx
1D   xxxxxxxx
1E   xxxxxxxx
1F   xxxxxxxx

20   10100000        BNK 0000
21   10010000 F0:    OUT B
22   10110000        OUT 0000
23   10010000        OUT B
24   10110000        OUT 0000
25   10010000        OUT B
26   00000000        ADD A, 0000
27   11111011        JMP F1
28   xxxxxxxx
29   xxxxxxxx
2A   11110001        JMP F0
2B   10110000 F1:    OUT 0000
2C   10010000        OUT B
2D   10110000        OUT 0000
2E   00000000        ADD A, 0000
2F   10100011        BNK 0011

30   10100000        BNK 0000
31   10100000 FBRET: BNK 0000
32   10010000 B0:    OUT B
33   00000000        ADD A, 0000
34   00000000        ADD A, 0000
35   10110000        OUT 0000
36   10010000        OUT B
37   10110000        OUT 0000
38   10010000        OUT B
39   10110000        OUT 0000
3A   10010000        OUT B
3B   10110000        OUT 0000
3C   11110001        JMP FBRET
3D   xxxxxxxx
3E   xxxxxxxx
3F   11110010        JMP B0
```

# 拡張ROMの実装

今回は、[Software ROM Board](https://elchika.com/article/ef1e092a-7bbf-4666-9aae-8d777b58e30f/#h_Software%20ROM%20Board) 用のソフトウェアとして実装した。
[hardware_cpu/software_rom_bank16_hex.ino at main · mikecat/hardware_cpu](https://github.com/mikecat/hardware_cpu/blob/main/software/rom/software_rom/software_rom_bank16_hex/software_rom_bank16_hex.ino)
このソフトウェアは、HEXファイルをシリアル通信で送り込むことで、ROMのデータを書き換えることができる。
以下は、今回のプログラム例のHEXファイルのデータである。

```
:10000000782008E12008E8F404EBA2A1FFFFFFFF3D
:10001000A0A090B0900000B090B0F1F2FFFFFFFF01
:10002000A090B090B09000FBFFFFF1B090B000A3A3
:10003000A0A0900000B090B090B090B0F1FFFFF29F
:00000001FF
```

# 実行結果

[Pmod ENC](https://digilent.com/shop/pmod-enc-rotary-encoder/) のBTN端子を入力ポートのビット3に、SWT端子を入力ポートのビット2に接続した。
また、出力ポートに [Dice Board](https://elchika.com/article/e6cf654e-8504-4796-b806-97110ab73bc6/#h_Dice%20Board) を接続した。
20Hzのクロックで実行した。

<iframe width="560" height="315" src="https://www.youtube.com/embed/43zr5JCpjQw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

スイッチの切り替えによって「R」と「FB」を出し分けることができている。