Retro Mini BASIC interpreter は、古典的なBASIC文法をベースに、C#で実装された軽量インタプリタです。 教育用、レトロプログラミング体験用、また簡単なグラフィック実験にも使えるよう設計されています。
- 数値・文字列変数、1次元/2次元配列をサポート
- 四則演算・比較・論理演算に対応
- 制御構文:
IF/THEN、FOR/NEXT、DO/LOOP UNTIL、GOSUB/RETURN、ON GOTO/GOSUB - 入出力:
PRINT、INPUT - 数学関数・文字列関数・乱数・時間関数を提供
- グラフィック命令(点、線、円、矩形、色、保存)を備える
- レトロ風のカラーパレット(0〜15)が利用可能
POINT(x,y)関数で画素の状態も読み取れる
- Visual Studio または
dotnet runで実行。 - 起動時に表示:
Retro Mini BASIC interpreter Version 0.1 by Cala Maclir 2025 Ready. > - 行番号をつけて入力するとプログラムに保存されます。
> 10 PRINT "HELLO" > 20 ENDRUNで実行、LISTで確認、NEWで消去できます。 - 行番号なしで入力した場合は即時実行(ダイレクトモード)。
EXITで終了。
- 代入
LET A = 10 B$ = "ABC" ' LETは省略可 - PRINT
PRINT "X="; A PRINT A, B - INPUT
INPUT "値を入力:"; X INPUT NAME$ - IF
IF A>0 THEN PRINT "POSITIVE" IF A=0 THEN 100 - GOTO / GOSUB / RETURN
10 GOSUB 100 20 PRINT "BACK": END 100 PRINT "SUB": RETURN - FOR / NEXT
FOR I=1 TO 10 STEP 2 PRINT I NEXT - DO / LOOP [UNTIL 条件]
DO INPUT A LOOP UNTIL A=0 - DIM
DIM A(10) DIM B$(5,5) - ON GOTO / ON GOSUB
INPUT N ON N GOTO 100,200,300 - STOP / END … プログラム終了
| 関数 | 内容 |
|---|---|
ABS(x) |
絶対値 |
INT(x) |
小数切り捨て |
SQR(x) |
平方根 |
SIN/COS/TAN(x) |
三角関数 (ラジアン) |
ATN(x) |
逆タンジェント |
LOG(x) |
自然対数 (x>0) |
EXP(x) |
e^x |
PI |
円周率 (3.14159...) |
RAD(x) |
度→ラジアン変換 |
DEG(x) |
ラジアン→度変換 |
SGN(x) |
符号 (-1,0,1) |
MIN(a,b) |
最小値 |
MAX(a,b) |
最大値 |
CLAMP(x,a,b) |
範囲に収める |
| 関数 | 内容 |
|---|---|
STR$(x) |
数値→文字列 |
VAL(s) |
文字列→数値 |
LEN(s) |
文字数 |
CHR$(n) |
文字コード→文字 |
ASC(s) |
文字→コード |
LEFT$(s,n) |
左からn文字 |
RIGHT$(s,n) |
右からn文字 |
MID$(s,p[,n]) |
部分文字列 |
SPC(n) |
n個の空白 |
TAB(n) |
桁揃え |
| 関数 | 内容 |
|---|---|
RND |
0.0〜1.0の乱数 |
RNDI(n) |
0〜n の整数乱数 |
RANDOMIZE [seed] |
乱数シード設定 |
TIMER |
実行開始からの経過秒数 |
- 画面管理
SCREEN [w,h] ' 初期化(既定640x480) CLS ' 画面クリア FLUSH ' バッファ更新 SAVEIMAGE "a.png" ' PNG保存 SLEEP ms ' 一時停止 - 色
※ paletteは0〜15のDOS風カラーパレット
COLOR r,g,b COLOR palette COLORHSV h,s,v - 描画
PSET x,y LINE (x1,y1)-(x2,y2)[,color] LINE -(x2,y2)[,color] ' shorthand CIRCLE x,y,r[,color] BOX x1,y1,x2,y2[,fill][,color] - 判定
IF POINT(x,y) THEN PRINT "DOT"
IF 文は ELSE にも対応しています。THEN の後も ELSE の後も、行番号または文を記述可能です。複数文は : で区切ります。
10 INPUT "点数"; S
20 IF S >= 60 THEN 100 ELSE 200
100 PRINT "合格": END
200 PRINT "不合格": ENDS=100→ 合格S=10→ 不合格S=0→ 不合格
10 INPUT "値"; A
20 IF A>0 THEN PRINT "正": PRINT "POS" ELSE PRINT "負": PRINT "NEG"
30 END10 FOR Y=-12 TO 12
20 FOR X=-39 TO 39
30 CA=X*0.0458
40 CB=Y*0.08333
50 A=CA
60 B=CB
70 FOR I=0 TO 15
80 T=A*A-B*B+CA
90 B=2*A*B+CB
100 A=T
110 IF (A*A+B*B)>4 THEN GOTO 200
120 NEXT I
130 PRINT " ";
140 GOTO 210
200 IF I>9 THEN I=I+7
205 PRINT CHR$(48+I);
210 NEXT X
220 PRINT
230 NEXT Y
10 SCREEN 640,480
20 CLS
30 MAX=100
40 FOR Y=0 TO 479
50 CI = -1.2 + Y*(2.4/479)
60 FOR X=0 TO 639
70 CR = -2.2 + X*(3.2/639)
80 XR=0: XI=0
90 I=0
100 FOR I=0 TO MAX
110 XT = XR*XR - XI*XI + CR
120 XI = 2*XR*XI + CI
130 XR = XT
140 IF XR*XR + XI*XI > 4 THEN GOTO 170
150 NEXT I
160 I=MAX
170 R = MIN(255, I*9)
180 G = MIN(255, I*7)
190 B = MIN(255, I*5)
200 COLOR R,G,B
210 PSET X,Y
220 NEXT X
230 IF Y MOD 8 = 0 THEN FLUSH
240 NEXT Y
250 FLUSH
10 SCREEN 1280,960
20 CLS
30 MAX=100
40 FOR Y=0 TO 959
50 CI = -1.2 + Y*(2.4/959)
60 FOR X=0 TO 1279
70 CR = -2.2 + X*(3.2/1279)
80 XR=0: XI=0
90 I=0
100 FOR I=0 TO MAX
110 XT = XR*XR - XI*XI + CR
120 XI = 2*XR*XI + CI
130 XR = XT
140 IF XR*XR + XI*XI > 4 THEN GOTO 170
150 NEXT I
160 I=MAX
170 R = MIN(255, I*9)
180 G = MIN(255, I*7)
190 B = MIN(255, I*5)
200 COLOR R,G,B
210 PSET X,Y
220 NEXT X
230 IF Y MOD 16 = 0 THEN FLUSH
240 NEXT Y
250 FLUSH
10 SCREEN 640,480
20 CLS
30 MAX=120
40 CR=-0.7
50 CI=0.27015
60 FOR Y=0 TO 479
70 Y0 = -1.2 + Y*(2.4/479)
80 FOR X=0 TO 639
90 X0 = -2.2 + X*(3.2/639)
100 XR = X0
110 XI = Y0
120 I=0
130 FOR I=0 TO MAX
140 XT = XR*XR - XI*XI + CR
150 XI = 2*XR*XI + CI
160 XR = XT
170 IF XR*XR + XI*XI > 4 THEN GOTO 200
180 NEXT I
190 I=MAX
200 R = MIN(255, I*5)
210 G = MIN(255, I*9)
220 B = MIN(255, I*3)
230 COLOR R,G,B
240 PSET X,Y
250 NEXT X
260 IF Y MOD 8 = 0 THEN FLUSH
270 NEXT Y
280 FLUSH
10 SCREEN 1280,960
20 CLS
30 MAX=120
40 CR=-0.7
50 CI=0.27015
60 FOR Y=0 TO 959
70 Y0 = -1.2 + Y*(2.4/959)
80 FOR X=0 TO 1279
90 X0 = -2.2 + X*(3.2/1279)
100 XR = X0
110 XI = Y0
120 I=0
130 FOR I=0 TO MAX
140 XT = XR*XR - XI*XI + CR
150 XI = 2*XR*XI + CI
160 XR = XT
170 IF XR*XR + XI*XI > 4 THEN GOTO 200
180 NEXT I
190 I=MAX
200 R = MIN(255, I*5)
210 G = MIN(255, I*9)
220 B = MIN(255, I*3)
230 COLOR R,G,B
240 PSET X,Y
250 NEXT X
260 IF Y MOD 16 = 0 THEN FLUSH
270 NEXT Y
280 FLUSH
10 SCREEN 1280,960
20 CLS
30 MAX=140
40 CR=-0.76
50 CI=0.08
60 FOR Y=0 TO 959
70 Y0 = -1.2 + Y*(2.4/959)
80 FOR X=0 TO 1279
90 X0 = -1.6 + X*(3.2/1279)
100 XR = X0
110 XI = Y0
120 I=0
130 FOR I=0 TO MAX
140 XT = XR*XR - XI*XI + CR
150 XI = -2*XR*XI + CI
160 XR = XT
170 IF XR*XR + XI*XI > 4 THEN GOTO 200
180 NEXT I
190 I=MAX
200 R = (I*9) MOD 256
210 G = (I*5) MOD 256
220 B = (I*13) MOD 256
230 IF I=MAX THEN R=0:G=0:B=0
240 COLOR R,G,B
250 PSET X,Y
260 NEXT X
270 IF Y MOD 16 = 0 THEN FLUSH
280 NEXT Y
290 FLUSH
10 SCREEN 1280,960
20 CLS
30 MAX=120
40 CR=-0.12
50 CI=0.74
60 FOR Y=0 TO 959
70 Y0 = -1.5 + Y*(3.0/959)
80 FOR X=0 TO 1279
90 X0 = -2.0 + X*(4.0/1279)
100 XR = X0
110 XI = Y0
120 I=0
130 FOR I=0 TO MAX
140 XT = XR*XR*XR - 3*XR*XI*XI + CR
150 XI = 3*XR*XR*XI - XI*XI*XI + CI
160 XR = XT
170 IF XR*XR + XI*XI > 9 THEN GOTO 200
180 NEXT I
190 I=MAX
200 R = (I*11) MOD 256
210 G = (I*17) MOD 256
220 B = (I*23) MOD 256
230 IF I=MAX THEN R=0:G=0:B=20
240 COLOR R,G,B
250 PSET X,Y
260 NEXT X
270 IF Y MOD 16 = 0 THEN FLUSH
280 NEXT Y
290 FLUSH
10 SCREEN 640,480
20 CLS
25 COLOR 255,255,255
30 X1=2
40 DIM D(1,255)
45 FOR L=0 TO 255
46 D(0,L)=-1
47 D(1,L)=-1
48 NEXT
60 FOR Y=-180 TO 180 STEP 4
61 FOR X=-180 TO 180 STEP 4
80 R=PI/180*SQR(X*X+Y*Y)
90 Z=100*COS(R)-30*COS(3*R)
100 V=INT(116+X/2+(16-Y/2)/2)
110 W=INT((130-Y/2-Z)/2)
120 IF (V<0)+(V>255) THEN GOTO 160
130 IF D(0,V)=-1 THEN GOTO 500
140 IF W<=D(0,V) THEN GOTO 700
150 IF W>=D(1,V) THEN GOTO 800
155 GOTO 160
160 NEXT X
161 NEXT Y
165 FLUSH
170 END
500 IF V=0 THEN GOTO 600
510 IF D(0,V-1)=-1 THEN GOTO 600
520 IF D(0,V+1)=-1 THEN GOTO 600
530 D(0,V)=INT((D(0,V-1)+D(0,V+1))/2)
540 D(1,V)=INT((D(1,V-1)+D(1,V+1))/2)
550 GOSUB 900
560 GOTO 160
600 D(0,V)=W
605 D(1,V)=W
610 GOSUB 900
620 GOTO 160
700 GOSUB 900
705 D(0,V)=W
707 IF D(1,V)=-1 THEN D(1,V)=W
710 GOTO 160
800 GOSUB 900
805 D(1,V)=W
807 IF D(0,V)=-1 THEN D(0,V)=W
810 GOTO 160
900 X1=V*3.5+30
905 Y1=600-W*3.5
910 GOSUB 2100
920 RETURN
2100 REM DOT(X1,Y1)
2110 PSET X1/2*1.2+30,(-Y1/2+310)*1.2
2120 RETURN
10 ' Retro Mini BASIC interpreter - 1920x1080 Hypercolor (no arrays, SQR版)
20 SCREEN 1920,1080
30 RANDOMIZE
40 CLS
50 W=1920: H=1080: CX=W/2: CY=H/2
60 T0=TIMER()
70 DO
80 T=TIMER()-T0
90 ' ---- 同心円グラデ ----
100 RMAX=SQR(CX*CX+CY*CY)
110 FOR R=0 TO RMAX STEP 6
120 HUE=(R*0.6+T*35) MOD 360
130 SAT=0.6+0.4*SIN(R*0.02+T*0.7)
140 VAL=0.35+0.65*(1-R/RMAX)
150 COLORHSV HUE,SAT,VAL
160 CIRCLE CX,CY,INT(R)
170 NEXT R
180 ' ---- 曲線トレイル ----
190 NPTS=1800: RBASE=MIN(W,H)*0.42
200 PH=T*22: TW=0.35*SIN(T*0.6)
210 PSET CX,CY
220 FOR K=0 TO NPTS
230 TH=(K/NPTS)*720+PH
240 R1=0.58+0.22*SIN(RAD(3*TH+T*40))
250 R2=0.25+0.18*SIN(RAD(7*TH-T*33))
260 R3=0.12+0.10*SIN(RAD(11*TH+T*21))
270 RR=(R1+R2+R3)*RBASE
280 A=RAD(TH+90*SIN(T*0.5))+TW*SIN(RAD(TH*0.9))
290 X=INT(CX+RR*COS(A)): Y=INT(CY+RR*SIN(A))
300 COLORHSV (TH*1.1+T*90) MOD 360,0.95,0.98
310 LINE -(X,Y)
320 NEXT K
330 ' ---- 光輪 ----
340 FOR I=0 TO 6
350 RR=RBASE*0.55+I*16
360 COLORHSV (T*60+I*38) MOD 360,0.7+0.3*SIN(T*0.8+I),0.85
370 CIRCLE CX,CY,INT(RR)
380 NEXT I
390 ' ---- 星粒(都度ランダム生成・配列なし)----
400 FOR S=0 TO 1200
410 X=INT(RND*W): Y=INT(RND*H)
420 IF RND<0.18 THEN COLORHSV (RND*360),0.8,0.95: PSET X,Y
430 NEXT S
440 FLUSH
450 SLEEP 16
460 LOOP
10 SCREEN 1280,960
20 CLS
30 CX=639.5
40 CY=479.5
50 FOR Y=0 TO 959
60 DY = Y-CY
70 FOR X=0 TO 1279
80 DX = X-CX
90 D1 = SQR(DX*DX + DY*DY)
100 D2 = SQR((DX-260)*(DX-260) + (DY+180)*(DY+180))
110 D3 = SQR((DX+220)*(DX+220) + (DY-160)*(DY-160))
120 V1 = SIN(D1/14)
130 V2 = COS((DX*0.018) + (DY*0.012))
140 V3 = SIN(D2/18)
150 V4 = COS(D3/22)
160 V5 = SIN((DX*DX - DY*DY)/60000)
170 V = V1 + V2 + 0.8*V3 + 0.7*V4 + 0.6*V5
180 R = 128 + 127*SIN(V*1.7)
190 G = 128 + 127*SIN(V*1.7 + 2.094)
200 B = 128 + 127*SIN(V*1.7 + 4.188)
210 GLOW = 220/(1 + D1/140)
220 R = R + GLOW*0.4
230 G = G + GLOW*0.7
240 B = B + GLOW
250 VIG = 1 - D1/820
260 IF VIG < 0 THEN VIG = 0
270 R = R * (0.35 + 0.65*VIG)
280 G = G * (0.35 + 0.65*VIG)
290 B = B * (0.45 + 0.55*VIG)
300 IF R < 0 THEN R = 0
310 IF G < 0 THEN G = 0
320 IF B < 0 THEN B = 0
330 IF R > 255 THEN R = 255
340 IF G > 255 THEN G = 255
350 IF B > 255 THEN B = 255
360 COLOR INT(R),INT(G),INT(B)
370 PSET X,Y
380 NEXT X
390 IF Y MOD 16 = 0 THEN FLUSH
400 NEXT Y
410 FLUSH
10 SCREEN 1280,960
20 CLS
30 PI=3.14159265
40 CX=639.5
50 CY=479.5
60 SCALE=240
70 FOR Y=0 TO 959
80 DY=Y-CY
90 FOR X=0 TO 1279
100 DX=X-CX
110 D=SQR(DX*DX+DY*DY)
120 IF ABS(DX)<0.0001 THEN TH=PI/2
130 IF ABS(DX)<0.0001 AND DY<0 THEN TH=-PI/2
140 IF ABS(DX)>=0.0001 THEN TH=ATN(DY/DX)
150 IF DX<0 THEN TH=TH+PI
160 R1=SCALE*(0.55+0.45*SIN(6*TH))
170 R2=SCALE*0.82*(0.55+0.45*COS(9*TH+0.4))
180 R3=SCALE*0.52*(0.55+0.45*SIN(12*TH+1.1))
190 R4=SCALE*1.18*(0.55+0.45*COS(3*TH-0.7))
200 A1=220/(1+ABS(D-R1)/2.6)
210 A2=180/(1+ABS(D-R2)/3.0)
220 A3=140/(1+ABS(D-R3)/2.2)
230 A4=120/(1+ABS(D-R4)/4.5)
240 CORE=200/(1+D/18)
250 WAVE=35*(SIN(D/14-TH*5)+1)
260 HALO=70/(1+ABS(D-SCALE*1.35)/10)
270 V=A1+A2+A3+A4+CORE+WAVE+HALO
280 R=20 + 1.10*A1 + 0.50*A2 + 0.80*A4 + CORE*0.8 + 70*(SIN(TH*2)+1)
290 G=10 + 0.70*A1 + 1.20*A2 + 0.60*A3 + CORE*0.5 + 70*(SIN(TH*2+2.094)+1)
300 B=30 + 0.50*A2 + 1.00*A3 + 0.90*A4 + CORE*1.0 + 70*(SIN(TH*2+4.188)+1)
310 R=R + V*0.08
320 G=G + V*0.06
330 B=B + V*0.10
340 VIG=1-D/520
350 IF VIG<0 THEN VIG=0
360 R=R*(0.35+0.65*VIG)
370 G=G*(0.35+0.65*VIG)
380 B=B*(0.40+0.60*VIG)
390 IF R<0 THEN R=0
400 IF G<0 THEN G=0
410 IF B<0 THEN B=0
420 IF R>255 THEN R=255
430 IF G>255 THEN G=255
440 IF B>255 THEN B=255
450 COLOR INT(R),INT(G),INT(B)
460 PSET X,Y
470 NEXT X
480 IF Y MOD 16 = 0 THEN FLUSH
490 NEXT Y
500 FLUSH
本書は Retro Mini BASIC interpreter の仮想マシン(VM)実装を理解・拡張するための技術ドキュメントです。
対象は VM.cs(実行機)、Compiler.cs(IL 生成)、GfxHost.cs(描画)、Program.cs(REPL)に跨る挙動ですが、VM 観点での仕様を「readme 形式」で網羅します。
注: 一部の名称は実装上のフィールド/関数に合わせています。以降の説明で 「数値/文字列」「配列」「IL命令」「ジャンプテーブル」 という用語を共通で使用します。
┌──────────────┐ 1) 行番号つきソース
│ Program.cs │ ────────────────────────────┐
└──────┬───────┘ │
│ 2) コンパイル(構文解析・IL生成) │
┌──────▼───────┐ │
│ Compiler.cs │ ──▶ CompiledProgram ───────┼─▶ 3) VM 実行
└──────┬───────┘ │
│(記号表/ジャンプ表を内包) │
┌──────▼───────┐ │
│ VM.cs │ ◀───────────────────────────┘
└───┬─────┬────┘
│ │
│ └────────► GfxHost.cs(描画/UI)
│
└──────────────► Console I/O(INPUT/PRINT)
Compiler.csは 行番号付きプログラムを IL 命令列(Op[]) に変換します(同時に 記号表・PC⇔行番号マップ・ON GOTO/GOSUB 用ジャンプテーブルを構築)。VM.csは Op[] を 0 から順に解釈し、スタック実行・レジスタ/配列領域・制御スタック(GOSUB など) を用いてプログラムを進行させます。- グラフィックス命令は VM から
GfxHostに委譲され、ダブルバッファで UI スレッドに反映されます。
- 数値:
double(数式/関数/比較演算に使用) - 文字列:
.NET string - 真偽の評価:
0を偽、0 以外を真**として扱います(比較/論理の結果も 0 or 非 0)。
- スカラは 数値配列 と 文字列配列を別々に持つ レジスタファイルに格納されます。
CompilerのSymtab.GetScalarSlot(name)は LSB に型ビットを埋め込むスロット番号を返します:slot & 1 == 0→ 数値(double[]の index はslot >> 1)slot & 1 == 1→ 文字列(string[]の index はslot >> 1)
- 配列も 数値配列 と 文字列配列に分かれて保持され、最大 2 次元。
DIM A(n)/DIM B$(r,c)で確保。インデックスは 0 起点。CompilerはDIM_ARR (a:slot, b:次元数)を発行し、**直前にプッシュされたサイズ(1 or 2 個)**を VM が消費して確保します。LOAD_ARR/STORE_ARRは b=1 or 2(次元数) に従って、VM がインデックスをスタックから読み、配列へアクセスします。
例:
DIM A(10)→PUSH 10→DIM_ARR a=A, b=1
例:A(I,J)=X→… I … J … X→STORE_ARR a=A, b=2
- VM は 評価スタックを持ち、式の評価や関数引数などはスタックで受け渡しします。
- 単項/二項演算はスタック上の値を ポップ→演算→プッシュ で進みます。
- スカラ数値:
numRegs[]、スカラ文字列:strRegs[] - 配列:
numArrs[],strArrs[](内部はdouble[]またはstring[]の 1D/2D ラッパ)
GOSUBは 復帰アドレスを制御スタックに積み、RETSUBでポップして復帰します。FOR/NEXTは ループ変数スロット/終端値/ステップ/比較方向/ボディPC などを VM 側で保持します(詳細は 5.1)。
Compiler.csで発行される命令を VM が解釈します。OpはCode(オペコード)とA/B/D/Sのオペランドスロットを持ちます。
| 命令 | 役割 |
|---|---|
PUSH_NUM (D) |
数値リテラルをスタックに積む |
PUSH_STR (S) |
文字列リテラルをスタックに積む |
LOAD (A=slot) |
スカラ変数をロードしてスタックへ |
STORE (A=slot) |
スタックトップをスカラ変数へ代入 |
LOAD_ARR (A=slot, B=dim) |
A の配列から B 次元のインデックスをポップ → 要素をプッシュ |
STORE_ARR (A=slot, B=dim) |
値と B 次元のインデックスをポップ → A の配列へ書き込み |
DIM_ARR (A=slot, B=dim) |
スタックからサイズ dim 個を読み、配列を確保 |
| 命令 | 役割 |
|---|---|
ADD/SUB/MUL/DIV/MOD/POW/NEG |
通常の算術 |
CEQ/CNE/CLT/CLE/CGT/CGE |
比較(結果は 0/非0) |
NOT/AND/OR |
論理演算(0/非0 で扱う) |
| 命令 | 役割 |
|---|---|
PRINT |
スタックトップを即座に出力バッファへ |
PRINT_SPC |
PRINT 項目間の区切り(半角空白) |
PRINT_SUPPRESS_NL |
行末の改行抑制 |
PRINT_NL |
改行出力 |
CALLFN (A=fnId, B=argc) |
関数/組み込みコマンド呼び出し(INPUT は特殊:B にスロットが入る) |
| 命令 | 役割 |
|---|---|
JMP (A=pc) |
無条件ジャンプ |
JZ (A=pc) |
スタックトップが **偽(0)**なら A へ |
GOSUB (A=pc) |
制御スタックに復帰アドレスを積んで A へ |
RETSUB |
制御スタックから復帰 |
ON_GOTO (A=tableIndex) |
スタックトップの値(1 起点)でテーブル分岐(GOTO) |
ON_GOSUB (A=tableIndex) |
同上(GOSUB) |
| 命令 | 役割 |
|---|---|
FOR_INIT (A=varSlot) |
スタックから end, step を読み、var = var(事前に STORE 済み) |
FOR_CHECK (A=varSlot, B=bodyPc) |
var が範囲内でなければ ループ終端へ、範囲内なら B(ボディ)へ |
| `FOR_INCR (A=varSlot | -1)` |
-
構文:
FOR i = start TO end [STEP step] … NEXT [i] -
コンパイル系列(概念図):
startを評価 →STORE iend→step(省略時 1)をプッシュ →FOR_INIT iFOR_CHECK i, bodyPc- ボディ
FOR_INCR i→FOR_CHECK i, bodyPcへ戻る or ループを抜ける
-
比較方向は
stepの符号で自動的に決まり、iが終端を超えた時点で終了します。
- 条件は 0/非0 で評価。
- パターン:
IF cond THEN <行番号>→JZ skip ; JMP lineIF cond THEN 文…→JZ afterThen ; 文… ; afterThen:IF cond THEN <行> ELSE <行>→JZ else ; JMP then ; else: JMP elseLineIF cond THEN 文… ELSE 文…→JZ else ; 文… ; JMP end ; else: 文… ; end:
:による 複文 は THEN/ELSE の両方で許可。
DOで ループ先頭 PC を積み、LOOP UNTIL condでcondを評価し、偽なら先頭へ JMP、真なら脱出。LOOP(UNTIL なし)は無条件ループ(JMPのみ)。
ON n GOTO l1,l2,...:n(1 起点)がkならlkへジャンプ。- 範囲外(<1 or >N)は 落下(何もしない)。
GOSUB版は復帰アドレスを積む。
- 数値/文字列:
ABS, INT, VAL, STR$, LEN, CHR$, ASC, LEFT$, RIGHT$, MID$ - 数学/時間/乱数:
SIN, COS, TAN, SQR, ATN, LOG, EXP, PI, RAD, DEG, SGN, MIN, MAX, CLAMP, RNDI, TIMER, RANDOMIZE - 出力補助:
SPC, TAB - 入力:
INPUT(特殊:CALLFN(A=INPUT, B=slot)でスロットに代入) - グラフィック(200 台):
SCREEN, CLS, COLOR, PSET, LINE, CIRCLE, BOX, FLUSH, COLORHSV, SAVEIMAGE, SLEEP, POINT
LINEは 3 形態をサポート:LINE (x1,y1)-(x2,y2)[,color]LINE -(x2,y2)[,color](省略/ショートハンド: 直前のペン座標→(x2,y2))LINE x1,y1,x2,y2[,color](保険のフラット形式)
Compilerは 省略フラグをargcの bit30 に埋め込み、VM はそれを見てGfxHost.SetPenを使った解釈を行います。
INPUT "prompt"; Xのように 文字列リテラルプロンプトを先出し可能。- VM は
CALLFN(INPUT, slot)を受けて コンソール読み取り→ 型に応じてnumRegsまたはstrRegsに代入します。
- ダブルバッファ:
backBuffer(描画/読取)とfrontBuffer(表示専用)を保持。 - FLUSH:
backBuffer.Clone()を UI スレッドに渡してPictureBox.Image差し替え。 - スレッド分離: UI は STA スレッド上で
Application.Run、ワーカー側はBlockingCollection<Action>経由で UI 操作要求を送達。 - 色:
COLOR r,g,bとCOLORHSV h,s,vをサポート(HSV→RGB は VM 側/Host 側いずれかで変換)。 - ペン座標:
PSET/LINE/CIRCLE/BOX実行後に 終点をペン位置に更新(ショートハンドLINE -(x2,y2)に利用)。 - POINT(x,y):
backBuffer.GetPixelを参照して 非黒(R|G|B ≠ 0)かを真偽で返す。
画像保存は
SAVEIMAGE "file.png"(PNG)。
Program.csは例外を受け取り、vm.LastLineとPcToLineで 行番号を添えて表示します(UNDEF'D STATEMENTの類は?表示)。- 構文/解決エラー(コンパイル時)は
"(at compile)"付きで REPL 表示。 - 実行時エラーは
"(program, line N)"の形式で表示されます。
代表的なエラー
SYNTAX ERROR at line L, col C: ...UNDEF'D STATEMENT (line X)(行番号解決不可)BAD JUMP TARGET(ジャンプ先異常)
既定では Ctrl+C ブレークは未実装です。必要なら VM の
Run()ループにConsole.KeyAvailableチェックを入れてBreakExceptionを投げる拡張が容易です。
GfxHost.Flushの頻度が高すぎると UI スレッド切替コストが増大します。描画ループでは フレーム単位でまとめて FLUSH するのが推奨です。POINTを大量に呼ぶ場合はGetPixelのロック/コピーが支配的になるため、走査はアプリ側でまとめるか ラスタに直接アクセスする API の導入を検討してください。
- ELSE 対応の IF:
CompileIf()の JZ/JMP を適切に並べる(本実装済)。 - WHILE/WEND:
cond→JZ end→body→JMP cond→endの IL 生成。 - INKEY$ / MOUSE:
GfxHostに入力状態を保持し、CALLFNから取得。 - テキスト描画 (
GTEXT):GfxHost.DrawStringかビットマップフォント描画で最小実装。 - SAVE/LOAD (プログラム):
Program.csに外部ファイル I/O コマンドを追加。
VM 側は 命令を増やすよりも
CALLFNを肥大化させる方が拡張容易です(新しいビルトインをFnIdに追加→VM のCALLFNスイッチで分岐)。
- 配列は 0 起点である点に注意(古典 BASIC では 0/1 起点が混在)。
LINE ..., PSET, ...の 旧式修飾は削除(現行は, colorのみ許容)。RND/PI/TIMERは 括弧省略の 0 引数を許可。その他は基本的に()が必要。
10 ' IF/ELSE の回帰テスト
20 A=100: IF A>=60 THEN X=1 ELSE X=2: PRINT "T1=";X ' 期待: T1=1
30 A=10: IF A>=60 THEN X=1 ELSE X=2: PRINT "T2=";X ' 期待: T2=2
40 A=0: IF A>=60 THEN X=1 ELSE X=2: PRINT "T3=";X ' 期待: T3=2
50 END10 ' FOR/NEXT と配列
20 DIM N(4)
30 FOR I=0 TO 4
40 N(I)=I*I
50 NEXT
60 FOR I=0 TO 4: PRINT N(I);: NEXT: PRINT
70 END10 ' グラフィック(円運動)
20 SCREEN 640,480: CLS
30 DO
40 H=(TIMER*60) MOD 360
50 COLORHSV H,1,1
60 X=320+150*COS(RAD(H)): Y=240+150*SIN(RAD(H))
70 PSET X,Y
80 FLUSH
90 LOOPOpはCode(OpCode列挙)、A/B/D/Sの 4 種のオペランドを持つ構造体。- 数値/文字列のプッシュは
Op.Num(v)/Op.Str(s)のファクトリ。 PcToLine[]により PC→行番号、LineToPcにより 行番号→PC の解決を行います(コンパイル後の再解決もあり)。
以上。VM の内部仕様に沿って拡張・最適化・デバッグを行う際のリファレンスとしてご利用ください。