SHARP ポケコン BASIC で容量を削ろう

# BASIC に課せられた様々な制限のお話

この記事は、ポケコンでプログラミングをするにあたって考えなくてはいけないこと、
そして可能な限り容量を節約するためのテクニックをご紹介していきます。

なお、ここに書かれている方法は基本的に大幅に可読性を犠牲にして容量を節約しますから、
**間違ってもモダンなプログラミングではやってはいけませんよ**。

なおこの記事では、メモリやファイルサイズのことを（さほど）気にせずに
リソースをふんだんに使えるプログラミング環境、またそのようなプログラムを書くことを指して
「富豪プログラミング」と呼ぶことにします。

## そもそもなぜ容量を削らなければならないか

ポケコンプログラミングはつねに容量との戦いです。
SHARP PC-G850VS は RAM の増設はできませんが、ポケコンとしては大きなメモリを持つ機種です。
それでも、ユーザーが使える RAM は 32KiB 弱しかありません。

この中で、以下のものをやりくりする必要があります。

+ 現在編集しているファイル
+ ストアされているプログラムすべて
+ データすべて
+ 自動変数
+ ワーク（BASIC ではあまり意識する必要はありませんが）
+ etc.

さらに、BASIC プログラム自身に以下の制限があります。

+ エディターが扱えるのは 65535 行まで
+ 1 行は 255 文字まで
+ 変数は 2 文字まで、1 文字変数は固定（後述）

こんな調子ですから、30KiB 全部使ったプログラムを書くことはできますが
もしそれをしてしまうと、PC に転送できなくなり、
**紙に書き写すかデータレコーダに録るかしか外部に転送する方法がない** という
悲惨な状態になります。

それでは、具体的な方法を見てみましょう。

### 行数を減らす

容量を削る上で最も簡単かつ効果的なのは、プログラム全体の行数を減らすことです。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/4724c7dc-e319-60a9-6cea-b64b8838f2a9.png)
> 永遠に HELLO と表示し続けるプログラム。32バイト。

同じ処理をマルチステートメントを使って2行に詰め込むと、29バイトになります。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/11c27dca-4c28-ba3d-79b7-56360ace4cbb.png)
> コロン `:` を使って、いくつでも命令をつなげて書ける。

減った 3B の内訳は、CR `0D` + 行番号 `001E` + 行の長さ（1B）- コロン `3A` です。
気になった人は機械語モニタで見てみてください。

### 変数を減らす・固定変数を活用する

PC-G850 の BASIC では変数名が 2文字までに制限されています。
2文字変数をひとつ減らすことで、数値変数なら 16B、文字列変数なら 23B 削れます。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/5dc7aee4-2bdf-89e4-0cc1-42f41a322890.png)
> 数値型の自動変数。代入 / 呼び出しと同時にメモリが確保される。
> `FRE` は、空きメモリをバイト数で返す命令。なぜか関数とは呼ばない。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/b75fa9d3-7906-2a13-1e9b-918c5f8be005.png)
> 文字型の変数は末尾に `$` をつける。
> 文字型の単純自動変数は 14 文字まで格納でき、使用容量は固定。これより長い文字列を扱うには、配列を使わなくてはいけない。

変数を減らすとプログラムの大幅な書き換えが必要になりますが、簡単なアイデアとしては

+ 値を保持する必要のないカウンター変数を使い回す
+ サブルーチン外で消しても良い変数を別の目的で使う
+ 2文字変数を 1文字変数で書き換える

などが考えられます。このうち 3番目については、仕様を踏まえた解説が必要でしょう。

ポケコンでは、1文字の変数はあらかじめ確保されています。
したがって、1文字変数を使ってもメモリは減りません（固定変数）。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/bbd79ba3-329d-7234-2bf7-6e345e4b6ce1.png)
> 固定変数を使ってもフリーエリアは減らない。

ただし、以下の2点に気をつけておく必要があります。

+ 文字列変数は 7 文字までしか保持できない
+ 同名の数値変数と文字列変数は同時に使えない

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/8eabefb9-1320-0a74-200f-90b11220db0a.png)
> `S$` で `S` を上書きしてしまうと、`S` にはアクセスできない。

14文字までの長い文字列を使うときは 1文字変数は使えません。

プログラム中では、変数は名前の長さと同じバイト数で保存されていますから、
できるだけたくさん使われる変数を固定変数に置き換えると効果が高いことになります。

### 行末のダブルクォートを省略する

文字列が行末にくるとき、ダブルクォーテーションは**省略できます**。
富豪プログラミングに慣れているとエラーが出るのが怖くて書いてしまう人がいますが、
省略してもなんら実行には問題ありません。

JS の行末のセミコロンみたいなもんです。**省略しましょう**。

プログラム中で文字列の出力を行ったり、変数に文字列を代入するときは、その操作を
できる限り行末でするようにしましょう。

もちろん、マルチステートメントで続けるときには省略はできません。

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/73029f59-d2c2-709b-6b3b-78d6f6f31738.png)
> `#` のアスキーコードを 16進数で表示せよ。

### 引数がひとつのときカッコを省略する

マイクロソフト系 BASIC でいう関数に相当する命令は、引数がひとつのものに限り
かっこを省略することができます。こちらも省略を推奨します。

実例は上を見てください。マニュアル通りであれば、`PRINT HEX$ ( ASC ("#"))` と書くところです。
スペースはここに打ち込んだように自動補完されるので、上に比べて 7B も無駄にしています。

この仕様が数式を `SQR X` のように書くことを可能にしているわけで、関数電卓として自然なものと思います。

### `SWITCH` `REPEAT` `IF - ENDIF` を使わない

これらの構造化 BASIC の命令は、学習用途にはよいものですが、容量を削るには別れを
告げなくてはいけません。というのも、構造化 BASIC 命令は **1行を独占する** からです。

ここまで読んでいただいた方なら 1命令が 1行を独占する意味がおわかりでしょう。
`THEN` の中身が長すぎて 255 文字を超えるような場合だけにしか使用価値はありませんし、
そもそもそんなプログラムを書くならルーチンを切り出して再設計するべきです。

そして、どのような構造化命令もインライン `IF` と `GOTO` で書き換えられます。

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116845/12813f81-d04d-c66c-31f8-bb90dcc97a66.png)
> 6行しかない画面で構造化 BASIC をデバッグするのは、結構たいへん。

### `THEN` `ELSE GOTO` を使わない

インライン `IF` 文の後には、直接命令を続けて書くことができます。
**`THEN` は書いてはいけません**。NOP のために 2B 消費します。

唯一の例外は、`IF 条件 THEN GOTO 行番号` の場合で、
これは `IF 条件 THEN 行番号` と `IF 条件 GOTO 行番号` のどちらで書いてもよいことになっています。
もちろん、ひとつのプログラムの中では統一されているべきです。

同様に、`ELSE GOTO 行番号` ではなく `ELSE 行番号` を使いましょう。2B 削れます。

ちなみに、条件下で変数操作をしたいときは、`IF 条件 LET 代入` とすると、
プログラム自体の容量は変わりませんが、1行 255 文字制限の助けになります。
この場合も、順番を入れ替えても構わない他のステートメントがあるなら、
変数の代入は後ろにしましょう。`LET` ぶんの 2B を削れます。

### アスキーコードの扱い

`INKEY$` などで入力を取り扱うとき、検出したいキャラクタコード範囲と頻度によって、
バイト数が短くなる扱い方は変わってきます。

#### アスキーコード2桁の場合

検出対象がアルファベット大文字等、10進数で 2桁になる場合は、`INKEY$` の結果を文字列変数に
保存しないほうがよい場合が多いです。

`IV=ASC INKEY$` としてキャラクタコードを取り出し、10進数の即値と判定します。

#### アスキーコード3桁の場合

アルファベット小文字やカタカナの場合は難しくなりますが、一般的に上記と同じアプローチをとると文字列変数の `$` を書かなくていいので短くなります。
ただし、判定箇所が少ない場合は直接文字列と比較するのがよいでしょう。

#### 定文字列に `CHR$` を使わない

`CHR$` 命令を使うことでキーボードから入力できない文字も表示できますが、
表示用の文字列などで動的に変更する必要がない場合は、極力使用を避けましょう。

ではどうするかというと、機械語モニタで該当する番地を探し、文字列を直接書き換えてしまいます。
このようにすると、2B + ASCIIコード だった容量を 1B に抑えられます。
他の文字列と結合していた場合は、`"` や `+` も減らせますので、さらに効果があがります。

### 16進数を使わない

`&H` は 2B です。見つけたら即座に排除して、10進数に変換して構いません。
もちろん計算機は計算をするための機械ですが、我々は富豪プログラマではないので、実行時に余分な計算をさせてはいけません。

### `END` を使わない

実行の順番を変えて、最後の行を最後に実行するようにしましょう。
そのほうが美しいです。

# さいごに

極力短いプログラムを書くこと。画面のなかに文字を詰め込むこと。
これは容量を節約するうえでも大切ですが、ポケコンの画面でデバッグするとき
スクロール量を減らすという意味でも効いてきます。

プログラムの可読性云々以前に、画面が狭いのがツラいんですよね……

あ、学校でやったら怒られるので、提出用のプログラムにこんなことをしてはいけませんよ。