search
LoginSignup
3

More than 5 years have passed since last update.

posted at

updated at

豊四季タイニーBASIC Arduino版のフリーエリアを増やす実験(4)

中間コードの見直し

豊四季タイニーBASIC を使用していると頻繁に 'Icode buffer full' というエラーに遭遇します。

TOYOSHIKI TINY BASIC
ARDUINO EDITION

OK
>PRINT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1

YOU TYPE: PRINT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
Icode buffer full
>

OK
>

これは、キーボードより入力されたコマンドラインバッファの内容を中間コードに変換した際に中間コードバッファに入りきらなかった場合に出るエラーで、特に豊四季タイニーBASIC の Arduino版は中間コードバッファのサイズが 64バイトと小さく、このエラーに遭遇しやすいものとなっています。
豊四季タイニーBASIC の Arduino版はコマンドラインバッファと中間コードバッファはそれぞれ 64バイトに設定されており、コマンドラインバッファに入力された文字数より中間コードが長いもの変換された場合、中間コードバッファが溢れる原因になります。
豊四季タイニーBASIC の中間コードは例えば BASIC の予約語で 5文字の 'PRINT' や 6文字の 'RETURN' はどちらも 1バイトの中間コードに変換されるため中間コードバッファを溢れさせる原因とはなりません。それ以外の、例えば 0 や 32767 などの定数は等しく 3バイトの中間コードに変換されます。コマンドラインバッファに入力された例えば 1文字の '0' や 2文字の '10' がそれより大きい 3バイトの中間コードに変換されれば中間コードバッファが溢れる原因となり得ます。それらの解決を目的として中間コードの見直しを行います。

変数の中間コードの見直し

豊四季タイニーBASIC では配列を除く変数は A から Z の 1文字で表しますが、中間コードは

I_VAR, 変数番号(A=0 ... Z=25)

の 2バイトとなっています。入力に対して中間コードが長くなるため中間コードバッファを溢れさせる原因となっています。
これへの対策として、中間コードの I_VAR を廃止し、A から Z にそれぞれ対応した 1バイトの I_VAR_A から I_VAR_Z を別に設けることで、変数の中間コードが常に 1バイトになるよう対策をしました。

ttbasic.patch3
--- ./basic.cpp 2016-05-10 00:00:00.000000000 +0900
+++ ./basic.cpp 2016-05-13 00:00:00.000000000 +0900
@@ -101,10 +101,18 @@ enum {
   I_GTE, I_SHARP, I_GT, I_EQ, I_LTE, I_LT,
   I_ARRAY, I_RND, I_ABS, I_SIZE,
   I_LIST, I_RUN, I_NEW,
-  I_NUM, I_VAR, I_STR,
+  I_NUM,
+  I_VAR_A, I_VAR_B, I_VAR_C, I_VAR_D, I_VAR_E, I_VAR_F,
+  I_VAR_G, I_VAR_H, I_VAR_I, I_VAR_J, I_VAR_K, I_VAR_L,
+  I_VAR_M, I_VAR_N, I_VAR_O, I_VAR_P, I_VAR_Q, I_VAR_R,
+  I_VAR_S, I_VAR_T, I_VAR_U, I_VAR_V, I_VAR_W, I_VAR_X,
+  I_VAR_Y, I_VAR_Z,
+  I_STR,
   I_EOL
 };

+#define is_ivar(c) ((c) >= I_VAR_A && (c) <= I_VAR_Z)
+
 // List formatting condition
 // 後ろに空白を入れない中間コード
 const unsigned char i_nsa[] PROGMEM = {
@@ -470,18 +478,17 @@ unsigned char toktoi() {

     //変数への変換を試みる
     if (c_isalpha(*ptok)) { //もし文字がアルファベットなら
-      if (len >= SIZE_IBUF - 2) { //もし中間コードが長すぎたら
+      if (len >= SIZE_IBUF - 1) { //もし中間コードが長すぎたら
         err = ERR_IBUFOF; //エラー番号をセット
         return 0; //0を持ち帰る
       }
       //もし変数が3個並んだら
-      if (len >= 4 && ibuf[len - 2] == I_VAR && ibuf[len - 4] == I_VAR) {
+      if (len >= 2 && is_ivar(ibuf[len - 1]) && is_ivar(ibuf[len - 2])) {
         err = ERR_SYNTAX; //エラー番号をセット
         return 0; //0を持ち帰る
       }

-      ibuf[len++] = I_VAR; //中間コードを記録
-      ibuf[len++] = c_toupper(*ptok) - 'A'; //変数番号を記録
+      ibuf[len++] = I_VAR_A + c_toupper(*ptok) - 'A'; //変数の中間コードを記録
       s++; //次の文字へ進む
     }
     else
@@ -601,9 +608,8 @@ void putlist(unsigned char* ip) {
     else

     //変数の処理
-    if (*ip == I_VAR) { //もし定数なら
-      ip++; //ポインタを変数番号へ進める
-      c_putch(*ip++ + 'A'); //変数名を取得して表示
+    if (is_ivar(*ip)) { //もし変数なら
+      c_putch(*ip++ - I_VAR_A + 'A'); //変数名を取得して表示
       if (!nospaceb(*ip)) //もし例外にあたらなければ
         c_putch(' '); //空白を表示
     }
@@ -628,7 +634,7 @@ void putlist(unsigned char* ip) {
       while (i--) //文字数だけ繰り返す
         c_putch(*ip++); //ポインタを進めながら文字を表示
       c_putch(c); //文字列の括りを表示
-      if (*ip == I_VAR) //もし次の中間コードが変数だったら
+      if (is_ivar(*ip)) //もし次の中間コードが変数だったら
         c_putch(' '); //空白を表示
     }

@@ -688,9 +694,8 @@ short ivalue() {
     break; //ここで打ち切る

   //変数の値の取得
-  case I_VAR: //変数の場合
-    cip++; //中間コードポインタを次へ進める
-    value = var[*cip++]; //変数番号から変数の値を取得して次を指し示す
+  case I_VAR_A ... I_VAR_Z: //変数の場合
+    value = var[*cip++ - I_VAR_A]; //変数の中間コードから変数の値を取得して次を指し示す
     break; //ここで打ち切る

   //括弧の値の取得
@@ -923,16 +928,15 @@ void iinput() {

     //値を入力する処理
     switch (*cip) { //中間コードで分岐
-    case I_VAR: //変数の場合
-      cip++; //中間コードポインタを次へ進める
+    case I_VAR_A ... I_VAR_Z: //変数の場合
       if (prompt) { //もしまだプロンプトを表示していなければ
-        c_putch(*cip + 'A'); //変数名を表示
+        c_putch(*cip - I_VAR_A + 'A'); //変数名を表示
         c_putch(':'); //「:」を表示
       }
       value = getnum(); //値を入力
       if (err) //もしエラーが生じたら
         return; //終了
-      var[*cip++] = value; //変数へ代入
+      var[*cip++ - I_VAR_A] = value; //変数へ代入
       break; //打ち切る

     case I_ARRAY: //配列の場合
@@ -982,7 +986,7 @@ void ivar() {
   short value; //値
   short index; //変数番号

-  index = *cip++; //変数番号を取得して次へ進む
+  index = *cip++ - I_VAR_A; //変数番号を取得して次へ進む

   if (*cip != I_EQ) { //もし「=」でなければ
     err = ERR_VWOEQ; //エラー番号をセット
@@ -1027,8 +1031,7 @@ void iarray() {
 void ilet() {
   switch (*cip) { //中間コードで分岐

-  case I_VAR: //変数の場合
-    cip++; //中間コードポインタを次へ進める
+  case I_VAR_A ... I_VAR_Z: //変数の場合
     ivar(); //変数への代入を実行
     break; //打ち切る

@@ -1114,11 +1117,11 @@ unsigned char* iexe() {
       cip++; //中間コードポインタを次へ進める

       //変数名を取得して開始値を代入(例I=1)
-      if (*cip++ != I_VAR) { //もし変数がなかったら
+      if (!is_ivar(*cip)) { //もし変数がなかったら
         err = ERR_FORWOV; //エラー番号をセット
         break; //打ち切る
       }
-      index = *cip; //変数名を取得
+      index = *cip - I_VAR_A; //変数名を取得
       ivar(); //代入文を実行
       if (err) //もしエラーが生じたら
         break; //打ち切る
@@ -1169,11 +1172,11 @@ unsigned char* iexe() {

       //変数名を復帰
       index = (short)(uintptr_t)lstk[lstki - 1]; //変数名を復帰
-      if (*cip++ != I_VAR) { //もしNEXTの後ろに変数がなかったら
+      if (!is_ivar(*cip)) { //もしNEXTの後ろに変数がなかったら
         err = ERR_NEXTWOV; //エラー番号をセット
         break; //打ち切る
       }
-      if (*cip++ != index) { //もし復帰した変数名と一致しなかったら
+      if (*cip++ - I_VAR_A != index) { //もし復帰した変数名と一致しなかったら
         err = ERR_NEXTUM; //エラー番号をセット
         break; //打ち切る
       }
@@ -1216,8 +1219,7 @@ unsigned char* iexe() {
       return clp; //行ポインタを持ち帰る

     //一般の文に相当する中間コードの照合と処理
-    case I_VAR: //変数の場合(LETを省略した代入文)
-      cip++; //中間コードポインタを次へ進める
+    case I_VAR_A ... I_VAR_Z: //変数の場合(LETを省略した代入文)
       ivar(); //代入文を実行
       break; //打ち切る
     case I_ARRAY: //配列の場合(LETを省略した代入文)

以上のパッチは GCC の拡張構文

switch( ) {
case value0 ... value9:
    ...

を使用している箇所があり、それに対応しないコンパイラでは書き直しを必要とする箇所があります。

定数の中間コードの見直し

豊四季タイニーBASIC では定数の中間コードは

I_NUM, 定数の値下位8bit, 定数の値上位8bit

という形式で格納され、3バイトの領域を占めます。
BASIC のプログラム中での定数は 1文字の '0' や 2文字の '10' 等各種あり、それらが全て 3バイトの中間コードに変換されてしまうのは中間コードバッファが溢れる原因となるため見直しを行います。
I_NUM を廃止し、

定数の値 0 ~ 9

I_NUM_0 ~ I_NUM_9

定数の値 10 ~ 255

I_NUM_B, 定数の値(10 ~ 255)

定数の値 256 ~ 32767, -1 ~ -32767

I_NUM_W, 定数の値下位8bit, 定数の値上位8bit

に変更することで 1文字の 0 ~ 9 の値は 1バイト、それ以外の値でも 入力された文字数と同じかより少ないバイト数で値を格納できることとなります。

ttbasic.patch4
--- ./basic.cpp 2016-05-13 00:00:00.000000000 +0900
+++ ./basic.cpp 2016-05-13 00:00:00.000000000 +0900
@@ -101,7 +101,8 @@ enum {
   I_GTE, I_SHARP, I_GT, I_EQ, I_LTE, I_LT,
   I_ARRAY, I_RND, I_ABS, I_SIZE,
   I_LIST, I_RUN, I_NEW,
-  I_NUM,
+  I_NUM_0, I_NUM_1, I_NUM_2, I_NUM_3, I_NUM_4, I_NUM_5,
+  I_NUM_6, I_NUM_7, I_NUM_8, I_NUM_9, I_NUM_B, I_NUM_W,
   I_VAR_A, I_VAR_B, I_VAR_C, I_VAR_D, I_VAR_E, I_VAR_F,
   I_VAR_G, I_VAR_H, I_VAR_I, I_VAR_J, I_VAR_K, I_VAR_L,
   I_VAR_M, I_VAR_N, I_VAR_O, I_VAR_P, I_VAR_Q, I_VAR_R,
@@ -111,6 +112,7 @@ enum {
   I_EOL
 };

+#define is_inum(c) ((c) >= I_NUM_0 && (c) <= I_NUM_W)
 #define is_ivar(c) ((c) >= I_VAR_A && (c) <= I_VAR_Z)

 // List formatting condition
@@ -445,13 +447,20 @@ unsigned char toktoi() {
         value = tmp; //0を持ち帰る
       } while (c_isdigit(*ptok)); //文字が数字である限り繰り返す

-      if (len >= SIZE_IBUF - 3) { //もし中間コードが長すぎたら
+      if (len >= SIZE_IBUF - ((value >= 0 && value <= 9) ? 1 : (value <= 255) ? 2 : 3)) { //もし中間コードが長すぎたら
         err = ERR_IBUFOF; //エラー番号をセット
         return 0; //0を持ち帰る
       }
-      ibuf[len++] = I_NUM; //中間コードを記録
-      ibuf[len++] = value & 255; //定数の下位バイトを記録
-      ibuf[len++] = value >> 8; //定数の上位バイトを記録
+      if (value >= 0 && value <= 9) {
+        ibuf[len++] = I_NUM_0 + value; //中間コードを記録
+      } else if (value <= 255) {
+        ibuf[len++] = I_NUM_B; //中間コードを記録
+        ibuf[len++] = value; //定数の値を記録
+      } else {
+        ibuf[len++] = I_NUM_W; //中間コードを記録
+        ibuf[len++] = value & 255; //定数の下位バイトを記録
+        ibuf[len++] = value >> 8; //定数の上位バイトを記録
+      }
       s = ptok; //文字列の処理ずみの部分を詰める
     }
     else
@@ -519,6 +528,34 @@ short getlineno(unsigned char *lp) {
   return *(lp + 1) | *(lp + 2) << 8; //行番号を持ち帰る
 }

+short getinum(unsigned char *lp) {
+  unsigned char i_num = *lp;
+
+  if (i_num >= I_NUM_0 && i_num <= I_NUM_9) {
+    return i_num - I_NUM_0; //定数を持ち帰る
+  } else if (i_num == I_NUM_B) {
+    return *(lp + 1); //定数を持ち帰る
+  } else if (i_num == I_NUM_W) {
+    return *(lp + 1) | *(lp + 2) << 8; //定数を持ち帰る
+  } else {
+    return 0;
+  }
+}
+
+unsigned char inumsize(unsigned char *lp) {
+  unsigned char i_num = *lp;
+
+  if (i_num >= I_NUM_0 && i_num <= I_NUM_9) {
+    return 1;
+  } else if (i_num == I_NUM_B) {
+    return 2;
+  } else if (i_num == I_NUM_W) {
+    return 3;
+  } else {
+    return 0;
+  }
+}
+
 // Search line by line number
 unsigned char* getlp(short lineno) {
   unsigned char *lp; //ポインタ
@@ -531,20 +568,23 @@ unsigned char* getlp(short lineno) {
 }

 // Insert i-code to the list
-void inslist() {
+void inslist(unsigned char size) {
+  unsigned short line = getinum(ibuf);
+  unsigned char isize = inumsize(ibuf);
+  size = 1 + 2 + size - isize;
   unsigned char *insp; //挿入位置
   unsigned char *p1, *p2; //移動先と移動元
   short len; //移動の長さ

-  if (getsize() < *ibuf) { //もし空きが不足していたら
+  if (size > 4 && getsize() < size) { //もし空きが不足していたら
     err = ERR_LBUFOF; //エラー番号をセット
     return; //処理を打ち切る
   }

-  insp = getlp(getlineno(ibuf)); //挿入位置を取得
+  insp = getlp(line); //挿入位置を取得

   //同じ行番号の行が存在したらとりあえず削除
-  if (getlineno(insp) == getlineno(ibuf)) { //もし行番号が一致したら
+  if (getlineno(insp) == line) { //もし行番号が一致したら
     p1 = insp; //p1を挿入位置に設定
     p2 = p1 + *p1; //p2を次の行に設定
     while (len = *p2) { //次の行が末尾でなければ繰り返す
@@ -555,20 +595,23 @@ void inslist() {
   }

   //行番号だけが入力された場合はここで終わる
-  if (*ibuf == 4) //もし長さが4(行番号のみ)なら
+  if (size == 4) //もし長さが4(行番号のみ)なら
     return; //終了する

   //挿入のためのスペースを空ける
   for (p1 = insp; *p1; p1 += *p1); //p1をリストの末尾へ移動
   len = p1 - insp + 1; //移動する幅を計算
-  p2 = p1 + *ibuf; //p2を末尾より1行の長さだけ後ろに設定
+  p2 = p1 + size; //p2を末尾より1行の長さだけ後ろに設定
   while (len--) //移動する幅だけ繰り返す
     *p2-- = *p1--; //後ろへズラす

   //行を転送する
-  len = *ibuf; //中間コードの長さを設定
+  len = size - 1 - 2; //中間コードの長さを設定
   p1 = insp; //転送先を設定
-  p2 = ibuf; //転送元を設定
+  *p1++ = size;
+  *p1++ = line & 0xff;
+  *p1++ = line >> 8;
+  p2 = ibuf + isize; //転送元を設定
   while (len--) //中間コードの長さだけ繰り返す
     *p1++ = *p2++; //転送
 }
@@ -598,10 +641,9 @@ void putlist(unsigned char* ip) {
     else

     //定数の処理
-    if (*ip == I_NUM) { //もし定数なら
-      ip++; //ポインタを値へ進める
-      putnum(*ip | *(ip + 1) << 8, 0); //値を取得して表示
-      ip += 2; //ポインタを次の中間コードへ進める
+    if (is_inum(*ip)) { //もし定数なら
+      putnum(getinum(ip), 0); //値を取得して表示
+      ip += inumsize(ip); //ポインタを次の中間コードへ進める
       if (!nospaceb(*ip)) //もし例外にあたらなければ
         c_putch(' '); //空白を表示
     }
@@ -675,7 +717,17 @@ short ivalue() {
   switch (*cip) { //中間コードで分岐

   //定数の取得
-  case I_NUM: //定数の場合
+  case I_NUM_0 ... I_NUM_9: //0~9の定数の場合
+    value = *cip++ - I_NUM_0; //定数を取得
+    break; //ここで打ち切る
+
+  case I_NUM_B: //1バイト定数の場合
+    cip++; //中間コードポインタを次へ進める
+    value = *cip; //定数を取得
+    cip += 1; //中間コードポインタを定数の次へ進める
+    break; //ここで打ち切る
+
+  case I_NUM_W: //2バイト定数の場合
     cip++; //中間コードポインタを次へ進める
     value = *cip | *(cip + 1) << 8; //定数を取得
     cip += 2; //中間コードポインタを定数の次へ進める
@@ -1280,8 +1332,8 @@ void ilist() {
   short lineno; //表示開始行番号

   //表示開始行番号の設定
-  if (*cip == I_NUM) //もしLIST命令に引数があったら
-    lineno = getlineno(cip); //引数を読み取って表示開始行番号とする
+  if (is_inum(*cip)) //もしLIST命令に引数があったら
+    lineno = getinum(cip); //引数を読み取って表示開始行番号とする
   else //引数がなければ
     lineno = 0; //表示開始行番号を0とする

@@ -1337,8 +1389,7 @@ void icom() {

   case I_LIST: //I_LISTの場合(LIST命令)
     cip++; //中間コードポインタを次へ進める
-    if (*cip == I_EOL || //もし行末か、あるいは
-      *(cip + 3) == I_EOL) //続いて引数があれば
+    if (cip[inumsize(cip)] == I_EOL) //もし行末か、あるいは続いて引数があれば
       ilist(); //LIST命令を実行
     else //そうでなければ
       err = ERR_SYNTAX; //エラー番号をセット
@@ -1417,9 +1468,8 @@ void basic() {
     }

     //中間コードの並びがプログラムと判断される場合
-    if (*ibuf == I_NUM) { //もし中間コードバッファの先頭が行番号なら
-      *ibuf = len; //中間コードバッファの先頭を長さに書き換える
-      inslist(); //中間コードの1行をリストへ挿入
+    if (is_inum(*ibuf)) { //もし中間コードバッファの先頭が行番号なら
+      inslist(len); //中間コードの1行をリストへ挿入
       if (err) //もしエラーが発生したら
         error(); //エラーメッセージを表示してエラー番号をクリア
       continue; //繰り返しの先頭へ戻ってやり直し

尚、豊崎タイニーBASIC には中間コードバッファの空きが 4バイトを切ると行番号のみを入力して既存の行を削除する操作が行えなくなるという不具合と思われる部分がありますが、その修正も同時に行っております。

その他の中間コードの見直し

豊四季タイニーBASIC では RND() 等の関数や @(~) の配列変数は常に '(' と込みで使用されるにも関わらず、中間コードは別となっているため中間コードのバイト数の無駄となっています。

@(~) → I_ARRAY, I_OPEN, ~, I_CLOSE
RND(~) → I_RND, I_OPEN, ~, I_CLOSE
ABS(~) → I_ABS, I_OPEN, ~, I_CLOSE
SIZE(~) → I_SIZE, I_OPEN, ~, I_CLOSE

常に '(' とセットで使用されるのであれば、'(' まで含めて中間コードとして扱えば、中間コードの '(' を表す I_OPEN が省略できるので中間コードの省メモリ化となります。

ttbasic.patch5
--- ./basic.cpp 2016-05-13 00:00:00.000000000 +0900
+++ ./basic.cpp 2016-05-13 00:00:00.000000000 +0900
@@ -67,10 +67,10 @@ const char kw23[] PROGMEM = ">";
 const char kw24[] PROGMEM = "=";
 const char kw25[] PROGMEM = "<=";
 const char kw26[] PROGMEM = "<";
-const char kw27[] PROGMEM = "@";
-const char kw28[] PROGMEM = "RND";
-const char kw29[] PROGMEM = "ABS";
-const char kw30[] PROGMEM = "SIZE";
+const char kw27[] PROGMEM = "@(";
+const char kw28[] PROGMEM = "RND(";
+const char kw29[] PROGMEM = "ABS(";
+const char kw30[] PROGMEM = "SIZE(";
 const char kw31[] PROGMEM = "LIST";
 const char kw32[] PROGMEM = "RUN";
 const char kw33[] PROGMEM = "NEW";
@@ -688,15 +688,20 @@ void putlist(unsigned char* ip) {
 }

 // Get argument in parenthesis
+short getparam2(void);
 short getparam() {
-  short value; //値
-
   if (*cip != I_OPEN) { //もし「(」でなければ
     err = ERR_PAREN; //エラー番号をセット
     return 0; //終了
   }
   cip++; //中間コードポインタを次へ進める

+  return getparam2();
+}
+
+short getparam2(void) {
+  short value; //値
+
   value = iexp(); //式を計算
   if (err) //もしエラーが生じたら
     return 0; //終了
@@ -758,7 +763,7 @@ short ivalue() {
   //配列の値の取得
   case I_ARRAY: //配列の場合
     cip++; //中間コードポインタを次へ進める
-    value = getparam(); //括弧の値を取得
+    value = getparam2(); //括弧の値を取得
     if (err) //もしエラーが生じたら
       break; //ここで打ち切る
     if (value >= SIZE_ARRY) { //もし添え字の上限を超えたら
@@ -771,7 +776,7 @@ short ivalue() {
   //関数の値の取得
   case I_RND: //関数RNDの場合
     cip++; //中間コードポインタを次へ進める
-    value = getparam(); //括弧の値を取得
+    value = getparam2(); //括弧の値を取得
     if (err) //もしエラーが生じたら
       break; //ここで打ち切る
     value = getrnd(value); //乱数を取得
@@ -779,7 +784,7 @@ short ivalue() {

   case I_ABS: //関数ABSの場合
     cip++; //中間コードポインタを次へ進める
-    value = getparam(); //括弧の値を取得
+    value = getparam2(); //括弧の値を取得
     if (err) //もしエラーが生じたら
       break; //ここで打ち切る
     if(value < 0) //もし0未満なら
@@ -788,12 +793,12 @@ short ivalue() {

   case I_SIZE: //関数SIZEの場合
     cip++; //中間コードポインタを次へ進める
-    //もし後ろに「()」がなかったら
-    if ((*cip != I_OPEN) || (*(cip + 1) != I_CLOSE)) {
+    //もし後ろに「)」がなかったら
+    if ((*cip != I_CLOSE)) {
       err = ERR_PAREN; //エラー番号をセット
       break; //ここで打ち切る
     }
-    cip += 2; //中間コードポインタを「()」の次へ進める
+    cip += 1; //中間コードポインタを「)」の次へ進める
     value = getsize(); //プログラム保存領域の空きを取得
     break; //ここで打ち切る

@@ -993,7 +998,7 @@ void iinput() {

     case I_ARRAY: //配列の場合
       cip++; //中間コードポインタを次へ進める
-      index = getparam(); //配列の添え字を取得
+      index = getparam2(); //配列の添え字を取得
       if (err) //もしエラーが生じたら
         return; //終了
       if (index >= SIZE_ARRY) { //もし添え字が上限を超えたら
@@ -1058,7 +1063,7 @@ void iarray() {
   short value; //値
   short index; //配列の添え字

-  index = getparam(); //配列の添え字を取得
+  index = getparam2(); //配列の添え字を取得
   if (err) //もしエラーが生じたら
     return; //終了

以上、3つのパッチを

$ patch -p0 < ttbasic.patch3

$ patch -p0 < ttbasic.patch4

$ patch -p0 < ttbasic.patch5

以上の順に当てることで中間コードの省メモリ化が果たせます。
『豊四季タイニーBASIC Arduino版のフリーエリアを増やす実験(1)』で中間コード換算でサイズが 353バイトとなるために 中間コードバッファの容量が標準で 256バイトである豊崎タイニーBASIC Arduino版ではそのままでは実行できなかった fibonacci.bas が今回の変更で実行できるようなりました。

TOYOSHIKI TINY BASIC
ARDUINO EDITION

OK
>100 INPUT N
>110 P=0; D=0
>120 FOR I=0 TO N
>130 IF I<=1 LET @(P)=I; GOTO 210
>140 C=0
>150 FOR J=0 TO D
>160 @(2*J+P)=@(2*J+P)+@(2*J+(P=0))+C
>170 C=@(2*J+P)>=10;
>180 @(2*J+P)=@(2*J+P)-10*C
>190 NEXT J
>200 IF C#0 D=D+1; @(2*D+P)=C; @(2*D+(P=0))=0
>210 PRINT I,": ",
>220 FOR J=D TO 0 STEP -1
>230 PRINT #1,@(2*J+P),
>240 NEXT J
>250 PRINT
>260 P=(P=0)
>270 NEXT I
>

OK
>PRINT SIZE()
7

OK
>RUN
N:70
0: 0
1: 1
2: 1
3: 2
4: 3
5: 5
6: 8
7: 13
8: 21
9: 34
10: 55
11: 89
12: 144
13: 233
14: 377
15: 610
16: 987
17: 1597
18: 2584
19: 4181
20: 6765
21: 10946
22: 17711
23: 28657
24: 46368
25: 75025
26: 121393
27: 196418
28: 317811
29: 514229
30: 832040
31: 1346269
32: 2178309
33: 3524578
34: 5702887
35: 9227465
36: 14930352
37: 24157817
38: 39088169
39: 63245986
40: 102334155
41: 165580141
42: 267914296
43: 433494437
44: 701408733
45: 1134903170
46: 1836311903
47: 2971215073
48: 4807526976
49: 7778742049
50: 12586269025
51: 20365011074
52: 32951280099
53: 53316291173
54: 86267571272
55: 139583862445
56: 225851433717
57: 365435296162
58: 591286729879
59: 956722026041
60: 1548008755920
61: 2504730781961
62: 4052739537881
63: 6557470319842
64: 10610209857723
65: 17167680177565
66: 27777890035288
67: 44945570212853
68: 72723460248141
69: 117669030460994
70: 190392490709135

OK
>

尚、以上の変更を施しても、豊四季タイニーBASIC の大域変数に変化はないのでビルド時の RAM のフリーエリアに変化はありません。

『豊四季タイニーBASIC Arduino版のフリーエリアを増やす実験(5)』に続く(予定)

番外:『avr-gcc のコード生成で switch ~ case の最適化によっては RAM を消費することがある』

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3