IchigoJamで機械語プログラムを作っていますが、POKE文で記述しているとプログラムの容量不足に陥ります。
例えば、「ret」文の記述に「POKE#700,#70,#47」となり、2byteを記述するために「POKE#700」を引いても、8byte必要になります。
16進数ではなく、10進数にすれば少しは文字数節約になりますが、 それもたかが知れています。
(,#70,#47→,112,71 =7byte)
また、作った機械語プログラムをネットや雑誌への公開する時に、バイナリファイルで公開するのも悩ましいところです。
ネットではD/Lした人にバイナリ転送の知識が必要になるし、 雑誌では不可能に近いと思います。
そこで、エンコード方法の一つである"BASE64"を参考に、"BASE16"エンコードを考えました。
理屈は簡単で、 1byteを上位4bitと下位4bitに分けて、それぞれ「@~O」のキャラクタに割り当てます。
ret → #70,#47 → G@DG=4byte となります。
base64(6bit区切りでのエンコード)よりエンコード後の文字数が多くなりますが、プログラムを実装する際に小さくできるので、結果的にメモリの節約になると思います。
プログラムにすると下記のようになります。
エンコードは、まずPOKE文等でメモリ内に機械語プログラムを記述した後に、上記プログラムを実行すると「@~O」の文字にエンコードされた文字列が表示されますので、頭に行番号を付けてプログラムリスト化するなりしてください。
これを実装したサンプルプログラムが下記になります。
機械語部分は、ただの逆スクロールですが、宇宙船のみスクロールせず現位置にとどまります。
衝突判定はしていません。
テンキーの1と3で左右に移動です。(一応、他のキーも使えますが…)
この程度(52byte)の機械語プログラムですら、デコードのプログラムを実装しても、40byte以上の節約になりました。
上記はBASE16デコード部の式を展開し、さらに省メモリ化して書いています。
上記はコメント文に機械語プログラムを記述しています。
1行で済む機械語ルーチンなのでデコード部分が簡単になってますが、機械語プログラムが複数行に渡るような大作(笑)の場合は、デコード部分に工夫が必要になります。
例えば、「ret」文の記述に「POKE#700,#70,#47」となり、2byteを記述するために「POKE#700」を引いても、8byte必要になります。
16進数ではなく、10進数にすれば少しは文字数節約になりますが、 それもたかが知れています。
(,#70,#47→,112,71 =7byte)
また、作った機械語プログラムをネットや雑誌への公開する時に、バイナリファイルで公開するのも悩ましいところです。
ネットではD/Lした人にバイナリ転送の知識が必要になるし、 雑誌では不可能に近いと思います。
そこで、エンコード方法の一つである"BASE64"を参考に、"BASE16"エンコードを考えました。
理屈は簡単で、 1byteを上位4bitと下位4bitに分けて、それぞれ「@~O」のキャラクタに割り当てます。
ret → #70,#47 → G@DG=4byte となります。
base64(6bit区切りでのエンコード)よりエンコード後の文字数が多くなりますが、プログラムを実装する際に小さくできるので、結果的にメモリの節約になると思います。
プログラムにすると下記のようになります。
BASE16_encode |
---|
機械語プログラムが、#700以後にある場合 |
10 for i=#700to#740 20 a=peek(i):?chr$(64+(a>>4));chr$(64+(a&#f)); 30 next:end |
BASE16_decode |
---|
#C04以後にある文字列を、#700以後にエンコードする場合 |
10 for i=0to#10 20 poke#700+i,(peek(#c04+i*2)-64)<<4+peek(#c05+i*2)-64 30 next:end |
エンコードは、まずPOKE文等でメモリ内に機械語プログラムを記述した後に、上記プログラムを実行すると「@~O」の文字にエンコードされた文字列が表示されますので、頭に行番号を付けてプログラムリスト化するなりしてください。
これを実装したサンプルプログラムが下記になります。
機械語部分は、ただの逆スクロールですが、宇宙船のみスクロールせず現位置にとどまります。
衝突判定はしていません。
テンキーの1と3で左右に移動です。(一応、他のキーも使えますが…)
エンコード無し(16進) |
---|
10 poke #700,#0b,#22,#12,#02,#df,#32,#50,#18,#20,#30,#03,#78,#f0,#2b,#05,#d0,#20,#38,#03,#78,#f0,#2b,#01,#d0,#20,#30,#03,#70,#01,#3a,#10,#0a 20 poke #720,#08,#28,#f0,#d1,#52,#18,#01,#20,#00,#23,#13,#54,#01,#30,#21,#28,#fb,#d1,#70,#47 80 cls:x=15 90 lc x,15:?chr$(#f0) 100 k=inkey() 110 if k!=0 then lcx,15:?" ":x=x+((k-1)%3-1) 120 lc rnd(33),0:?"*" 130 a=usr(#700,0) 140 goto90 free()=646byte |
エンコード無し(10進) |
---|
10 POKE#700,11,34,18,2,223,50,80,24,32,48,3,120,240,43,5,208,32,56,3,120,240,43,1,208,32,48,3,112,1,58,16,10 20 POKE#720,8,40,240,209,82,24,1,32,0,35,19,84,1,48,33,40,251,209,112,71,214,16,56,0,16,56,124,254,124,56,16,0,60 80 cls:x=15 90 lc x,15:?chr$(#f0) 100 k=inkey() 110 if k!=0 then lcx,15:?" ":x=x+((k-1)%3-1) 120 lc rnd(33),0:?"*" 130 a=usr(#700,0) 140 goto90 free()=658byte |
BASE16 エンコード |
---|
1'@KBBAB@BMOCBE@AHB@C@@CGHO@BK@EM@B@CH@CGHO@BK@AM@B@C@@CG@@ACJA@@J@HBHO@MAEBAH@AB@@@BCACED@AC@BABHOKMAG@DG 10 fori=0to#33:poke#700+i,peek(#c05+i*2)+peek(#c04+i*2)*16-1088:next 80 cls:x=15 90 lcx,15:?chr$(#f0) 100 k=inkey() 110 if k!=0 then lcx,15:?" ":x=x+((k-1)%3-1) 120 lc rnd(33),0:?"*" 130 a=usr(#700,0) 140 goto90 |
機械語ルーチン |
---|
r2=#b r2=r2<<8 r2+=#DF @LBL1 r0=r2+r1 r0+=#20 r3=[r0+0] r3-#f0 if 0 goto @LBL2 r0-=#20 r3=[r0+0] r3-#f0 if 0 goto @LBL2 r0+=#20 [r0+0]=r3 @LBL2 r2-=1 r0=r2>>8 r0-8 if !0 goto @LBL1 r2=r2+r1 r0=1 r3=0 @LBL3 [r2+r0]=r3 r0+=1 r0-#21 if !0 goto @LBL3 ret |
この程度(52byte)の機械語プログラムですら、デコードのプログラムを実装しても、40byte以上の節約になりました。
上記はBASE16デコード部の式を展開し、さらに省メモリ化して書いています。
上記はコメント文に機械語プログラムを記述しています。
1行で済む機械語ルーチンなのでデコード部分が簡単になってますが、機械語プログラムが複数行に渡るような大作(笑)の場合は、デコード部分に工夫が必要になります。