FOR文を実装
2012年6月5日
FOR文が使えるようにした。

幾つか制約があるが、それを踏まえた上でプログラミングすれば、まともに使える(はず)。制約とは、以下のとおり。
・NEXT文では、変数指定の必要はない。
・TOで指定された値と同一にならないと、停止しない。例えば、「FOR A=0 TO 9 STEP 2」などとすると、永久ループになる。
・上記の性質と関係するが、例えば「FOR A=10 TO 9」などと記述すると、ループの中身が65536回実行される。
FOR文、NEXT文のコンパイルルーチンは、次のとおり。
離れた場所にあるFOR文とNEXT文の間でのプロセスの移動には、スタックを用いている。なので、FORループの中でGOTOを用いたりRETURNしたりすると、暴走することになる。まぁ、そういう仕様と言うことで。
ちなみに、memcpy()にはZ80のブロック命令が用いられているので、ここの部分は結構高速である。いずれにせよ、一通り使えるようになってから、速度向上とファイルサイズの圧縮のために、記述方法を全体的に見直すつもりにはしている。
出来上がったFOR文で一万回ループを実行してみると、凡そ0.5秒ほどだった。普通のBASICインタープリターの20倍ほどの速度である。

幾つか制約があるが、それを踏まえた上でプログラミングすれば、まともに使える(はず)。制約とは、以下のとおり。
・NEXT文では、変数指定の必要はない。
・TOで指定された値と同一にならないと、停止しない。例えば、「FOR A=0 TO 9 STEP 2」などとすると、永久ループになる。
・上記の性質と関係するが、例えば「FOR A=10 TO 9」などと記述すると、ループの中身が65536回実行される。
FOR文、NEXT文のコンパイルルーチンは、次のとおり。
char compileFor(){
char e;
int variableAddress, *XX, *YY;
// init statement
while ((e=source[0])==' ') source++;
variableAddress=(int)(&g_variables[(e-'A')*2]);
e=compileLet();
if (e) return e;
// TO statement
if (strncmp(source,"TO ",3)) return 1;
source+=3;
e=compileInt();
if (e) return e;
// STEP statement
while (source[0]==' ') source++;
if (!strncmp(source,"STEP ",5)) {
source+=5;
object[0]=0xD5; // PUSH DE
object++;
e=compileInt();
if (e) return e;
memcpy(object,"\xED\x53\x00\x00\xE1\x19",6); // LD (yyyy),DE; POP HL; ADD HL,DE
YY=(int*)(object+2); // See below
object+=6;
} else {
// STEP 1
YY=0;
memcpy(object,"\xEB\x23",2); // EX DE,HL; INC HL
object+=2;
}
object[0]=0x22; // LD (xxxx),HL
XX=(int*)(object+1); // See below
object+=3;
// FOR statement main routine follows.
memcpy(object,
"\x18\x12" // JR skip:
"\x2A\x34\x12" // LD HL,(1234)
"\x11\x01\x00" // LD DE,0001
"\x19" // ADD HL,DE
"\x22\x34\x12" // LD (1234),HL
"\xAF" // XOR A
"\x11\x34\x12" // LD DE,XXXX
"\xED\x52" // SBC HL,DE
"\xC8" // RET Z
"\xE1" // POP HL
"\x21\x34\x12" // skip: LD HL,1234
"\xE5" // PUSH HL
,24);
*XX=(int)object+14;
if (YY) *YY=(int)object+6;
object+=3;
((int*)object)[0]=variableAddress;
object+=7;
((int*)object)[0]=variableAddress;
object+=11;
((int*)object)[0]=(int)object-19;
object+=3;
return 0;
}
char compileNext(){
memcpy(object,
"\xE1" // POP HL
"\x18\x01" // JR skip:
"\xE9" // back: JP (HL)
"\xCD\x34\x12" // skip: CALL back:
,7);
object+=5;
((int*)object)[0]=(int)object-2;
object+=2;
return 0;
}離れた場所にあるFOR文とNEXT文の間でのプロセスの移動には、スタックを用いている。なので、FORループの中でGOTOを用いたりRETURNしたりすると、暴走することになる。まぁ、そういう仕様と言うことで。
ちなみに、memcpy()にはZ80のブロック命令が用いられているので、ここの部分は結構高速である。いずれにせよ、一通り使えるようになってから、速度向上とファイルサイズの圧縮のために、記述方法を全体的に見直すつもりにはしている。
出来上がったFOR文で一万回ループを実行してみると、凡そ0.5秒ほどだった。普通のBASICインタープリターの20倍ほどの速度である。