IchigoJam R は、2020年末に発表されたIchigoJamの新型です。
紹介ページ:IchigoJam R & IchigoSand 発表、HHKB他USBキーボード対応するRISC-V版、Zen言語でかんたんマシン語両対応 zen4ij #IchigoJam #riscv #zen / 福野泰介の一日一創 / Create every day by Taisuke Fukuno
販売ページ:【β版】IchigoJam 組み立て済完成品 R | Programming Club Net...
※IchigoJamはjig.jpの登録商標です。
本体の写真
パッケージ
基板表面
基板裏面
実行速度
紹介ページでは「なんとインタプリター、10倍速!」と主張していますが、
実測では従来製品と比べて8~9倍速程度のようです。
また、ビデオ出力ありでの速度が大幅に上がっている一方、VIDEO 0
による速度の改善効果は下がっているようです。
詳しくはこのあたりをどうぞ。
自分の記事:IchigoJam速度比較 ~IchigoJam Rが10倍速いってマジ?~ - Qiita
他の人の記事:IchigoJam Rの速度検証 - SHIROのIchigoJam日記
時間の精度
以前書いた記事のキッチンタイマー(本格版)を実行したところ、
1時間に設定して約3秒早くカウントが終わりました。
これは1日あたり72秒、1ヶ月(30日)あたり2160秒(36分)ずれる計算になります。
時計として使うには、ちょっと精度が低いという印象が強いです。
Complex expression耐性
以下のプログラムでネストが深い式を作って実行させ、どこまでComplex expressionが出ずに実行できるかを調べました。
プログラム
#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;
}
従来製品では、以下のような実験結果になりました。
ファームウェア | 実行できた式 | Complex expressionになった式 |
---|---|---|
1.0.0 (VER()=10017) |
?1-(2-(3-(4-5))) ?(((1)))
|
?1-(2-(3-(4-(5-6)))) ?((((1))))
|
1.3.1 (VER()=13106) |
?1-(2-(3-4)) ?((1))
|
?1-(2-(3-(4-5))) ?(((1)))
|
1.4.1 (VER()=14114) |
?1-(2-(3-(4-(5-6)))) ?((((1))))
|
?1-(2-(3-(4-(5-(6-7))))) ?(((((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モジュールを以前と同じように接続しても使えません。
うちでは、このような自作のモジュールを使っています。
従来製品では、GNDとVCCが合うように左側の端子に接続し、飛び出ているコードを右側のEX1に接続します。
しかし、IchigoJam R ではこの接続では動きません。
そもそも仕様変更により「EX1」は無くなっています。
IchigoJam R では、このEEPROMモジュールは、GNDとVCCが合うように「右側」の端子に接続し、
飛び出ているコードを5ピンの端子の真ん中に接続することで、使うことができるようです。
キーボード
IchigoJam R では、USBキーボードが使えます。
ただし、一部の製品はうまく動かないようです。
実際に、いくつかの製品について動くかどうかを調べてみました。
なお、今回の検証結果は各製品1個についての実験結果です。
同じ型番・製品でも、ロットの違いや個体差などにより結果が変わる可能性が否定できません。
うまく動いたもの
YDKBU11BK (BUFFALO)
- 実験中、動作が止まることはありませんでした。
- Num Lock は効かず、テンキーを押すと(機能の実行ではなく)数字が入力されました。
- Caps Lock は効いたものの、状態を表すLEDは点灯しませんでした。
さらに、この機種は従来製品の IchigoJam S (1.3.1) でも使えました。
パッケージなどに記載は見当たりませんでしたが、こっそりPS/2にも対応していたようです。
HC56TU (DENSO)
以前中古屋で購入したバーコードリーダーです。
読み取った内容をUSBキーボードとして入力するモードがあります。
- 初期状態では、バーコードのデータと入力でアルファベットの大文字と小文字が逆になりました。
- Caps Lock を手動設定でONとするモードにすることで、改善しました。
- Caps Lock の状態を自動判別するモードでは、改善しませんでした。
- CRやLFは、CODE128のデータ・ターミネータともに反応しませんでした。
- ターミネータとしてEnterを設定すると、期待通り改行処理が行われました。
便利なように、LED1
とLED0
のバーコード(CODE128)を用意しました。
名刺サイズ (55mm × 91mm) に合わせたサイズの画像になっています。
うまく動かなかったもの
SKB-KG2BK (サンワサプライ)
- 最初の数文字~数十文字程度は入力できましたが、その後キーを押しても反応しなくなりました。
- スイッチを入れ直すと、最初の「Ichigojam~」すら表示されないことがありました。
AOK-184WH (アオテック)
- 最初の数文字~数十文字程度は入力できましたが、その後キーを押しても反応しなくなりました。
- スイッチを入れ直すと、最初の「Ichigojam~」すら表示されないことがありました。
- このとき、通常は点灯するキーボードのLEDが点灯していませんでした。
ワンボタンキーボード (TOKYO FLIP-FLOP)
Arduinoの開発環境を用いたプログラミングにより、動作を変えることができます。
今回は、初期状態(キーを押すとw
が入力される)で実験を行いました。
なお、写真中のUSBケーブルは付属していませんでした。
- キーを押しても反応しませんでした。
公式でUSBハブ内蔵のキーボードは動かないとされていますが、ハブでなくても、
USB Composite Device が挟まるタイプのデバイスは動かないようになっているという仮説が考えられます。
マシン語呼び出し時の引数
IchigoJam 1.2以降の仕様においては、
- 第1引数:
USR
の第2引数で指定した値 - 第2引数:フォントRAMの0番地相当のアドレス
- 第3引数:フォントROMの0番地相当のアドレス
- 第4引数:符号なし除算を行う関数のポインタ
という引数が渡されることになっているはずです。
出典:
- c4ij - イチゴジャム レシピ
- IchigoJam-BASIC/USR.txt at master · fu-sen/IchigoJam-BASIC · GitHub
- レジスタ不足に上位レジスタとスタック操作 / IchigoJamではじめるArmマシン語その12 #IchigoJam #asm / 福野泰介の一日一創 / Create every day by Taisuke Fukuno
以下のプログラムを用いて、マシン語呼び出し(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:?""
マシン語部分のソースコード
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
従来製品 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
に沿っていることが読み取れます。
従来製品 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において、
アドレス0x00000000はRESET service routing vectorであるよう(User Manualの5.1.2を参照)なので、
これが割り算ルーチンであるとは考えにくいでしょう。
アライメントされていないメモリアクセス
マシン語で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)
マシン語部分のソースコード
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
従来製品ではこのような読み込みは許されず、1.4.1ではSegmentation Faultになります。
一方、IchigoJam R では3322
が出力され、このような読み込みも許されることがわかりました。
この記事のまとめ
IchigoJam R においては、
- BASICのプログラムが従来製品の1.4.1に比べて8~9倍速程度で実行できる
- 従来製品より時刻の精度が上がっているが、時計を作るにはまだ誤差が大きい印象
- 従来製品より大幅に複雑な式が使える
- 従来製品とEEPROMの接続方法が異なる
- USBキーボードを用いるが、一部のキーボードでは止まったり動かなかったりする
- マシン語を実行する際、割り算ルーチンのポインタは渡されない
- アライメントされていないメモリアクセスが可能
となっているようです。