HSP : Hot Soup Processor ver3.6 / onion software 1997-2020(c)

# プログラミング・マニュアル

1. クイックスタート
2. プログラミングガイド
   1. スクリプトの形式
   2. スクリプトの編集と実行
   3. HSPのしくみ
   4. HSPを機能拡張する（New!）
   5. HSP拡張プラグイン、拡張モジュール（New!）
   6. 拡張ランタイム（New!）
   7. HSPによるゲーム開発（New!）
   8. ワンキーヘルプ
   9. 実行ファイルの作成方法
   10. スクリーンセーバーの作成
   11. 起動オプションのパース
   12. 実行ファイルの書き換えについて
   13. HSPスクリプトサンプル
   14. メインウィンドウ非表示EXEファイル
   15. ディレクトリ移動の無効化
   16. デバッグウィンドウ
   17. HSP拡張マクロ
   18. スクリプトの色分けとタブ
   19. かんたん入力
   20. Peasエディタ
   21. UTF8対応版ランタイムの利用について
   22. 64bit(x64)対応版ランタイムの利用について
   23. スクリプトエディタの拡張機能について
3. スクリプト記述の基本
   1. HSP言語の規定
   2. 命令の書式
   3. パラメータ
   4. マルチステートメント
   5. コメント
   6. 式
   7. 文字列
   8. 変数
   9. 配列変数
   10. 関数
   11. ラベル
   12. 条件判断
   13. 繰り返し命令
   14. メモリノートパッド命令
   15. ソート命令
   16. システム変数
4. 拡張文法
   1. モジュール
   2. ユーザー定義命令
   3. ユーザー定義関数
   4. 関数と命令の違いについて
   5. モジュール定義命令
   6. プリプロセッサ命令
   7. #defineマクロについて
   8. 標準マクロ定義ファイル
   9. API呼び出し
   10. COMコンポーネント呼び出し
   11. HSPテンプレート(AHT)機能
5. HSPの基本動作概念
   1. HSPのタスク
   2. 画面とウィンドウ
   3. フォントと文字表示（New!）
   4. 画像ファイルの利用
   5. カレントポジション
   6. 配置オブジェクト
   7. レイヤーオブジェクト（New!）
   8. カレントカラー
   9. CEL関連命令について
   10. 入力の取得（New!）
   11. メモリバッファの使用
   12. マルチメディア再生
   13. 未初期化の変数検出
   14. コンソール版HSP
   15. 割り込み
   16. 標準モジュール名
   17. コモンディレクトリ
   18. エラーメッセージ
   19. HSPシステムの許容範囲
   20. PACKFILEと暗号化
   21. Cソースへの変換
   22. 起動設定#bootoptの利用について
   23. コールバックルーチン（New!）

## クイックスタート

このマニュアルは、HSPによるプログラミング方法と言語仕様全般を解説したものになっています。 初めてプログラミングに挑戦するという人は、最初に「初心者のためのHSP入門」を 読むことをお勧めします。

ある程度プログラミングの経験がありHSPが初めてという方は、 このクイックスタートをお読みいただいて実際に使ってみることをお勧めします。 使っている過程でわからないことが出てきたら、このマニュアルを検索したりヘルプブラウザを活用したりして 調べてみてください。

- HSPで実行されるプログラムリストをスクリプトと呼んでいます。 これを編集するエディタが、HSPスクリプトエディタ(hsed3.exe)です。これを起動してスクリプトを記述したら、 [F5]キーを押して実行させることができます。 付属のサンプルプログラムなどを実行して試してみてください。
- HSPでは、`pos a, 10`のように「命令」+「パラメータ(「,」で区切って複数指定することも できます)」という形式でスクリプトを書いていきます。命令は1行目から順番に実行されていきます。
- HSPスクリプトエディタでキーワード(命令)にカーソルを合わせて[F1]を押すと、 ヘルプブラウザによって手軽にリファレンスを参照することができます。
- 命令の基本はBASICに近いものになっています。`変数 = 数値`で変数代入をしたり、 `screen 0, x*64, y`のように数値、変数を混ぜた式をパラメータとして 指定することができます。
- 変数には数値と文字どちらでも代入が可能です。変数や型の宣言は必要ありません。 文字列は`a = "string"`のように"（半角ダブルクォーテーション）で囲んだ形で記述します。 文字列の演算は結合(「+」)のみ可能です。
- 変数の配列は、「変数(要素)」の形でカッコを使って指定します。配列を使用するためには、 あらかじめdim、sdim命令で宣言を行ないます。
- プログラムの流れを制御するための基礎的な仕組みとしてgoto、gosub命令が用意されています。 そこで指定するラベルは、行の先頭に「*ラベル名」の形で定義します。
- 繰り返しの記述は通常、repeat〜loop命令で行ないます。これは、C言語のfor、while、doを 簡略化したものと捉えることができます。 この他にも、while〜wend、do〜until、for〜next、switch〜caseなどC言語ライクなマクロ命令が用意されています。 詳しくは、「標準マクロ定義ファイル」を参照してください。
- 命令の定義や、関数を定義することもできるほか、名前空間を分けてソースを再利用するための仕組みが 用意されています。詳しくは、「モジュール」の節および モジュール機能ガイドを参照してください。
- 作成したスクリプトは[ctrl]+[F9]で実行ファイルに変換することができます。 詳しくは、「実行ファイルの作成方法」を参照してください。

## プログラミングガイド

### スクリプトの形式

HSPはプログラム（命令と実行の順序を記したもの）によって動作します。そのもとになるのがスクリプト（ソースファイル）です。これはテキストファイルの形式で、拡張子は .hsp または、.as になります。(拡張子 .hsp は、HSP3.0から追加されたものです。) スクリプトはHSPに付属しているHSPスクリプトエディタ(HSED3.EXE)や、テキストエディタなどのアプリケーションで作成することができます。

### スクリプトの編集と実行

HSPスクリプトエディタ(HSED3.EXE)を使用すると、簡単にHSPのスクリプトを作成、編集、 実行することが可能です。HSPスクリプトエディタを使用した場合は

1. テキストのスクリプトを書く
2. 「コンパイル+実行」のメニューを選ぶか、ファンクションキーのF5を押して、スクリプトを実行させる

という手順だけで、簡単にHSPのスクリプトを実行して動作を確かめることができます。

HSPスクリプトエディタ(HSED3.EXE)は、HSPのスクリプト編集用のエディタです。コンパイルや実行なども自動的に行なうことができます。 HSPスクリプトエディタを使用する場合には、HSED3.EXEと同じディレクトリに以下のファイルが存在している必要があります。

HSED3.EXE

  HSPスクリプトエディタ本体

HSED3_EN.EXE

  HSPスクリプトエディタ本体(英語版)

HSP3.EXE

  HSP実行ファイル本体

HSP3DEBUG.DLL

  HSPデバッグモジュールDLL

HSPRT

  HSPランタイムモジュール

HSPCMP.DLL

  HSPコードコンパイラDLL

HSPスクリプトエディタは、本体(HSED3.EXE)とHSP実行ファイル(HSP3.EXE) が必ず同じディレクトリに存在していなければなりません。 また、実行ファイル(EXEファイル)を作成するためには、ランタイム ファイル(HSPRT)も、同じディレクトリに必要です。

HSED3_EN.EXE を起動することで、メニューその他のインターフェースが 英語になります。HSP3本体の機能自体に変更はありませんが、英語版の環境で 利用したい方は、こちらをお試しください。

HSPスクリプトエディタを起動すると、新規のテキスト編集ウィンドウ が表示されます。あとは普通のテキストエディタと同じように、ファイルをロードしたり、 セーブしたりしながらHSPのスクリプトを記述していきます。 基本的な操作は、Windowsに付属している「メモ帳」とほぼ同じです。 文字列のカット&ペースト、検索などもメニューから選ぶことができます。

編集しているHSPスクリプトを実行させてみるには、メニューから 「コンパイル+実行」を選ぶか、またはファンクションキーのF5を押します。 「実行」またはCTRL+F5を押すと、最後にコンパイルされたスクリプトを実行します。

コンパイル中にエラーが発生した場合は、コンパイル後にエラーの内容と エラーの出た行番号を知らせるダイアログが表示されます。

`例： test.hsp(2) : error 7 : ラベル名はすでに使われています (2行目)`

上の例では、「ラベルが重複している」エラーが行番号2で発生したと いうことを示しています。 エラーが発生した場合は、「カーソル」メニューの 「指定行に移動」（またはCTRL+J）を選んで、 エラーが発生した行へ移動して その内容をチェックしてみてください。

一般的な使い方としては、HSPスクリプトエディタで編集、実行を繰り返しながらスクリプトを作成して、 完成したらメニューから「実行ファイル自動作成」([Ctrl]+[F9])を使って完成したスクリプトを EXEファイルやSCRファイル(スクリーンセーバー)に変換して１本のソフトができあがります。

HSPのスクリプトエディタでは、以下のファイルを実行時に作成します。 これらのファイルは、コンパイル時にのみ必要なファイルなのでスクリプトができあがった時には 削除してかまいません。

HSPTMP

  ソーススクリプトのテンポラリファイル

OBJ

  実行オブジェクトのテンポラリファイル

ソーススクリプトのアイコンを、HSED3.EXEのアイコン上かまたは、ウィンドウ内にドロップするだけで 編集をすることができるようになっています。また、インストーラーからHSPをインストールした場合は、 ソーススクリプトのアイコン(「.hsp」の拡張子を持つファイル)をダブルクリックするだけで、 ソースを開くことができます。

ソーススクリプトが編集状態になった後は、そのソースファイルが格納されているディレクトリが カレントディレクトリとなります。

### HSPのしくみ

HSPは、以下のような流れでスクリプトを実行しています。

コンパイル

  .hspの拡張子を持つソーススクリプト(テキストファイル)を解析し、 HSP用のオブジェクトファイル(AXファイル)を作成します。 （ここまでの作業はスクリプトエディタ(HSED3.EXE)とコードジェネレーター(HSPCMP.DLL)が行なっています）

実行

  オブジェクトファイル(AXファイル)を読み込み、それを実行します。 (この部分は、通常はHSP本体(HSP3.EXE)が行なっています)

  ソーススクリプトのコンパイルは、瞬時に行なわれオブジェクトファイルを作成します。 オブジェクトファイルは、ソーススクリプトの余計な部分、コメントなどを排除して コンパクトに、そして高速に実行できる形式になったバイナリデータを格納しています。

  HSP本体は指定された オブジェクトファイルを読み込み実行するための核となる部分です。 これはHSP3.EXEですべて行われますので、それ以外に余計なDLLやモジュールは必要としません。

  もし、あなたがHSPで作ったソフトを配布したいと思ったら、単体で実行できるEXE(実行) ファイルを作成することができます。EXEファイルを作成すると、 そのファイルをダブルクリックするだけでスクリプトが動作するようになります。 オンラインソフトや同人ソフトとして配布する際には便利な機能です。

  詳細は、「実行ファイルの作成方法」を参照してください。

  バッチファイルやコマンドプロンプトから直接HSP3.EXEをファイル名指定 付きで実行させることもできます。`hsp3 demo.ax` のように入力すると、"demo.ax"というオブジェクトファイルが実行されます。

  HSPでは、HSP3.EXE以外にもいくつか拡張された実行用ファイルが存在します。 これらは、「HSPランタイム」と呼ばれており、用途によりいくつかの種類が存在します。 詳しくは、「HSPランタイム」の項目を参照してください。

### HSPを機能拡張する

HSPでは、拡張プラグインや拡張モジュールという方法で、命令を増やしたり、機能を拡張することができます。これは、高度な機能を手軽に扱うことのできる便利なものですが、その仕組みを知っておくと、さらに活用の幅が広がります。
そのためには、Windows OSがファイルを実行する仕組みをまず覚えておきましょう。
.exeという拡張子を持つファイルをダブルクリックして起動すると、プログラムが動き出すということは皆さん知っていると思います。 Windowsにインストールされたプログラム、アクセサリなども最終的に.exeのファイルが起動されています。 それに対して、.dllという拡張子も存在します。これは、.exeファイルが起動した後に追加で読み込むことのできるプログラムのファイルです。単体では起動することができず、必ず別なプログラムから呼び出されるものです。

    			拡張子   内容
    			---------------------------------------------------
    			 .exe    実行するプログラムを格納するファイル
    			 .dll    後から読み込むことのできるプログラムを格納するファイル

Windowsには、カーネルと呼ばれるOSの中核部分があり、そのプログラムが.exeファイルを起動しています。 起動したプログラムが、ウインドウを出したり、文字を表示するためのプログラムを、.dllファイルの形で提供しています。これらは、Windows API(Win32API)、COMコンポーネント、あるいは独立したライブラリ(DirectXやOpenGLなど)として多数用意されています。
.dllファイルはWindowsに最初から入っているものもあれば、ユーザーが独自に作成することもできます。これが、WindowsのOS自体に組み込まれているプログラム拡張の仕組みになります。

### HSP拡張プラグイン・モジュール

HSPでは、拡張プラグイン及びモジュールという形で機能を追加していくことが可能です。 これにより、HSP本体だけでは実現できない処理が可能になります。
拡張プラグインは、DLL(.dll)形式でプログラムに機能を追加する形の拡張機能です。HSPでは、専用に用意された拡張DLLだけでなく、Windowsに標準で用意されているDLL(Windows API)、C/C++用に作成されたDLLを呼び出して使用することが可能になっています。
他にも、Windowsの機能を拡張するためのCOMコンポーネント(ActiveX)を任意に呼び出すことも可能です。 HSP拡張プラグインは、DLLを使用した拡張機能の中で、主にHSP向けに用意されたものを指します。

HSP拡張モジュールは、拡張プラグインと同様にHSPの機能を追加するものですが、 DLLを使用せず、HSPそのもので記述されている点が異なります。 いずれの場合も、HSP上で決められた記述をすることで、新しい命令・関数などが追加され、機能を使うことができるようになります。
HSPフルセットには、多くのプラグインやモジュールが同梱されています。 詳しくは、拡張プラグイン・モジュール一覧を参照してください。

### HSPランタイム

「HSPランタイム」は、HSPの動作システム全体を置き換えるための仕組みです。 これは、特定の用途、システムに依存した機能を利用するためのものです。
たとえば、スクリプトエディタに「mes "OK"」とだけ書いた場合には、標準ランタイムと呼ばれる、 最も一般的な命令セットでHSPが動作します。(これがHSP3.EXEにあたります) HSPランタイムは、プリプロセッサ命令「#runtime」によって、スクリプト中で指定します。 指定されたランタイムは、スクリプト実行時及び、実行ファイル作成時に反映されます。

HSPでは用途に合わせて「HSPランタイム」を変更して使用する場面が出てきます。 たとえば、HSP3Dishランタイムは、マルチプラットフォーム化が可能な命令のみをサポートし、 それ以外の命令でエラーを返します。これにより、スマートフォンやタブレット等で動かす場合と、 同等の動作を確認することができるようになります。
また、コンソール版HSP(HSPCL)ランタイムのようにウインドウ表示を行なわず、コマンドプロンプト上で 動作するようなシステムを選択することも可能です。

HSPランタイムは、HSPの機能を拡張したり変更するという意味で「HSP拡張プラグイン」に近いものですが、 「HSPランタイム」は、さらに深くHSP全体の機能を置き換えるものとして考えてください。 HSPフルセットには以下のHSPランタイムが同梱されています。 使用する際の参考にしてください。それぞれのランタイムごとの詳細は、別途マニュアルが用意されていますので、そちらを参照してください。

コンソール版HSPランタイム(hsp3cl)

  コンソールアプリケーション専用の実行ファイルを作成する際に使用します。 ウインドウの表示や画像の操作などはサポートされていません。

HSP3Dishランタイム(hsp3dish)

  スマートフォンやタブレットなど、マルチプラットフォーム化が可能な命令のみをサポートしています。 通常の描画命令の他に、拡張された命令が用意されています。また、描画速度も向上しています。

HGIMG4ランタイム(hsp3gp)

  HGIMG4ランタイムで作成したアプリケーションの実行ファイルを作成する際に使用します。 通常の描画命令は使用できず、HGIMG4で用意された専用の命令を使用します。 HGIMG4はHSP3Dishランタイムの上位互換で動作します。

コンパクト版HSPランタイム(hsp3c)

  標準のHSP3から、COM関連機能を削除し、ランタイムにかかるファイルサイズを30%以上削減しています。 コンパクト版HSPランタイムは、Windowsの互換性を重視して古いコンパイラで作成されており、Windows9xを始めとする幅広いWindows OSをサポートします。 過去のOSやスクリプトを使用する場合にご使用ください。 最新のOSを使用する場合は、コンパクト版は推奨されません。標準HSP3など他のランタイムの方が効率よく実行されます。

HSP3UTFランタイム(hsp3utf)

  通常のHSP3ランタイムと同等の機能を持っていますが、文字列をすべてUnicode(UTF8)形式で処理するランタイムです。 Unicode形式のテキストを扱えるほか、幅広い文字コードを表示させることが可能です。

64bit版HSP3ランタイム(hsp3_64)

  通常のHSP3ランタイムと同等の機能を持っていますが、64bitアプリケーションとして動作するランタイムです。 hsp3utfと同様に文字列をすべてUnicode(UTF8)形式で処理されます。 32bit版のプラグインとの互換性はありません。今後、64bit対応のプラグインも拡充される予定です。

### HSPによるゲーム開発

HSPは様々な用途に使うことができますが、最も使われているのは手軽なゲーム開発の場面です。 以下を参考にしながら、ゲームの内容や用途に合わせて、HSPの拡張機能を使うかどうかを決めることができます。

一般的な2Dゲーム

  最も一般的なHSP3標準ランタイム(拡張機能を使用しない)でも、一般的なゲームを作成することが可能です。 HSP3標準ランタイムは、基本的なWindows APIのみを利用するため、幅広い機種で互換性が高く、作りやすいメリットがあります。ただし、描画速度が遅く、画面を大量に書き換えるゲームなどには向いていません。
  sample/gameフォルダ内にサンプルとなるスクリプトを多数収録していますので参考にしてみてください。
  効果音や音楽の再生には、別途hspogg拡張プラグインを追加することで、細かい制御がサポートされます。 また、手軽な2D物理エンジンとしてOBAQ拡張プラグインを追加することもできます。

高度な2Dゲーム

  HSP3標準ランタイムに比べて高速な画面の書き換えが可能なHSP3Dishランタイムが用意されています。
  これらのランタイムは、Windows上だけでなくandroid,iOS,linux,html5など幅広い環境で動作させることが可能です。 描画にもDirectX、OpenGLといったGPU支援のあるAPIを使用しているため大量の画像を処理しても速度が落ちにくいメリットがあります。
  また、標準スプライト機能を使うことにより手軽に2Dの物体(スプライト)を管理することができ、大幅に工数が削減できます。
  HSP3Dishランタイムは、HSP3標準ランタイムと比べてサポートされていない機能(複数のウインドウサポートなど)があり、描画の手順にも若干の違いがあります。
  sample/hsp3dish、sample/spriteフォルダ内にサンプルとなるスクリプトを収録していますので参考にしてみてください。

3Dゲーム

  3D表示を使ったゲームは、HSP3標準ランタイムのみでは機能が足りない場合が多いです。 HSPでは、3D表示用ランタイムとしてHGIMG3,HGIMG4を用意しています。HGIMG3は、古くからサポートされてきたDirectX8を使用するもので、現在は新規の開発を終了しています。 現在は、これに替わるHGIMG4の機能を随時拡張しています。OpenGLを使用し、HSP3Dishと同様にandroid,iOS,linux,html5のプラットフォームをサポートしています。
  3Dの機能が必要な場合は、HGIMG4を使用頂けば、FBX形式の3Dデータ表示、アニメーションの再生、3D物理エンジン、ポストエフェクト、カスタムシェーダーの使用など多くのモダンな機能を利用可能です。 ただし、すべてのGPU、グラフィックカードをサポートしているわけではありません。Windows7以前の古い機種で動作させる場合は、起動できなかったり、表示がおかしくなるなどの現象が出る可能性があります。
  sample/hgimg4、sample/pronama3dフォルダ内にサンプルとなるスクリプトを収録していますので参考にしてみてください。

### ワンキーヘルプ

スクリプトエディタで編集している文字列から、HSP命令のヘルプをボタン１つで呼び出す機能が 「ワンキーヘルプ機能」です。

使用方法は、調べたいキーワードにキャレット(エディタ内のカーソル位置)を合わせて[F1]キーを押すだけです。 該当する命令の説明、パラメータの意味などが表示されます。 もしキーワードに該当するヘルプがなかった場合は、アルファベット順のキーワード検索ヘルプが表示されます。 サンプルスクリプトが何をしているのかを調べる時、命令の詳細が思い出せない時などに便利な機能です。

ワンキーヘルプは、いくつかの表示方法を選ぶことができます。 標準では、HSPヘルプマネージャによるヘルプ閲覧に設定されています。

標準で、ヘルプマネージャは、HSP Docs Library(統合ドキュメントビューアー)に キーワードを指定してヘルプを表示します。 これらは、HSPスクリプトエディタの「オプション」ダイアログにある「動作」メニューを選択して、 設定することが可能です。

ヘルプのデータは、通常はスクリプトエディタ(HSED3.EXE)の下にある、hsphelpという ディレクトリに格納されています。もし、他のディレクトリに移したい場合や、 ヘルプ表示をしようとした際にエラーが出る場合は、「オプション」ダイアログにある「ディレクトリ」 メニューを選択して、ヘルプデータのディレクトリを再設定してください (たとえば、「c:\hsp36\hsphelp」など)。 入力が空白の場合は、HSED3.EXEの下にあるHSPHELPというディレクトリを参照します。

「HSP Docs Library」およびヘルプファイルについての詳細は、 「HDL_Readme.txt」「HS_BIBLE.txt」を参照してください。

### 実行ファイルの作成方法

HSPでは、ユーザーが作成したスクリプトや、そこで使用されるデータファイルなどを、 ひとまとめにしてEXEファイルを作成することができるようになっています。 また、EXEファイルの一種であるSCRファイル(スクリーンセーバーモジュール)も同じ手順で作成できます。

スクリプトエディタから現在編集中のファイルをEXEファイルに変換する場合には、 メニューから「HSP」→「実行ファイル自動作成」を選択するか、 「CTRL」+「F9」を押すだけでカレントディレクトリに実行ファイルが作成されます。 通常は、「hsptmp.exe」という実行ファイルが生成されますが、ソーススクリプト内のオプション指定により、 ファイル名を始めとして、様々な設定を行なうことができます。

    #pack "ファイル名"            [PACKFILE追加ファイル指定]
    #epack "ファイル名"           [PACKFILE追加ファイル指定]

上の例では、packfileに追加されるファイルを指定しています。 指定されたファイルは、実行ファイル作成時にリソースとして一緒にパックされます。 #packは、通常の形式でパックします。#epackは、暗号化してパックされます。 重複したファイルを追加しようとした場合には、無視されます。 「start.ax」は、実行ファイル自動作成の際に自動的に追加されるため、特に追加ファイルとして指定する必要はありません。

#pack、#epackで指定されたファイルは、スクリプトからは使用できますが、 外部からは見えなくなります。これには次のような効果があります。

- ゲームで使用する画像ファイルなどを取り出せないように保護する
- 各ファイルの無駄なクラスタをなくしディスク容量を軽減する
- 大量のデータファイルがディレクトリにちらばるのを防ぐ

次の例では、「a.txt」「a.bmp」というファイルを 実行ファイルと一緒にパックし、"a.bmp"は暗号化します。

    #pack "a.txt"
    #epack "a.bmp"

    #packopt キーワード名 パラメーター  [自動作成オプション指定]

実行ファイル自動作成の動作を指定します。 キーワード名、の後スペース又はTABを入れてパラメーター (文字列の場合は「"strings"」のように指定)を記述して下さい。 #packoptで指定できるキーワードは以下の通りです。

#packoptで指定できるキーワード
キーワード | 内容 | 初期値
--- | --- | ---
name | 実行ファイル名 | "hsptmp"
runtime | 使用するランタイム | "hsprt"
type | 実行ファイルのタイプ | 0
| (0=EXEファイル) |
| (1=フルスクリーンEXE) |
| (2=スクリーンセーバー) |
xsize | 初期ウィンドウXサイズ | 640
ysize | 初期ウィンドウYサイズ | 480
hide | 初期ウィンドウ非表示SW | 0
orgpath | 起動時ディレクトリ移動無効化SW | 0
icon | アイコンファイル設定 | なし
version | バージョンリソース設定 | なし
manifest | マニフェスト設定 | なし
lang | 言語コード設定 | なし
upx | UPX圧縮設定 | なし

次の例では、「test.scr」というスクリーンセーバーを「hsp3c.hrt」というランタイムを使用して作成します。

    	#packopt type 2
    	#packopt name "test"
    	#packopt runtime "hsp3c.hrt"

尚、「start.ax」はデフォルトで暗号化されたものがpackfileに追加されます。 また、「#packopt runtime "ランタイムファイル名"」で指定された ランタイムファイル(拡張子がhrtのもの)は、hspcmp.dllと同じディレクトリか、 または、runtimeディレクトリに置かれているものが使用されます。

「初期ウィンドウ非表示SW」のオプションで1を指定した場合には、「初期ウィンドウ非表示SW」がONになります。 (詳しくは、「メインウィンドウ非表示EXEファイル」を参照してください。)

「起動時ディレクトリ移動無効化SW」のオプションで1を指定した場合には、「起動時ディレクトリ移動無効化SW」がONになります。 (詳しくは、「ディレクトリ移動の無効化について」を参照してください。)

icon,version,manifest,lang,upxのキーワードは、実行ファイル生成後にiconinsツールを使用して設定されます。 これにより、実行ファイルのアイコンやバージョン情報、マニフェストなどを埋め込むことが可能です。
詳しくは、「実行ファイルの書き換えについて」の項を参照してください。

### スクリーンセーバーの作成

HSPでは、Windowsのコントロールパネルから選択可能なスクリーンセーバーモジュールの作成が可能です。 これは、特殊な命令を使いフルスクリーン画面を作成し、そこに描画された内容がそのままセーバーとなると いうもので、通常のスクリプトを少し手直しするだけで作成できます。 スクリーンセーバーの詳細については、サンプルファイルのあるディレクトリ 「ssaver」にある、「arusave.hsp」ファイルを開いてみてください。 スクリプトの書き方と実際の作成手順、そしてサンプルスクリプト本体が 書かれていますので参考にしてみてください。

標準の機能では、パスワード保護のないシンプルなスクリーンセーバーが作成可能です。

スクリーンセーバーモジュール(SCRファイル)の作成は、色々な約束事があります。 サンプルのスクリプトなどを参考にしながら、覚えていってください。 スクリーンセーバーの設定画面(ID0)のウィンドウサイズは、通常のHSP実行時とは異なり、 スクリーンセーバー作成時に設定されたウィンドウサイズに固定されます。

### 起動オプションのパース

HSPでは、起動オプションを取り込みスクリプトでそれに応じた処理をすることができます。 これにより、色々な機能を持たせたEXEをバッチファイルから呼び出したり、EXEファイルのアイコン上に ファイルをドロップするような処理することが可能になります。

この機能をスクリプトエディタ上から試すために、HSPメニューに「起動オプション...」があります。 これを選択すると、スクリプトエディタ上から起動した時にも、擬似的に起動オプションをつける ことができます。 実際にスクリプトで起動オプションの内容を調べる場合には、 dir_cmdlineというシステム変数を使用します。詳しくは、システム変数一覧を参照してください。

### 実行ファイルの書き換えについて

通常、EXEファイルやSCRファイルを作成すると、Windowsから見たアイコンはHSP3.EXEと同じものになっています。 カスタムのアイコンファイルを用意することで、配布用の実行ファイルアイコンを自由に書き換えることができます。 他にも、バージョン情報やマニフェスト、言語コード、UPX圧縮などを実行ファイルに適用することができます。

「実行ファイル自動作成」を行なう際に、ソーススクリプトに#packopt命令により適用する情報を記述しておくことが可能です。
#packopt命令で指定する、icon,version,manifest,lang,upxのキーワードは、実行ファイル生成後にiconinsツールを使用して、実行ファイルの書き換えを行ないます。

    	// 埋め込むアイコンファイルを指定
    	#packopt icon "test.ico"
    	// 埋め込むバージョン情報を記述したファイルを指定
    	#packopt version "test.txt"
    	// UPXを使用し圧縮する場合"1"を設定する
    	#packopt upx "1"
    	// 言語を指定 デフォルトは日本語 (1041)10進数で記述
    	#packopt lang "1041"

上の例では、「test.ico」というアイコンファイルのアイコンを使用し、「test.txt」ファイルに記述されたバージョン情報を設定します。 アイコンファイルは、.ico形式のファイルを指定する必要があります。
また、言語コードを「1041」(日本語)に設定します。これは、日本語 (1041) や英語 (U.S.)(1033) など10進数で記述されたロケールIDを指定するものです。ロケールIDの詳細は、こちらを参照してください。
UPX圧縮に「1」を指定した場合は、UPXによる実行ファイル圧縮が有効になります。 (UPX圧縮を使用する場合は、upx.exe(Win32 console version)をあらかじめダウンロードしてiconinsツールと同じフォルダに配置する必要があります。)

バージョン情報の設定は、別途テキストファイルを作成しておく必要があります。

    	_FILEVERSION=1.0.0.0
    	_PRODUCTVERSION=1.0.0.0
    	Comments=テスト
    	CompanyName=Test!
    	FileDescription=テスト
    	FileVersion=1.00
    	InternalName=test
    	LegalCopyright=Copyright (C) 2017 Test!
    	OriginalFilename=test.exe
    	ProductName=テスト
    	ProductVersion=1.00

１行ごと必要な項目に「=」で内容を記述します。 行の先頭部分に｢;｣(セミコロン)を付けると、その行はスキップされます。 このファイルは、Kpan氏が作成したLet's HSPIC! のバージョン情報用ファイルと互換があります。

より広範囲にリソースを変更する場合は、Resource Hacker等のツールをご利用下さい。

### HSPスクリプトサンプル

HSP3デモのタイトルから「サンプルスクリプトを見る」を選択するか、 HSPがインストールされたディレクトリ(C:\hsp36\など)にある、 「sampview.exe」を実行することで、HSPスクリプトサンプルを閲覧、実行 することのできる、サンプルビューアーが起動します。

HSPには、スクリプトエディタからも実行可能なサンプルが多数収録されています。 使用する用途に合わせて、参考になるサンプルを探してみると良いでしょう。 HSPに同梱されているサンプルスクリプトは、すべて自由に改変、再利用しても構いません。

### メインウィンドウ非表示EXEファイル

EXEファイル作成時に、メインウィンドウを非表示にして起動させることが可能です。 #packopt命令により設定されるオプションで、`#packopt hide 1`と指定して 実行ファイルを作成([ctrl]+[F9])すると、その実行ファイルは起動時にメインウィンドウ(ウィンドウID0)が 表示されなくなります。

これは、起動時にウィンドウサイズを変更したり、表示位置を変更しても一瞬だけウィンドウがデフォルトの 位置に見えてしまうのを防ぎたいという人のためのオプションです。 このオプションをONにした場合は、`gsel 0,1`などの命令でウィンドウをアクティブ にしない限りメインウィンドウは表示されません。 メインウィンドウの位置やサイズをちらつきなく変更したい場合や、メインウィンドウ を表示する必要のないアプリケーションを作りたい時に活用してみてください。

また、メインウィンドウが非表示になっている場合でも、ウィンドウは存在しているので、 メインウィンドウに対して行なった描画やオブジェクト配置は正常に実行され保存されています。

### ディレクトリ移動の無効化

#packopt命令により設定されるオプションで、`#packopt orgpath 1`と指定して 実行ファイルを作成すると、その実行ファイルは起動時にカレントディレクリ (作業ディレクトリ)を自分自身のあるディレクトリに移動しなくなります。

このオプションは、通常ONにする必要はありません。 通常の実行ファイル起動時の動作は、起動されたEXEファイルのあるディレクトリに カレントディレクリ(作業ディレクトリ)が合わせられます。つまり、システム変数 dir_curとdir_exeは同一の場所になっています。 ショートカット起動で指定された作業フォルダを反映したいなどの特殊な事情で、 起動時のカレントディレクリを変更したくない場合にのみ、このスイッチをONにして下さい。

ディレクトリ移動を無効にした状態では、起動時のシステム変数dir_curとdir_exeが 同一でなくなることを前提にスクリプトを作成しておく必要がありますのでご注意下さい。

### デバッグウィンドウ

スクリプト実行中にHSPの状態や変数の内容をチェックするためのデバッグウィンドウが搭載されています。 これは、スクリプトエディタの「HSP」メニューの「Debugウィンドウ表示」のスイッチを入れることで、 実行時に常に表示させておくことが可能です。 また、このスイッチが入っていない場合でも、HSPでエラーが起こった場合には自動的にポップアップ表示されます。

デバッグウィンドウの左上のタブで表示カテゴリーを選択します。 選択された情報が、下のウィンドウに表示されます。 情報は、カテゴリーの選択をした時点でのものになります。内容は、 自動的には更新されないので、別な時点での情報を知りたい時には、再び カテゴリーのウィンドウをクリックしてください。

全般

  現在実行されている行、HSPの状態(実行モード)などが表示されます。 また、gosubやloopのネストレベル(深さ)を始めとするシステム変数も表示されます。

変数

  定義されている変数の内容を表示します。 「配列変数を表示」のチェックボックスを入れると、配列の内容を 一覧で表示します。また、「変数のダンプ」のチェックボックスを 入れることで、変数バッファの内容を16進数で表示します。 変数のダンプや配列の表示は、あまりにも大きなサイズが確保されて いる変数の場合は、すべてが表示されない場合があります。

  文字列型の変数に、バッファの容量を越えた文字列が代入されている 場合は、警告メッセージが表示されます。その場合は、sdim命令 などで確保するバッファのサイズを大きくしてください。

  「モジュール変数を表示」にチェックがついている場合は、 モジュール内部で使用されている変数の内容も表示します。 また、「表示項目をソート」にチェックがついている場合は、 変数名リストがアルファベット順にソートされ検索がしやすくなります。

ログ

  logmes命令によって送られたメッセージを表示します。 デバッグのためのメッセージなどを確認することができます。

[実行]ボタン

  assert命令や[停止]ボタンによって一時的に停止されたスクリプトの実行を再開します。

[次行]ボタン

  assert命令や[停止]ボタンによって一時的に停止された状態から、 1行だけスクリプトの実行を再開します。 これにより、1行単位で実行内容を確認するステップ動作を行なうことができます。

[停止]ボタン

  実行中のスクリプトを一時的に停止します。 awaitやwait命令などの時間待ち部分でのみ停止させることができます。

### HSP拡張マクロ

HSP拡張マクロは、標準的にサポートされているマクロの定義セットで、 commonフォルダ内の「hspdef.as」ファイルが設定を行なっています。 スクリプトのコンパイル時には、自動的に「hspdef.as」が読み込まれることになります。

様々な拡張マクロ、モジュールが用意されていますので、「#include」命令を使用して 明示的に利用することができます。 たとえば、「#include "hsp261cmp.as"」を記述すれば、HSP2.61互換として動作します。

### スクリプトの色分けとタブ

HSPスクリプトエディタ(HSED3.EXE)では、キーワードの色分けと、タブ切り替えによる複数ファイルの編集に 対応しています。 初期状態でキーワードは、以下の条件で色分けされます。

スクリプトエディタが色分けするキーワード 一覧
キーワード種類 | 色
--- | ---
コメント | 黄緑色
命令/関数/システム変数 | 水色
プリプロセッサ命令 | 水色
文字列 | 白色
ラベル定義 | 黄色
マクロ | 深緑色
その他 | 白色

キーワードや背景の色は、オプション設定のダイアログでユーザーが変更することができます。 また、複数ファイルを読み込み、タブで切り替えながら編集することが可能です。 タブを直接クリックするか、「ウィンドウ」メニューで編集しているファイルを切り替えることができます。

### かんたん入力

スクリプトエディタの右クリックメニューにかんたん入力項目が 用意されています。これは、利用したい機能の内容を選ぶだけで、パラメーターや オプションの選択を別ダイアログのユーザーインターフェースによって行なう ことのできる初心者向けのアシスト機能です。

かんたん入力は、スクリプトエディタと同時に起動するHSPアシスタント内からも呼び出すことが可能です。詳しくは、HSPアシスタントマニュアルを参照してください。

たとえば、「色を指定」という機能を選択した場合には、指定する色をR,G,Bまたは16進数により入力するための ボックスや、 色見本の中から指定することのできるダイアログが現われます。 ユーザーが指定したい色をダイアログに入力して「OK」ボタンを押すことで、 スクリプトエディタのカーソル位置にスクリプトが自動的に生成されます。 「かんたん入力」は、自分が利用したい機能のキーワードが不明な時や、 フォント名、カラーコードなど直感的でないパラメーターを入力する補助となります。

「かんたん入力」機能は、HSPテンプレートツール(AHT)をベースに 作られています。「かんたん入力」で表示されるダイアログや生成されるソースの 内容は、すべて「ezinput」フォルダ内にあるAHTファイル(拡張子が「.aht」ファイル) によって定義されています。このファイルをユーザーが作成することで、 「かんたん入力」で利用できる項目も自由にカスタマイズすることが可能です。 AHTファイルについての詳細は、doclibフォルダにある「aht.txt」を参照してください。

また、「かんたん入力」及び「AHTマネージャー」を構築するベースとなる モジュール「mod_aht.as」が同梱されています。 「mod_aht.as」を使用することにより、AHTファイルの内容を手軽に編集、参照 することができるようになります。 AHTについての詳細は、別途ドキュメントaht.txtを参照してください。

### Peasエディタ

スクリプトエディタに替わるもう1つのスクリプト作成環境Peasエディタが 同梱されています。これは、メニューから「Peasエディタ」を選択するか、または直接 「ahtman.exe」から起動させて使用します。

「Peasエディタ」は、メイン画面の「追加」ボタンからパーツを選択し、 「編集」ボタンによりパーツの内容を編集することができます。 これは、ahtフォルダに含まれているAHTテンプレートファイルを1つの アイコン(パーツ)と見なして、マウス操作と簡単なパラメーター調整だけで 視覚的にスクリプト作成を行なう支援をするものです。

「Peasエディタ」は、HSPテンプレートツール(AHT)の技術をベースに 作成されており、モジュール「mod_aht.as」とともにすべてHSPで作成 されています。

「Peasエディタ」は、ソースコードが公開されており、誰でも自由に機能の 改編、拡張、再配布を行なうことが可能です。

「Peasエディタ」の詳細は、別途ドキュメントpeas.htmを参照してください。

### UTF8対応版ランタイムの利用について

HSP3の標準ランタイムをunicode(UTF-8)文字ベースで動作させるための HSP3UTFランタイム(hsp3utf.exe)が同梱されています。
通常のHSP3ランタイムと同等の機能を持っていますが、文字列の扱いのみ unicode(UTF-8)となっています。 SJISの文字コードを扱う標準のHSPでは表現できない文字を扱うことのできる 新しいHSP3として今後も改良を続ける予定です。 使用する場合は、スクリプトの先頭に以下の行を指定してください。

    			#include "hsp3utf.as"

詳しくは、ドキュメント hsp3utf.txtをご覧ください。

### 64bit(x64)対応版ランタイムの利用について

64bit(x64)アプリとして動作するHSP3標準ランタイム(hsp3_64.exe)を同梱しています。 使用する場合は、スクリプトの先頭に以下の行を指定してください。

    			#include "hsp3_64.as"

64bit(x64)ランタイムは、64bit版のWindows上でのみ動作します。 同梱されているバージョンには、将来の64bit化に向けてのβテスト版とお考えください。 通常は、標準の32bit版HSP3ランタイム(hsp3.exe)をご使用頂いて問題ありません。
現状では、64bitランタイムに以下の注意点があります。

- 従来のHSPスクリプトを64bitアプリとして動作させるランタイムです 　(整数値の範囲など従来の仕様が変わるものではありません)
- 1つの変数が使用するメモリバッファは2GB(32bit)が上限となります 　(スクリプト全体が使用できるメモリサイズは64bitアプリに準拠します)
- 文字コードをUTF8として扱います(hsp3utfと同様)
- 従来の32bit版プラグインとは互換性がありません
- COM関連の命令が正しく動作しないことがあります
- #packopt命令によるアイコンの書き換えに対応していません

### スクリプトエディタの拡張機能について

HSP3.51から、改造版 HSP3スクリプトエディタ(inoviaさん、Tetr@podさんによる実装)の拡張機能を取り込んでいます。
以下の機能が追加されています。

- カスタムキーワード色分け機能
- ファイルの自動バックアップ機能
- プロポーショナルフォントなど従来設定できなかったフォントへの対応
- ウィンドウの分割機能
- 背景画像表示機能
- ラベルのダブルクリックによる移動(リンクラベル)
- ユーザー定義命令・関数の色分け機能
- 高速化された置換機能

拡張された機能は、「ツール」→「オプション」メニューの各項目でON/OFFが可能です。
リンクラベル機能を使用する場合は、オプションの「エディタ」→「色」項目で「リンクラベルを使用する」にチェックを入れてください。以降は、スクリプト中のラベルが色分けされ、ダブルクリックすることで定義されたラベルに移動できます。
背景画像表示機能により、指定した画像をエディタ背景右下に常に表示しておくことができます。オプションの「エディタ」→「色」項目で設定することができます。
ファイルの自動バックアップ機能は、オプションの「全般」→「動作」から設定することができます。一定時間ごとに編集中のファイルのバックアップを作成することで、強制終了やリセットなど不測の事態にスクリプトを失うことがなくなります。(バックアップは、HSPのインストールフォルダ内の「backup」フォルダに作成されます。)

## 基本文法

### HSP言語の規定

HSPでは、文法を大きく2つのカテゴリに分けています。

基本文法

  プログラム作成のために必要な最低限の文法。 初心者の方や、小さなプログラムを作成する場合には、基本文法のみを 使用しておいて問題ありません。

拡張文法

  HSPの機能を拡張するための仕組みを含む文法。 他のユーザーに向けて機能を提供する場合や、大規模なプログラムを 作成する場合に必要となります。 初心者の方は、拡張文法を特に覚えなくても支障ありません。 中上級者の方は、ステップアップのために拡張文法をマスターして いくことをお勧めします。

このドキュメントでは、基本文法、拡張文法ともに詳しい仕様を 解説しています。

### 命令の書式

スクリプトはどのような順番で、どのような処理をするかをまとめた テキストファイルです。その中の、どのような処理をするかを指示する ものを命令(ステートメント)と呼びます。

BASICやCと同じように、命令はファイルの先頭行から下に向かって順に 実行されていきます。

命令（ステートメント）は、プログラムの流れを制御したり、画面や ファイルなどの入出力を行ないます。 プログラム制御命令、入出力制御命令など多くの命令が存在します。 スクリプトはアルファベットの大文字・小文字を区別しません。 どちらで記述してもかまいません。

    ; 例:
    	randomize

上の例では、randomizeという命令を実行します。 命令には、多くの場合実行内容を細かく指定するためのパラメーターが付加 されることになります。

### パラメータ

HSPでは、命令の後にパラメータを付加する書式が基本になっています。 命令に付加するパラメータと、命令の間はスペースで空けておく必要があり ます。またパラメータが複数ある場合は、「,(カンマ)」で区切ります。

たとえば`width 640,480`というスクリプトは、 「width」が命令(ステートメント)にあたり、640と480という数値がパラメータになります。 この組み合わせで、「ウィンドウサイズを640x480にする」という意味になります。

ドキュメントファイルの「命令のリファレンス」の説明で、

    statement p1,p2

    p1=0〜3(1)
    p2=0〜65535(0)

とあった場合は、この命令にはp1,p2の2つのパラメータが指定できます。 p1のとれる値が0〜3まで、省略した場合の値が1であり、 p2のとれる値が0〜65535まで、省略した場合の値が0であることを示しています。

パラメータの省略をすることもできます。上の例では、p2の値を省略して`statement 3` と書くこともできます。その場合は、省略した時の値が自動的に設定されて、`statement 3,0` と書いたのと同じことになります。

パラメータの途中だけを省略する場合は、`statement ,100` のように省略したパラメータを飛び越えて別のパラメータを指定すること ができます。この場合は、`statement 1,100`と書いたのと同じことになります。 省略時の値は、命令ごとに異なります。

`statement`とだけ書いてパラメータを指定しなかった場合でも、 自動的に「1,0」という値が設定されます。基本的にパラメーターは重要な順番に並んでいるので、 すべてを省略することはあまりありません。また、文字列を指定するパラメータや、 変数名でなければならないパラメータなどは省略ができないことがあります。

### マルチステートメント

命令と命令の間を:（半角コロン）で区切って1行に複数の命令を記述することも 可能です。これをマルチステートメントと呼びます。たとえば、

    ; 例：
        mes "こんにちは" : mes "さようなら"

は、

        mes "こんにちは"
        mes "さようなら"

のように書くのと同じことです。 このように複数の命令を１行にまとめて書くことができます。 1行の長さに制限はありませんが、テキストエディタで見やすい程度に納めるようにした方がいいでしょう。

### コメント

1行の中で;（半角セミコロン）以降はコメント(注釈)とみなし無視されます。

    ; 例
        pos 320,100 : mes "(^_^)"        ; 顔マーク表示

CやJavaと同様のコメント記述が可能です。 1行の中で//（ダブルスラッシュ）以降はコメントとみなし無視されます。 「/*」から「*/」までの間は、コメントとみなし無視されます。

    ; 例
        pos 320,100 : mes "(^_^;)"        // 顔マーク表示
        /* ここはコメントです */ goto *start

スペース、タブは見やすくするために自由に入れることができます。 (標準の設定では全角スペースも、スペースとみなされます。)

### 式

命令のパラメータを指定する場所では、以下のようなデータや演算子を含む式を 書くことができます。

HSPで利用できるデータなど 一覧
データ | 種類
--- | ---
-2147483648〜2147483647 | 10進整数(32ビット)
-???.???〜???.??? | 10進実数(64ビット倍精度)
$0〜$FFFFFFFF | 16進整数
0x0〜0xffffffff
%0〜%111111... | 2進整数
0b0〜0b111111...
'A' | 文字コード（8ビット整数値）
"文字列" | 文字列
変数 | 値を保持するキーワード
関数 | 値を変換するキーワード

HSPで利用できる演算子 一覧
演算子 | 種類
--- | ---
+,-,*,/ | 加算，減算，乗算，除算
&,|,^ | 論理演算(and,or,xor)
\ | 割り算の余り
=,<,>,! | 条件式（同じ，小さい，大きい，同じでない）
==,<=,>=,!= | 条件式2（同じ，以下，以上，同じでない）
<<,>> | 左、右方向にビットシフト

たとえば、`statement 1+2+3+4`は、`statement 10` と書いたのと同じになります。これを数式といいます。

数式の評価は優先順位の高い順に行なわれます。 たとえば`2+7*2`は、16になります。

式の評価では、以下の演算子の中で優先順位が高いものを優先します。

演算子の優先順位
演算子 | 優先順位
--- | ---
* / | 4（高い）
+ - | 3
比較演算子 | 2
& | ^ | 1（低い）

計算の順番を変更したい場合はカッコを使って`(2+7)*2` のように書けば、カッコ内の2+7が先に計算されて結果は18になります。

条件式や論理演算は、後に説明する条件判断の際に使われます。

カッコは、いくつでも多重に使うことが可能です。もし、カッコの使い方が 間違っている場合は、エラー(priority error)が表示されます。 また、入力した値を加工するための関数を式の中で使うことができます。

整数値、実数値、文字列、関数を混在した式を書くことも可能です。 その場合は、計算する最初の項に合わせて、後の項が型変換されます。 たとえば、「8 + 4.5」のような計算は、最初の8(整数)に合わせて4.5は、4(整数)として扱われます。 逆に、「4.5 + 8」の場合は4.5(実数)に合わせられて12.5という計算結果になります。

数値が整数か実数かは、小数点があるかないかで区別されます。 実数型での演算を行なう場合には、明示的に「8.0 + 4.5」のように小数点を 入れるようにする必要があります。 また、「1.0e+10」のように「e」に続けて指数部を指定することで大きな桁を持つ実数を表現することも可能です。 ただし、実数値の扱いは整数値に比べてメモリ効率や速度のコストが高くなりますので注意してください。

'（半角シングルクォーテーション）で囲まれた文字は、文字コードを示す整数値として解釈されます。 長い文字列や全角文字を指定した場合でも、最初の1バイトのみがコードとして解釈されます。

`'A'`の場合は、「A」を示す文字コード(65)となります。 その際に、文字列と同様の「\」記号による特殊コード(「\n」等)を記述することができます。 「'」そのものの文字コードを示す場合は、`'\''`を指定します。 また、「\」の文字コードを示す場合は、`'\\'`を指定してください。

### 文字列

命令のパラメータに文字列を指定する場所では、文字列を"（半角ダブルクォーテーション）で囲むことで 文字列を記述することができます。

    ; 例：
        mes "This is test message..."

「+」を使って文字列同士、または変数との結合をすることができます。 たとえば、`"ABCD"+"EFGH"`は、`"ABCDEFGH"`と同じになります。 `"VALUE="+val`は、`"VALUE=5"`（変数valが5の場合）になります。

「\」は特殊な意味を持つキャラクタとして解釈されます。

- 「\n」 は、改行して次の行にするという意味になります。
- 「\t」 は、TABコードを表わします。
- 「\r」 は、リターンコード(0x0d)を表わします。
- 「\"」 は、ダブルクォート(")を表わします。
- 「\\」 は、ただの「\」になります。

ですから、ディレクトリを示すための文字列、たとえば`"C:\WINDOWS\SYSTEM"`などの文字列は、 `"C:\\WINDOWS\\SYSTEM"`と記述しないと認識されません。

また、1行に収まりきらない長い文字列をまとめて記述することも可能です。

        mes {"
        ここには、1行まるまる直接メッセージを
        書いてもいいです。
    "}

このように、「{"」から「"}」までの間はすべて文字列として解釈されます。 複数行に渡っている時は、1行の最後に改行コードが挿入されます。

HSPでは、文字列を扱うための命令や関数を多く用意しています。

文字列を扱う命令・関数 一覧
キーワード | 機能
--- | ---
getstr | バッファから文字列読み出し
strmid | 文字列の一部を取り出す
instr | 文字列の検索をする
getpath | パスの一部を取得
strf | 書式付き文字列に変換
cnvwtos | unicodeを通常文字列に変換
cnvstow | 通常文字列をunicodeに変換
strtrim | 指定した文字だけを取り除く
split | 文字列から分割された要素を代入
noteadd | 指定行の追加・変更
notedel | 行の削除
noteget | 指定行を読み込み
noteinfo | メモリノートパッド情報取得
notesel | メモリノートパッド設定
noteunsel | 対象バッファの復帰
notesave | 対象バッファ保存
noteload | 対象バッファ読み込み

この他にも、拡張プラグイン・モジュール等でも多くのサポートが行なわれます。

### 変数

任意の名前をつけた変数を扱うことができます。変数とは、代入により内容 を変化させることのできる容れ物のようなものです。

変数は、アルファベットまたは日本語で始まる59文字(半角)以下の文字列で 識別されます。変数は代入により数値や文字列などさまざまな情報を格納することができます。 また、1つの変数の中にインデックスをつけて複数の情報を格納するための、配列変数を利用することができます。

数値として記憶できる範囲は、数式で指定できる値と同じ-2147483648から2147483647までの32ビット整数値、 または実数値(64ビット倍精度)です。

変数にラベルと同じ名前や、命令と同じ名前は使用できません。変数は、代入命令や、数式の中で使うことができます。

代入命令は次のようなものです。

    ; 例
        x=100                ; 変数xに100という数値を代入
        y=200                ; 変数yに200という数値を代入

命令の数値指定として使うと、

    ; 例
        x=100:y=200:pos x,y        ; (100,200)に移動

のようになります。 変数に値を代入すると、以前まで記憶されていたものは消され、新しい 値が保持されることになります。 変数に文字列を代入する場合にも同じように、

    ; 例
        x="strings"      ; 変数xに「strings」という文字列を代入
        mes x            ; 変数xの内容を画面に表示

代入は"="の先にあるものが数値ならば数値を、文字列ならば文字列として 記憶します。変数には、記憶しているものが文字列だった場合の文字列型や、 数値だった場合の数値型、実数(小数)だった場合の実数型などいくつかの状態があります。

パラメーターが数値を必要としている命令に文字列型の変数を指定したり、 パラメーターが文字列を必要としている命令に数値型の変数を指定すると、 「Type mismatch」エラーが出てしまうので注意してください。

いくつかの特殊な代入命令があります。「変数」+「演算子」+「=」+「パラメーター」で もとの変数に対して指定された演算子とパラメーターの計算を行ないます。

    ; 例
        a=10        ; 変数aに10を代入
        a+=2        ; 変数aに2を加算(a=a+2と同じ)

上の例では、変数aは12という値になります。 このように、「+=」を使用すると「+=」以降が変数a自身に対して加算されます。 同じように「-=」や「*=」などすべての演算子を使用することができます。 (また、「=」を省略して「変数」+「演算子」+「パラメーター」の形式でも同様の動作になります。 ただし、この書式はC言語やJavaなどとの互換性がないため推奨はされません。)

単純な加算と減算は、以下の書式で行なうことも可能です。

    ; 例
        a+            ; 変数aに1を加算
        a-            ; 変数aから1を減算

「変数」に「+」または「-」を付加することで+1と-1を実行します。 (C言語やJavaと互換のある書式、a++やa--でも同じ結果になります。ただし、式の中で使用することはできません)

変数の型を特定するために、変数の型を強制的に変更することができます。そのための関数がintとstrです。

    ; 例
        a=int(a)    ; 変数aを数値型にする
        b=str(b)    ; 変数bを文字列型にする
        c=double(c) ; 変数cを実数型にする

変数の型を変更しても、できる限りその内容を保持しようとします。

"123"という文字列を含んだ変数を数値型に変更すると、123という数値になりますし、 逆に123という数値型の変数を文字列型に変更すると"123"という文字列に変換されます。

### 配列変数

配列変数を使うと、変数に記憶させるものに対して番号をつけて大量に管理することができるようになります。

通常は、変数aには1つの数値、または文字列しか記憶させることができません。 しかし配列変数を使えば、これに複数の数値、または文字列を記憶させることができます。 配列変数は以下のようにして使われます。

    ; 例
        a(0)=10        ; 変数aの要素0に10を代入
        a(1)=20        ; 変数aの要素1に20を代入
        a(2)=30        ; 変数aの要素2に30を代入

変数の後にカッコをつけ、その後に数値による番号を指定します。 この番号を「配列の要素」といい変数の中のどこに記憶されているかを特定します。 要素は通常0から始まる整数値を指定します。

配列変数の要素は、代入された時点で自動的に確保されます。 たとえば、`a(2)=5`のように書いた場合は、a(2)が自動的に確保され 5という値が代入されます。ただし、`a(1000)=0`と書いた場合には、 a(0)〜a(1000)までの要素すべてがメモリに確保されてしまうので、 要素の数値は0から順番に使用するように注意してください。

また、配列要素の確保が行なわれるのは代入時のみですので、 `a=b(10)`と書いた場合に、配列変数bに要素10が確保されていない場合は、 エラーになるので注意してください。 なお、配列要素の自動確保で次元を拡張することはできません。

配列要素をあらかじめ確保しておきたい場合には、dim命令を使用します。

    ; 例
        dim a,20    ; 変数aの要素は0〜19まで使えるようになります

また、dim命令を使って多次元配列を作成することも可能です。

    ; 例
        dim a,10,5  ; 変数aは2次元配列が使用できます
        a(0,0)=1    ; 要素(0,0)に1を代入
        a(1,0)=2    ; 要素(1,0)に2を代入
        a(0,1)=3    ; 要素(0,1)に3を代入

この場合は、「変数名(1つめの要素, 2つめの要素)」のように","(カンマ)を2つ使って要素を指定してください。 同様にdim命令を使って4次元までの配列を作成することができます。

変数の型が連想配列をサポートしている場合には、要素として整数値以外を指定することができます。

    ; 例
        a("test")=10        ; 変数aの要素"test"に10を代入
        a(5.5)=20           ; 変数aの要素5.5に20を代入

要素が変わっても同じ変数なので、要素ごとに変数の型を混在させることはできません。 変数aが文字列型ならば、すべての要素も文字列型となります。数値型も同様です。

    ; 例
        a(0)=0
        a(10)="string"        ; エラーになります

配列変数を示す別な書式もあります。

    ; 例
        a.5=123        ; 変数aの要素5に123を代入

この書式は、HSP ver2までの書式と互換性があります。「a.5」は、「a(5)」と同じ意味になります。 多次元配列の場合は、「a.1.0」のように"."(ピリオド)で区切ります。

この書式では、"."(ピリオド)の後に続く1項目だけを要素として認識します。 要素に式を使いたい場合は、`a.(a+5)=10`のようにカッコでくくる必要があります。

文字列型の変数に配列を使う場合にはsdim命令を使います。 sdim命令では、まず扱う文字列の初期文字数を指定してから、要素の数を 指定します。たとえば、

    ; 例
        sdim a,32,5  ; 変数aは32文字までの文字列を5つの要素で扱えます
        a(0)="test"
        a(1)="message"

のように変数名の次に2つのパラメータを指定します。 また、sdim命令により文字列の初期文字数を確保することもできます。

    ; 例
        sdim a,200    ; 変数aは200文字までの文字列を扱えます

これは配列変数ではなく、通常の変数になります。 文字列型の変数に代入されるデータが、初期文字数をオーバーした場合には 自動的に再確保が行なわれますので、初期文字数を気にする必要はほとんどの 場合ありません。

代入するパラメータに「,」をつけることで、配列変数への代入を連続して 行なうこともできます。

    ; 例
    	tmp=1,5,10	; 配列に連続して代入をする

上の例では、`tmp(0)=1:tmp(1)=5:tmp(2)=10`と同じことになります。 配列の途中から代入を開始するには、

    ; 例
    	tmp(2)=10,20,50	; 要素2から連続して代入をする

この例では、`tmp(2)=10:tmp(3)=20:tmp(4)=50`と同じことになります。 `tmp="ABC","DEF","GHI"`のように文字列を連続して代入することもできます。

### 関数

関数は、パラメーター式の中である値をもとに変換された値を得るために使用します。 関数を示す名前に続いてカッコ内にパラメーターを指定することで、結果を返します。

    ; 例
    	x=sin(3.141592)

上の例では、3.141592のサイン(正弦)値を求めて変数xに代入します。 関数には、三角関数などの算術関数から、文字列を扱うものなど様々な種類があります。 関数は、必ず式の中で使う必要があります。命令(ステートメント)として使用することはできません。

HSP3には標準で以下のような関数が用意されています。

標準で利用できる関数 一覧
関数名 | 戻り値 | 内容
--- | --- | ---
int | 整数 | パラメーターを整数に変換する
rnd | 整数 | 0〜(パラメーター-1)の乱数を発生する
strlen | 整数 | 文字列の長さを返す
length | 整数 | 変数の持つ配列要素数を返す(1次元)
length2 | 整数 | 変数の持つ配列要素数を返す(2次元)
length3 | 整数 | 変数の持つ配列要素数を返す(3次元)
length4 | 整数 | 変数の持つ配列要素数を返す(4次元)
vartype | 整数 | 変数の型を返す
varptr | 整数 | 変数のデータを示すアドレスを返す
varuse | 整数 | 変数の使用状況を返す
gettime | 整数 | 日付・時間を返す
str | 文字列 | パラメーターを文字列に変換する
dirinfo | 文字列 | 特定のディレクトリ情報を返す
peek | 整数 | バッファから値を取り出す
wpeek | 整数 | バッファから値を取り出す
lpeek | 整数 | バッファから値を取り出す
double | 実数 | パラメーターを実数に変換する
sin | 実数 | サイン値を返す(パラメーター単位はラジアン)
cos | 実数 | コサイン値を返す(パラメーター単位はラジアン)
tan | 実数 | タンジェント値を返す(パラメーター単位はラジアン)
atan | 実数 | アークタンジェント値を返す
sqrt | 実数 | ルート(平方根)値を返す
logf | 実数 | 対数値を返す
expf | 実数 | 指数値を返す
abs | 整数 | 整数の絶対値を返す
absf | 実数 | 実数の絶対値を返す
limit | 整数 | 整数値を範囲内に収める
limitf | 実数 | 実数値を範囲内に収める
sysinfo | 整数 | システム情報を取得します
ginfo | 整数 | ウィンドウに関する情報を取得します
objinfo | 整数 | ウィンドウオブジェクトに関する情報を取得します

### ラベル

プログラム上の位置を示すためにラベルとして名前をつけることができます。 ラベルは*(アスタリスク)の後に続く59文字(半角)以下の文字列で示します。

    ; 例

    *label
        mes "Wait.":wait 100
        goto *label            ; 無限ループを生成

ラベルは主に、goto命令、gosub命令またはbutton命令の飛び先を指定するのに使用されます。 ラベル名は、1つのソーススクリプトで重複することはできません。 また、変数名と重複することもできません。

スクリプトエディタの「カーソル」メニューにある「ラベル一覧」([F11]) を選択すると、現在編集中のスクリプトに含まれるラベルの一覧がリストボックス に表示され、任意のラベルを選ぶことでダイレクトに該当する行にジャンプすることができます。 スクリプトが大きくなって移動が不便な場合や、どのあたりにラベルを振ったか 忘れてしまった時などに便利です。

変数にラベルの位置を代入することも可能です。

    ; 例
    	a=*test

この場合は、変数aに「*test」という位置を代入します。 以降は、変数aはラベル型の変数として扱われ、パラメーターにラベル指定を行なう場所に、 ラベル型の変数を指定することができるようになります。

以下は、ラベル型を使用したサンプルスクリプトの例です。

    ; ラベル型変数のテスト
    ;
        ldim a,2
        mes "TYPE="+vartype(a)
        mes "USE(0)="+varuse(a(0))
        a(0)=*test
        a(1)=*test2
        mes "USE(0)="+varuse(a(0))
        gosub a(0)
        gosub a(1)
        mes "OK"
        stop
    *test
        mes "JUMP OK"
        return
    *test2
        mes "JUMP OK2"
        return

ラベル型変数は、vartype関数によりタイプ値「1」が取得されます。 また、varuse関数により有効なラベルが代入されているかどうかを判別することが可能です。 ラベル型変数の配列は、`ldim a,20`と指定して確保することができます。

ラベル型変数は、スクリプトの飛び先が見えにくくなり、全体の把握が難しくなります。 その反面、on〜gotoなどで処理しきれない大量の飛び先を効率よく管理できます。

HSPではローカルラベル機能をサポートしています。 これは、特殊な名前のラベルを何度でも使うことのできる機能で、 何でもない部分や、名前をつけるのが面倒な時などに便利です。

    ; 例
        mes "GO!"
    *@
        mes "A="+a
        a+
        if a<5 : goto *@back    ; 前のローカルラベルに戻る
        stop

ローカルラベルは、「*@」という名前で定義します。これは、他の ラベルと違い1つのソーススクリプト内で何度でも置くことができます。

このローカルラベルを、goto命令などで参照する時には、 「*@back」と「*@forward」を使います。 「*@back」は、その場所より上にあり一番近いローカルラベルを指します。 「*@forward」は、その場所より下にあり一番近いローカルラベルを指す ものになります。

「*@back」は、「*@b」と省略することが可能です。 「*@forward」は、「*@f」と省略することが可能です。

ローカルラベルは、多用するとかえって見にくいスクリプトになって しまう可能性もあるので、後の見やすさを考えてから使ってみてください。

### 条件判断

条件判断を行う場合には、if命令を使用します。if命令は、パラメータで 示された条件が満たされた場合は、それ以降の命令を実行し、そうでなければ 次の行から実行を続けます。

    ; 例
        a=10
        if a=10 : mes "aは10です。"

上の例では、「a=10」の部分が条件式になります。条件式には主に、

HSPで利用できる条件式 一覧
条件式 | 意味
--- | ---
a=b | aとbは等しい
a!b | aとbは等しくない
a<b | aはbよりも小さい
a>b | aはbよりも大きい
a<=b | aはbよりも小さいか等しい
a>=b | aはbよりも大きいか等しい

を使います。if＋条件式の後は：(コロン)で区切り、それに続いて 条件が満たされた場合に実行される部分を書きます。 (C言語やJavaと同じように「=」「!」を「==」「!=」のように記述することも可能です)

複合的な条件判断をするために、論理演算式を使うこともできます。

HSPで利用できる論理演算式 一覧
条件式 | 意味
--- | ---
a&b | aとbがともに正しい (and)
a|b | aとbのどちらかが正しい (or)

これによって複数の条件を一度に記述することができます。

    ; 例
        a=10:b=20
        if a=10|b=10 : mes "aかbのどちらかが10です。"

上の例では、「a=10」と「b=10」という条件式を|(or)でつないで、 どちらかが正しい場合には、正しいという結果が出るようにしています。 論理演算は、「|」「&」といった記号の他に「or」「and」という文字列でも記述することができます。

    ; 例
                if (a=10)or(b=10) : mes "aかbのどちらかが10です。"

上のように書いても、結果は同じになります。

HSPでは、論理演算はビットごとの演算として扱われているため、 C言語やJavaなどで用いられる「&&」「||」などの論理演算子とは等価ではありませんのでご注意下さい。

もし、条件によってプログラムの流れを変えたい場合には、

    ; 例
        a=10
        if a>10 : goto *over10
        mes "aは10以下です。"
        stop
    *over10
        mes "aは10より大きいです。"
        stop

上のように、goto命令で別なラベルに飛ばすことも可能です。 else命令を使って条件が満たされなかった場合の処理を書くことも可能です。

    ; 例
        a=10
        mes "aは、"
        if a=10 : mes "10です。" : else : mes "10ではありません。"
        stop

この場合は、else命令のある部分までは、条件を満たした場合に実行され、 else命令以降は、条件を満たされなかった場合に実行されます。 次の行以降は、条件に関わらず通常通りに実行されます。

条件判断の実行を、複数行で行なうこともできます。 if命令の条件が正しい時に実行されるスクリプトを複数行に渡って記述する場合は、

    ; 例
        a=10
        if a>5 {
            mes "TRUE"
            mes "(MULTILINE IF)"
        }
        stop

のように「{」で始めて「}」で終わる部分までを指定します。 (上の例では、見やすくするためにC言語風に行の最初にTABを入れてありますが、 特に必要なものではありません。ユーザーの見やすいように記述してください)

else命令でも複数行の指定ができます。

    ; 例
        a=10
        if a>5 {
            mes "TRUE"
            mes "(MULTILINE IF)"
        } else {
            mes "  FALSE"
            mes "  (MULTILINE IF)"
        }
        return

ただし、elseの後に「{」を記述しなければ複数行にはならないので注意してください。

    ; 例
        if a>5 {
            mes "TRUE"
        }
        else                    ; <- 間違い
        {
            mes "false"
        }

上の例は間違いです。エラーになります。 else命令もまたif命令と同じように、「{」がない限りは、その1行のみが有効範囲になります。

また、if命令を多重に実行させることも可能です。

    ; 例
        a=10
        b=10
        c=10
        if a>5 {
            if b>5 {
                if c>5 { mes "--3" } else { mes "--2" }
            } else {
                mes "--1"
            }
        }

上の例では、変数aが5より大きい場合は「--1」を、変数a,bが5より大きい場合は「--2」を、 変数a,b,cが5より大きい場合は「--3」を表示します。 if命令の多重化は、スクリプトがだんだんと複雑になっていくので、注意して使ってください。 HSPでは、128重までの多重化が可能です。

### 繰り返し命令

repeat〜loop命令は、HSPで繰り返しの動作をさせる場合に欠かすことのできない重要な命令です。

    ; 例
        repeat 5
        mes "繰り返し["+cnt+"]"
        loop

のように、repeat命令の後に回数を指定すると、loop命令までを指定回数だけ繰り返します。 繰り返し中は、システム変数cntが0、1、2、3…と自動的にカウントアップされていきます。

繰り返し回数に0が指定された場合には、repeat〜loopの範囲は実行されずに通過します。 また、繰り返し回数指定を省略するかまたはマイナス値を指定すると無限ループになります。 repeat〜loop命令と、break、continue命令を組み合わせることにより、さらに高度な処理が可能になります。

repeat〜loop命令の繰り返しを、途中で強制的に脱出させるのがbreak命令です。

    ; 例
        repeat 5
        if cnt=2 : break
        mes "繰り返し["+cnt+"]"
        loop
        stop

上の例では、システム変数が2になった時点、つまり3回目のループになると、if命令の判断によって、 break命令が実行されるしくみになっています。 break命令が実行されると、繰り返しの回数がまだ残っていても、強制的に繰り返しから抜け出し、 loop命令の次にある命令から実行を続けます。 break命令以降(上の例では、mes命令)は実行されません。

この命令を使うと、たとえば次のようなスクリプトが作成可能です。

    ; 例
        repeat
            getkey a,1
            if a>0 : break
            await 10
        loop
        stop

上のスクリプトでは、マウスの左ボタンを押すまで待つループになります。 repeat命令の回数指定を省略するかまたはマイナス値を指定すると無限ループになります。 それを利用してボタンの状態が1になるまでは、ずっと同じところを繰り返すようになっています。

ボタンが押されると、break命令が実行され繰り返しから抜け出します。 いままでは、このようなスクリプトはgoto命令を使って繰り返しの構造を記述する必要がありましたが、 repeat〜loopとbreak命令を使うことにより、ラベルを用意することなく手軽に条件付きの無限ループが実現できます。

continue命令は、逆に繰り返しをやり直すための命令です。

    ; 例
        repeat 5
            if cnt=2 : continue
            mes "cnt="+cnt
        loop
        stop

上の例では、システム変数cntが2になった時点で、continue命令が実行されるしくみになっています。 continue命令が実行されると、repeat命令まで戻り次の繰り返しを実行します。

上のスクリプトが実行されると、

    ; 表示例
        cnt=0
        cnt=1
        cnt=3
        cnt=4

のような表示になり、システム変数cntが2の時だけmes命令が実行されないのがわかります。 最初はわかりにくいかもしれませんが、continue命令は、loop命令の場所ではないが、loop命令と同じ働きをするとも 言えます。 繰り返しのカウンタは、continue命令が実行された場合でも、loop命令と同様1つ増加します。 もし、最後の繰り返しでcontinue命令が実行されると、repeat〜loopが終わった状態、つまりloop命令の次の命令から 実行を続けます。

さらにもう1つ、continue命令にはパラメータを指定する使い方が存在します。 continueの後に、数値または数値型変数を指定することにより、繰り返しのカウンタ を示すシステム変数cntの内容を変更することができます。

たとえば、「continue 1」と指定した場合は、システム変数cntの内容は1になり、 その値のままrepeat命令の次から繰り返しを続けます。 パラメータを省略して、ただの「continue」だけの場合はloop命令と同様の処理、 数値を指定すると、カウンタの値を変更して繰り返しをやり直すことになります。 ですから、

    ; 例
        repeat 1
            await 10
            getkey a,1
            if a=0 : continue 0
        loop
        stop

このようなスクリプトでは、通常1回だけしか実行されないはずの、repeat〜loopの 繰り返しですが、マウスの左ボタンが押されていない場合は、カウンタが0に戻され 無限ループの状態になります。これで、ボタンを押すまで待つという動作になります。

このbreak命令と、continue命令は、ともにrepeat〜loop命令の中で使用しなければ なりません。また、repeat〜loop命令の中であれば、いくつでも使用できます。

注意しなければならないのは、プログラムの流れとしてrepeat〜loop命令は、 順番につながっていなければなりません。repeat命令があったら、そこから下の行に必ずloop命令がなければ いけないということです。 repeat命令だけを、gosubでジャンプしたサブルーチンの中で実行したり、 goto命令でスクリプト上の別な場所にジャンプしてloop命令を実行させたりして、 repeat〜loop命令の順番が狂ってしまうとコンパイラはエラーを出してしまいます。

repeat〜loop命令は、多重に使うことができます。

    ; 例
        repeat 5
            if cnt=2 : continue
            repeat 2
                mes "中のループcnt="+cnt
            loop
            mes "外のループcnt="+cnt
        loop
        stop

上の例では、わかりやすいようにTABを入れて記述していますが、5回繰り返す構造の 中に、さらに2回繰り返すrepeat〜loop命令があります。 繰り返しが多重になった場合でも、break、continue命令は対応するループに対して機能します。 これは、システム変数cntが、やはり対応したループに対しての値を示すのと同様です。

repeat〜loop命令の中からgoto命令などで別な場所にプログラム制御が移ると、 次に再びrepeat命令を実行した場合に、多重の繰り返しになってしまいます。 必ずrepeat〜loop命令は、正常にループ終了するか、break命令で抜けるように してください。goto命令で脱出すると多重ループになりエラーが発生します。 repeat〜loop命令は、通常32まで多重に繰り返し処理が可能です。

### メモリノートパッド命令

メモリノートパッド命令セットは、行単位で文字列を扱うことのできるユニークで 便利な機能です。これにより、テキストファイルを読み込み解析したり加工することが容易になります。 また、コンボボックスやリストボックスを表示するための命令(combox,listbox命令) パラメータ指定に使う文字列や、ディレクトリの内容を調べるdirlist命令が 返す文字列なども、メモリノートパッド命令を使うことでシンプルに処理できるようになります。

メモリノートパッド命令は、簡単に言えばWindowsの「メモ帳」のようなテキスト エディタを使うような感覚で、複数行を含んだ文字列を扱うための命令セットです。 通常の文字列は、「ABCDEFG」のように任意の文字が集まったものです。 複数行を含んだ文字列というのは、「ABCD\nEFGH」のように間に改行(\n)があり 1行目は「ABCD」、2行目は「EFGH」というようにちょっと複雑な構造になっている文字列のことです。 テキストエディタなどでロード・セーブすることのできるテキストファイルも、 このような複数行を含んだ文字列と言えます。 これらの文字列を行単位で取り出したり、修正をしたりすることがメモリノートパッド命令で可能です。

メモリノートパッド命令 一覧
命令 | 主な機能 | 備考
--- | --- | ---
notesel | メモリノートパッドとして扱う変数の指定 |
noteadd | 指定行に内容追加 | 挿入/上書きモードあり
noteget | 指定行の内容読み出し |
notedel | 指定行の削除 |
noteload | ファイルから内容読み込み |
notesave | 内容をファイルに書き出し |
noteunsel | 以前に選択されていた変数に復帰する |
noteinfo | メモリノートパッドに関する情報を取得する |
notemax | 全体の行数を取得する | マクロとして使用
notesize | 全体のサイズ(バイト数)を取得する | マクロとして使用
notefind | 特定の文字列を含む行を検索する | 関数として使用

基本的な使い方は、まずnotesel命令で文字列型の変数を指定します。 それ以降は、メモリノートパッド命令はすべて、そこで指定した変数が対象になります。

指定した変数は、文字列型である必要があります。 notesel命令で変数を指定した後は、自由にnoteinfo,noteadd,noteget,notedel命令を使って 変数の内容(文字列)にアクセスすることができるようになります。

    ; 例
        a="ONION\nTOMATO\nCARROT"
        notesel a
        noteget b,0
        mes "index0="+b
        noteget b,1
        mes "index1="+b
        noteget b,2
        mes "index2="+b
        stop

上の例では、変数aに「ONION」「TOMATO」「CARROT」という3行が代入されています。 まずnotesel命令で、変数aを指定してから、各行の内容をnoteget命令を使って取り出して表示しています。

メモリノートパッド命令では、行の指定に「インデックス」という単位を使用しています。 最初の行はインデックス0、その次はインデックス1、そのまた次はインデックス2…というふうに続きます。 行数だと最初は1行目ということになりますが、インデックスは0から始まるので注意してください。 つまり、「noteget b,0」は最初の行を変数bに読み出す…という意味になります。

    ; 例
        a="ONION\nTOMATO\nCARROT"
        notesel a
        noteadd "POTATO"
        mes a
        stop

上の例では、変数aに代入されている「ONION」「TOMATO」「CARROT」という3つの要素に、 「POTATO」という行を追加しています。4行目の「mes a」で、その結果を表示しているので確認することができます。 内容を追加するには、noteadd命令を使用します。「noteadd "POTATO"」は、最後の行に「POTATO」を追加します。

    noteadd "POTATO",1

と指定をすると、インデックス1の位置に追加されます。 それまでインデックス1以降にあった内容は下にずれることになります。また、

    noteadd "POTATO",1,0

は、同じくインデックス1の位置に追加されますが同じ行に上書きで追加されます。 それまでインデックス1にあった内容は消去され、かわりに指定した内容になります。 指定したインデックスの行を削除する命令も用意されています。

    notedel 1

は、インデックス1を削除します。 これらのメモリノートパッド命令は、他の命令と組み合わせた時に威力を発揮します。 メモリノートパッド命令は、テキストファイル、mesbox命令で入力される複数行 テキスト、dirlist命令の結果、comboxおよびlistbox命令で指定するパラメータの 処理に使用することができます。 また、1行単位で取り出した文字列は、poke,peek命令などで1文字単位で扱うことが 可能です。

    ; 例
        notesel a
        noteload "aaa.txt"
        idx=0
        repeat notemax
            noteget b,idx
            mes "index"+idx+"="+b
            idx++
        loop
        stop

上の例では、"aaa.txt"という名前のテキストファイルを読み込み、そのすべての行を インデックス番号とともに表示します。 noteload命令を使用することにより、変数に確保されたメモリバッファのサイズを 読み込みファイルに合わせて調節します。 全体の行数がわからない場合は、noteinfo関数か、notemaxを使用します。 notemaxは、メモリノートパッドの対象になっているテキストの行数が代入されている マクロでシステム変数と同じように使用することができます。

    ; 例
        sdim list,32000
        sdim tmem,32000
        tmem=""
        fid=""
        ;
        notesel list
        dirlist list,"*.*"
        sel=0
        objsize 160,24
        listbox sel,150,list    ; ID=0
        button "SELECT",*ok    ; ID=1
        pos 180,0
        mesbox tmem,450,200    ; ID=2
        stop
    *ok
        noteget fname,sel
        exist fname
        if (strsize<=0)or(32000<strsize) : goto *lderr
        bload fname,tmem
        objprm 2,tmem
        stop
    *lderr
        dialog "LOAD ERROR!"
        stop

これは、メモリノートパッド命令と、その他の命令を組み合わせたサンプルです。 カレントディレクトリにあるファイル一覧をリストボックスに表示して、そこで 選んだファイルの内容を、右側にあるメッセージボックスの中に表示します。

dirlist命令やlistbox命令で使われている文字列は、いずれもメモリノートパッド 命令で扱うことのできる複数行のテキストです。 最初は複雑に感じるかもしれませんが、多くのデータを一度に扱うことができ、 使い方次第で応用範囲がさらに広がります。

### ソート命令

ソート命令セットは、数値、文字列の並べ替え(ソート)をするための命令群です。 複数のデータや文字列を効率よく加工することが容易になります。

ソート命令セット一覧
命令 | 主な機能 | 備考
--- | --- | ---
sortval | 配列変数を数値でソート |
sortstr | 配列変数を文字列でソート |
sortnote | メモリノート文字列をソート |
sortget | ソート元のインデックスを取得 | ソート実行後に取得する

数値のソートは、配列変数に代入されている整数値、または実数値に対して行なわれます。

    		dim n,10
    		sortval n,0

上の例では、変数nが持つ１次元配列の範囲n(0)〜n(9)を小さい数値の順番に並び替えます。
命令が実行されると、配列変数の内容がすべて並べ替えられます。 ソートの順番は、２番目のパラメーターで指定できます。0の場合は、数値の小さい順、1の場合は大きい順にソートされます。

    		sdim n,64,10
    		sortstr n,0

上の例では、文字列型変数nが持つ１次元配列の範囲n(0)〜n(9)を文字コードが小さい順番に並び替えます。
命令が実行されると、配列変数の内容がすべて並べ替えられます。 ソートの順番は、２番目のパラメーターで指定できます。0の場合は、文字コードが小さい順、1の場合は文字コードが大きい順にソートされます。

他にも、メモリノートパッド命令(notesel)で指定された改行入りの文字列をソートするための、sortnote命令が用意されており、用途によって使い分けることが可能です。
ソート命令が実行された後は、もともとの位置を調べるためのnoteget命令を使用することができます。 これは、直前に実行されたソート結果の項目が、もともとどの位置(インデックス)にあったかを調べるものです。

    		sortget a,0

上の例では、インデックス0(最初の項目)がもともと、どこにあったものかを変数aに取得しています。 (インデックス値は、配列の要素値と同じものになります)
たとえば、変数aの値が5だった場合は、インデックス5に入っていた項目が並べ替えられてインデックス0になったことを示しています。

### システム変数

システム変数はシステム起動時、または特定の命令を実行した時に自動的に 値が代入される変数です。普通の変数と同じように参照することができますが、 代入文(=)により値を代入することはできません。 一部のシステム変数には、return命令により値を代入することが可能です。

システム変数 一覧
変数名 | 設定される内容
--- | ---
system | 未定義
hspstat | HSPランタイムの情報を取得する(*1)
hspver | HSPのバージョン番号(*2)
cnt | repeat〜loopループのカウンター
err | エラーコード
stat | 色々な命令のステータスなどを代入する汎用システム変数
mousex | マウスカーソルのX座標
mousey | マウスカーソルのY座標
mousew | マウスホイール値
strsize | getstr命令で読み出したByte数
refstr | 文字列を保存する汎用のシステム変数
refdval | 実数値を保存する汎用のシステム変数
looplev | repeat〜loopのネストレベル
sublev | サブルーチン(モジュール)のネストレベル
wparam | 割り込み時に保存されるWindowsのシステム値(wParam)
lparam | 割り込み時に保存されるWindowsのシステム値(lParam)
iparam | 割り込み要因を示す値
thismod | 現在の有効なモジュール変数
notemax | メモリノートパッドの行数
notesize | メモリノートパッドの文字数
hwnd | 現在のウィンドウハンドル
hdc | 現在のデバイスコンテキスト
hinstance | 現在のインスタンスハンドル
ginfo_mx | スクリーン上のマウスカーソルX座標
ginfo_my | スクリーン上のマウスカーソルY座標
ginfo_act | アクティプなウィンドウID
ginfo_sel | 操作先ウィンドウID
ginfo_wx1 | ウィンドウの左上X座標
ginfo_wy1 | ウィンドウの左上Y座標
ginfo_wx2 | ウィンドウの右下X座標
ginfo_wy2 | ウィンドウの右下Y座標
ginfo_vx | ウィンドウのスクロールX座標
ginfo_vy | ウィンドウのスクロールY座標
ginfo_sizex | ウィンドウ全体のXサイズ
ginfo_sizey | ウィンドウ全体のYサイズ
ginfo_winx | 画面のクライアントXサイズ
ginfo_winy | 画面のクライアントYサイズ
ginfo_sx | 画面の初期化Xサイズ
ginfo_sy | 画面の初期化Yサイズ
ginfo_mesx | メッセージの出力Xサイズ
ginfo_mesy | メッセージの出力Yサイズ
ginfo_r | 現在設定されているカラーコード(R)
ginfo_g | 現在設定されているカラーコード(G)
ginfo_b | 現在設定されているカラーコード(B)
ginfo_paluse | デスクトップのカラーモード
ginfo_dispx | デスクトップ全体のXサイズ
ginfo_dispy | デスクトップ全体のYサイズ
ginfo_cx | カレントポジションのX座標
ginfo_cy | カレントポジションのY座標
ginfo_intid | メッセージ割り込み時のウィンドウID
ginfo_newid | 未使用ウィンドウID
dir_cur | カレントディレクトリ(フォルダ)
dir_exe | 実行ファイルがあるディレクトリ(フォルダ)
dir_win | Windowsディレクトリ(フォルダ)
dir_sys | Windowsシステムディレクトリ(フォルダ)
dir_cmdline | コマンドライン文字列
dir_desktop | デスクトップディレクトリ(フォルダ)
dir_mydoc | マイドキュメントディレクトリ(フォルダ)
dir_tv | HSPTVディレクトリ(フォルダ)

*1

  以下の情報がすべて合計された値になります。

  - デバッグモード = 1
  - スクリーンセーバー起動時 = 2
  - コンソールモード = 16
  - Macintosh版HSP = $80
  - HSP3Dish = $100
  - Linux版HSP = $1000
  - UTF8文字列ランタイム = $20000
  - 64ビットランタイム = $40000
*2

  バージョンコード(8bit) + マイナーバージョンコード(8bit)の値になります。 例えば3.5は$3500になります。

## 拡張文法

### モジュール

モジュール機能は、スクリプトを整理し再利用可能にするための仕組みです。 ある程度HSPを習得した中上級者には、モジュール機能は便利なものになる はずです。モジュール内で扱う変数をまとめて保存するモジュール変数や、 ローカル変数など多彩な応用が可能になっています。

また、モジュール機能を使わない人であっても、他の人がモジュール機能を 使って追加した新しい命令を使うことが可能です。これは、DLLによる拡張 プラグインの仕組みと変わりません。

たとえば、「test1.as」というソーススクリプトがあったとしましょう。 このソーススクリプトには、変数aと変数bを使っているとします。 別な人が、「test2.as」というソーススクリプトを作ったとして、そこに とても便利なサブルーチンがあったとしたら、どうなるでしょう。 「test2.as」で変数aと変数bという名前を使っていなければ問題なく、 そのままサブルーチンだけを持ってくることができるかもしれません。 しかし、もし「test2.as」でも変数aと変数bを別な用途で使っていたとしたら とてもやっかいです。

HSPモジュール機能を使うと、「test1.as」から「test2.as」のスクリプトを 呼び出すことが可能になりますが、「test1.as」と「test2.as」で使われて いる変数は(たとえ名前が同じであっても)独立したものとして扱われます。 また、この独立したスクリプト内のサブルーチンを、新規命令として登録 することができ、パラメータを渡したり、受け取ったりすることが可能です。

過去に作ったモジュールを再利用したり、人に使ってもらうために公開したり、 誰か他の人が作ったモジュールを使うなど、HSPスクリプトをより広く応用 することが可能になります。

HSPモジュールを使いこなすためには、モジュール指定命令、ユーザー拡張命令 などについて覚える必要があります。これらは、単体でも便利な機能を 提供する命令です。一度に覚えようとしないで、わかるところから1つづつ マスターしていきましょう。

ユーザー定義命令は、HSPモジュール機能とともに追加された命令の1つで、 新しい名前の命令をユーザーが任意に追加できるというものです。 これは、HSPモジュール機能とは別に単体で使っても非常に強力なものと なるでしょう。

### ユーザー定義命令

ユーザー定義命令は、以下のように使います。

    ; 例
        goto *main

    #deffunc routine
        mes "sub-routine"
        return

    *main
        routine
        stop

HSPの命令には「routine」はありませんから、いままでならエラーになって しまうところですが、実際にこのスクリプトを実行すると、「sub-routine」 という表示がされて、「routine」という命令が実行されます。 ユーザー定義命令は、「#deffunc」という命令によって定義できます。

    #deffunc 命令の名前

で、新しい名前の命令が追加されます。 これ以降、新しい命令が出てきた場合には、「#deffunc」のある場所に サブルーチンジャンプします。

つまり、

    ; 例
        goto *main

    *routine
        mes "sub-routine"
        return

    *main
        gosub *routine

のようなスクリプトでも、

    ; 例
        goto *main

    #deffunc routine
        mes "sub-routine"
        return

    *main
        routine

「#deffunc」は実際に命令を使う位置より前に置いても、後に置いてもかまいません。 またユーザー定義命令は、サブルーチンにパラメータを渡すことを可能にしています。

    ; 例
        goto *main

    #deffunc routine int prm1, int prm2
        mes "パラメータ1は、"+prm1+"です。"
        mes "パラメータ2は、"+prm2+"です。"
        return

    *main
        routine 10, 20

いままでのgosub命令では、値をサブルーチンに渡す時には、あらかじめ 決められた変数に値を入れて、呼び出すしかありませんでした。 ユーザー定義命令では、それに代わってスマートな方法で値を渡すことを 可能にしています。

また、渡すパラメータは数値だけでなく、文字列、変数(配列)など いくつものバリエーションがあります。 #deffunc命令では、

    #deffunc 新規命令の名前 パラメータータイプ1 エイリアス名1,…

という書式でパラメーターの情報を指定します。 パラメータータイプは、以下の中から選ぶことができます。

パラメータータイプ一覧
タイプ | 内容
--- | ---
int | 整数値
var | 変数(配列なし)
array | 変数(配列あり)
str | 文字列
double | 実数値
label | ラベル
local | ローカル変数

エイリアス名は、渡されたパラメーターの内容を示すもので、変数と ほとんど同じ感覚で使用することができます。 ただし、varとarrayの使い分けには注意が必要です。

    ; 例
    #deffunc routine1 var prm
        mes "変数の内容は、"+prm
        return

    #deffunc routine2 array prm
        mes "変数の内容は、"+prm
        return

上の例では、「routine1 a(1)」のように呼び出した場合、「a(1)」 という指定がそのままパラメーターとして渡されます。 それに対して、「routine2 a(1)」の場合は、「a」だけがパラメーター として渡されます。ですから、routine2では、a(0)の内容が表示 されることになります。また、エイリアスとしてのprmは、routine1の 場合は、「prm.1」という指定をすることはできない(エラーとなります) のに対して、routine2は「prm.1」という指定が可能です。 このように、変数パラメーターの受け渡しには2つの種類があることを 覚えておいてください。

パラメータータイプ「local」は少し特殊な指定です。これは、厳密には パラメーターではなく新規命令の動作を指定するものです。

    ; 例
    #deffunc routine1 int prm,local a
        mes "ローカル変数="+a
        a=prm
        return

上のようにlocalは、パラメーター記述の最後に付加して指定することを 推奨します。localに続いて指定された名前は、ローカル変数として 新規命令が実行された時点で初期化されます。通常、HSPの変数はグローバル なものとして何らかの値を常に保持していますが、ローカル変数の場合は この命令が実行される時に作成され、命令の終了とともに破棄されます。 ローカル変数は、命令の再帰(自分自身を呼び出すこと)を行なう場合などで 変数の値を独自に保持しておく時などに有効です。その他、変数名の局所化 などプログラムを整理する際にも役立ちますが、ローカル変数を多用する ことは、実行効率や速度を求める場面では、初期化のためのオーバーヘッドが あることを留意してください。

特殊な用途として、パラメータータイプの替わりに「onexit」を記述することで、 クリーンアップ命令として登録することができます。 クリーンアップ命令は、HSPスクリプト実行の終了時に自動的に呼び出されます。

    ; 例
    #deffunc 名前 onexit

モジュールによって機能を拡張した場合などにその後始末、システムやメモリの解放などを 行なうために利用することができます。

### ユーザー定義関数

ユーザー定義関数は、ユーザー定義命令と同様に新規の関数を作成するものです。

    #defcfunc 新規関数の名前 パラメータータイプ1 エイリアス名1,…

のように、ユーザー定義命令と同じ書式で定義することができます。 #defcfuncにより、式の中で評価される関数の動作をスクリプトで記述する ことができます。

    ; 例
    #defcfunc half int a
        return a/2

上の例では、halfという名前の関数を登録しています。 halfは、パラメーターの半分の値を返す関数として作られています。 たとえば、式の中で「half(4)」のように使用することで、「2」という値に 加工されます。 関数が返す値は、return命令のパラメーターで指定します。 返値で指定された値と型がそのまま式に反映されます。

### 関数と命令の違いについて

関数と命令という2つの機能呼び出し方法が標準で用意されています。 この2つは、どちらも必要に応じてユーザーが選択することができますが、 以下のようなルールで運用されることを推奨しています。

- 命令 = パラメーターをもとに動作する機能を提供するもの
- 関数 = パラメーターを一定の法則で加工して値を返すもの

システムが提供する多くの機能は、命令として提供しており、関数のサポートは 算術関数など必要以上の混乱をしない範囲に留めています。 特に初心者などに公開されるスクリプトについては、関数を多用すると 理解し難いものになる場合もあります。

### モジュール定義命令

モジュールは、変数名やラベル名を独立して扱えるソースの単位を指します。

    ; 例
    #module
    #deffunc test1
        a=a+1
        mes "test1が呼び出されたのは、"+a+"回です。"
        return
    #global

モジュールは、必ず「#module」で始まり「#global」を最後に書くのが お約束だと思って下さい。 この例では、test1という新規命令を呼び出すたびに、変数aに 保存されている回数がカウントアップしていきます。 これを呼び出す側の例として、以下のようなスクリプトを作ってみます。

    ; 例
        a=5
        repeat a
            test1
        loop
        mes "aの内容="+a
        stop

通常のサブルーチン呼び出しと考えると、変数aの名前がtest1の中でも 使用されているため、正しく動作しないはずです。 しかし、モジュール定義命令を使ってソースが分離されているため、 モジュールの中にある変数aと、外にある変数aが別なものとして扱われて いるので、上のスクリプトは正しく動作します。

このように、「#module」〜「#global」で区切られた区間と それ以外の区間をまったく別な空間として変数やラベルの名前が重複していても、 まったく問題なくそれぞれのスクリプトが動作するという点が、 モジュールの基本的な概念です。独立した機能を持ったサブルーチンを、 ユーザー定義命令により完全に分離することで、スクリプトの再利用や カプセル化を進めることができます。

また、モジュール定義の際に名前を付加することができます。

    ; 例
    #module mo
    #deffunc test1
        a=a+1
        mes "test1が呼び出されたのは、"+a+"回です。"
        return
    #global

上の例では、「mo」という名前のモジュールが定義されます。 名前を省略して「#module」だけにした場合でも、他と重複しないように 「_m数値」というモジュール名がシステム側で自動的に付けられます。 モジュールを複数定義した場合には、このモジュール名により識別 されています。

もし、モジュールの外から、モジュール内の変数を参照したい場合には 「変数名@モジュール名」という書式を使うことができます。逆に、 モジュール内部から外(#global部分)の変数を参照する場合には、 「変数名@」という書式により参照することができます。 ただし、この書式はモジュール内部と外部に依存関係ができてしまうため ソースの完全な分離ではなくなってしまいますので、推奨はされません。

#### モジュール変数の定義

モジュール変数は、複数の変数やデータをまとめて管理することができる データ格納方法を提供します。複数の変数をモジュール内に隠蔽することに より、大量のデータをシンプルに整理することができる便利な機能です。

モジュール内部で使用する変数をモジュール変数と呼び、それらをモジュール 単位で保存するため、モジュールの型を持つ変数を作成することができます。 モジュール変数を作成する準備として以下のような定義を行なっておく 必要があります。

    #module a x,y,z

上の例では、aというモジュールにx,y,zという３つの変数を持たせています。 #module命令は、従来からあるように「a」という名前を持つ空間を定義する ための命令ですが、3.0では「a」という名前に対応した変数を定義することが できるようになっています。モジュール「a」が使用する変数x,y,zという意味で、 このx,y,zをモジュール変数と呼びます。

モジュール変数を使うためには、モジュールの型を持つ変数を作成しておく 必要があります。

        newmod v,a

上の例では、モジュール「a」のための変数vを初期化します。 これで変数vには、モジュール「a」が持つモジュール変数x,y,zという 内容が丸ごと格納されることになります。 変数は、最初に整数値0の状態で初期化されます。ただし、モジュールごとに 初期化のための命令を用意することも可能です。

    #modinit
        x=1:y=2:z=3
        return

上の例は、モジュール変数初期化のためのルーチン(コンストラクタ)を定義して います。#modinitで定義される初期化ルーチンは、1つのモジュールにつき 1つだけ書くことができます。 #modinitが定義されているモジュールは、newmod命令を使用した時に自動的に 初期化ルーチンが呼び出されます。 #modinitは、#deffuncと同様に引数を指定することができます。

    #module a x,y,z
    #modinit int p1,int p2,int p3
        x=p1:y=p2:z=p3
        return
    #global
        newmod v,a,1,2,3

上のように定義されている場合に、newmod命令が実行された場合は、 newmodに記述されたパラメーターがそのまま渡されます。

最初は分かりにくいかもしれませんが、変数vは複数の変数をまとめて入れて おくことのできる入れ物になっています。複数のファイルが1つのフォルダに 格納されているのと同じようなイメージです。

格納されている変数、つまりモジュール変数の内容にアクセスするためには、 モジュール内で処理するための命令を専用に定義する必要があります。

    #modfunc viewxyz
        mes "x="+x+"/y="+y+"/z="+z
        return

上の例は、モジュール変数x,y,zを表示するための命令定義です。 引数の指定などは、#deffuncと同様に行なうことができますが、この命令を 呼び出すためには、命令の引数として最初にモジュール型の変数を指定する 必要があります。

つまり、viewxyzという命令を呼び出すためには、

    #module a x,y,z
    #modinit int p1,int p2,int p3
        x=p1:y=p2:z=p3
        return
    #modfunc viewxyz
        mes "x="+x+"/y="+y+"/z="+z
        return
    #global
        newmod v,a,1,2,3    ; ここでモジュール変数を初期化する必要がある
        viewxyz v
        stop

という記述をしなければなりません。 これで、モジュール型の変数vに格納されているx,y,zの内容をviewxyzという ユーザー定義命令の中で表示させることができます。 見かけ上は、vという変数1つにx,y,zという3つの要素を入れておくことが できるようになり、データをセットで扱う場面などでは有効です。

#modfuncで定義したルーチン内では、自分自身のモジュール変数を表わす システム変数thismodを使用することができます。 thismodは、モジュール変数を処理するルーチン内から、別な命令・関数を 呼び出すためのモジュール変数として指定する場合に使用します。

#modfuncは、命令として定義されますが、関数として定義するための#modcfuncも 用意されています。関数として定義した場合も、命令と同様に最初のパラメーターに モジュール型の変数を指定する必要があります。

モジュール型の変数も配列を使用することができます。 この場合に、いくつか特殊な使用方法が用意されています。

先ほどのnewmod命令は、モジュール型の配列変数に新しい値を代入するための命令です。 `newmod v,a` は、v(0)という配列に代入されますが、もう一度実行した場合は、v(1)に代入 されます。つまり、newmod命令は変数vの配列で空いている場所を自動的に 確保してデータを格納するための命令です。

逆に、newmod命令で格納した配列要素を削除することもできます。

        delmod v(0)

上の例では、変数vの要素0にあたる内容を削除します。 削除された要素は、これ以降使用できなくなり、新たにnewmod命令が実行 された時に再利用されます。 また、必要な場合は、delmod命令によりモジュール変数が破棄された際に 自動的に呼び出されるルーチン(デストラクタ)を定義することも可能です。

    #modterm
        mes "BYEBYE"
        return

上の例では、モジュール変数が破棄された時に「BYEBYE」を表示します。 指定された要素が使用中であるかどうかは、varuseという関数で調べる ことができます。

        if varuse(v(0))=0 : mes "v(0)は使用されていない"

また、配列の要素がどこまで使用可能かどうかは、length関数で得ることが できます。

        mes "配列要素の最大="+length(v)

さらに、モジュール型の変数で使用可能な要素すべてを繰り返し処理する ためのforeach命令が用意されています。 foreach命令は、repeat命令と同じように使用できますが、ループ回数の 代わりにモジュール型の変数名を指定します。 以降は、loop命令までの間をすべての要素の数だけ繰り返します。

繰り返しの際に、システム変数cntが要素の値として変化します。 システム変数cntは、delmod命令で削除された要素はスキップされます。

つまり、

        foreach v
            viewxyz v(cnt)
        loop

のように書くと、モジュール型の変数vで有効な要素すべてに対して、 viewxyzという命令を実行するという意味になります。

モジュール型変数を別な変数に代入した場合は、もとの変数のクローンになります。 (もとの変数に代入されていた内容をすべてコピーするわけではありません) その場合は、varuse関数の戻り値が、クローンであることを示す「2」になります。 モジュール型変数のクローンは、もとの変数と同じように内容を参照することは可能ですが、 モジュール変数の破棄を行なうことはできません。また、既に破棄されたモジュール変数の内容を参照しないように注意してください。

モジュールについての解説は、別途ドキュメントモジュール機能ガイドでも行なっています。

### プリプロセッサ命令

プリプロセッサ命令は、プログラム実行時ではなく、コンパイル時に解釈し 実行される命令のことです。プリプロセッサ命令は、他の命令と区別がつく ように、行の最初に「#」に続けて記述しなければなりません。

プリプロセッサ命令により、スクリプトの記述そのものをカスタマイズする ことも可能になります。ただし、使いすぎるとプログラムそのものがわかり にくくなったりするので、HSPを一通り使った上級者の方に使用をおすすめ します。

プリプロセッサ命令は、通常「#」から始まる1行が対象となりますが、 最後が「\」で終わるプリプロセッサ行は次行に継続されます。 1行に収まらない定義を行なう場合に、利用することができます。

    ; 例
    #define aaa mes "AAA"\
        :mes "BBB"\
        :mes "CCC"

        aaa
        stop

上の例では、この3行すべてが#defineプリプロセッサ命令として解釈されます。 プリプロセッサ命令として装備されている命令は以下の通りです。

    #include "filename"			[別ファイルを結合]

"filename"で指定されたファイルも同時にコンパイルされます。 スクリプトエディタで入りきらないスクリプトも、includeで別ファイルに 分割すればコンパイルすることができるようになります。

    #define 新規名称  元名称		[新規名称を登録する]

新しい別名(エイリアス)を追加するためのものです。 HSPの従来ある命令の名前を、別な名前でも使用できるようになります。 新規名称は、予約されている命令語や変数名と重ならない20文字以内の、 スペースを含まない英文字列でなければなりません。

    ; 例
    #define pr print
        pr "message..."
        stop

上のように#defineの後に、新規名称と元の名称をスペースで区切り記述 します。すると、「pr」という命令が新しく登録され、「print」命令と まったく同様に使うことができるようになります。

元の名称は、命令の名前である必要はありません。数値や、記号なども 新規の名称として登録することが可能です。

    ; 例
    #define is =
    #define plus +
        a is 5 plus 10
        mes "A="+a
        stop

上の例では、「=」という記号を「is」という言葉に、「+」という記号を 「plus」という言葉にそれぞれ置き換えるように定義しています。 すると、「a is 5 plus 10」という行は、「a = 5 + 10」と同じことになります。

パラメーターを使用して展開することも可能です。

    ; 例
    #define reset(%1) %1=10
        reset a
        mes "A="+a
        stop

上の例では、「reset a」を「a=10」というスクリプトに変換します。

このほか、特殊展開パラメーターなど多くのオプションが用意されています。 詳しくは、「#defineマクロについて」の項を参照してください。

    #const マクロ名 数値式			[マクロ名の定数定義]

指定されたマクロ名に置換え文字列を設定します。 #defineと同様ですが、#constは定数(数値)の置き換えを行なう場合に あらかじめ計算を行なった結果を置き換えます。

    ; 例
    #const KAZU 100+50
        a=KAZU
    ;    ↓(展開後)
    ;    a=150

あらかじめソース内で使用する値が確定している場合、ソースの 高速化に有効です。すでに定義されているマクロを含めることも可能なので、

    ; 例
    #const ALL 50
    #const KAZU 100*ALL
        a=KAZU
    ;    ↓(展開後)
    ;    a=5000

のように使用することができます。 計算式は、整数のみで演算子および数値の記述スタイルは、スクリプトで 使用している式と同様のものが使えます。

    ; 例
    #const KAZU $5+3*10
        a=KAZU
    ;    ↓(展開後)
    ;    a=35

    #undef マクロ名					[マクロ名の取り消し]

すでに登録されているマクロ名を取り消します。 登録されていないマクロ名に対して指定してもエラーにはならず 無視されます。

    #if 数値式                      [数値からコンパイル制御]
    #ifdef マクロ名                 [マクロ定義からコンパイル制御]
    #ifndef マクロ名                [マクロ定義からコンパイル制御]
    #else                           [コンパイル制御を反転]]
    #endif                          [コンパイル制御ブロック終了]

コンパイルのON/OFFを指定します。 #ifは指定した数値が0ならば以降のコンパイル出力をOFFにして コンパイル結果を無視します。数値が0以外の場合は、出力がONとなります。 このコンパイル制御は、#endifが出るまでの区間を対象にします。 #if、#ifdef、#ifndefのいずれかには、#endifがペアで存在している 必要があります。

    ; 例
    #if 0
        mes "ABC"        ; この部分は無視されます
        a=111            ; この部分は無視されます
        mes "DEF"        ; この部分は無視されます
    #endif

#ifの指定には式を使うことも可能なので、

    ; 例
    #define VER 5
    #if VER<3
        mes "abc"        ; この部分は無視されます
        a=111            ; この部分は無視されます
        mes "def"        ; この部分は無視されます
    #endif

のような使い方もできます。計算式の記述は#const命令と同様です。

#ifdefは、指定したマクロ名が定義されていれば出力をONに、そうでない 場合は、出力をOFFにします。 マクロ名は、#defineや#constによって定義されたものになります。

    ; 例
    #define SW
    #ifdef SW
        mes "ABC"        ; この部分はコンパイルされます
        a=111            ; この部分はコンパイルされます
        mes "DEF"        ; この部分はコンパイルされます
    #endif

#ifndefは、#ifdefとは逆に指定したマクロ名が定義されていれば出力をOFFに、 そうでない場合は、出力をONにします。 また、ブロック内に#elseを入れることで条件が逆の場合の動作を記述 することができます。

    ; 例
    #ifdef SW
        mes "AAA"        ; SWが定義されている場合
    #else
        mes "BBB"        ; SWが定義されていない場合
    #endif

また、#if、#ifdef、#ifndef〜#endifのプロックを入れ子にすることも 可能です。

    ; 例
    #ifdef SW
        #ifdef SW2
            mes "AAA"        ; SWとSW2が定義されている場合
        #else
            mes "BBB"        ; SWが定義されている場合
        #endif
    #endif

    #uselib "filename"            外部DLLの指定
    #func 新規名称  関数名  タイプ        外部DLL呼び出し命令登録

外部DLL内のプログラムを呼び出すための命令を増やすことができます。 これにより、HSP本体のプログラムから、C言語やDelphiなどで作成した DLL内の関数を呼び出すことが可能になります。

func命令により、外部の関数もHSPの命令として定義できるので、 HSPを自由に機能拡張することが可能です。外部DLL作成の方法や、 HSPとのパラメータ受け渡しの詳細は、「拡張プラグイン作成リファレンスマニュアル」を参照してください。

普通に使う場合には、まったく必要のない命令ですので、特に覚えて おかなくても問題ありません。

### #defineマクロについて

#defineマクロは、あくまで個人がスクリプトを書きやすくカスタマイズしたい 場合に使うもので、初心者向きではありません。ここで説明した機能も、 頻繁に利用するものではありませんので、必要な場合にのみ参照してください。

#defineマクロは、基本的に置き換え文字列を登録します。

    ; 例
    #define hyouji mes
        hyouji "AAAAA..."
    ;    ↓(展開後)
    ;    mes "AAAAA..."

#define、#const命令の直後に「global」を入れることで、すべてのモジュールで 永続的に利用することのできるマクロを作成することができます。

    ; 例
    #module
    #define global test 0x1234
    #global
        a=test            ; aに0x1234が代入される

通常は、モジュール内で#defineを定義した場合には、それ以外のモジュール およびグローバルなエリアでは、同じ名前は認識されません。 global指定を入れることで、それ以降のすべての場所で定義した名前を マクロで置き換えることができるようになります。

単純な置き換えマクロの他に、引数付きの展開が可能です。 引数は、マクロ名の後にカッコで囲んだ%1,%2,%3…の引数名で指定を 行ないます。 引数は必ず「%数値」で指定する必要があり、数値は1から順番に記述 してください。CやC++のプリプロセッサのようにシンボル名では指定 できないので注意してください。

    ; 例
    #define hyouji(%1) mes "prm="+%1
        hyouji "AAAAA..."
    ;    ↓(展開後)
    ;    mes "prm="+"AAAAA..."

また、引数に初期(デフォルト)値を設定することが可能です。

    ; 例
    #define hyouji(%1="PRM=",%2=123) mes %1+%2
        hyouji "AAA",a
        hyouji "BBB"
        hyouji ,b
    ;    ↓(展開後)
    ;    mes "AAA"+a
    ;    mes "BBB"+123
    ;    mes "PRM="+b

初期(デフォルト)値は、マクロを使用した時に省略された場合に 自動的に補完される値です。初期値を省略された場合は、補完されません。 マクロ引数の指定では#defineで指定する側では、カッコで囲んで いますが、実際に使用する時にはカッコなしで指定してください。

    ; 例
    #define hyouji(%1) mes "prm="+%1
        hyouji("AAAAA...")

のような記述はエラーになるので注意してください。 ただし、ctypeオプションを使用することで以下のようなカッコ付き記述が 可能になります。

    ; 例
    #define ctype kansu(%1) (%1*5+1)
        a=kansu(5)

このオプションは、計算式など命令部分以外にマクロを使用したい時に 有効です。一見、C言語などの関数のように振舞いますが、実際にはマクロで 置き換えているだけなので、応用範囲は狭いので注意してください。 この記述方法は、本来のHSP文法とは異なるため自分のスタイルで記述したい というカスタマイズ用途以外での利用は推奨していません。

マクロの展開時に特殊な動作を行なうキーワードを設定することが可能です。 この特殊キーワードは、主にことなるマクロ間でパラメータを共有したり、 入れ子構造をスタックによって実現するためのものです。

    ; 例
    #define start(%1) %tstart %s1 mes "START"
    #define owari %tstart mes %o

ここで指定されている「%s1」や「%o」などが特殊展開マクロです。 これを使ったサンプルは、以下のように展開されます。

    start "OK"    → mes "START"
    owari         → mes "OK"

このように、異なるマクロ間でデータを共有させることが可能になります。 特殊展開マクロは、以下の種類と機能があります。

特殊展開マクロ 一覧
マクロ | 機能
--- | ---
%t | タグ名を設定する
%n | ユニークなラベル名を生成する
%i | ユニークなラベル名を生成してスタックに積む
%o | スタックに積まれた文字列を取り出す
%p | スタックに積まれた文字列を取り出す(スタック維持)
%s | 引数パラメーターをスタックに積む
%c | 改行を行なう

特殊展開マクロは、「%」に続けて英文字1文字+パラメータで表現します。 以降のキーワードと識別するために、特殊展開マクロの後には半角スペースを 入れて下さい。「%tabc aaa」のようスペースを含む部分までが特殊展開マクロ と判断されます。

特殊展開マクロでは、一般的なスタック(First In Last Out)を持っています。 このスタックは、同じタグ名を持つマクロで共有させることができます。 タグ名は、「%tタグ名」のように「%t」に続けて半角英文字16字以内で指定 します。先の例では「%tstart」と指定された「start」がタグ名にあたります。

「%s」は、引数パラメーターをスタックに積むための特殊展開マクロです。 「%s1」と指定すると、「%1」のパラメータをスタックに1段積みます。

スタックに積まれた文字列を取り出す場合は、「%o」を使用します。 「%o」は、スタックに積まれた文字列を取り出して展開します。スタックなので、 最後に積まれたものが最初に取り出されます。「%o0」と指定すると、 スタックを取り出しますが文字列の展開は行ないません(スタック取り出しのみ)。

スタックを戻さずに内容だけを取り出すのが「%p」です。「%p0」は、次に 取り出されるスタックの内容を展開します。「%p1」は、もう一段深いスタック を取り出します。以降、「%p0」〜「%p9」までを指定することが可能です。

ラベル生成の例を以下に示します。

    ; 例
    #define start %tstart *%i
    #define owari %tstart await 20:stick a:if a=0 : goto *%o

これを使ったサンプルは、以下のように展開されます。

        start  → *_start_0000
        owari  → await 20:stick a:if a=0 : goto *_start_0000

「%i」は、他と重ならないようなユニークなラベル名を生成してスタックに 1段積みます。「%i0」と指定するとラベル名をスタックに1段積みますが、 展開は行ないません。また、「%n」は、ユニークなラベル名を生成して展開 するだけで、スタックには積みません。

上の例では、ラベル名生成によってラベルを新しく作成して、ループ構造を 実現しています。この方法を使えば、入れ子になってもラベル名が重なる ことのないループ構造を構築することができます。 また、1つのソーススクリプトファイル内ですべてのスタックが取り出されて いなかったマクロ(タグ名)は、コンパイル時にエラーが報告されます。 かならず、すべてのスタックが取り出されて終わるようなマクロ命令の構成に しておいてください。

※標準定義マクロのwhile〜wend、do〜until、for〜nextは特殊展開マクロによって作られています。

特殊な場面において、「%c」によって改行を挟んで展開することが可能です。 「%c」の部分で行が分割されて展開されます。主に複数のプリプロセス文に 展開されるようなマクロを定義する用途などに使用することができます。 ただし、現状ですべてのプリプロセッサがマクロ展開に対応しているわけでは ありません。多用しすぎると、かえって見難くなることもありますので、 よくご理解の上お使いください。

    ; 例
        goto *@f
    #define def(%1,%2) #deffunc %1 %c mes %2 %c return
        def test, a
    *@
        a = 10
        test
        a = 20
        test
        stop

### 標準マクロ定義ファイル

プリプロセッサでは、commonディレクトリにある「hspdef.as」を標準の マクロ定義として必ず最初に読み込みます。 「hspdef.as」には、システムで定義するシンボル名やマクロ、基本的な モジュールなどが追加されていく予定です。

ユーザーは「hspdef.as」を編集しないように注意してください。 個別にカスタマイズを行ないたい場合は、「userdef.as」を用意することで 「hspdef.as」と同様に自動的に読み込まれます。「userdef.as」ファイルが 存在しない場合は、適用されません。

標準マクロ定義ファイルは、以下の順に検索されます。ファイルが見つからない場合は無視されます。

1. ソースファイルのあるディレクトリにある「hspdef.as」
2. commonディレクトリにある「hspdef.as」

また、プリプロセッサが自動的に追加するマクロがあります。 以下のマクロは、自動的に追加され、#ifdef、#ifndef命令などでこれらの マクロを判別して分岐させることが可能です。

プリプロセッサが自動的に追加するマクロ 一覧
命令 | 内容
--- | ---
_debug | デバッグモード時
__hsp30__ | ver3.0以降使用時
__file__ | 使用時点で解析されているファイル名
__line__ | 使用時点で解析されている行番号
__date__ | 使用時点の日付
__time__ | 使用時点の時刻
__hspver__ | HSPバージョン番号(*)
__runtime__ | HSPランタイム名

*上位8bit・下位8bitがそれぞれメジャー・マイナーバージョンを示します。

標準マクロは、より個人が書きやすい記述を実現するために用意されたものです。 C言語など、すでに慣れた形式でスクリプトを記述したい場合にお使いください。 見かけ上命令と同様に動作するほか、同様のマクロを新しく定義する ことも可能です。詳しくは、別項「#defineマクロについて」を参照してください。

以前のスクリプトを動かす際に、新しく追加されたマクロ名がすでに変数名や ラベル名として使われているとエラーになります。doやfor、nextなど短い単語は 衝突する可能性が高いので、注意してください。衝突してしまっている 場合は、変数名・ラベル名を別なものに置き換えれば動作させることができます。

#### do〜untilマクロ

untilに続く条件が満たされるまで、doからuntilまでの部分を繰り返します。 条件が満たされている場合でも、最低一回はdo〜until内を実行します。 untilに続く条件を省略した場合は、繰り返しを行ないません。 また、_continueにより繰り返しの先頭から再開、_breakにより繰り返しを脱出することができます。

    ; 例
        a=0
        do
            a=a+1
            mes "A="+a
        until a>5    ; aが5以上になるまでdo以下を繰り返す

#### while〜wendマクロ

whileに続く条件が満たされている間だけ、whileからwendまでを繰り返します。 条件が満たされていない場合はねwhileからwendまでを実行しません。 whileに続く条件を省略した場合は、無限に繰り返しを行ないます。 _continueにより繰り返しの先頭から再開、_breakにより繰り返しを脱出することができます。

    ; 例
        a=0
        while a<5
            a=a+1
            mes "a="+a
        wend        ; aが5以下の間だけwhile以下を繰り返す

#### for〜nextマクロ

    for 変数名,初期値(0),終値(0),増分(1)

をパラメーターとして指定すると、for〜nextの間を指定回数繰り返します。 カッコ内は省略した場合の値です。変数名は省略できません。 指定された変数をカウンターとして使用し、初期値から始まって、 1回繰り返すごとに増分を足していきます。終値に達した時点で、 繰り返しから抜けます(終値はループに含みません)。

最初から終値の条件が満たされている場合は、繰り返しを実行しません。 また、_continueにより繰り返しの先頭から再開、_breakにより繰り返しを脱出することができます。

    ; 例
        for a,0,5,1
            mes "A="+a
        next        ; aが0から4の間(5回)繰り返す

この例では、変数aは0,1,2,3,4と５回繰り返してループを終わります。 増分にマイナス値を指定することも可能です。

    ; 例
        for a,5,0,-1
            mes "A="+a
        next        ; aが5から1の間(5回)繰り返す

この場合、変数aは5,4,3,2,1と5回繰り返してループを終わります。 forマクロは、内部でマクロ展開後に特殊な新規命令exgotoを生成します。 exgoto命令はforマクロのための補助命令で単体で使用することは推奨 していません。

#### switch〜case〜swendマクロ

switch〜case〜swendは、ブロック内に複数の条件判断と処理をまとめて書くことが できる構文です。`switch 比較元`でswitchブロックを開始します。 比較元のパラメーターは、変数または式を指定することができます。

switch以降は`case 比較値` を置くことで、これ以降に「比較元」が「比較値」と同じだった場合に処理する 内容を記述することができます。 caseは、ブロック内に複数記述することができ、それぞれの比較値ごとの処理を 指定できます。 caseの比較が正しい場合は、swbreakが存在するまで以降の命令を実行します。 また、caseの替わりに`default`を置くと、 caseで指定したどの比較値にもあてはまらない条件の場合に以降が実行されます。

switchブロックが終了した場合は、`swend`を必ず最後に 書いておく必要があります。以下は、switchマクロを使用したスクリプトの例です。

    ; 例
        a=0
        switch a    ; aを比較対象とする
        case 0        ; aが0だった場合
            mes "A=0"
            swbreak    ; case0の条件実行終了
        case 1        ; aが1だった場合
            mes "A=1"
        default        ; aが0以外だった場合
            mes "A!=0"
            swbreak
        swend

この例では、変数aの内容が0か1かそれ以外かで条件分岐を行なっています。 「case 0」以降は、「swbreak」までが実行されますが、「case 1」の場合は、 「swbreak」が存在しないため、「default」以降に実行される「mes "A!=0"」も 含めて実行されるので注意してください。

### API呼び出し

HSP3では、外部のDLLがエクスポートする関数を呼び出す機能を利用できます。 これはあらかじめ指定した引数でDLLを呼び出すための機能で、 ver2.5以降のHSP拡張プラグインを含めて柔軟な使い方が可能です。

DLL呼び出しの概要は以下のものになります。

    #uselib "filename"			外部DLLの指定

HSPから呼び出す外部DLLのファイル名を指定します。 DLLのファイル名は、拡張子も含めて完全に書く必要があります。 ファイル名を省略した場合は、実行時にスクリプトからDLL名を指定 してリンクを行なうことになります。

    #func 新規名称  関数名  タイプ		外部DLL呼び出し命令登録

外部DLLを呼び出すための新しい命令を登録します。 新規名称、関数名、タイプをスペースで区切って書きます。 関数名は、"関数名"のようにダブルクォートで囲むことで、DLLの完全な エクスポート名を記述することができます。 ダブルクォートで囲んでいない場合は、「_関数名@16」というVC++の エクスポート規約に基づいた名前に変換されます。

タイプには、引数の詳細を記述します。 #deffuncと同様に、引数の型を「,」で区切って指定してください。 引数の数や、型の順番に制限はありません。 引数の型として使用できる文字列は以下の通りです。

引数の型 一覧
型 | 内容
--- | ---
int | 整数値(32bit)
var | 変数のデータポインタ(32bit)
str | 文字列ポインタ(32bit)
wstr | unicode文字列ポインタ(32bit)
sptr | ポインタ整数値または文字列のポインタ(32bit)
wptr | ポインタ整数値またはunicode文字列のポインタ(32bit)
double | 実数値(64bit)
label | ラベルポインタ(32bit)
float | 実数値(32bit)
pval | PVal構造体のポインタ(32bit)
bmscr | BMSCR構造体のポインタ(32bit)
comobj | COMOBJ型変数のデータポインタ(32bit)
prefstr | システム変数refstrのポインタ(32bit)
pexinfo | EXINFO構造体のポインタ(32bit)
nullptr | ヌルポインタ(32bit)

以下は、4つの引数を指定して実行する例です。

    ; 例
    #uselib "test.dll"
    #func test "_func@16" var,int,int.int
        test a,1,2,3    ; test.dllのfunc(&a,1,2,3)が呼び出される

DLLからの関数インポートは、最初に命令が実行された時点で行なわれます。 インポートされる関数名が見つからない場合は、命令を実行した時点で エラーになります。(起動時にはエラーになりません) 外部API呼び出しの戻り値は、32bit整数としてシステム変数statに代入 されます。

タイプに数値を指定した場合は、ver2.5以降のDLLタイプ指定と互換性のある 引数が自動的に設定されます。 これにより、HSP2.61までの拡張プラグインをそのまま利用可能です。 ただし、ver2.5とは以下の点で互換性の注意が必要です。

- BMSCR構造体は、flagからcolorまでのフィールドのみ参照できます
- PVal構造体は、ver2.5のPVAL2構造体と互換があります
- PVal構造体のflagフィールド(型タイプ値)やlenフィールド(配列情報)をDLL側で書き換えることはできません
- PVal構造体から文字列型の配列変数にアクセスすることはできません

これ以外の点においては、HSP ver2.5と同等の情報が受け渡されます。

また、関数名の前に「onexit」を入れることにより、終了呼び出し関数として 登録することができます。

    ; 例
    #func test onexit "_func@16" str,int,int

上の例では、アプリケーション終了時に自動的に"_func@16"が呼び出されます。

#funcと同様に、#cfunc命令により関数として登録することも可能です。

    #cfunc 新規名称  "関数名"  タイプ名1,…		外部DLL呼び出し関数登録

引数パラメーターは、#func命令と同じものを使用することができます。 #cfunc命令によって登録された新規名称は、関数として式の中に記述することが 可能です。

    ; 例
    #uselib "test.dll"
    #cfunc test "_func@16" var,int,int.int
        res=test(a,1,2,3)    ; test.dllのfunc(&a,1,2,3)が呼び出される

登録された関数の戻り値として、外部呼出しの結果取得された整数値(32bit int)をそのまま返します。 HSP2.5互換の呼び出しでは、システム変数statに返される値を関数の戻り値とします。

### COMコンポーネント呼び出し

HSP3では、COMオブジェクト型変数と、COMインターフェースの定義および 呼び出しを行なうための機能を利用できます。

COMコンポーネントの機能を使用するには、COMオブジェクト型変数を作成する 必要があります。通常は、newcom命令を実行して、指定したCOMのクラスや インターフェースに対応したCOMオブジェクト型変数を作成します。以降、 作成された変数を介して、メソッドを呼び出すことができるようになります。 COMオブジェクト型変数は、COMオブジェクトのインターフェースポインタを 格納しており、オブジェクトの生成と破棄はHSP内部で管理されます。 (明示的に破棄を行なうdelcom命令も用意されています。)

最も手軽にCOMを利用する手段として、オートメーションを介したアクセスが 可能です。この方法は、IDispatchインターフェースを提供している オートメーションオブジェクトを使用するときに用いることができます。

    ; 例
        newcom ie, "InternetExplorer.Application"

上の例では、"InternetExplorer.Application"というプログラムID (ProgID)を持つオブジェクトを 作成し、オブジェクトのインターフェースポインタを変数ieに格納します。 プログラムIDは、VBScriptやJavaScript等で使われているクラスID定義文字列と同様のものです。

プログラムIDの代わりに、GUIDによるクラスID("{0002DF01-0000-0000-C000-000000000046}" の形式の文字列)を 指定することも可能です。 COMオブジェクト型変数が作成された後は、プロパティの参照や設定、メソッドの呼び出しが可能になります。

COMオブジェクト型変数では、配列要素としてプロパティを示す文字列を 指定することで、プロパティの参照と設定を行なうことができます。

    ; 例
        ie("Visible")=1

上の例は、COMオブジェクトとして初期化された変数ieの"Visible"という 名前のプロパティに1という整数値を設定するものです。

    ; 例
        mes "ウィンドウ位置("+ie("Left")+","+ie("Top")+")"
        mes "ウィンドウサイズ("+ie("Width")+","+ie("Height")+")"

上の例では、プロパティの内容を参照して表示を行なっています。 COMオブジェクトが返すプロパティの値は、HSP内部で適切に型変換されて そのまま使用することができるようになります。

COMオブジェクトのメソッドを実行する場合は、mcall命令を使用します。

    ; 例
        mcall ie,"Navigate","http://www.onionsoft.net/"

上の例では、"Navigate"というメソッドを"http://www.onionsoft.net/"という 文字列型の引数を渡して実行します。 引数の数や、型はそのままメソッドに渡されることになります。 実行したメソッドの返値は、comres命令で設定された変数に代入されます。 また、メソッド実行が成功した場合にはシステム変数statは0になり、 エラーが起こった場合には、システム変数statに結果コード(HRESULT値)が 代入されます。

mcall命令の特殊な記述方法として、以下のように書くことも可能です。

    ; 例
        ie->"Navigate" "http://www.onionsoft.net/"

変数に続いて「->」とメソッド名を記述し、パラメーターを指定します。 これは、C++等と近い記述方法を利用したい人のためにあるもので、 コンパイラによって自動的にmcall命令に置き換えられます。

COMオブジェクトのプロパティに"$coclass","$interface" を指定することで オブジェクトのcoclass名・interface名を取得することが可能です。

        pobj("$coclass")   : coclass名
        pobj("$interface") : interface名

オートメーション(IDispatch)を使わずに直接COMインターフェース (カスタムインターフェース)のメソッドを呼び出すことも可能です。

    #usecom インターフェース名 "IID文字列" ["CLSID文字列"]	COMインターフェース登録

カスタムインターフェースを使用するため、インターフェース名の 定義を行います。"IID文字列"にはインターフェースIDを文字列形式の GUID ("{000214EE-0000-0000-C000-000000000046}"のような文字列)で 指定します。"CLSID文字列"には、クラスIDを文字列形式のGUIDで 指定しますが、省略することもできます。

"CLSID文字列"を指定した場合には、newcom命令の第2パラメータに そのインターフェース名を指定することが可能です。この場合には #usecom命令のクラスIDを持つオブジェクトを作成し、指定された インターフェースを取得してCOMオブジェクト型変数に格納します。

    #comfunc 新規名称 メソッドインデックス タイプ名1,…		COMメソッド登録

#usecom命令によりインターフェースの定義を行なった直後に、#comfunc命令で COMメソッド及び引数パラメーターを登録することができます。 タイプ名の指定は#func命令の場合と同じです。タイプ名にwstrを指定すると、 メソッドの引数としてunicode(OLESTR)文字列を渡すことができます。 (unicodeからHSP文字列の変換には、cnvwtos関数を利用することができます)

    ; 例
        ; hsp3.exeへのショートカットshortcut.lnkを作成する
        ;
        #define CLSID_ShellLink   "{00021401-0000-0000-C000-000000000046}"
        #define IID_IShellLinkA   "{000214EE-0000-0000-C000-000000000046}"
        #define IID_IPersistFile  "{0000010b-0000-0000-C000-000000000046}"

        #usecom IShellLinkA IID_IShellLinkA
        #comfunc IShellLink_SetPath 20 str

        #usecom IPersistFile IID_IPersistFile
        #comfunc IPersistFile_Save 6 wstr,int

        newcom slink, CLSID_ShellLink
        IShellLink_SetPath slink, dirinfo(1)+"\\hsp3.exe"
        IPersistFile_Save  slink, dirinfo(0)+"\\shortcut.lnk", 1

        mes "ショートカットを作成しました。"
        delcom slink
        stop

また、生成されたインターフェースから別のインターフェースを問い合わせる querycom命令が用意されています。ただし、mcall命令の実行時には 自動的に必要なインターフェースを問い合わせるようになっているので、 COMオブジェクトが目的のインターフェースを提供していることがあらかじめ わかっている場合には、あえてquerycom命令を実行する必要はありません。

COMオブジェクト型変数を作成するもう1つの方法として、 ActiveXコントロールを配置するためのaxobj命令が用意されています。

axobj命令は、指定されたクラスID(CLSID)またはプログラムID(ProgID)から、 ActiveXコントロールをHSPのウィンドウ内に配置します。 axobj命令で指定された変数は、COMオブジェクト型変数として初期化されます。

    ; 例
        axobj ie, "Shell.Explorer.2",640,480
        ie->"Navigate" "www.onionsoft.net"

axobj命令で生成されたウィンドウは、HSPが管理する配置オブジェクトとして 登録されます。axobj命令実行後には、オブジェクトIDがシステム変数statに 代入されます。

axobj命令で生成されたウィンドウの破棄は、cls命令などで画面の初期化が 行なわれた際に行なわれます。clrobj命令などで明示的に破棄することも 可能です。ただし、COMオブジェクト型変数として初期化された変数は、 そのまま残されるため、ユーザーが破棄するようにしてください。 COMオブジェクト型変数の破棄は、別な値が代入された時やHSP終了時に 自動的に行なわれるためメモリリーク等が発生することはありませんが、 使用不可能になったCOMのポインタが残ってしまうため、意識して破棄して おくことを推奨します。

なお、axobj命令を使用するには、実行環境にAtl.dllまたはAtl71.dllが インストールされていなければいけません。これらのDLLがない場合には エラーになります。

また、COMイベントを管理するための機能が利用可能です。これにより、 COMオブジェクトから通知されるイベントの通知をスクリプトで取得する ことができます。

    comevent p1,p2,p3,*label

        p1      : イベント管理オブジェクトを格納する変数名
        p2      : イベント取得元の変数名
        p3      : コネクションポイントGUID
        *label  : イベントサブルーチンのラベル

p2で指定された変数(COMオブジェクト型)から任意のイベントを取得するための 準備を行ないます。p2で指定されたCOMオブジェクトは、すでにnewcom命令により 初期化されている必要があります。p1で指定された変数を、イベント処理を行なう ための特別なCOMオブジェクト型として初期化します。

p3でコネクションポイントGUIDを文字列形式で指定します。 p3の指定を省略した場合は、IProvideClassInfo2によって得られるデフォルトの コネクションポイントを検索します。

*labelでイベント処理サブルーチンのラベルを指定します。 イベント取得の準備に失敗した場合は、エラーが発生します。 成功した場合は、これ以降イベントが発生するたびに*labelで指定された場所に サブルーチンジャンプの割り込みが発生します。

*labelで指定されたイベントサブルーチンでは、comevdisp関数、comevarg命令により イベントの内容を細かく取得することが可能です。

すでにイベントの取得が開始されている変数に対して、 再度comevent命令を実行した場合は、以前のイベントキューの設定は無効になります。 イベントの取得は、delcom命令によりCOMオブジェクトが破棄されるまで継続されます。

    ; 例
        #define IID_DWebBrowserEvents2 "{34A715A0-6587-11D0-924A-0020AFC7AC5D}"

        axobj ie, "Shell.Explorer.2", 640, 480
        comevent ie_event, ie, IID_DWebBrowserEvents2, *event
        stop
    *event
        ;    COMイベント処理
        title "EVENT="+comevdisp(ie_event)
        return

上の例では、IEコンポーネントのイベントDWebBrowserEvents2を取得 して、*eventサブルーチンを呼び出しています。 発生したイベントの種類を識別するディスパッチID(DISPID)を取得するため、 comevdisp関数を使用します。

comevarg命令を実行するとイベントのパラメータを取得することができます。

    comevarg p1,p2,p3,*label

        p1 : 結果が代入される変数名
        p2 : イベント管理COMオブジェクト変数名
        p3 : パラメータのインデックス (0〜)
        p4 : 取得モード (0=通常の変換, 1=文字列に変換, 2=Variant型)

p2で指定された変数(イベント管理オブジェクトを格納したCOMオブジェクト型変数) のイベント処理サブルーチン内でイベントのパラメータ(引数)が取得され、 その結果がp1に代入されます。p3は引数のインデックスで、最初の引数を 取得する場合には0を、2番目の引数を取得するには1を指定します。 p3は取得モードで、省略するか0を指定した場合は、HSP標準の変数型に 変換されます。1を指定した場合は文字列に変換した状態で取得されます。 2を取得すると、以下のVariant型変数として取得されます。

HSP3では、COMのための変数型として、Variant型変数を使うことができます。 Variant型変数はCOMオートメーションで使用されるさまざまな型のデータが 格納される変数です。COMイベントのパラメータとして参照型(ByRef)の変数が 指定された場合に、その参照先を書き換えることができるように、HSP3に 導入されています。また、参照型でないパラメータもVariant型の変数として 取得することが可能です。

Variant型変数は、COMイベント処理サブルーチンの中でcomevarg命令を実行 する際に、第4パラメータに2を指定することによって取得できます。 Variant 変数は、COM型変数の場合と同じように、プロパティ名を指定すると それぞれ以下の値を参照できます。プロパティ名の大文字・小文字は区別 されません。

以下は、変数vをVariant型変数とした時に参照できる特殊なプロパティです。

    v("value") : Variantに格納されている値

v に格納されている値を参照します。例えば、整数型(VT_I4)の Variantなら a = v("value") とすると変数 a が整数型になり、 値が代入されます。v("value")への代入も可能です。 v("val")やv("")で参照することも可能です。

    v("isbyref") : 参照かどうかを示す値

Variantが参照型(byRef)の場合には1を返します。

    v("isarray") : SafeArrayかどうかを示す値

Variantが配列(SafeArray)の場合には1を返します。

    v("vartype") : VariantのVARTYPE値

2 (VT_I2), 3 (VT_I4), 8 (VT_BSTR) などといったVARTYPE値を 参照します。代入時( v("vartype")=2 など)は型の変換を試みます。 型変換に失敗するとエラーになります。v("vt")でも指定可能です。

    v("vtmask") : VariantのVARTYPE値のマスク値

VARTYPE値のVT_BYREFとVT_ARRAYを取り除いた値を返します。

    v("refptr") : VariantのVARTYPE値

vが参照型(VT_BYREF)のとき参照ポインタを返します。 vが参照型(VT_BYREF)でないときはエラーになります。

また、Variant型変数ではSafeArrayをある程度扱えるようにしています。 SafeArrayを格納しているVariant型変数では以下のプロパティが有効です。

    v("value",n) : SafeArray の要素 n の値

SafeArrayの要素nの値を参照します。この例は1次元のSafeArray の場合で、例えば2次元配列ではv("value",m,n)とします。単に v("value")とするとSafeArrayを格納したVariant型を返します。

    v("arraylbound",n) : SafeArrayの次元nの要素の下限
    v("arrayubound",n) : SafeArrayの次元nの要素の上限
    v("arraycount",n)  : SafeArrayの次元nの要素の要素数

それぞれ、指定された次元の要素の下限、上限、要素数を返します。 nを省略すると1が指定されます。

    v("arrayptr") : SafeArray のポインタ

格納されているSafeArrayを取得します(戻り値は整数型)。 代入を行うと、与えられたSafeArrayを格納します。

    v("bstrptr") : BSTR文字列 のポインタ

格納されている文字列(BSTR)ポインタを取得します。

COMオブジェクトのプロパティ取得時に、プロパティ名の前にピリオドを つけておくと、プロパティを Variant 型で返します。(メソッド呼び出し時に 返される戻り値についても同様です。)

        vname = pDoc(".Title")    ; vname は Variant 型
        name = vname("value")    ; name は文字列型

上の例は

        name = pDoc("Title")    ; name は文字列型

と書いた場合と同じです。 また、

        dimtype a, vartype("variant"), 20

などとして明示的にVariant型配列を確保することも出来ます。

Variant型とHSPの配列を相互に変換するための、sarrayconv命令が 追加されています。

    sarrayconv  p1, p2, p3, [p4]

        p1    : 結果を格納する変数
        p2    : 変換元の変数
        p3(0) : 変換のモード
        p4(0) : バイナリーデータのサイズ

        p3=0：配列変数p2全体からSafeArrayを作成し、Variant型変数p1に
              格納します。
        p3=1：Variant型変数p2に格納されているSafeArrayを配列変数p1に
              格納します。p1の領域は再確保されます。
        p3=2：変数p2からp4バイト分だけのバイナリデータからSafeArray
              (VT_UI1型)を作成し、p1に格納します。
        p3=3：Variant型変数p2に格納されている1次元SafeArray (VT_UI1型
              またはVT_I1型)のバイナリデータを変数p1に格納します。
              変数p1の領域はあらかじめ確保されている必要があります。

### HSPテンプレート(AHT)機能

HSPテンプレート(AHT)機能は、HSP3.1以降で追加されたもので、 ソースの自動生成及び外部データとの連携を行なうための様々な仕組みを提供します。

AHTには、様々な役割があります。

- 新しいHSPソーススクリプトを自動生成するためのルールを取り決める
- 再利用可能なHSPソーススクリプト同士の相互依存関係を定める
- HSPで使用可能なデータ形式やそのツールの依存関係を定める

つまり、誰でも手軽にHSPのソーススクリプトを生成するための方法と、 ひな型となるスクリプトやツールを簡単に利用するための仕組みを サポートするものだと考えてください。

HSPでは、スクリプトを再利用するための手段として、モジュールや マクロ、プラグインなどを提供してきました。しかしこれらは、 それぞれのユーザーが独自のルールで作成し、公開を行なってきました。 AHTは、これを決められたルールで書式化することで、各種の定型 スクリプトを共通の方法で、より手軽に再利用・連携を行なうことを 目指しています。

テンプレートは、HSP3.1から導入される新しい概念です。 一般的なプログラム言語で使われるテンプレートという概念は、 コードの抽象化を行なうための高度なメカニズムを指すことが多いのですが、 HSPの場合はもっと単純に「ひな型」を提供することに主眼を置いて、 より手軽にスクリプトを再利用するために活用されることになります。

AHTは、再利用可能なHSPソーススクリプトの情報及び、管理を行なう ツールを含めた仕組みの総称です。 AHTで使用する、定義ファイルは拡張子「.aht」を持つ、「AHTファイル」と呼ばれます。

AHTファイルは、HSP3のソーススクリプトファイル(.hsp)と互換性があります。 AHTファイルをそのままHSP3のソースとしてコンパイルを行なうことが可能です。 AHTファイルは、プリプロセッサ命令を使用してAHTヘッダと呼ばれる定義情報が指定されています。 また、特別な書式のコメントを使用してダイアログインターフェース定義を行なっています。 AHTファイルは、従来のHSP3ソーススクリプトを手軽にひな型として再利用するための形式です。 最小限の修正で、従来のスクリプトをAHTファイルとして扱うことができます。

AHTについての詳細は、別途ドキュメントaht.txtを参照してください。

## HSPの基本動作概念

### HSPのタスク

HSPは中間言語処理によるシングルタスクのインタプリタです。 HSPと他のWindowsアプリケーションはマルチタスクで同時に走らせることができます。

ただし、そのために1つだけHSPのスクリプトを作る上で約束事があります。 HSPでは「キーを押さないと進まないなどの無限にループになる可能性のある部分では、 必ずwaitかawait命令を入れる必要がある」という約束事を覚えておいてください。

waitやawait命令はWindowsの他のタスクに空き時間を回すための重要な役割があります。 これを守らないと、無限ループでWindowsのほぼすべてのタスクを奪ってしまい、 マルチタスク動作に影響が出ます。その結果、ウィンドウが正常にドラッグできなくなったり、 タスクの切り替えができなくなったりと変な状態になります。 (Windowsそれものが動かなくなるなどの致命的な状態にはなりません。 誤ってHSPで無限ループを作ってしまった場合は、[Ctrl]+[Alt]+[Del]を同時に押して HSPのタスクを終了させればもとの状態に戻ります) しかし、この約束事を気にしすぎる必要もありません。1秒以内に確実に終わる程度の ループであれば、waitを入れる必要はありません。waitを入れることでさらに処理速度が 遅くなってしまいます。

最も問題になるのは、たとえば「マウスのボタンを押すまで待つ」というような部分を、

    ; 例
    *mwait
        getkey a,0
        if a=0 : goto *mwait

のように記述すると、そこで無限ループになってしまいます。これを、

    ; 例
    *mwait
        getkey a,0
        await 5
        if a=0 : goto *mwait

このように修正すれば、無限ループは回避され正常にタスクが実行されます。 「await 5」は、非常に短い単位でのウェイトなので、これによってボタンの入力が しにくくなることもありません。

await命令はループ内の時間経過を一定に保つための命令です。 指定した時間だけ待つには、wait命令を使用してください。 CPU(Windows)に対する負担はawait命令もwait命令も変わりありません。

### 画面とウィンドウ

HSPの中心となる機能は、画面に文字や画像、そして点、線などを描画する ものです。HSPでは複数の描画バッファが用意されていて、それぞれに ウィンドウを割り当て、複数のウィンドウ画面を操作することが可能です。

HSPでは描画対象となる仮想的な画面を複数持つことができます。 これらの仮想画面は、ウィンドウIDと呼ばれる数値で管理されます。

ウィンドウID0は、最初に現れる画面を指します。2つ以上のウィンドウを 開くことがないのであれば、ウィンドウIDは特に意識せずに使用できます。

HSPのメイン画面とは別に新しいウィンドウを開いたり、メモリ上に仮想 画面を作成する場合には、ウィンドウID1以降を使うことになります。 ウィンドウIDの値に制限はありませんが、ID1から順番に使用するように してください。

新しい仮想画面は、初期化してから使用しなければなりません。そのための 命令がscreen命令およびbuffer命令です。screen命令で初期化をすると、 その画面は新しいウィンドウとしてディスプレイ上に表示されます。 (このウィンドウは標準でサイズを変更することができます。) それに対して、buffer命令で初期化をすると、その画面はメモリ上に存在 するだけとなり、ディスプレイからはその内容は見えません。このような 画面は、ほかの画面に画像の一部をコピーするためのテンポラリとして、 また一時的な画像の保存場所などに使用することができます。

screenやbuffer命令などでウィンドウIDを初期化した後は、その画面に 対して自由に描画を行なうことができます。mes命令により文字を表示 したり、boxf命令による塗りつぶしやline命令による直線の描画など 色々な命令が用意されています。 複数のウィンドウIDを使用している場合は、描画の対象をgsel命令によって 変更することができます。また、gcopy命令やgzoom命令により別なウィンドウ IDの画面に描かれている画像をコピーしてくることが可能です。

画面は、cls命令により再度初期化することができます。 また、picload命令により画像を読み込んだ場合にも初期化されます。 高速に画面全体を切り替えてアニメーションなどを行なう場合には、 redraw命令により再描画スイッチをOFFにしておき、画面の書き換えを 行なった後、再描画スイッチをONにしてください。これにより、画面の 書き換え中の結果が画面に反映されなくなり、スムーズで高速な書き換え を行なうようになります。また、書き換えの際にはcls命令は使わずに boxf命令などで画面をクリアするようにしてください。

### フォントと文字表示

最も基本的で単純な文字表示のための命令がmes(print)です。 mes命令とprint命令はどちらも同じ機能です。(伝統的なBASICの文法に合わせてprintというキーワードも利用可能になっていますが、HSP3ではmes命令を推奨しています。)
mes命令に続いて、画面に表示したい文字列を「"(ダブルクォーテーション)」で囲んで指定します。

    ; 例
    			mes "こんにちは!"

上の例では、「こんにちは!」を表示します。表示位置は、pos命令で指定するカレントポジションが参照されます。 文字を表示した後は、カレントポジションは次の行に自動的に移動します。ただし、オプションの設定によって最後の文字の後ろに移動させることも可能です。
表示される文字は、標準で設定された書体が使用されますが、font命令によって書体を切り替えることが可能です。

    ; 例
    			font msgothic,20,1

上の例では、MSゴシックという書体の20ポイントサイズ、太字の設定に変更しています。 これ以降にmes命令で文字を表示した場合は、指定した書体のものになります。
font命令は以下のようなパラメーターを持っています。

    			font "fontname",p1,p2,p3
    			"fontname"   : フォント名
    			p1=1〜(12)   : フォントの大きさ
    			p2=0〜(0)    : フォントのスタイル
    			p3=1〜(1)    : フォント修飾の幅

フォント名では、インストールされているフォント名を指定することができます。 Windowsでは、標準的にインストールされているフォントをマクロ名で指定することが可能です。msminchoと記述した場合は"ＭＳ 明朝"、msgothicと記述した場合は"ＭＳ ゴシック"が選択されます。
フォントの大きさはドット数に比例した論理サイズで指定します。 数が大きいほど、文字も大きくなります。フォントのスタイルは、

    			  スタイル 1  : 太文字
    			  スタイル 2  : イタリック体
    			  スタイル 4  : 下線
    			  スタイル 8  : 打ち消し線
    			  スタイル16  : アンチエイリアス

の値を指定できます。数値を合計することで複数のスタイルを同時に指定することも可能です。p2パラメーターを省略すると、0(通常のスタイル)になります。
mes命令のパラメーターは以下の通りです。

    			mes "strings",sw
    			"strings" : 表示するメッセージまたは変数
    			sw(0)     : オプション

mes命令の最初のパラメーターは、文字列または変数名を指定することができます。変数名を指定した場合は、変数に代入されている内容を表示します。
2番目のオプションパラメーターによって文字の修飾と動作の設定を行うことが可能です。

    			   値  : マクロ名       : 動作
    			 -----------------------------------------------------
    			   1   : mesopt_nocr    : 最後に改行しない
    			   2   : mesopt_shadow  : 影付き文字を描画する
    			   4   : mesopt_outline : 縁取り文字を描画する
    			   16  : mesopt_gmode   : gmodeの設定を反映する(HSP3Dishのみ)

オプションに2またはmesopt_shadowを指定した場合は、影の付いた文字を描画します。 オプションに4またはmesopt_outlineを指定した場合は、縁取りの付いた文字を描画します。 影と縁取りは、objcolor命令で指定された色が使用されます。 また、影と縁取りの幅はfont命令の第4パラメーターで指定することができます。

上の例では、左が影付き文字、中央が縁取り文字、右が通常の文字になります。 オプションに16またはmesopt_gmodeを指定した場合は、gmodeで指定した半透明、色加算などの設定を反映した描画を行います。 (このオプションは、HSP3Dish及びHGIMG4でのみ有効です。通常のHSP3ランタイムでは無視されます)
オプション値は加算することで複数を同時に指定することができます。 「mesopt_nocr+mesopt_shadow」を指定した場合は、影付きで改行しない表示となります。

### 画像ファイルの利用

画面内に表示する画像データとして、標準的な画像フォーマットのファイル を読み込んで使用することができます。

HSPから標準で利用できる画像ファイル形式 一覧
拡張子 | フォーマット
--- | ---
bmp | Windows標準ビットマップ画像データ
png | PNG形式画像圧縮データ
jpg | JPEG形式画像圧縮データ
gif | GIF形式画像圧縮データ(アニメーションを除く)
ico | Windows標準アイコン画像データ

画像の読み込みは、picload命令またはcelload命令によって行ないます。 ファイル名を指定することで、自動的にフォーマットを判別して読み込みます。 また、メモリストリーム機能を使用することで、メモリ上に存在する ファイルデータから画像を読み込むことも可能です。 pngフォーマットについては、COM機能を利用して読み込むことが可能です。 詳しくは、「HSP3支援モジュール」を参照してください。

### カレントポジション

メッセージの出力、グラフィックデータのコピー、ボタンの配置などは カレントポジションと呼ばれる座標を対象に行なわれます。

カレントポジションはテキストエディタのカーソルのようなもので、 メッセージが出力された後は、カレントポジションも改行した次の行に 自動的に移動します。 カレントポジションは、pos命令によって変更することが可能です。また、 objsize命令によって、カレントポジションの移動量を調節することができます。 cls命令などで画面が初期化された時は、カレントポジションも(0,0)にリセットされます。

### 配置オブジェクト

HSPでは、押しボタン、入力ボックスなどの部品を配置オブジェクトと呼び、画面内で 自由に配置することが可能です。 HSPでは、標準で以下のようなオブジェクトを使用することができます。

HSPから標準で利用できるオブジェクト 一覧
オブジェクト名 | 配置命令 | 内容
--- | --- | ---
ボタン | button | 押されると指定ラベルのプログラムを実行
入力ボックス | input | 数値や文字列を入力
メッセージボックス | mesbox | 複数行の文字列を入力
チェックボックス | chkbox | ON/OFFチェックマーク切り替え
コンボボックス | combox | 複数要素から選択可能な枠
リストボックス | listbox | 複数要素から選択可能な枠
レイヤーオブジェクト | layerobj | ユーザー定義描画オブジェクト

それぞれのオブジェクトには、オブジェクトIDという番号が割り振られ管理されています。 オブジェクトIDは、0から始まる整数値で、配置命令の実行後にシステム変数statに 代入されます。通常は、画面内に配置した順に自動的に0,1,2,3…と数値が割り振られます。

オブジェクトIDが必要な時は、オブジェクトの一部を後から消したい時、状態を変更したい 時などです。また、押しボタンが押された時には、システム変数statに押されたボタンの オブジェクトIDが代入されます。

オブジェクトは簡単に配置して使うことができますが、より奥深い使い方をマスターすれば、 さらに高度なスクリプトを書くことも可能になります。 オブジェクトの内容を後から変更するための、objprm命令や、オブジェクトにWin32メッセージを 直接送るsendmsg命令はそのような高度な使用のために用意された命令です。

ここに挙げた配置オブジェクト以外にも、Windowsがサポートするコントロールを winobj命令により追加できるほか、axobj命令によりActiveXコントロールを直接 追加することも可能です。

### レイヤーオブジェクト

レイヤーオブジェクトは、ユーザーによって定義される配置オブジェクトです。 あらかじめ画面上の描画を行うサブルーチンを登録しておくことで、指定したタイミング(レイヤー)で描画を実行させることができます。

    		　例 :
    				layerobj 320,240,objlayer_normal,*laysub,100
    				stop
    			*laysub
    				return

レイヤーオブジェクトはそれ自体では何の機能も持ちません。 画面内の特定のエリアをレイヤーオブジェクトとして管理し、描画のタイミングごとにユーザーのプログラム(コールバックルーチン)を呼び出します。 使い方は色々ありますが、後から追加されるモジュールなどで描画部分だけを独立して管理することができます。
また、objprm命令でパラメーターを受け取ることができ、独自の配置オブジェクトを設置したり、作成することも可能です。
layerobj命令は以下の書式で記述します。

    			layerobj p1,p2,p3,*label,p4
    			p1,p2     ; レイヤーオブジェクトのXYサイズ（ドット単位）
    			p3(0)     : レイヤーID
    			*label    : レイヤーオブジェクトの処理サブルーチン
    			p4=0〜(0) : オプション値

レイヤーオブジェクトに描画領域を通知するため、p1,p2でX,Yサイズをドット単位で指定する必要があります。p1,p2の指定を省略した場合は、画面全体のサイズとなります。 これで、カレントポジションX,Yを左上の座標として、p1,p2で指定したサイズまでをレイヤーオブジェクトとして扱います。 p3パラメーターで、描画するレイヤーを指定します。

    			  p3 : 描画するレイヤー
    			 --------------------------------------------------------------
    			    0 : 描画を行わない(objlayer_min)
    			    1 : 背景レイヤー(objlayer_bg)
    			    2 : 通常の描画レイヤー(objlayer_normal)
    			    3 : GUIレイヤー(objlayer_posteff)
    			    4 : 最前面の描画レイヤー(objlayer_max)
    			 +256 : レイヤーオブジェクトの重複登録を許可する(objlayer_multi)

*labelパラメーターで、レイヤーオブジェクトの処理サブルーチンを指定します。 このサブルーチンはユーザー自身が用意する必要があります。 p4パラメーターで、レイヤーオブジェクトに設定する任意の整数値を設定することができます。
*labelパラメーターで指定された、レイヤーオブジェクトの処理サブルーチンは、 様々なタイミングで呼び出されます。このサブルーチンは、コールバックルーチンと呼ばれる仕様を守る必要があります。(wait,await,stop等の時間待ちを行う命令を記述することができません)
処理サブルーチン呼び出し時には、システム変数iparam,wparam,lparamに必要な情報が代入されますので、この内容に従って処理を記述してください。

    			システム変数  : 内容
    			---------------------------------------------------------------
    			iparam        : 受け渡されるパラメーター
    			wparam        : 配置オブジェクトID
    			lparam        : 呼び出し要因

処理サブルーチンの中では、最初にlparamに代入された呼び出し要因を調べて、それに応じた処理を行ってください。 呼び出し要因は、以下の値のいずれかになります。

    			   値 : 呼び出し要因(マクロ名)
    			 --------------------------------------------------------------
    			    1 : 新しく設置された(objlayer_cmdinit)
    			    2 : オブジェクトが削除される前(objlayer_cmdterm)
    			    3 : objprm命令でint値を渡された(objlayer_cmdprmi)
    			    4 : objprm命令で文字列を渡された(objlayer_cmdprms)
    			    5 : objprm命令でdouble値を渡された(objlayer_cmdprmd)
    			    6 : 描画するタイミング(objlayer_cmddraw)
    			    7 : 一定時間が経過した(未実装)(objlayer_cmdtime)

必ずしも、すべての呼び出し要因を処理する必要はありません。 必要な要因だけを処理して、素早くreturn命令でシステムに処理を戻してください。
1はlayerobj命令が実行された直後、2はclrobj命令などでオブジェクトが破棄される直前に呼び出されます。 3〜5は、objprm命令で値が渡された際に呼び出されます。値の型によって値が変わるので、それに応じた対応を行う必要があります。 int値の場合は、iparam。文字列の場合は、refstr、double値の場合は、refdvalにそれぞれ渡された値が代入されます。
6は、redraw命令が実行された適切なタイミングで呼び出されます。 これは、あらかじめlayerobj命令で描画するレイヤーとして指定したレイヤーを描画するタイミングになります。
有効な描画画面に描画命令で描画を行ってください。(必ず描画を行う必要はありません) 描画の際にredraw命令は必要ありません。boxf命令やmes命令など任意の描画命令を使用できます。
レイヤーオブジェクトに割り当てられた描画エリアは、オブジェクト情報からユーザーが取得して範囲を考慮する必要があります。 objinfo関数を使用して以下のようなスクリプトで描画範囲を取得することができます。

    			; 描画の左上X,Y座標(x,y)を取得する
    			i=objinfo(wparam,objlayer_info_axis):x=i&0xffff:y=i>>16
    			; 描画のX,Yサイズ(sx,sy)を取得する
    			i=objinfo(wparam,objlayer_info_size):sx=i&0xffff:sy=i>>16

上の例では、変数x,yに左上のX,Y座標、変数sx,syにX,Yサイズが代入されます。
レイヤーオブジェクトは、HSP3標準ランタイム、HSP3Dish及びHGIMG4ランタイムで使用可能です。 HSP3標準ランタイムでは、他の配置オブジェクトはWindowsのシステムが描画しているためそれらに影響を与えることはできません。 HSP3Dish及びHGIMG4ランタイムでは、すべての配置オブジェクトをメイン画面に描画しているため、より自由に描画の優先度をコントロールできます。 フェードイン・アウトやポストエフェクト、描画済みの画面に対する加工処理など、様々な用途で使用することができます。

### カレントカラー

メッセージの出力、点、線、矩形の描画などに使用される色がカレントカラー です。これは、color命令、rgbcolor命令、hsvcolor命令、palcolor命令で変更することができます。

    		　例 :
    			color 100,200,300	; R=300,G=200,B=300に設定する
    			hsvcolor 10,20,30	; HSV形式で色を設定する
    			rgbcolor $112233	; 32bitのRGB形式で色を設定する
    			palcolor 20		; パレットカラーによる色の設定

cls命令などで画面が初期化された時は、カレントカラーは黒色にリセットされます。 また、pget命令により指定された座標の色をカレントカラーに設定することができます。
システム変数ginfo_r、ginfo_g、ginfo_bでカレントカラーのRGBをそれぞれ参照することができます。

### CEL関連命令について

2Dキャラクター表示のためのセル描画命令が用意されています。 これは、従来のgcopy命令に代わる分かりやすい記述と構造を持っています。
セル描画命令により、キャラクターの描画コストを減らし、その素材を明確に 分けることができます。もちろん、従来と同様のgcopy命令による表示も同じ ように利用することができます。
セル関連命令は、celload命令、celdiv命令、celput命令で構成されています。 celload命令は、表示用の画像(テクスチャ)をファイルから読み込むための命令です。 picload命令と同様に、指定された画像ファイルをメモリ上に展開しますが、 読み込み先のバッファを指定可能になっています。

    		　例 :
    			celload "a.bmp",1

上の例では、「a.bmp」という画像をウィンドウID1に読み込みます。 これは、buffer命令でウィンドウID1を初期化して、picload命令で画像を 読み込むという処理を1度に行ないます。 また、celload命令では空いているウィンドウIDに対して読み込みを行なうことが 可能になっています。

    		　例 :
    			celload "a.bmp"
    			id = stat

上の例では、変数idに読み込まれたウィンドウIDが代入されます。 このようにウィンドウIDのパラメーターが省略された場合には、未使用のIDが 使用され、その番号がシステム変数statに代入されます。
読み込まれた画像は、その全体または一部をcelput命令により描画することが できます。celput命令は、2Dスプライトに近い描画方法を提供します。

    		　例 :
    			celload "a.bmp",1
    			pos 100,100
    			gmode 2
    			celput 1

上の例では、ウィンドウID1にある画像(「a.bmp」の画像)を(100,100)の位置に、 コピーモード2で描画します。 描画位置は、pos命令で設定されたカレントポジションとなります。 描画される画像のサイズは、画像全体(ウィンドウID1のサイズ)になります。 これを変更する場合には、celdiv命令(後述)を使用します。
celput命令には、他にもいくつかのパラメーターが用意されています。

    			celput id,no,zoomx,zoomy,angle

    			id=0〜(1) : 画像素材を持つウインドゥID
    			no=0〜(0) : 分割画像No.
    			zoomx=0.0〜(1.0) : 横方向の表示倍率(実数)
    			zoomy=0.0〜(1.0) : 縦方向の表示倍率(実数)
    			angle=0.0〜(0.0) : 回転角度(単位はラジアン)

zoomx,zoomy,angleにより、元画像の素材に対して変倍、回転などの加工を設定 することができます。 zoomx,zoomy,angleの指定を省略するか、等倍の設定(1,1,0)になっている場合は、 自動的に高速なコピーが内部で実行されます。逆に、等倍でない描画を行なう場合は、 grotate命令と同様の描画処理が実行されます。
描画時は、gmode命令により、コピーモード及び透過率を指定することができます。 gcopy命令やgrotate命令と同様のオプションを指定可能です。 ただし、gmode命令で指定したコピーサイズは、celputの際には反映されないので 注意してください。
描画終了後は、描画したサイズに応じてカレントポジションを右に移動します。

    		　例 :
    			repeat 5
    			celput 1
    			loop

上の例では、同じ画像が横に５個並べて描画されます。
元画像の素材を決められたサイズで複数のパーツに分割することができます。 celdiv命令は、ウィンドウIDごとにパーツのサイズ分割を設定しておくための命令です。

    		　例 :
    			celload "a.bmp",1
    			celdiv 1,64,64

上の例では、ウィンドウID1の画像を64×64の画像パーツ単位に分割します。 celput命令では、分割された大きさで描画を行ないます。 たとえば、256×256ドットの画像を64×64ドットごとに分割した場合には、 画像１６個分(全体を4×4に分割)として扱われます。 分割された画像は、celput命令の2番目のパラメーターで指定されます。 ここで指定される、「分割画像No.」は分割された領域を0,1,2…という数値で 表現しています。 4×4に分割した場合は、以下のような順番で番号が付けられます。

0 | 1 | 2 | 3
--- | --- | --- | ---
4 | 5 | 6 | 7
8 | 9 | 10 | 11
12 | 13 | 14 | 15

celdiv命令は、描画の基点位置を設定する機能も持っています。 通常は、gcopy命令と同様にcelput命令も描画位置(pos命令で指定した座標)から 右下方向に向けて矩形を描画します。 しかし、描画の基点位置を設定することで、描画位置が元画像の中心に来る形や、 右下にするなど自由な変更を行なえます。また、回転もこの基点を中心に行います。

    		　例 :
    			celload "a.bmp",1
    			celdiv 1,64,64,32,32

上の例では、ウィンドウID1の画像を64×64のパーツ単位に分割し、 描画の基点(中心)が(32,32)つまり、中心になるように設定されます。

画像素材の分割設定は、ウィンドウIDごとに保存されており、cls命令などで初期化された 時点では、分割なし(画面全体をサイズとして指定)の設定になっています。 分割の設定は、必ず指定されたウィンドウIDに画像素材が読み込まれた状態で行なってください。 分割の設定を行なった後に、画像素材の読み込みを行なった場合は、分割設定はリセットされます。

### 入力の取得

HSPでは、ユーザーからの入力を受け取る様々な方法用意されています。 基本的なGUIパーツ(配置オブジェクト)や、マウスからの入力、さらにキーボードや接続されたデバイスの情報などを取り込むことが可能です。 以下は、GUIパーツ(配置オブジェクト)を使用して数値の入力を可能にして、内容を表示するスクリプトの例です。

    例:
    				a=1
    				input a
    				button "OK",*ok
    				stop
    			*ok
    				mes a
    				stop

配置オブジェクトを利用することで、手軽にユーザーからの入力を得ることができます。
さらに直接、キーボードやマウスの状態を調べる命令も用意されています。

    			入力を得る命令  :  内容
    			-------------------------------------------------------
    			  getkey        :  キーボードの特定のキーが押された情報を得る
    			  stick         :  ゲーム等で多く使用されるキーボードの情報を得る
    			  onkey         :  キーが押されるたびに入力を受け取る
    			  onclick       :  マウスボタンが押されるたびに入力を受け取る
    			  mousex        :  マウスのX座標を得る(システム変数)
    			  mousey        :  マウスのY座標を得る(システム変数)

この他にも、接続されたデバイスや通信を行った結果を取得するなど、様々な機能が用意されています。 ゲーム等で利用されるゲームパッド(ジョイスティック)は、拡張モジュールの mod_joystick.as、mod_joystick2.asを利用して取得できます。詳しくは、jstick命令のヘルプを参照してください。

### メモリバッファの使用

変数が保持するメモリ空間を、データを取り扱うメモリバッファとして 操作することができるようになっています。 これは、データの長さが不定の文字列やバイナリデータなど 様々な形式を処理する際に利用できます。

変数が管理するメモリバッファのサイズは可変長で、、Windowsが許す限りの メモリを扱うことができ、上限はありません。 メモリバッファは、通常sdim命令またはalloc命令で確保することができます。 (途中でサイズを拡張する場合は、memexpand命令を使用します。) メモリバッファのファイル入出力は、通常bload命令、bsave命令で行ないます。

また、テキストファイルを扱うためにnoteload命令、notesave命令 が用意されています。詳しくは、「メモリノートパッド命令」の項を参照してください。 扱う対象がテキストファイルであっても、バイナリのデータであっても基本的に メモリバッファの扱いは変わりません。

メモリバッファのデータに対しては、poke命令、wpoke命令、lpoke命令等で直接 アクセスができるほか、peek関数、wpeek関数、lpeek関数により内容を読み出すことが 可能になっています。 尚、データをまとめてコピー、フィルするために、memcpy命令、memset命令が用意されています。

外部DLLやプログラムで扱う場合には、変数が扱うメモリバッファのポインタをvarptr関数により取得することが可能です。 また、変数が扱うメモリバッファのサイズをvarsize関数により取得することが可能です。

### マルチメディア再生

HSP3では、WAV形式のPCM音声ファイル、SMF(MID)形式の標準MIDIファイル、 CDの音声トラック再生、AVI/MPEG動画ファイルやMP3/WMA等の圧縮音声 ファイルなどを手軽に扱うことができます。 また、MCIコントロールデバイスとして登録されている機器のコントロール も可能です。

再生するためのファイルは、mmload命令によってHSPに登録され、mmplay命令で 再生を開始することができます(再生中断はmmstop命令)。 それぞれのサウンドは、ループ再生、再生終了までのウエイトを選択できます。

ただし、MIDIのループ再生には問題があり完全なループ演奏にはなりません。 WindowsのMIDIシーケンサーが演奏開始まで時間がかかるのと、MIDIデータには 最初に音源を初期化するコードや音色の指定などで時間がかかる場合が多いため 演奏終了から、ループまでがうまくつながらないことが多いからです。

ですから、この機能はあくまで簡易のループということをご了承ください。 そもそもループポインタが指定できないので、前奏まで戻るのも変なんですが、 ゲーム中にBGMが終わってしまうのが寂しいのを避けるためと割り切って使う ことはできるかもしれません。

MIDIのデバイスドライバによっては、MIDI演奏をストップした直後に、再びMIDI 演奏を開始するとWindowsがフリーズしたり演奏されないものがあるようです。 そのような場合には、MIDI演奏終了後に１〜２秒ほどのウエイト(wait 20など)を 入れてから、次の演奏を開始するようにしてみてください。

WAV形式のPCM音声は、最初にすべてメモリにロードされてから再生されるので ループ再生も問題なく行われます。ただし、WAVファイルのサイズだけメモリ を占有するので、あまりにも巨大なファイルは、mci命令で再生するようにしてください。

ogg形式の圧縮音声ファイルをhspoggプラグインから扱うことにより、 メモリ効率良く、完全なループ演奏を実現することが可能です。

### 未初期化の変数検出

スクリプトのコンパイル時に、未初期化の変数参照を検出することができます。 未初期化の変数参照とは、値を設定していない変数の内容を読み出すことを 指しています。

    		a=1:b=2		; 変数aとbを初期化
    		c=a*b+x		; 変数xは初期化されていない

たとえば、上の例では変数aとbは代入により値が入っていますが、 変数xは、「c=a*b+x」という計算の時点でまだ値が代入されていません。 HSPでは、このような場合内容を0として扱っていて、代入しない状態でも 気楽に変数を使用することができました。ただし、これにはデメリットも あります。

    		hensu=1		; 変数hensuを初期化
    		kotae=hensuu*2	; 2倍したものを計算

上の例では、変数hensuを2倍した値を計算しようとしていますが、 間違って「hensuu」と打ってしまいました。このような場合でも、計算は 実行されますが、変数hensuuは0のため思った計算結果にはなりません。 こうした打ち間違いによるミスは、なかなか見つけることが難しく 不具合を生み出す原因になります。

HSPコンパイラは、こうした参照をコンパイル時に検出します。 上のスクリプトでは、[F7]キーを押してレポートを表示すると、 以下のような警告が表示されます。

    		#未初期化の変数があります(hensuu)

標準の状態では、レポートで警告を行なうだけですが、これをエラーとして 扱うように変更することもできます。

    		#cmpopt varinit 1	; 未初期化の変数参照をエラーにする

上のようにプリプロセッサ命令#comoptを使って、未初期化の変数参照を エラーとして扱う設定をコントロールすることができます。 エラーとして扱わない場合は、以下のように記述します。

    		#cmpopt varinit 0	; 未初期化の変数参照をエラーにしない

#comoptによる設定は、それ以降の行で反映されます。 上のスクリプトであれば、最初の行に入れることでエラーを発生させる ことができます。

    		#cmpopt varinit 1	; 未初期化の変数参照をエラーにする
    		hensu=1		; 変数hensuを初期化
    		kotae=hensuu*2	; 2倍したものを計算

この場合、以下のようなエラーメッセージが出て実行はされません。

    		???(3) : error 39 : 未初期化変数を使用しようとしました (3行目)
    		--> kotae=hensuu*2

すべての未初期化変数参照を標準的にエラーとしたい場合は、 commonフォルダにあるhspdef.asに、#cmpoptの設定を追加してください。 変数が初期化されているという検出は、以下のいずれかの状態で判断されています。

    		・変数への直接代入(変数=値)
    		・dim,sdim,dimtype,ddim,ldim,allocによる初期化
    		・dup,dupptrによる初期化

これ以外の状態は未初期化と判断されます。 たとえば、stick命令のような指定変数に直接値を返す場合は考慮されません。 ですから、この機能を利用する場合はなるべく使用する変数を最初に 意識して初期化することをおすすめします。

### コンソール版HSP

HSP3では、コマンドプロンプト上で動作するテキスト表示のみのランタイムを 標準で同梱しています。 コンソール版HSP(HSPCL)は、以下のような場面で使うことができます。

- バッチファイルの代用として単純処理を行なう
- WindowsベースのWEBサーバー上でのCGI実行する
- メモリやCPUの負担をより軽くしてHSPスクリプトを実行する

HSPCLを使用する場合は、スクリプトの先頭に以下の行を付加して下さい。

    #runtime "hsp3cl"

ランタイムが切り替わり、自動的にコマンドライン上で動作する状態になります。 尚、ウィンドウ、オブジェクト、マルチメディアに関する命令は使用できません。 sample/hspclフォルダに、HSPCL用のサンプルが収録されているほか、 sample/hspcl/cgiフォルダにCGIとして動作させる場合のサンプルがあります。

HSPCLでは、専用のinput命令が実装されています。書式は以下の通りです。

    input p1,p2,p3                標準入力を取得

    p1=変数名  : 情報を格納する変数名
    p2=1〜     : 変数に代入される最大文字数
    p3=0〜(0)  : 改行コード認識フラグ(0=なし/1,2=認識)

    ・説明

        標準入力の内容をp1で指定された変数に代入します。
        p1で指定された変数は文字列型に変更され結果が代入されます。
        この場合、文字列のサイズは、p2で指定した文字数までとなります。
        p2の指定が省略された場合は、変数に代入できる最大値となります。
        p3を省略するか、0を指定した場合は標準入力すべて(EOFまで)を
        そのまま取得します。p3に1が指定された場合は、改行コード(13)を
        終端として認識します。標準入力として、キーボード入力(コンソール)
        を使用する場合には、p3を1または2にすることにより[Enter]キーで入力を
        確定させることが可能になります。
        (p3が1の場合はLFを、2の場合はCR+LFを改行として認識します)
        通常は、文字列終端(文字コード0)までを取得しますが、p3の値に
        16を加算することで、文字コード0を含む標準入力バッファ内容すべてを
        変数バッファに取り込みます。バイナリデータなど0を含むコードを
        取得する場合に指定するようにしてください。

入力結果の文字数はシステム変数strsizeに保存されます。

それ以外の機能は、HSP3相当として使用することが可能です。 拡張プラグインや、COM関連命令も使用可能です。(ただし、プラグイン側が BMSCR構造体を取得して使用するような場合に動作エラーとなることがあります。) 実行ファイル作成、PACKFILEの埋め込み、暗号化などもサポートしています。

### 割り込み

色々な要因による割り込み処理がサポートされています。 割り込み処理のために以下の命令が用意されています。

    onkey goto/gosub *label          キー割り込み実行指定
    onclick goto/gosub *label        マウスクリック割り込み実行指定
    oncmd goto/gosub *label,p1       ウィンドウメッセージ割り込み実行指定
    onexit goto/gosub *label         プログラム終了時に割り込み
    onerror goto/gosub *label        エラー発生時に割り込み

    button goto/gosub "name",*label  ボタンを押した時の割り込み

onkey命令でラベルを指定すると、それ以降はHSPのウィンドウがアクティブな 時にキー入力が発生するたびに*labelで指定したラベルにジャンプ (またはサブルーチンジャンプ)します。 onclick命令でラベルを指定すると、それ以降はHSPのウィンドウ上でマウス クリックが行なわれるたびに*labelで指定したラベルにジャンプ (またはサブルーチンジャンプ)します。 oncmd命令は、ウィンドウに特定のメッセージが通知された時の処理を 設定するためのものです。(詳しくは、リファレンスを参照してください。)

button命令も割り込み設定の1つとして考えることができます。 配置したボタンが押された時の処理を設定することができます。

onexit命令は、クローズボックスや[Alt]+[F4]によりプログラムが 中断された場合の処理を設定します。

onerror命令は、スクリプトが原因でHSP内部でエラーが発生した時の、 処理を設定します。 onerror命令によりエラー後の処理を指定した場合であっても、 必要な処理が終わったら、そのままアプリケーションの実行は再開 せずに、なるべくend命令で終了させてください。 onerror命令は、エラーから回復させるものではありません。 エラー発生の原因によっては、HSPのシステム自体が不安定になったり 障害が発生することも有り得ます。 onerror命令を使う場面としては、実行ファイル作成時にエラーが 発生した場合にアプリケーション側で独自のエラー表示を行ないたい 場合や、特定のエラーが発生する場合にだけデバッグのための表示を 行なうなどが考えられます。

割り込みによるジャンプ(on??? gosubを使わない割り込み)が発生した 場合には、サブルーチンやrepeat〜loopのネストはすべて0(初期状態)に 戻されます。サブルーチン先で割り込みジャンプが行なわれる場合などは メイン側にプログラム制御を戻すようにしてください。

キーやマウス入力による割り込みは、stop命令および、wait、await命令で 停止している時にのみ割り込みを受け付けます。 また、割り込みによりジャンプを行なった後は以下のシステム変数が セットされます。

割り込み要因 | iparam | wparam | lparam
--- | --- | --- | ---
onkey | 文字コード | wParam | lParam
onclick | マウスボタンID | wParam | lParam
oncmd | メッセージID | wParam | lParam
onexit | 終了要因 | ウィンドウID | lParam
onerror | 0(なし) | エラー番号 | エラー発生行番号

システム変数iparamには、割り込み要因ごとのパラメータが代入されます。 また、wparam,lparamはWindowsメッセージとして渡されたパラメータが そのまま格納されています。

イベント割り込み実行の一時的なON/OFFをすることも可能です。

    onkey 0

で一時的にキー割り込みを停止します。

    onkey 1

で一時停止したキー割り込みを再開させることができます。同様に、

    onclick 0 / onclick 1 / onerror 0 / onerror 1 / onexit 0 / onexit 1

なども使用できます。

onexit命令は、Windowsシャットダウン時(再起動、電源を落とす等)にも 割り込み処理が実行されます。 これにより、スクリプト実行中にシャットダウンが起こった場合でも、 それを検知して適切な終了処理を行なうことが可能になりましたが、 以下のような制限がありますので、終了時を判定するスクリプトを作成 する場合は、留意しておいてください。

onexitでジャンプされた直後は、システム変数iparamに終了要因が値として 保存されています。 iparam = 0 の場合は、ユーザーの意思でプログラムを終了。 iparam = 1 の場合は、Windowsシャットダウンによる終了です。

Windowsシャットダウン時の終了処理には、await、wait、stop命令 などでシステムにアイドルタイム(待ち時間)を発生させた場合には、 シャットダウン処理を中止します(シャットダウンされません)。 await、wait、stop命令を使わずにend命令で終了した場合には、 そのままシャットダウン処理が継続されます。

シャットダウン処理を中止させないで、終了処理を行ないたい場合には、 待ち時間の発生する命令(await、wait、stop命令など)は使用できません。 (ただし、コモンダイアログ(dialog命令)などは使用できます) 最小限の終了処理だけを行なって、そのまま終了するようにしてください。

### 標準モジュール名

HSPがシステムで使用している標準キーワードは「@hsp」というモジュール名の 空間に割り当てられています。 これは、たとえばmes命令であれば、「mes@hsp」という名前が正式な名称であることを 意味します。ただし、通常のグローバルな空間でも使用できるように「@hsp」がない 名称も別名として登録されているので、いままで通りの命令名でそのまま使用することが できます。

たとえば、mes命令であれば「#define global mes mes@hsp」が最初から定義されている のと同じです。(プリプロセッサ処理後に、標準キーワードは「@hsp」が付けられた 正規の名称に展開されます。コンパイル時に出力される「hsptmp.i」を開いてみると わかると思います。)

これにより、標準キーワードとして登録されている名称そのものを別名にしたり、 ユーザーが定義し直すことが可能になります。 以下は、mes命令をマクロにより置き換えている例です。

    ; 例
    #undef mes
    #define mes(%1) mes@hsp "MES->"+%1
        mes "メッセージです。"
        stop

「mes」というキーワードを、#undef命令により取り消した後、再定義しています。 HSPで使用される標準キーワードすべては、同様に取り消し、再定義することが可能です。 以下は、mes命令をユーザー定義命令に置き換えている例です。

    ; 例
    #undef mes
    #module
    #deffunc mes str _p1
        _x = ginfo_cx : _y = ginfo_cy
        pos _x+1,_y+1
        color 0,0,0
        mes@hsp _p1
        pos _x,_y
        color 0,192,255
        mes@hsp _p1
        return
    #global
        mes "mes命令を影文字にしてみました。"
        stop

標準キーワードの再定義は、それ以降のキーワードすべてに影響があるため 注意して使用してください。

### コモンディレクトリ

「#include」命令で挿入されるファイルは、通常はソースファイルと 同じ(カレント)ディレクトリにあるものが使われますが、そこにない場合は、 コモンディレクトリにあるものを使います。

コモンディレクトリは、HSED3.EXEやHSP3.EXEと同じディレクトリにある 「common」という名前になります。 たとえば、「c:\hsp」というディレクトリにHSED3.EXEがあり、 「c:\script」というディレクトリで「test.as」というスクリプトを編集して いるとすると、「#include」命令でファイルをサーチする順番は、

1. 「c:\script」にあるファイルをサーチ
2. なければ「c:\hsp\common」にあるファイルをサーチ
3. それでもなければエラー

のようになります。 拡張プラグインを使うためのファイル、(hspext.asなど)は、すべて コモンディレクトリに置かれています。 また、hsp3util.asなど、よく使われるファイルも格納されています。

### エラーメッセージ

スクリプトの書き間違いや、指定のミスなどでHSPの実行中にエラーを 発見した時には、エラーコードとエラー行番号が表示されるようになっています。 詳しくはエラーメッセージ一覧をご覧ください。

### HSPシステムの許容範囲

現バージョンでのシステムリソースの許容範囲は以下の通りです。 この値をオーバーしてしまった場合、正常な動作の保証はできません。

システムリソースの許容範囲 一覧
ソースファイル(.AS)の最大サイズ | 無制限
オブジェクトファイル(.AX)の最大サイズ | 無制限
宣言できるラベルの最大数 | 無制限
宣言できる変数の最大数 | 無制限
識別されるラベル・変数の最大文字数 | 59文字(半角)
命令内で使用できる文字列の最大 | 無制限
変数に保持できる文字列の最大 | 無制限(メモリが許す限り)
変数に保持できる配列の最大 | 無制限(ただし4次元まで)
定義可能なプラグインの最大数 | 合計8192タイプまで
プラグインで拡張可能な命令最大数 | 1タイプあたり65536まで
定義可能なモジュールの最大数 | 無制限
モジュール内のユーザー定義命令最大数 | 無制限
oncmd命令で定義可能な割り込み最大数 | 無制限
ウィンドウIDの最大数 | 無制限
1画面内のオブジェクト数 | 16384個(1024個で警告あり)
表示できるウィンドウの最大サイズ | 無制限(メモリが許す限り)
表示できる画像の最大サイズ | 無制限

### PACKFILEと暗号化

HSPでは、実行ファイルを作成する際に、読み込まれるファイルをまとめて 1つの実行ファイルに埋め込むことが可能です。 これを、ファイルのパックと呼んでおり、まとめるファイル一覧の情報を 記録したファイルをパックファイル(PACKFILE)と言います。

パックファイルには、無制限に複数のファイルを指定することができます。 パックファイル内で階層を表現することはできません。 ファイル名は、拡張子も含めて15文字までが識別対象になります。 (ロングファイル名でも認識します。)

また、1つの実行ファイルにパックするのではなく、独立したファイルに パックすることも可能です。この独立したファイルをDPMファイルと呼び、 拡張子が「.dpm」になります。

実行ファイルを作成した場合に、ファイルを読み込みのために検索される順序は、 以下の通りです。

1. 実行ファイルにパックされたファイル
2. 実行ファイルと同じ場所に置かれた「data.dpm」にパックされたファイル
3. カレントディレクトリにあるファイル

パックする際に、暗号化を施すことができます。 これにより、実行ファイルやDPMファイルを解析されることを難しくします。 暗号化されたファイルを扱う場合には、いくつかの方法があり、 解析のされにくさも異なります。

実行ファイルに暗号化してパック

  HSP2.61と同様に、パックされる内容をもとに生成された暗号キーを使用して、 暗号化を行ないます。標準的な方法です。

DPMファイルに暗号化してパック(1)

  スクリプトエディタのメニューから、「DPMファイル作成」機能を使用して作成します。 作成されたDPMファイルは、暗号化されている場合にはエディタ等でデータの確認が 難しくなりますが、HSPを使って用意に中身を取り出すことが可能です。 実行ファイルにパックする場合よりも、セキュリティは低くなります。

DPMファイルに暗号化してパック(2)

  HSP3.0から追加された、個別の暗号キーを設定した状態でDPMファイルを作成します。 作成されたDPMファイルは、暗号化に使用されたキーコード(32bit値)をスクリプトから 指定しない限り正しく開くことができません。 暗号キーをスクリプト内に持っているため解析が難しく、 実行ファイルにパックする場合よりも、セキュリティは高くなります。

  暗号キー設定によるDPMファイルを作成は、スクリプトエディタの「ツール」→「指定フォルダからDPM作成」メニューを 選択することでツールを呼び出すことができます。 このツールでは、指定フォルダに含まれるファイルをDPMファイル内に暗号化して格納することができます。 詳しくは、サンプルスクリプトの「mkpack.hsp」を参照してください。

#epack命令により暗号化されたファイルをbload命令で読み込む場合は、 ファイルのオフセット値を指定することができませんので注意してください。

HSPが提供する暗号化機能は、必ずしも完全なセキュリティを保障するものではありません。 問題が発覚したり、セキュリティホールが発見された場合には、今後も適宜対応して いきたいと考えています。また、ファイル解析や改ざんなどに対する対処は、 今後も継続していく予定です。

### Cソースへの変換

HSP3のスクリプトを、C/C++言語用のソースコードとしてコンパイル可能な形に変換することができます。
これは、HSP3ソースコンバーター(hsp3cnv)を呼び出すことにより行ないます。 通常は、HSPスクリプトエディタの「ツール」→「指定ファイルをCソースに変換」メニューを選択して、サポートツール(cnvsrc)を呼び出してください。

変換されたCソースコードの運用については、C/C++言語についての知識が必要となります。詳しくは、 HSP3ソースコンバーターマニュアルをご覧ください。

### 起動設定#bootoptの利用について

新規プリプロセッサ命令、#bootoptが追加されています。 これはランタイムの細かい動作設定を行なうためのものです。 以下の書式で設定を変更することができます。

    		#bootopt オプション名  設定スイッチ

    		オプション名 : オプションの種類
    		設定スイッチ : 1または0の数値

スクリプトランタイムの細かい動作設定を行ないます。 オプション名、の後スペース又は TAB を入れて設定スイッチ1か0の数値を記述して下さい。 #bootoptは、スクリプト内の任意の位置に記述することが可能です。 複数の指定があった場合は、最後に設定されたものが全体の設定となります。 #bootoptで指定できるオプション名は以下の通りです。

    		  オプション |      内      容                | 初期値
    		 -----------------------------------------------------------
    		  notimer    | 高精度タイマーの使用           | 自動設定
    		             | (0=使用する/1=使用しない)      |
    		  utf8       | UTF-8形式文字列の使用          | 自動設定
    		             | (0=使用する/1=使用しない)      |
    		  hsp64      | 64ビットランタイムの使用       | 自動設定
    		             | (0=使用する/1=使用しない)      |
    		 -----------------------------------------------------------

通常は、これらの設定は自動的に行なわれます。 「高精度タイマーの使用」は、標準HSPランタイムにおいて高精度タイマー(マルチメディアタイマー)を使用するための設定です。
HSP3.4では、すべてのスクリプト実行時に高精度タイマーを使用してawait命令などの待ち時間を調整していましたが、 それほど厳密なタイマー制御が必要ないツールや常駐アプリケーションにおいて、CPUの負荷が増大することがありました。 HSP3.5以降では、スクリプト中でawait命令を使用している場合のみ、高精度タイマーを使用する形に修正されています。
明示的に高精度タイマーの使用をON/OFFしたい場合に、#bootopt命令を使用することができます。 「#bootopt notimer 1」をスクリプトに記述した場合は、高精度タイマーを使用しません。

### コールバックルーチン

コールバックルーチンは、割り込み時など限定された状況で、すぐにreturn命令により処理を終了させる必要があるものを指します。
以下の状況で呼び出されるラベル、またはユーザー定義命令はコールバックルーチンとして扱われます。

    	・#deffunc命令で定義されるクリーンアップ命令の実行時
    	・モジュール型変数のコンストラクタ、デストラクタ実行時
    	・配置オブジェクト(objlayer)によるユーザー割り込み
    	・es_setgosub命令によるスプライト表示割り込み(HSP3Dish)

コールバックルーチンは、通常のサブルーチンと同様に記述することができますが、 時間待ちや停止をさせることができません。 以下の命令は、コールバックルーチンで使用することができません。

    	wait,await,stop等の時間待ちを行う命令
    	処理を別なプログラムに移行するrun命令
    	screen,bgscrなどシステムタスクを実行する命令

コールバックルーチンの実行時は、通常とは異なり別なシステムにタスクを 戻す必要がある状態のため、このような制約が発生します。
コールバックルーチンでは、できる限り必要な処理だけを実行して、 すぐにreturn命令でタスクを終了してください。 もし、コールバックルーチンの実行中に使用できない命令があった場合は、 エラー42(コールバック内で使用できない命令です)というエラーが発生します。