ORANGE-4のI/Oの仕様を調査してみた (暴走もあるよ)

[ORANGE-4](http://www.picosoft.co.jp/ORANGE-4/index.html)には、3本のI/Oピンを制御できる命令がある。
今回は、これらのI/Oについて挙動を調べてみた。
今回対象とするファームウェアのバージョンは1.09である。

# 免責

今回は、仕様に無い挙動や、仕様に反する使い方を扱う。
そのため、ファームウェアのバージョンやそれまでの操作、個体差、環境などによって挙動が変わる可能性が考えられる。
この記事の内容を参考にした、または参考にしなかった結果生じた一切の損害について、筆者は責任を負わない。

# I/Oの仕様

ORANGE-4には、PORT1、PORT2、PORT3の3本のI/Oピンがある。
また、これらの操作用の以下の命令がある。
(マニュアル「必ずわかるORANGE-4機械語プログラミング」(2020/3/23版) に基づく)

|機械語|ニモニック|動作 (仕様)|
|---|---|---|
|F70|ioctrl|Aレジスタの値により、Yレジスタで指定したポートの設定を行う。<br>A=0 : デジタル出力<br>A=2 : デジタル入力|
|F71|out|Yレジスタで指定したポートに、Aレジスタの値(0または1)を出力する。<br>ポートはioctrl命令でデジタル出力の設定をしておく必要がある。|
|F72|in|Aレジスタに、Yレジスタで指定したポートの状態を読み込む。<br>ポートはioctrl命令でデジタル入力の設定をしておく必要がある。|

# 調査方法

## ソフトウェア

パラメータをキーを用いて自由に指定してioctrl命令、out命令、in命令を実行できる以下のプログラムを用意した。

以下のプログラムは、[MikeAssembler](https://mikecat.github.io/mike_assembler/)でアセンブルできる。
また、先頭の`target orange4`を取ればORANGE-4 IDEでもアセンブルできる。

```
target orange4

	; 操作方法
	; 1. コマンドを選択する (状態を2進LEDに表示する)
	;    0 : ioctrl
	;    1 : out
	;    2 : in
	;    その他 : 無効 (無視)
	; 2. ポートを選択する (0-f)
	; 3. パラメータを選択する (0-f) (ioctrl, out のとき)
	;    読み込んだ値を7セグメントLEDに表示する (in のとき)

	org 0x00

	ay
	scall 0
	ldi 0
	ldyi 0xE
	st
	ldyi 0xF
	st
main_loop:
	; 2進LEDを消灯する
	scall 0xD
	; モード選択
	call key_input
	cpi 0
	jmpf not_ioctrl
	; 0 : ioctrl
	ldyi 0
	scall 1
	; ポートを選択する
	call key_input
	outn
	ldyi 6
	scall 1
	; パラメータを選択する
	ay
	call key_input
	; コマンドを実行する
	ioctrl
	scall 0
	jmpf main_loop

	org 0x80

not_ioctrl:
	cpi 1
	jmpf not_out
	; 1 : out
	ldyi 1
	scall 1
	; ポートを選択する
	call key_input
	outn
	ldyi 6
	scall 1
	; パラメータを選択する
	ay
	call key_input
	; コマンドを実行する
	out
	scall 0
	jmpf main_loop
not_out:
	cpi 2
	jmpf main_loop
	; 2 : in
	ldyi 2
	scall 1
	; ポートを選択する
	call key_input
	outn
	ldyi 6
	scall 1
	; コマンドを実行する
	ay
	in
	outn
	jmpf main_loop

key_input:
	ink
	jmpf key_input
	; キー入力後、チャタリング対策で待つ
	abyz
	ldi 0
	scall 0xC
	abyz
key_release_wait:
	ink
	jmpf key_released
	jmpf key_release_wait
key_released:
	; キー入力後、チャタリング対策で待つ
	abyz
	ldi 0
	scall 0xC
	abyz
	ret
```

このプログラムの機械語のORANGE-4のモニター向け表現は、以下のようになった。

```
E00:3E080AE4AF4EDF60
E10:BCC0F80A0E1F60BC
E20:1A6E13F60BCF70E0
E30:F0B
E80:C1FA1A1E1F60BC1A
E90:6E13F60BCF71E0F0
EA0:BC2F0BA2E1F60BC1
EB0:A6E13F721F0B0FBC
EC0:280EC20FCDFC6280
ED0:EC2F61
```

## ハードウェア

調査対象のポートから、GNDとVDDそれぞれに20kΩ(公称値)の抵抗を接続し、GNDからポートの電圧を測定する。

![ポート状態確認用ハードウェア](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/68b7caa3-d299-26c3-6e09-0360c2f1b6e3.png)

測定される電圧から、ポートの状態を以下のように判定する。

|電圧|状態|
|---|---|
|3.3V付近|HIGHを出力|
|1.65V付近より高く、3.3V付近より低い|プルアップ|
|1.65V付近|ハイインピーダンス|
|0V付近より高く、1.65V付近より低い|プルダウン|
|0V付近|LOWを出力|

# 調査

## リセット時の状態

電源投入直後は、3ポートとも「LOWを出力」となっていた。
プログラムを実行してポートの状態を変更した後、RSTボタンによりプログラムの実行を止めた時は、ポートの状態は維持された。
RESETボタンによるリセットを行うと、ポートの状態は「LOWを出力」に戻った。

## ポートの選択

ioctrl命令では、Yレジスタの値によって以下のポートの状態が変わった。

|Yレジスタの値|状態が変わったポート|
|---|---|
|1|PORT1|
|2|PORT2|
|3|PORT3|
|0,4,5,6,7,8,9,A,B,C,D,E,F|なし|

out命令では、Yレジスタの値によって以下のポートの状態が変わった。

|Yレジスタの値|状態が変わったポート|
|---|---|
|1|PORT1|
|2|PORT2|
|3|PORT3|
|0,4,5,6,7,8,9,A,B,C,D,E,F|なし|

in命令においては、PORT1、PORT2、PORT3全てにHIGHを入力した状態で実行した結果、
Yレジスタが1,2,3の時はAレジスタの値が1となり、それ以外の時はAレジスタの値が0となった。
よって、ioctrl命令やout命令と同様に、Yレジスタの値1,2,3がそれぞれPORT1,PORT2,PORT3に対応すると推測できる。

## ポートの設定

リセット後、PORT1について、ioctrl命令によるポートの設定を行った。
Aレジスタの値として0～Fを昇順に試した結果、命令実行後のポートの状態は以下のようになった。

|Aレジスタの値|ポートの状態|
|---|---|
|0,1,F|LOWを出力|
|2,3,4,5,6,7,8,9,A,B,C,D,E|ハイインピーダンス|

また、リセット後、Aレジスタの値として3～Fを昇順に試した結果、ポートの状態は「LOWを出力」のまま変わらなかった。
さらに、リセット後、PORT1をデジタル入力に設定(Aレジスタの値=2)した後、Aレジスタの値として1を用いると、ポートの状態は「ハイインピーダンス」のまま変わらなかった。
したがって、Aレジスタの値が0,2,F以外の場合、ポートの状態は変わらないと推測できる。

仕様にあるAレジスタの値0,2でポートの設定を行った場合、その後もioctrl命令でポートの設定を変更したり、out命令でポートの出力を設定したりできる。
一方、Aレジスタの値Fでポートの設定を行った場合、ioctrl命令やout命令を用いても反応せず、ポートの状態は「LOWを出力」のままとなった。
一旦RSTボタンでプログラムの実行を止めて再実行しても、反応は復活しなかった。
この設定を行う前のポートの状態が「デジタル入力」「デジタル出力、0を出力」「デジタル出力、1を出力」のどれであっても、この現象が見られた。
また、あるポートがこの設定により反応しなくなっても、他のポートはこの設定をしない限り反応は維持されるようだった。

したがって、ioctrl命令実行時のAレジスタの値とポートの設定は以下のようになっていると考えられる。

|Aレジスタの値|ポートの設定|
|---|---|
|0|デジタル出力|
|2|デジタル入力 (ハイインピーダンス)|
|F|ポートを無効化し、LOWを出力するように固定|
|その他|変更なし|

## ポートへの出力

ioctrl命令でPORT1をデジタル出力に設定した後、Aレジスタの値として0～Fを昇順に用い、out命令をPORT1について実行したところ、ポートの状態は以下のようになった。

|Aレジスタの値|ポートの状態|
|---|---|
|0|LOWを出力|
|1～F|HIGHを出力|

さらに、Aレジスタの値0でout命令を実行した後、Aレジスタの値2～Fそれぞれについてout命令を実行 (それぞれの実行の前にAレジスタの値0でout命令を実行) すると、全てポートの状態は「HIGHを出力」となった。

したがって、ポートがデジタル出力に設定されている場合、out命令におけるAレジスタの値とポートの状態の関係は以下のようになっていると考えられる。

|Aレジスタの値|ポートの状態|
|---|---|
|0|LOWを出力|
|1～F|HIGHを出力|

ioctrl命令でPORT1をデジタル入力に設定した後、Aレジスタの値として0～Fを昇順に用い、out命令をPORT1について実行したところ、ポートの状態は全て「ハイインピーダンス」のまま変わらなかった。

さらに、PORT1について以下のような操作を行うと、PORT1の状態は以下のようになった。

|操作|操作の詳細|ポートの状態|
|---|---|---|
|プログラムの実行を開始|-|LOWを出力|
|デジタル出力に設定|ioctrl, A=0|LOWを出力|
|0を出力|out, A=0|LOWを出力|
|デジタル入力に設定|ioctrl, A=2|ハイインピーダンス|
|1を出力|out, A=1|ハイインピーダンス|
|デジタル出力に設定|ioctrl, A=0|HIGHを出力|
|デジタル入力に設定|ioctrl, A=2|ハイインピーダンス|
|0を出力|out, A=0|ハイインピーダンス|
|デジタル出力に設定|ioctrl, A=0|LOWを出力|

よって、デジタル入力に設定してある状態でのout命令による操作も保存され、デジタル出力に設定した際に反映されるようである。
さらに、デジタル出力に設定した状態で、さらにAレジスタの値0でioctrl命令を実行(デジタル出力に設定)しても、ポートの状態は変わらない(HIGHの出力も維持される)ようだった。

## ポートからの入力

「ポートの選択」での実験結果より、PORT1、PORT2、PORT3をデジタル入力に設定した状態では、HIGHはAレジスタの値1として読み込まれ、無効なポートはAレジスタの値0として読み込まれることがわかっている。

また、PORT1で実験をすると、LOWはレジスタの値0として読み込まれることがわかった。

ポートをデジタル出力に設定した場合、1を出力している状態ではAレジスタの値1、0を出力している状態ではAレジスタの値0が読み込まれた。
Aレジスタの値2でout命令を実行した後でも、Aレジスタの値1が読み込まれた。
ポートを無効化(Aレジスタの値Fでioctrl命令を実行)した場合も、Aレジスタの値0が読み込まれた。

よって、in命令はポートの設定によらず、HIGHをAレジスタの値1、LOWをAレジスタの値0として読み込むようであることがわかった。

# 結論

ORANGE-4 (ファームウェアのバージョンは1.09) は、I/O関係について以下のような挙動をするらしいことがわかった。

## ポートの選択 (共通)

|Yレジスタの値|ポート|
|---|---|
|1|PORT1|
|2|PORT2|
|3|PORT3|
|その他|無効|

## ioctrl命令

ioctrl命令は、選択したポートの状態をAレジスタの値によって以下のように設定する。

|Aレジスタの値|ポートの設定|
|---|---|
|0|デジタル出力|
|2|デジタル入力|
|F|LOWの出力に固定し、以降のioctrl命令やout命令を無視|
|その他|変化なし|

リセット(電源投入・RESETボタン)時は、デジタル出力に設定したような状態になっている。
RSTボタンによりプログラムの実行を止めても、状態の設定は維持される。

デジタル入力は、ハイインピーダンスのみ(プルアップ・プルダウンなし)のようである。

## out命令

out命令は、選択したポートの出力をAレジスタの値によって以下のように設定する。

|Aレジスタの値|出力|
|---|---|
|0|LOW|
|非0|HIGH|

出力の設定はポートがデジタル入力に設定されている時でも有効であり、設定後ポートをデジタル出力に設定すると設定した値が出力される。

## in命令

in命令は、選択したポートの状態を以下のようにAレジスタの値に反映する。

|ポートの状態|Aレジスタの値|
|---|---|
|LOW|0|
|HIGH|1|

この関係は、ポートがデジタル出力に設定されている場合でも同様である。(出力している値が読み込まれる)
無効なポートを選択した場合は、Aレジスタの値は0となる。



# ポートの物理仕様

[ORANGE-4のサイト](http://www.picosoft.co.jp/ORANGE-4/index.html)に載っている回路図より、ポートとマイコンのピンの対応は以下のようになっている。

|ポート|ピン番号|ピン名|
|---|---|---|
|PORT1|10|OSC2|
|PORT2|14|RB5|
|PORT3|7|RB3|

使われているマイコン PIC32MX120F032B のデータシートを参照すると、この中で5VトレラントになっているのはPORT2 (RB5) だけであることがわかる。

# ORANGE-4の暴走再び

今回のプログラムによる検証中も、[前回のXorshiftのプログラム](https://qiita.com/mikecat_mixc/items/e7f511875f1c77e78f4a)の実行時に見られた暴走現象が度々発生した。

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

今回の暴走はキー入力時に発生することがあり、発生すると以下の症状が見られた。

* キー操作を受け付けない (数字キーだけでなく、RSTキーも)
* 2進LEDが通常より明るく点灯する
* 押したキーによって2進LEDが点灯することがある (例えば、Cキーを押すとLED3が点灯する)

検証のためにプログラムを削っていったところ、以下のプログラムを実行し、RSTキーで実行を止めると、別の不都合が発生することがわかった。
(「発生することがある」ではなく、100%に近い確率で発生するようである)

```
target orange4

	; ORANGE-4 Ver 1.09 暴走テスト
	; 1. このプログラムをアセンブルし、モニターから入力する
	; 2. モニターを終了する (qコマンド)
	; 3. 1→RUNで実行する
	; 4. RSTで止める
	; 5. ビープ音が繰り返し鳴り、キー入力を受け付けなくなる

	; ※モニターを終了せずにgコマンドで実行した場合、RSTを押しても不都合は起きない
	; ※モニターを経由せず、キーでプログラムを打ち込んで実行しても不都合が起きる

	org 0x00

	ay
	scall 0
main_loop:
	; 2進LEDを消灯する
	ldi 0
	ldyi 0xE
	st
	ldyi 0xF
	st
	scall 0xD
	; モード選択
	ldi 1
	cpi 1
	jmpf main_loop
	; 1 : out
	ldyi 0
	scall 1
	; ポートを選択する
	ldi 1
	outn
	ldyi 1
	scall 1
	; コマンドを実行する
	scall 0
	jmpf main_loop
```

```
E00:3E080AE4AF4ED81C
E10:1F03A0E1811A1E1E
E20:0F03
```

このプログラムを実行し、RSTキーで実行を止めると、以下の現象が発生した。

* 約134BPMで「ピッ」という短い音が鳴り続ける
* キー操作を受け付けなくなる
* 前述の暴走とは異なり、キーを押しても2進LEDは点灯しない

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

このような暴走現象が発生する詳しい条件は、まだよくわかっていない。