IchigoJam R に関する諸々

IchigoJam R は、2020年末に発表された[IchigoJam](https://ichigojam.net/)の新型です。

紹介ページ：[IchigoJam R ＆ IchigoSand 発表、HHKB他USBキーボード対応するRISC-V版、Zen言語でかんたんマシン語両対応 zen4ij #IchigoJam #riscv #zen / 福野泰介の一日一創 / Create every day by Taisuke Fukuno](https://fukuno.jig.jp/3077)
販売ページ：[【β版】IchigoJam 組み立て済完成品 R | Programming Club Net...](https://hello002.stores.jp/items/5fdc3daff0b10819906d9c74)

※IchigoJamはjig.jpの登録商標です。

# 本体の写真

## パッケージ

![IchigoJam R パッケージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/c1d9e76f-59f6-d872-0989-f102a25c783f.jpeg)

## 基板表面

![IchigoJam R 基板表面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/6923a771-5fb7-b84a-7775-6c0deb32213b.jpeg)

## 基板裏面

![IchigoJam R 基板裏面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/f5566fd7-c5e4-48e7-7052-45bfbf249710.jpeg)

# 実行速度

[紹介ページ](https://fukuno.jig.jp/3077)では「なんとインタプリター、10倍速！」と主張していますが、
実測では従来製品と比べて8～9倍速程度のようです。
また、ビデオ出力ありでの速度が大幅に上がっている一方、`VIDEO 0`による速度の改善効果は下がっているようです。

詳しくはこのあたりをどうぞ。

自分の記事：[IchigoJam速度比較 ～IchigoJam Rが10倍速いってマジ？～ - Qiita](https://qiita.com/mikecat_mixc/items/eb1786dc384d579b7705)
他の人の記事：[IchigoJam Rの速度検証 - SHIROのIchigoJam日記](https://www.ichigojaman.jp/entry/ichigojamr)

# 時間の精度

以前書いた記事の[キッチンタイマー(本格版)](https://qiita.com/mikecat_mixc/items/e203aec47206c8cb5491#%E6%9C%AC%E6%A0%BC%E7%89%88)を実行したところ、
1時間に設定して約3秒早くカウントが終わりました。
これは1日あたり72秒、1ヶ月(30日)あたり2160秒(36分)ずれる計算になります。
時計として使うには、ちょっと精度が低いという印象が強いです。

# Complex expression耐性

以下のプログラムでネストが深い式を作って実行させ、どこまでComplex expressionが出ずに実行できるかを調べました。

<details><summary>プログラム</summary><div>

```cetester.c
#include <stdio.h>

void meow(int cur, int max) {
	putchar(cur % 10 + '0');
	if (cur < max) {
		putchar('-');
		if (cur + 1 < max) putchar('(');
		meow(cur + 1, max);
		if (cur + 1 < max) putchar(')');
	}
}

int main(void) {
	int i;
	for (i = 1; i <= 100; i++) {
		putchar('?');
		meow(1, i);
		putchar('\n');
	}
	putchar('\n');
	for (i = 1; i <= 100; i++) {
		int j;
		putchar('?');
		for (j = 0; j < i; j++) putchar('(');
		putchar('1');
		for (j = 0; j < i; j++) putchar(')');
		putchar('\n');
	}
	return 0;
}
```

</div></details>

従来製品では、以下のような実験結果になりました。

|ファームウェア|実行できた式|Complex expressionになった式|
|---|---|---|
|1.0.0 (VER()=10017)|`?1-(2-(3-(4-5)))`<br>`?(((1)))`|`?1-(2-(3-(4-(5-6))))`<br>`?((((1))))`|
|1.3.1 (VER()=13106)|`?1-(2-(3-4))`<br>`?((1))`|`?1-(2-(3-(4-5)))`<br>`?(((1)))`|
|1.4.1 (VER()=14114)|`?1-(2-(3-(4-(5-6))))`<br>`?((((1))))`|`?1-(2-(3-(4-(5-(6-7)))))`<br>`?(((((1)))))`|

IchigoJam R (1.5b, VER()=15001)では、以下のようになりました。

実行できた式

```
?1-(2-(3-(4-(5-(6-(7-(8-(9-(0-(1-(2-(3-(4-(5-(6-(7-(8-(9-(0-(1-(2-(3-(4-(5-(6-(7-(8-(9-(0-(1-(2-(3-(4-(5-(6-(7-(8-(9-(0-(1-(2-(3-(4-(5-(6-(7-(8-(9-(0-1)))))))))))))))))))))))))))))))))))))))))))))))))
?(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((1)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
```

Syntax errorになった式

```
?((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
```

引き算を使った式では、Too longにならない限界までネストを深くしても、エラーにならずに実行できました。
カッコのみを重ねた式では、98段のネスト(あと2段深くするとToo long)でSyntax errorとなりました。
Complex expressionは確認できませんでした。

惜しくも書ける式フルサポートとはならなかったものの、
従来製品と比べて書ける式の深さが大幅(1.4.1と比べて約24倍)に改善していることがわかります。

# EEPROMモジュールの使用法

IchigoJam R ではI2Cの端子の配置が従来製品から変わっているため、
EEPROMモジュールを以前と同じように接続しても使えません。

うちでは、このような自作のモジュールを使っています。

<img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/e2b5bb9d-a5a0-31ba-4d8c-d595fb730609.jpeg" alt="EEPROMモジュール"> <img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/7e4e5984-7a07-3d0a-1ecf-9af0ae8e09f2.png" alt="EEPROMモジュール 配線図">

従来製品では、GNDとVCCが合うように左側の端子に接続し、飛び出ているコードを右側のEX1に接続します。

![EEPROMモジュール 従来製品との接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/3ec848d4-0042-e5f1-8b92-813af7aa8faf.jpeg)

しかし、IchigoJam R ではこの接続では動きません。
そもそも仕様変更により「EX1」は無くなっています。

IchigoJam R では、このEEPROMモジュールは、GNDとVCCが合うように「右側」の端子に接続し、
飛び出ているコードを5ピンの端子の真ん中に接続することで、使うことができるようです。

![EEPROMモジュール IchigoJam R との接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/53a13311-7992-942f-643f-27a28bee8f88.jpeg)

# キーボード

IchigoJam R では、USBキーボードが使えます。
ただし、一部の製品はうまく動かないようです。
実際に、いくつかの製品について動くかどうかを調べてみました。

なお、今回の検証結果は各製品1個についての実験結果です。
同じ型番・製品でも、ロットの違いや個体差などにより結果が変わる可能性が否定できません。

## うまく動いたもの

### YDKBU11BK (BUFFALO)

<img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d47ac8a0-7779-810c-14b1-142b6b3babe1.jpeg" alt="YDKBU11BK パッケージ"> <img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/f2d86a61-3b15-024a-2c9c-ea754fb6f987.jpeg" alt="YDKBU11BK 本体">

* 実験中、動作が止まることはありませんでした。
* Num Lock は効かず、テンキーを押すと(機能の実行ではなく)数字が入力されました。
* Caps Lock は効いたものの、状態を表すLEDは点灯しませんでした。

さらに、この機種は従来製品の IchigoJam S (1.3.1) でも使えました。
パッケージなどに記載は見当たりませんでしたが、こっそりPS/2にも対応していたようです。

### HC56TU (DENSO)

以前中古屋で購入したバーコードリーダーです。
読み取った内容をUSBキーボードとして入力するモードがあります。

<img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/eaeebaf9-9900-53fa-edc5-914a6f17ec9e.jpeg" alt="HC56TU 本体">

* 初期状態では、バーコードのデータと入力でアルファベットの大文字と小文字が逆になりました。
  * Caps Lock を手動設定でONとするモードにすることで、改善しました。
  * Caps Lock の状態を自動判別するモードでは、改善しませんでした。
* CRやLFは、CODE128のデータ・ターミネータともに反応しませんでした。
* ターミネータとしてEnterを設定すると、期待通り改行処理が行われました。

便利なように、`LED1`と`LED0`のバーコード(CODE128)を用意しました。
名刺サイズ (55mm × 91mm) に合わせたサイズの画像になっています。

<img width="34%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/44f2c3a5-c8f5-d11e-370c-d3efa1b14fbe.png" alt="バーコード">

## うまく動かなかったもの

### SKB-KG2BK (サンワサプライ)

<img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/c950a965-fcc3-2e02-f5ff-3c60414ac084.jpeg" alt="SKB-KG2BK パッケージ"> <img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/daf5b3db-d4d2-1fd0-32a2-8e67b57d5df6.jpeg" alt="SKB-KG2BK 本体">

* 最初の数文字～数十文字程度は入力できましたが、その後キーを押しても反応しなくなりました。
* スイッチを入れ直すと、最初の「Ichigojam～」すら表示されないことがありました。 

### AOK-184WH (アオテック)

<img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/70a69ff4-8cbc-90f7-0580-aca7a6638558.jpeg" alt="AOK-184WH パッケージ"> <img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d6d0114c-a6eb-3bb2-106b-3c95a1e3d12a.jpeg" alt="AOK-184WH 本体">

* 最初の数文字～数十文字程度は入力できましたが、その後キーを押しても反応しなくなりました。
* スイッチを入れ直すと、最初の「Ichigojam～」すら表示されないことがありました。 
  * このとき、通常は点灯するキーボードのLEDが点灯していませんでした。

### ワンボタンキーボード (TOKYO FLIP-FLOP)

Arduinoの開発環境を用いたプログラミングにより、動作を変えることができます。
今回は、初期状態(キーを押すと`w`が入力される)で実験を行いました。
なお、写真中のUSBケーブルは付属していませんでした。

<img width="48%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/472f9945-a3e2-3ad3-2b77-f18a00ec24a8.jpeg" alt="ワンボタンキーボード 本体+ケーブル">

* キーを押しても反応しませんでした。

公式でUSBハブ内蔵のキーボードは動かないとされていますが、ハブでなくても、
USB Composite Device が挟まるタイプのデバイスは動かないようになっているという仮説が考えられます。

# マシン語呼び出し時の引数

IchigoJam 1.2以降の仕様においては、

* 第1引数：`USR`の第2引数で指定した値
* 第2引数：フォントRAMの0番地相当のアドレス
* 第3引数：フォントROMの0番地相当のアドレス
* 第4引数：符号なし除算を行う関数のポインタ

という引数が渡されることになっているはずです。

出典：

* [c4ij - イチゴジャム レシピ](https://15jamrecipe.jimdofree.com/basic/c4ij-c-%E8%A8%80%E8%AA%9E/)
* [IchigoJam-BASIC/USR.txt at master · fu-sen/IchigoJam-BASIC · GitHub](https://github.com/fu-sen/IchigoJam-BASIC/blob/master/USR.txt)
* [レジスタ不足に上位レジスタとスタック操作 / IchigoJamではじめるArmマシン語その12 #IchigoJam #asm / 福野泰介の一日一創 / Create every day by Taisuke Fukuno](https://fukuno.jig.jp/2035)

以下のプログラムを用いて、マシン語呼び出し(`USR`関数)時の引数の値を調査しました。

```
10 ' ﾏｼﾝｺﾞ ﾊﾟﾗﾒｰﾀ ﾁｮｳｻ
20 POKE#700,183,47,41,224,151,15,0,0,19,135,207,15,8,195,76,195,16,199,84,199,19,5,7,240,8,203,35,42,39,0,147,133,5,112,249,154,49,69,51,135,165,0,28,67,19,135,79,17,42,151,28,195,51,7,166,0,28,67
30 POKE#73A,19,135,79,18,42,151,28,195,51,135,166,0,28,67,19,135,79,19,42,151,28,195,113,21,227,90,5,252,130,128,164,70,41,164,32,96,97,96,162,96,227,96,32,70,128,56,128,56,32,97,104,70,96,97,112,36
40 POKE#772,36,1,9,25,1,32,131,67,15,36,38,160,13,93,5,85,41,160,21,93,5,85,43,160,29,93,5,85,1,60,244,213,100,70,112,71
50 X=USR(#700,#1234)
60 ?"ARG0 = #";HEX$([1],4);HEX$([0],4)
70 ?"ARG1 = #";HEX$([3],4);HEX$([2],4)
80 ?"ARG2 = #";HEX$([5],4);HEX$([4],4)
90 ?"ARG3 = #";HEX$([7],4);HEX$([6],4)
100 ?"PC   = #";HEX$([9],4);HEX$([8],4)
110 ?"SP   = #";HEX$([11],4);HEX$([10],4)
120 ?"*(ARG1+#700):";:FORI=0TO15:?" ";HEX$(PEEK(#818+I),2);:NEXT:?""
130 ?"*ARG2       :";:FORI=0TO15:?" ";HEX$(PEEK(#828+I),2);:NEXT:?""
140 ?"*(ARG3&~#1) :";:FORI=0TO15:?" ";HEX$(PEEK(#838+I),2);:NEXT:?""
```

<details><summary>マシン語部分のソースコード</summary><div>

```
R7 - #B7
GOTO @TRAD
MODE RV32C

	R31 = PC + 0
	R14 = R31 + #FC ' R14 = @ARG0
	[R14 + 0]L = R10
	[R14 + 4]L = R11
	[R14 + 8]L = R12
	[R14 + 12]L = R13
	R10 = R14 + -#100
	[R14 + 16]L = R10
	[R14 + 20]L = R2

	R11 = R11 + #700

	R13 &= -2

	R10 = 12
@LOOP_RV32C
	R14 = R11 + R10
	R15 = [R14 + 0]L
	R14 = R31 + #114 ' R14 = @ARG1_DATA
	R14 += R10
	[R14 + 0]L = R15
	R14 = R12 + R10
	R15 = [R14 + 0]L
	R14 = R31 + #124 ' R14 = @ARG2_DATA
	R14 += R10
	[R14 + 0]L = R15
	R14 = R13 + R10
	R15 = [R14 + 0]L
	R14 = R31 + #134 ' R14 = @ARG3_DATA
	R14 += R10
	[R14 + 0]L = R15
	R10 += -4
	IF R10 >= R0 GOTO @LOOP_RV32C

	RET

MODE M0
@TRAD
	R12 = R4
	R4 = @ARG0
	[R4 + 0]L = R0
	[R4 + 1]L = R1
	[R4 + 2]L = R2
	[R4 + 3]L = R3
	R0 = R4
	R0 -= #80
	R0 -= #80
	[R4 + 4]L = R0
	R0 = R13
	[R4 + 5]L = R0

	R4 = #70
	R4 = R4 << 4
	R1 = R1 + R4

	R0 = 1
	BIC R3, R0

	R4 = 15
@LOOP_M0
	R0 = @ARG1_DATA
	R5 = [R1 + R4]
	[R0 + R4] = R5
	R0 = @ARG2_DATA
	R5 = [R2 + R4]
	[R0 + R4] = R5
	R0 = @ARG3_DATA
	R5 = [R3 + R4]
	[R0 + R4] = R5
	R4 -= 1
	IF PL GOTO @LOOP_M0
	R4 = R12
	RET

ORGR #100
@ARG0
	SPACE 4
@ARG1
	SPACE 4
@ARG2
	SPACE 4
@ARG3
	SPACE 4
@PC
	SPACE 4
@SP
	SPACE 4
@ARG1_DATA
	SPACE 16
@ARG2_DATA
	SPACE 16
@ARG3_DATA
	SPACE 16
```

</div></details>


## 従来製品 1.0.0 (VER()=10017)

```
ARG0 = #00001234
ARG1 = #00001234
ARG2 = #10000188
ARG3 = #10000187
PC   = #10000188
SP   = #10000DF0
*(ARG1+#700): D1 FA 48 E0 00 F0 22 FB 45 E0 FF F7 1F F8 31 4B
*ARG2       : B7 2F 29 E0 97 0F 00 00 13 87 CF 0F 08 C3 4C C3
*(ARG3&~#1) : 00 00 B7 2F 29 E0 97 0F 00 00 13 87 CF 0F 08 C3
```

1.1以降における第2引数以降の仕様には従っていないものの、
「第1引数は`USR`の第2引数」という仕様には従っていることがわかります。

## 従来製品 1.3.1 (VER()=13106)

```
ARG0 = #00001234
ARG1 = #0FFFF9E0
ARG2 = #00005310
ARG3 = #00005171
PC   = #100000E0
SP   = #10000E30
*(ARG1+#700): B7 2F 29 E0 97 0F 00 00 13 87 CF 0F 08 C3 4C C3
*ARG2       : 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF
*(ARG3&~#1) : 00 22 43 08 8B 42 74 D3 03 09 8B 42 5F D3 03 0A
```

第4引数の値付近から読み取ったデータを逆アセンブルすると、

```
	R2 = 0
	R3 = R0 >> 1
	R3 - R1
	IF CC GOTO 118
	R3 = R0 >> 4
	R3 - R1
	IF CC GOTO 97
	R3 = R0 >> 8
```

となりました。
意図はわかりにくいですが、第1引数や第2引数を操作している様子が見られ、
割り算ルーチンである可能性が感じられます。
その他の引数も、仕様どおりになっていそうであることがわかります。

さらに、PC(≒フォントRAMの場所)およびSPの値が、
[IchigoJam BASIC RAM 4kbyte のつかいかた（メモリマップ） #KidsIT #IchigoJam #lpc1114 / 福野泰介の一日一創 / Create every day by Taisuke Fukuno](https://fukuno.jig.jp/2386)
に沿っていることが読み取れます。

## 従来製品 1.4.1 (VER()=14114)

```
ARG0 = #00001234
ARG1 = #0FFFFD80
ARG2 = #00005800
ARG3 = #000056D5
PC   = #10000480
SP   = #10000310
*(ARG1+#700): B7 2F 29 E0 97 0F 00 00 13 87 CF 0F 08 C3 4C C3
*ARG2       : 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF
*(ARG3&~#1) : 00 29 01 D1 00 20 70 47 01 22 00 23 52 00 49 00
```

第4引数の値付近から読み取ったデータを逆アセンブルすると、

```
	R1 - 0
	IF !0 GOTO @L708
	R0 = 0
	RET
@L708
	R2 = 1
	R3 = 0
	R2 = R2 << 1
	R1 = R1 << 1
```

となっていました。
最初に割る数が0でないかをチェックしている様子が読み取れるので、
これは割り算ルーチンの冒頭部分だと考えるのは妥当だと考えられます。
その他の引数も、仕様どおりになっていそうであることがわかります。

また、PC(≒フォントRAMの場所)よりSPがメモリ上で前に来ていることが読み取れます。

## IchigoJam R (1.5b, VER()=15001)

```
ARG0 = #00001234
ARG1 = #20000A2C
ARG2 = #20000010
ARG3 = #00000000
PC   = #2000112C
SP   = #20007D70
*(ARG1+#700): B7 2F 29 E0 97 0F 00 00 13 87 CF 0F 08 C3 4C C3
*ARG2       : 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF
*(ARG3&~#1) : 71 AA 01 00 00 00 00 00 00 00 00 00 00 00 00 00
```

第1引数～第3引数の値は仕様に沿った値になっているものの、第4引数が0になっています。

第4引数の値付近から読み取ったデータは、

```
MODE RV32C
GOTO #CE
NOP
```

というマシン語に相当します。
ただし、そもそもIchigoJam R のCPUである[GD32VF103CBT6](https://www.gigadevice.com/microcontroller/gd32vf103cbt6/)において、
アドレス0x00000000はRESET service routing vectorであるよう(User Manualの5.1.2を参照)なので、
これが割り算ルーチンであるとは考えにくいでしょう。

(CPUの種類の出典：[IchigoJam BASIC 1.5β1、USBキーボード対応 RISC-V版 IchigoJam Rβ 出荷スタート！ #KidsIT #IchigoJam / 福野泰介の一日一創 / Create every day by Taisuke Fukuno](https://fukuno.jig.jp/3103))

# アライメントされていないメモリアクセス

マシン語でnの倍数でないアドレスからnバイトの読み込みができるかを実験しました。
今回は、以下のコードでn=4についてのみ実験しました。

```
10 ' unaligned read test
20 POKE#700,183,47,6,224,23,5,0,0,19,5,197,15,5,5,8,65,130,128,59,160,1,48,0,104,112,71
30 POKE#800,#11,#22,#33,#44,#55,#66,#77,#88
40 ?HEX$(USR(#700,0),4)
```

<details><summary>マシン語部分のソースコード</summary><div>

```
R7 - #B7
GOTO @TRAD
MODE RV32C

	R10 = PC + 0
	R10 = R10 + #FC ' R10 = @ARRAY
	R10 += 1
	R10 = [R10 + 0]L
	RET

MODE M0
@TRAD
	R0 = @ARRAY
	R0 += 1
	R0 = [R0 + 0]L
	RET

ORGR #100
@ARRAY
```

</div></details>

従来製品ではこのような読み込みは許されず、1.4.1ではSegmentation Faultになります。
一方、IchigoJam R では`3322`が出力され、このような読み込みも許されることがわかりました。

# この記事のまとめ

IchigoJam R においては、

* BASICのプログラムが従来製品の1.4.1に比べて8～9倍速程度で実行できる
* 従来製品より時刻の精度が上がっているが、時計を作るにはまだ誤差が大きい印象
* 従来製品より大幅に複雑な式が使える
* 従来製品とEEPROMの接続方法が異なる
* USBキーボードを用いるが、一部のキーボードでは止まったり動かなかったりする
* マシン語を実行する際、割り算ルーチンのポインタは渡されない
* アライメントされていないメモリアクセスが可能

となっているようです。