
AIのMSXプログラミング能力が約3年半で爆上がり! マシン語化までできました
毎度、rorosukeです。
AIが一般的に浸透し始めた年である2023年5月に、ChatGPTを使ってMSX BASICプログラムを作ってみたという記事を投稿しました。
それがこちらです。
このときは、すごく短くて簡単なプログラムを作ってみています。
結果、MSX BASICプログラムについては、何度かトライしたり、見本となるプログラムを用意して、なんとか動くものができたというレベルでした。
それから約3年経った今、思い付きで、
再度MSX BASICプログラムを作成してみました。
今回使用したのは、Claude Sonnet 5.5(中)です。
多少の期待をしないではありませんでしたが、40年も前の8ビットパソコンですから、やっぱりこんなものかという結果になったとしても想定の範囲。期待と不安が半分ぐらいの感じです。
ゲームを作ってみました
プロンプトはこれだけです。
👦
msx1で動作する、シューティングゲームを作成してください。
出力されたコードがこちらです。
10 REM === SPACE SHOOTER for MSX1 ===
20 SCREEN 1,2:COLOR 15,1,1:WIDTH 32:KEY OFF
30 DEFINT A-Z:X=RND(-TIME)
40 DIM EX(2),EY(2),ED(2)
50 HI=0
60 GOSUB 1000
70 GOSUB 1100:GOSUB 1200
80 REM ---- MAIN LOOP ----
90 TIME=0
100 S=STICK(0):IF S=0 THEN S=STICK(1)
110 IF S=2 OR S=3 OR S=4 THEN PX=PX+5
120 IF S=6 OR S=7 OR S=8 THEN PX=PX-5
130 IF PX<0 THEN PX=0
140 IF PX>239 THEN PX=239
150 IF (STRIG(0) OR STRIG(1)) AND BF=0 THEN BF=1:BX=PX:BY=PY-8:GOSUB 1700
160 IF BF THEN BY=BY-8:IF BY<0 THEN BF=0
170 FOR I=0 TO 2
180 EY(I)=EY(I)+SP:EX(I)=EX(I)+ED(I)
190 IF EX(I)<0 THEN EX(I)=0:ED(I)=-ED(I)
200 IF EX(I)>239 THEN EX(I)=239:ED(I)=-ED(I)
210 IF EY(I)>=184 THEN GOSUB 1400:GOTO 270
220 IF EY(I)<0 THEN 270
230 IF BF THEN IF ABS(BX-EX(I))<12 AND ABS(BY-EY(I))<14 THEN GOSUB 1600:GOTO 270
240 IF MU=0 THEN IF ABS(PX-EX(I))<12 AND ABS(PY-EY(I))<12 THEN GOSUB 1650
270 NEXT I
280 IF OV THEN 3000
290 IF MU>0 THEN MU=MU-1
300 IF MU AND 2 THEN PUT SPRITE 0,(PX,200),15,0 ELSE PUT SPRITE 0,(PX,PY),15,0
310 IF BF THEN PUT SPRITE 1,(BX,BY),10,2 ELSE PUT SPRITE 1,(0,200),10,2
320 FOR I=0 TO 2:Y=EY(I):IF Y<0 THEN Y=200
330 PUT SPRITE I+2,(EX(I),Y),8+I,1:NEXT I
340 IF XC>0 THEN PUT SPRITE 5,(XX,XY),9,3:XC=XC-1 ELSE PUT SPRITE 5,(0,200),9,3
350 IF TIME<3 THEN 350
360 GOTO 90
1000 REM ---- SPRITE DEFINE ----
1010 FOR N=0 TO 3
1020 L$="":R$=""
1030 FOR K=0 TO 15:READ A,B:L$=L$+CHR$(A):R$=R$+CHR$(B):NEXT K
1040 SPRITE$(N)=L$+R$
1050 NEXT N
1060 RETURN
1100 REM ---- TITLE ----
1110 CLS:LOCATE 9,6:PRINT "SPACE SHOOTER"
1120 LOCATE 6,10:PRINT "CURSOR/STICK : MOVE"
1130 LOCATE 6,12:PRINT "SPACE/TRIGGER: FIRE"
1140 LOCATE 6,15:PRINT "HI-SCORE";HI
1150 LOCATE 5,19:PRINT "PRESS SPACE TO START"
1160 IF STRIG(0) OR STRIG(1) THEN 1160
1170 IF NOT (STRIG(0) OR STRIG(1)) THEN 1170
1180 RETURN
1200 REM ---- GAME INIT ----
1210 CLS:SC=0:LV=3:PX=120:PY=160:BF=0:XC=0:SP=2:MU=0:OV=0
1220 FOR I=0 TO 2:GOSUB 1400:NEXT I
1230 FOR J=1 TO 40
1240 LOCATE INT(RND(1)*32),INT(RND(1)*21)+2:PRINT ".";
1250 NEXT J
1260 GOSUB 1500:RETURN
1400 REM ---- SPAWN ENEMY(I) ----
1410 EX(I)=INT(RND(1)*224):EY(I)=-INT(RND(1)*90)-10:ED(I)=INT(RND(1)*5)-2
1420 RETURN
1500 REM ---- HUD ----
1510 LOCATE 0,0:PRINT "SC";SC;" HI";HI;" LIFE";LV;" "
1520 RETURN
1600 REM ---- ENEMY HIT ----
1610 BF=0:SC=SC+10:IF SC>HI THEN HI=SC
1620 XX=EX(I):XY=EY(I):XC=6
1630 SP=2+SC\300:IF SP>5 THEN SP=5
1640 GOSUB 1400:GOSUB 1500:GOSUB 1710:RETURN
1650 REM ---- PLAYER HIT ----
1660 LV=LV-1:XX=PX:XY=PY:XC=10:MU=40
1670 GOSUB 1400:GOSUB 1500:GOSUB 1710
1680 IF LV<=0 THEN OV=1
1690 RETURN
1700 REM ---- SOUND: SHOT ----
1702 SOUND 7,&B10111110:SOUND 0,40:SOUND 1,0
1704 SOUND 8,16:SOUND 11,0:SOUND 12,5:SOUND 13,9
1706 RETURN
1710 REM ---- SOUND: EXPLOSION ----
1712 SOUND 6,18:SOUND 7,&B10110111
1714 SOUND 8,16:SOUND 11,0:SOUND 12,20:SOUND 13,9
1716 RETURN
3000 REM ---- GAME OVER ----
3010 FOR I=0 TO 5:PUT SPRITE I,(0,200):NEXT I
3020 LOCATE 11,10:PRINT "GAME OVER":LOCATE 9,12:PRINT "SCORE";SC
3030 TIME=0
3040 IF TIME<150 THEN 3040
3050 GOTO 70
5000 REM ---- SPRITE 0 : PLAYER ----
5010 DATA &H01,&H80,&H01,&H80,&H03,&HC0,&H03,&HC0
5020 DATA &H07,&HE0,&H07,&HE0,&H0F,&HF0,&H1F,&HF8
5030 DATA &H3F,&HFC,&H7F,&HFE,&HFF,&HFF,&HFF,&HFF
5040 DATA &HEF,&HF7,&HC7,&HE3,&H83,&HC1,&H01,&H80
5100 REM ---- SPRITE 1 : ENEMY ----
5110 DATA &H00,&H00,&H03,&HC0,&H0F,&HF0,&H1F,&HF8
5120 DATA &H3F,&HFC,&H7F,&HFE,&HFF,&HFF,&HDB,&HDB
5130 DATA &HFF,&HFF,&H7F,&HFE,&H3C,&H3C,&H18,&H18
5140 DATA &H30,&H0C,&H60,&H06,&H00,&H00,&H00,&H00
5200 REM ---- SPRITE 2 : BULLET ----
5210 DATA &H01,&H80,&H01,&H80,&H01,&H80,&H01,&H80
5220 DATA &H01,&H80,&H01,&H80,&H00,&H00,&H00,&H00
5230 DATA &H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00
5240 DATA &H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00
5300 REM ---- SPRITE 3 : EXPLOSION ----
5310 DATA &H01,&H80,&H01,&H80,&H21,&H84,&H11,&H88
5320 DATA &H09,&H90,&H05,&HA0,&H03,&HC0,&H7F,&HFE
5330 DATA &H7F,&HFE,&H03,&HC0,&H05,&HA0,&H09,&H90
5340 DATA &H11,&H88,&H21,&H84,&H01,&H80,&H01,&H80前回試したときには、MSX BASICとは思えないコードが出てきたりしていましたが、
「あれれ?」
今回は一発で、いかにもなコードが現れました。果たして、ちゃんと動作するのでしょうか?
これはもしや……!
早速オンラインエミュレータの「MSXPen」で実行してみました。

MSXPenの覚えておくと便利なキー操作
便利なキー操作をメモっておきます。
F9:ポーズ。プログラムやリスト出力の一時停止
F9+Ctrl:ストップ。プログラムやリスト出力の中断
Alt+V の後に Ctrl+V:MSX BASICへのテキストのペースト
実行してみました
実行した結果がこちらです。



キャラクターもデザインされていますし、背景に星も描かれています。上部から敵が現れますし、自機を左右に移動してビームも発射できます。
ビームと敵の当たり判定と、敵と自機の当たり判定もできていますし、スコア表示と残機の表示もあります。
完璧じゃん!
turbo Rモードに切り替える
あまりにしっかりしたコードに大満足ですが、しょせんMSX1ですから、動作速度はかなりゆっくりです。本エミュレータにはturbo Rモードがあるので、それに切り替えると数倍の速度でプレイできます。
切り替えるには、右下の歯車マークをクリックして「Select Machine」を選択します。

ここで「MSX turbo R Japan(NTSC)」を選択してから、再度コードを実行します。

実行すると、数倍の速度で動作します。

無茶な挑戦?「マシン語」化!
turbo Rモードもいいのですが、「もしや」と思ってマシン語化に挑戦してみました。マシン語化といっても、やりかたはいろいろです。
大きくわけて、一部だけマシン語化する方法と、フルマシン語化する方法があります。
これについてClaudeに、次のように「一部をマシン語化する方法」がお勧めされたので、そで行くことにしました。
BASIC+マシン語の併用版(おすすめ): タイトル、初期化、スプライト定義はBASICのまま残し、毎フレームの当たり判定や移動などの重い部分だけをマシン語にして USR で呼びます。BASICの読みやすさを残しつつ、十分速くなります。
Claudeがコードの作成に要した時間は、MSX BASICのみのときは1分もかかりませんでしたが、BASIC+マシン語の併用版では完成までに5分以上かかりました。
理由を聞いてみたところ次のような手間によるものだそうです。
アセンブラとエミュレータの導入
Z80用マシン語の作成
アセンブルと動作検証
DATA文とチェックサムの生成
アセンブラとエミュレータの導入って、そこから?
さっすがエージェントですねーっ!
こうして出力されたのが次のコードです。
10 REM === SPACE SHOOTER MSX1 (machine code edition) ===
20 CLEAR 300,&HCFFF
30 DEFINT A-Z
40 SCREEN 1,2:COLOR 15,1,1:WIDTH 32:KEY OFF
50 HI!=0
60 GOSUB 2000
70 GOSUB 1000
80 DEFUSR0=&HD000:DEFUSR1=&HD003
90 GOSUB 1100:GOSUB 1200
100 REM ---- MAIN LOOP ----
110 TIME=0
120 X=USR0(0)
130 EV=PEEK(&HD80A)
140 IF EV AND 1 THEN GOSUB 1700
150 IF EV AND 6 THEN GOSUB 1710:GOSUB 1500
160 IF PEEK(&HD80E) THEN 3000
170 IF TIME<2 THEN 170
180 GOTO 110
1000 REM ---- SPRITE DEFINE ----
1010 RESTORE 5000
1020 FOR N=0 TO 3
1030 L$="":R$=""
1040 FOR K=0 TO 15:READ A,B:L$=L$+CHR$(A):R$=R$+CHR$(B):NEXT K
1050 SPRITE$(N)=L$+R$
1060 NEXT N
1070 RETURN
1100 REM ---- TITLE ----
1110 CLS:LOCATE 9,6:PRINT "SPACE SHOOTER"
1120 LOCATE 6,10:PRINT "CURSOR/STICK : MOVE"
1130 LOCATE 6,12:PRINT "SPACE/TRIGGER: FIRE"
1140 LOCATE 6,15:PRINT "HI-SCORE";HI!
1150 LOCATE 5,19:PRINT "PRESS SPACE TO START"
1160 IF STRIG(0) OR STRIG(1) THEN 1160
1170 IF NOT (STRIG(0) OR STRIG(1)) THEN 1170
1180 RETURN
1200 REM ---- GAME INIT ----
1210 CLS:X=USR1(0)
1220 FOR J=1 TO 40
1230 LOCATE INT(RND(1)*32),INT(RND(1)*21)+2:PRINT ".";
1240 NEXT J
1250 GOSUB 1500:RETURN
1500 REM ---- HUD ----
1510 SC!=(PEEK(&HD80B)+256!*PEEK(&HD80C))*10:LV=PEEK(&HD80D)
1520 IF SC!>HI! THEN HI!=SC!
1530 LOCATE 0,0:PRINT "SC";SC!;" HI";HI!;" LIFE";LV;" "
1540 RETURN
1700 REM ---- SOUND: SHOT ----
1702 SOUND 7,&B10111110:SOUND 0,40:SOUND 1,0
1704 SOUND 8,16:SOUND 11,0:SOUND 12,5:SOUND 13,9
1706 RETURN
1710 REM ---- SOUND: EXPLOSION ----
1712 SOUND 6,18:SOUND 7,&B10110111
1714 SOUND 8,16:SOUND 11,0:SOUND 12,20:SOUND 13,9
1716 RETURN
2000 REM ---- LOAD MACHINE CODE (D000H) ----
2010 RESTORE 6000:A=&HD000:LN=0
2020 READ N:IF N=0 THEN RETURN
2030 S=0:FOR J=1 TO N:READ D:POKE A,D:A=A+1:S=S+D:NEXT J
2040 READ C:LN=LN+1:IF C<>S THEN PRINT "DATA ERROR LINE";5990+LN*10:END
2050 GOTO 2020
3000 REM ---- GAME OVER ----
3010 FOR I=0 TO 6:PUT SPRITE I,(0,200):NEXT I
3020 LOCATE 11,10:PRINT "GAME OVER":LOCATE 9,12:PRINT "SCORE";SC!
3030 TIME=0
3040 IF TIME<150 THEN 3040
3050 GOTO 90
5000 REM ---- SPRITE : PLAYER ----
5010 DATA &H01,&H80,&H01,&H80,&H03,&HC0,&H03,&HC0
5020 DATA &H07,&HE0,&H07,&HE0,&H0F,&HF0,&H1F,&HF8
5030 DATA &H3F,&HFC,&H7F,&HFE,&HFF,&HFF,&HFF,&HFF
5040 DATA &HEF,&HF7,&HC7,&HE3,&H83,&HC1,&H01,&H80
5050 REM ---- SPRITE : ENEMY ----
5060 DATA &H00,&H00,&H03,&HC0,&H0F,&HF0,&H1F,&HF8
5070 DATA &H3F,&HFC,&H7F,&HFE,&HFF,&HFF,&HDB,&HDB
5080 DATA &HFF,&HFF,&H7F,&HFE,&H3C,&H3C,&H18,&H18
5090 DATA &H30,&H0C,&H60,&H06,&H00,&H00,&H00,&H00
5100 REM ---- SPRITE : BULLET ----
5110 DATA &H01,&H80,&H01,&H80,&H01,&H80,&H01,&H80
5120 DATA &H01,&H80,&H01,&H80,&H00,&H00,&H00,&H00
5130 DATA &H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00
5140 DATA &H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00
5150 REM ---- SPRITE : EXPLOSION ----
5160 DATA &H01,&H80,&H01,&H80,&H21,&H84,&H11,&H88
5170 DATA &H09,&H90,&H05,&HA0,&H03,&HC0,&H7F,&HFE
5180 DATA &H7F,&HFE,&H03,&HC0,&H05,&HA0,&H09,&H90
5190 DATA &H11,&H88,&H21,&H84,&H01,&H80,&H01,&H80
6000 DATA 16,195,72,208,195,6,208,62,120,50,0,216,62,160,50,1,216,1821
6010 DATA 16,175,50,2,216,50,6,216,50,7,216,50,10,216,50,11,216,1541
6020 DATA 16,50,12,216,50,14,216,62,2,50,5,216,62,3,50,13,216,1237
6030 DATA 16,237,95,50,15,216,33,16,216,6,4,197,229,205,232,209,225,2185
6040 DATA 16,17,4,0,25,193,16,243,201,175,50,10,216,175,205,213,0,1743
6050 DATA 16,183,32,5,62,1,205,213,0,79,214,2,254,3,48,15,58,1374
6060 DATA 16,0,216,198,4,56,4,254,240,56,19,62,239,24,15,121,214,1722
6070 DATA 16,6,254,3,48,11,58,0,216,214,4,48,1,175,50,0,216,1304
6080 DATA 16,175,205,216,0,50,64,216,62,1,205,216,0,33,64,216,182,1905
6090 DATA 16,40,33,58,2,216,183,32,27,62,1,50,2,216,58,0,216,1196
6100 DATA 16,50,3,216,58,1,216,214,8,50,4,216,58,10,216,246,1,1567
6110 DATA 16,50,10,216,58,2,216,183,40,16,58,4,216,214,8,48,6,1345
6120 DATA 16,175,50,2,216,24,3,50,4,216,33,16,216,6,4,197,229,1441
6130 DATA 16,205,233,208,225,17,4,0,25,193,16,243,58,6,216,183,40,1872
6140 DATA 16,4,61,50,6,216,205,13,210,201,35,35,35,126,183,40,2,1422
6150 DATA 16,53,201,43,70,43,43,126,128,254,240,56,16,203,120,14,239,1849
6160 DATA 16,40,2,14,0,175,144,35,35,119,43,43,121,119,35,58,5,988
6170 DATA 16,216,134,119,254,184,56,4,43,195,232,209,87,43,94,35,58,1963
6180 DATA 16,2,216,183,40,72,58,3,216,147,48,2,237,68,254,12,48,1606
6190 DATA 16,60,58,4,216,146,48,2,237,68,254,14,48,48,175,50,2,1430
6200 DATA 16,216,123,50,8,216,122,50,9,216,62,6,50,7,216,58,11,1420
6210 DATA 16,216,60,50,11,216,32,7,58,12,216,60,50,12,216,205,184,1605
6220 DATA 16,209,58,10,216,246,2,50,10,216,43,195,232,209,58,6,216,1976
6230 DATA 16,183,192,58,0,216,147,48,2,237,68,254,12,208,58,1,216,1900
6240 DATA 16,146,48,2,237,68,254,12,208,58,13,216,61,50,13,216,32,1634
6250 DATA 16,5,62,1,50,14,216,58,0,216,50,8,216,58,1,216,50,1221
6260 DATA 16,9,216,62,10,50,7,216,62,40,50,6,216,58,10,216,246,1474
6270 DATA 16,4,50,10,216,43,195,232,209,58,12,216,183,62,4,32,20,1546
6280 DATA 16,58,11,216,254,60,62,4,48,11,58,11,216,254,30,62,3,1358
6290 DATA 16,48,2,62,2,50,5,216,201,58,15,216,71,135,135,128,60,1404
6300 DATA 16,50,15,216,71,237,95,168,201,205,216,209,254,224,56,2,214,2433
6310 DATA 16,224,119,35,175,119,35,205,216,209,230,7,254,5,56,2,214,2105
6320 DATA 16,5,214,2,119,35,205,216,209,230,31,60,119,201,33,32,216,1927
6330 DATA 16,58,6,216,230,2,62,200,32,4,58,1,216,61,119,35,58,1358
6340 DATA 16,0,216,119,35,175,119,35,62,15,119,35,58,2,216,183,62,1451
6350 DATA 16,200,40,4,58,4,216,61,119,35,58,3,216,119,35,62,8,1238
6360 DATA 16,119,35,62,10,119,35,17,16,216,6,4,14,8,213,19,19,912
6370 DATA 16,19,26,183,27,27,32,4,26,61,24,2,62,200,119,35,27,874
6380 DATA 16,26,119,35,62,4,119,35,121,119,35,12,209,19,19,19,19,972
6390 DATA 16,16,219,58,7,216,183,62,200,40,4,58,9,216,61,119,35,1503
6400 DATA 16,58,8,216,119,35,62,12,119,35,62,9,119,58,7,216,183,1318
6410 DATA 16,40,4,61,50,7,216,33,32,216,17,0,27,1,28,0,205,937
6420 DATA 3,92,0,201,293
6430 DATA 0
マシン語が行番号6000以降のDATA文に記載されていて、行番号2000からの処理でメモリにロード。メインループ内で呼び出しているようです。
それなりに複雑なコードができていることに感動です。
そうは言っても、実際に動くかどうかは別問題。確認をするべく、同様にMSXPenにペーストして動かしてみました。
結果……。
ちゃんと動きました!

画面構成はBASIC版とほぼ同じですが、動きが速いです。先ほどのturbo Rぐらいの感じでしょうか。
ただ、そこはマシン語なので、こんなモノじゃないだろうと思っていたら、ゲームの速度は、CPUの速さではなく タイマー待ちでコントロールするようになっていました。
具体的には、毎フレームの最後に「IF TIME<2 THEN 170」という待ちがあります。TIME は画面の垂直帰線ごとに増えるカウンタで、NTSCなら毎秒60回となっています。現在は、1フレームは2カウント。処理が十分速く終わる場合、NTSC環境では約1/30秒ごとにゲームの状態を更新する仕組みになっていました。
なので、
170 IF TIME<1 THEN 170のように書き換えるだけで、2倍の速度になりました。まだまだ余裕がありそうなので、敵数やビーム数を増やしたりも十分できそうです。
※BASIC+マシン語版は、タイマーでゲームの進行速度を調整しているため、turbo Rモードに切り替えても、通常は速くなりません。速度を上げるには、本文で紹介したように、タイマーの待ち時間を短くしてください。
いやー!
それにしてもですよ…。
MSXでキャラクターを動かすには、Z80のマシン語を知っているだけでは足りません。画面表示を担当するVDPの仕組みや、表示用メモリであるVRAMの使い方も知っている必要があります。
今回のコードでは、スプライトの座標や色などのデータを作り、BIOSの機能を使ってVRAMに転送していました。
画面の初期設定やキャラクターの絵柄の登録はBASICに任せつつ、動きを更新する部分をマシン語で処理しています。
つまり、CPUの命令だけでなく、MSXというパソコン固有の仕組みまで踏まえて、プログラムを組み立てているということになります。
そこまでわかって作ってくれるのかーっ!
コードがマシン語に置き換えられたというだけでも驚きですが、ハードウェアの仕組みに合わせて、BASICとマシン語を組み合わせてくれたことに、さらなる感動を覚えずにはいられませんね。
今回作成されたコード
Claudeが作成したコードをファイルとしても公開しておきます。
MSX BASICコード
MSX BASIC+マシン語コード
アセンブラコード
まとめ:レガシーマシンもお任せあれ!
3年前には、
「MSX BASICプログラムが作れたよ。でも、まあこんなものかな」
という感じでしたが、まさか、マシン語プログラムまで一発で完成できるとは……これは出来すぎですし、本当に期待以上です。
そうなると、
あー、本格的にMSXプログラムを作ってみたい
という衝動にかられますね。
困ったなー!
やりたいことは他にもあるので!
以上!
続きの記事を出しました
楽しくなったので、続きの記事を書きました。
#生成AI #Claude #AIプログラミング #MSX #MSXBASIC #マシン語 #Z80 #レトロPC #レトロゲーム #ゲーム制作 #プログラミング #AI活用