異次元ワープロ2号機

他のブログに当てはまらない「その他」カテゴリ用ブログです。

DATA文safeな85進法変換関数

DSF-Base85(DATA statement safe-Base 85)とでも命名しましょうか。

現在はSTN-BASIC専用ですが、一部手直しで他のBASICにも移植できるはずです。
21:20差し替え…プチコン3号でも動くように、符号ありとなしの変換まわりを書き換えました。

特長

  • 制御文字が出現しない
  • スペース・ダブルクォーテーション・円記号(バックスラッシュ)が出現しない
  • UTF-8テキストとして保存した時、1文字=1バイト
  • という特徴を備えつつ、16進数文字列に比べて1.6倍の密度で保存できる

プログラムリスト

32bit整数型配列⇔DSF-Base85の変換を行う例です

DIM BX$
BX$="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()*+,-./:;<=>?[]^"

DEF B85A2S(A)
 DIM I,J,R$,CN
 R$=""
 FOR I=0 TO LEN(A)-1
  CN=TOUINT32(A[I])
  FOR J=0 TO 4
   INC R$,BX$[UMOD(CN,85)]:CN=FLOOR(CN/85)
  NEXT
 NEXT
 RETURN R$
END

DEF B85S2A(R$)
 DIM I,J,W,WT
 DIM AR%[0]
 FOR I=0 TO LEN(R$)-1 STEP 5
  W=0:WT=1
  FOR J=0 TO 4
   W=W+INSTR(BX$,R$[I+J])*WT:WT=WT*85
  NEXT
  PUSH AR%,TOINT32(W)
 NEXT
 RETURN AR%
END

DEF UMOD(N,M)
 RETURN N-FLOOR(N/M)*M
END

DEF TOINT32(N)
 RETURN N-(N>2147483647)*4294967296
END

DEF TOUINT32(N)
 RETURN N+(N<0)*4294967296
END

STN-BASIC:バージョン0.2系で予定している新機能ピックアップ

新機能「サブスクリーン」関するもの

SUBSCREEN

サブスクリーンを使用開始

DISPLAY

描画先をメインスクリーンかサブスクリーンか選択

新機能「TALK命令」に関するもの

TALK

読み上げ開始。声は男声・女声のいずれかを選択可

TALKCHK()

読み上げが実行中か確認

細やかな新機能

CHKCHR()

コンソール上に表示された1文字の文字コードを取得

GCOLOR

グラフィックの既定描画色を指定。これによりGの付く描画命令で色の数値を省略可能に

GDRAWBANK

グラフィックの描画先バンクを指定。単純表示用の"GRP"と、BGやスプライトとして呼び出せる"USER"のいずれかを選択可

GLINE

グラフィック上に直線を描く。線が滲むのは仕様

SPHIDE・SPSHOW

スプライトを隠したり、隠したものを再表示

SPHOME

スプライトの移動や拡大の原点を変更

REPEATSTR$()

文字列を繰り返した結果を得る

『STN-BASIC』を公開しました

令和8年8月8日午後8時から88時間限定*1で公開します。
初回アクセス時にややくどい表示が出ますので一応読んでください。命令一覧等へのリンクはページ内にあります。

https://waaimyroom2.at-ninja.jp/STNBASIC.html

21:00以降の追記

完全な動作にはパソコンを推奨します。
スマートフォンでも動作しますが、キー入力およびマウスボタン入力が取れないため*2実質的に使えません。ご注意ください。
また、ゲーム機内蔵ブラウザーでは、ほとんどの場合動作しません*3。

*1:常時公開するにはまだ不完全なので一度公開を中断して完成度を上げる予定

*2:USB端子等でキーボードとマウスを接続すれば可能

*3:JavaScriptの比較的新しい機能を使用しているため、アップデートが提供されていないブラウザーはほぼ全て不可

STN-BASIC:サンプルゲーム1

穴掘り法による迷路ゲームで、毎回固定の全8フロアです。
右下のマスがゴール。

※そこまでボリュームはないですが、セーブ機能はないため暇なときに遊んでください。

'--------------------------------------------
'@#NAME:SAMPLE GAME NO.1
'--------------------------------------------

'<<[!USE STDLIB]>>
'↑この行のみ、動作に影響しますので省かないでください。ほかのコメント行は省略OK
SETBTREPEAT 16

VAR B,I,R,X,Y
VAR BX,BY
VAR GX,GY
VAR OX,OY
VAR PX,PY
VAR SX,SY
VAR NOW_FLOOR
'********************************************
VAR KR

DEF KRANDOMIZE SEED
 KR=SEED
END

DEF KRND() 'クロサワ式乱数
 KR=KR XOR &H9630
 KR=KR-&H6553 AND &HFFFF
 KR=((KR<<2) OR (KR>>14)) AND &HFFFF
 RETURN KR
END

DEF SRND(M)
 RETURN KRND() MOD M
END
'********************************************
ACLS
SPSET 64,304:SPOFS 64,23*16,13*16
DIM MYSP,WALL,ANIMCNT=0,ANIMCHR=0
DIM MVX,MVY
DIM ANIMX=0,ANIMY=0
RESTORE @CHARACTER
READ MYSP,WALL

FOR NOW_FLOOR=1 TO 8
 SX=13:SY=8
 GOSUB @MAZE
 CLS
 ?NOW_FLOOR;"F"

 PX=16:PY=16
 SPSET 0,MYSP
 SPOFS 0,PX,PY,-1'実際はZ座標は無視

 'BGMは未実装なので略

 WHILE !GOALCHK()
  VSYNC 1
  B=BTRIG()

  IF ANIMCNT>0 THEN
   SPCHR 0,ANIMCHR
   INC ANIMX,MVX:INC ANIMY,MVY
   SPOFS 0,ANIMX,ANIMY
   DEC ANIMCNT
   IF ANIMCNT MOD 4==0 THEN INC ANIMCHR
   CONTINUE
  ENDIF
  IF B==0 || ANIMCHK() THEN CONTINUE
  OX=PX:PX=PX+(!(4AND B)-!(8AND B))*16
  OY=PY:PY=PY+(!(1AND B)-!(2AND B))*16
  IF BGGET(1,PX/16,PY/16) THEN PX=OX:PY=OY
  IF BGGET(1,OX/16,PY/16) THEN PX=OX:PY=OY
  IF BGGET(1,PX/16,OY/16) THEN PX=OX:PY=OY
  IF PX==OX && PY==OY THEN CONTINUE
  MVX=SGN(PX-OX):MVY=SGN(PY-OY)
  IF B AND 2THEN SPCHR 0,MYSP+4
  IF B AND 4THEN SPCHR 0,MYSP+8
  IF B AND 8THEN SPCHR 0,MYSP
  IF B AND 1THEN SPCHR 0,MYSP+12
  IF B AND 15 THEN
   ANIMCNT=15
   ANIMCHR=SPCHR(0)
   ANIMX=OX+MVX:ANIMY=OY+MVY'初回分移動量
   SPOFS 0,ANIMX,ANIMY
  ENDIF
 WEND
 'BGM(ジングル)は未実装なので略
NEXT
BGCLR 0:BGCLR 1:SPCLR 64:CLS
?"GAME CLEAR!"
?"THANK YOU FOR YOUR PLAYING!"
END

DEF ANIMCHK()
 RETURN ANIMCNT!=0
END

DEF GOALCHK()
 RETURN !ANIMCHK() && PX==23*16 && PY==13*16
END

DIM TPX,TPY
@MAZE
 KRANDOMIZE 17*NOW_FLOOR+16
 DIM T[SX*2,SY*2]
 DIM P[0]

 BX=SRND(8)*2+5
 BY=SRND(5)*2+5

 FILL T,1

 BGCLR 0:BGCLR 1

 WHILE 1
  IF GET(BX-2,BY)!=1 && GET(BX+2,BY)!=1 && GET(BX,BY-2)!=1 && GET(BX,BY+2)!=1 THEN
   IF MAX(P)<0 THEN BREAK
   REPEAT
   '乱数生成で次アドレス
    I=SRND(LEN(P)/2 OR 0)
   UNTIL P[I*2]>=0
   BX=P[I*2]
   BY=P[I*2+1]
   P[I*2]=-1
   P[I*2+1]=-1
   CONTINUE
  ENDIF

  R=SRND(4)'ほうこうせってい
  IF R==0 THEN GX=-1:GY=0
  IF R==1 THEN GX=1:GY=0
  IF R==2 THEN GX=0:GY=-1
  IF R==3 THEN GX=0:GY=1

  IF BX+GX<1||BY+GY<1 THEN CONTINUE
  IF BX+GX>SX*2-3||BY+GY>SY*2-3 THEN CONTINUE

  '注:二次元配列の添字は式を認識しないので変数に代入する
  'また、二次元の添字は 宣言 if 代入のどれか以外では認識しない
  TPX=BX+GX*2:TPY=BY+GY*2
  IF T[TPX,TPY]==1 THEN
   T[BX,BY]=0'移動前のマス
   INC BX,GX:INC BY,GY:T[BX,BY]=0'途中のマス
   INC BX,GX:INC BY,GY:T[BX,BY]=0'移動完了
   PUSH P,BX
   PUSH P,BY
  ENDIF

 WEND

 FOR Y=0 TO SY*2-2
  FOR X=0 TO SX*2-2
   B=T[X,Y]
   BGPUT 0,X,Y,103
   BGPUT 1,X,Y,B*WALL
  NEXT
 NEXT

RETURN

@CHARACTER
DATA 756 'MY SPRITE
DATA 107 'WALL BG : 163に変えると木になります

DEF GET(X,Y)
 IF X>=0 && X<SX*2-1 && Y>=0 && Y<SY*2-1 THEN
   RETURN T[X,Y]
  ELSE
   RETURN 0
  ENDIF
END

※THENやANDの前後で詰めているのは、そう書いても動くことを確かめる意図的なものです。
特定のキーワードのみピンポイントで省略されたスペースを補うので、基本的にアルファベットの演算子の前後は空けてください。

STN-BASICの作例

こんなものが動く予定、というサンプルを載せていきます

※素材バンク変更のため、一部修正しました(8/7)
※マウス入力のサンプルを追加(8/7)
※名称が決まったため記事タイトル変更(8/7)
※スプライトのテンプレート番号が標準ライブラリと被るので変更(8/8)

スプライト歩行テスト

操作に関係なくスプライトが下→右→下→右…と歩き、画面下端まで行くと終了します。

ACLS
DIM Q=4,PY=0
GOSUB @SUB

Q=0:PY=32
GOSUB @SUB

SPSET 2,&H100+4
DIM I,W,X=128,Y=0
WHILE Y<240
	?Y
	FOR I=0 TO 3
		SPCHR 2,&H100+4+I
		FOR W=1 TO 16
			IF W MOD 4==0 THEN INC Y:SPOFS 2,X,Y
			VSYNC 1
		NEXT
	NEXT
	FOR I=0 TO 3
		SPCHR 2,&H100+I
		FOR W=1 TO 16
			IF W MOD 4==0 THEN INC X:SPOFS 2,X,Y
			VSYNC 1
		NEXT
	NEXT
WEND
END

@SUB
	SPDEF &H100+Q,"SPU0",48*2+16,PY,16,16
	SPDEF 1+&H100+Q,"SPU0",48*2+16*0,PY,16,16
	SPDEF 2+&H100+Q,"SPU0",48*2+16,PY,16,16
	SPDEF 3+&H100+Q,"SPU0",48*2+16*2,PY,16,16
RETURN

動作例:

マウス入力のサンプル(超簡易ペイント)

Tabキーで終了。色は白とピンクとマゼンタで、マウスの押すボタンで決まります。

ACLS
REPEAT
 VSYNC
 CLS
 ?TCHX
 ?TCHY
 ?TCHST
 IF TCHST>0 THEN GPSET TCHX,TCHY,RGB(255,255-255*(TCHST-1)/2,255) 
UNTIL BUTTON(0)==32
スクリーンショットに使用した著作物に関する表示

スプライトは著作権者「Lanea Zimmerman」による素材「Tiny 16: Basic」(ライセンスCC-BY 3.0ほか)のcharacters.pngを使用
入手元:opengameart.org

フォントは「美咲フォント」を使用。ライセンスは下記ページ内を参照
入手元:littlelimit.net

独自圧縮フォーマット(ADE:ArrayDictionaryEncode)に関するメモ

説明は飛ばして、バリエーションを一通りまとめるだけ。
具体的な仕様はプログラムが全て(いつものやつ)

用語

分割数:配列1要素(32bit)をいくつに区切って使うかの数
塊:配列1要素を指定した分割数で区切った値1個のこと。4分割ならちょうど1byteになる
追加圧縮:ADEの別名。もとはSRD画像フォーマットの一部なのでそう呼んでいたが、後にADE単独で使うようにもなった
バージョン値:『SRD画像圧縮ライブラリ』のwiki記事を参照。&h23→&h20という変な順番なのは、ASCII文字として解釈した時それぞれ『#』、スペースになる値を付けたから

初期版ADE(バージョン値:&h23)

参照が成立しない値をそれぞれ1塊として記録していたので、ランダムに近いデータはとても肥大する。

NEW_ADE(バージョン値:&h20)

初期版から「参照が成立しない値」が連続した数を記録するように変えたため、ランダムに近いデータがほとんど肥大しなくなった。
『SRD画像圧縮ライブラリ』の2022/06/01の更新記録にある「PackBits的なロジックを加える」というのはこの変更のこと。
細かいことをいうと2バージョン存在する(参考:2022/06/10の更新記録)

ADC

手元には存在するが非公開の模様。
NEW_ADEとの違いは参照不成立連続数の表現が違う(参照成立時の参照位置が常に1塊の代わりに、0,連続数の2塊で記録)ことらしい。
おそらくNEW_ADEと比べて圧縮率の差が少ない以前に、デコーダのバグを発見できず修正ができなかったので没になった。

XADE

NEW_ADEデコーダをJavaScriptへ移植するにあたり、計算過程で出る値が必ず非負になるようにビット演算を乗除算に変更したもの。
ついでに、2の冪でない分割数*1を指定できるよう手を加えて実験したものの、圧縮率がほとんど優れていないのでこの変更点は没になった。
分割数が2の冪であるときの圧縮データはNEW_ADEと全く同じ。


ところで、初期の頃は月に複数回規格が変わるような有様なので、おちおち使えない感じでしたね。
今はめったに改定しないので安心して使えますね(?)

更新(大きな変更のみ)

  • 4/10…ADCとXADEの制作経緯がほとんど嘘なことが分かったので修正。

*1:この場合、プログラムの簡略化のため端数のbitは使わない(たとえば3分割だと10bit×3ごとに、あまりの2bitは未使用)