ActBa64

ActBa 言語仕様

ActiveBasic 互換の サブセット 仕様です。完全互換ではありません。

実装 成果物 対象
actba64(既定) actba64.exe 64bit PE(PE32+ / AMD64)
actba64 -actba32 同じ actba64.exe 32bit PE(PE32 / i386)

言語仕様は 1 本です。残る差分はポインタ / String / HANDLE の幅(8 vs 4)、呼び出し規則(Microsoft x64 vs stdcall)、PE 形式だけです。
ビルド手順は build.md、ActiveBasic との差分は different.md を参照。


1. ソースとプロジェクト

1.1 ファイル

拡張子 意味
.abp ソース 1 ファイル
.pj プロジェクト(複数 .abp を結合)

1.2 字句

行の継続:

Print a +_
    b +_
    c

行末の _(直後の空白・'注釈 可)で次行と 1 文に結合する。継続の先にある整数は式の一部で、行番号にはならない。
( ) / [ ] 内では、( [ , の直後や閉じ括弧前など、パラメータ単位の改行も可(改行は空白扱い)。

1.3 ディレクティブ行

行 意味
#console Include\console\console.idx を自動挿入
#n88basic / #N88BASIC / #prompt N88BASIC 互換モード(§1.6)。#prompt は完全別名
#USEWINDOW=0\|1 0=CUI / 1=GUI。ソース・.pj どちらでも可。#n88basic / #prompt は GUI を強制
#strict 型の暗黙変換を warning(As で抑制)。Long→Byte はデータ欠落、引数の String→VoidPtr と VoidPtr→Long も含む
#include "path" / #include <path> ファイル挿入(深さ上限あり)。"..." はソース相対(絶対パス可)、<...> は Include\ 配下を検索
#define / #ifdef / #ifndef / #else / #endif 条件付きコンパイル(§7.7)
#resource リソース埋め込み(§5.2.2)
その他 #... 字句レベルで行スキップ、または実装依存で無視

ソース 1 行の最大長は 1024 バイト(PP_LINE_MAX。改行は含まない)。超過するとコンパイルエラー(too long line)。長い If 条件などは複数行へ分割する(§1.2 の _ や括弧内改行)。

Include\default\default.idx(Windows.sbp / WinConsts.sbp / WinTypes.sbp、Math.abp、CvtNum.abp、Pow.abp、Sleep.abp、Space.abp、DoubleStr.abp、BasicFile.abp、StringGc.abp、StrUtils.abp、Val.abp、Using.abp、SplitPath.abp、Memory.abp、BasicCmd.abp)は 常時 先頭へ挿入される(src/Include)。
#console / #n88basic はそれに加えて各プロファイル idx を挿入する。

1.4 .pj(プロジェクト)

' ActiveBasic Project file.
#NAME=example
#PLATFORM=32          ' AB4.20 が stage0 を 32bit ホストとして出すため。出力ターゲットではない
#USEWINDOW=0          ' 0=CUI / 1=GUI
#OUTPUT_RELEASE=.\out.exe
#SOURCE
a.abp
b.abp
指令 意味
#SOURCE 必須。続く行が入力 .abp(結合して 1 コンパイル単位)
#OUTPUT_RELEASE .pj からの相対出力パス。-o が無いとき使う(-o が優先)
#USEWINDOW サブシステム(既定 CUI。#n88basic で GUI)
#MODULETYPE 0 = EXE(既定)、1 = DLL(§8.4)
#PLATFORM AB4.20 用。actba64 は読まない(対象は -actba32)
その他 無視

#SOURCE のソースはテキスト結合して 1 コンパイル単位 になる。各ファイルと、そこから読む #include には、結合前に §7.7 を掛ける。指令行と無効な行は空行のまま残し、診断の行番号は元のファイルに合わせる。

1.5 CLI

actba64 <src.abp|.pj> [-actba32] [-o <out>]

-o は .abp では必須。.pj に #OUTPUT_RELEASE があれば省略でき、そのパスは .pj のあるディレクトリ基準になる。-o を付けたときはそちらが優先。-actba32 は引数のどこでも可。省略時は PE32+。

EXE のエントリは結合後ソースのトップレベル文です。終了は ExitProcess(n) または End(終了コード 0)。
DLL(#MODULETYPE=1)の入口は DllMain です(§8.4)。
#n88basic 時の End は窓を出して閉じるまで待つ(actba64)。

1.6 N88BASIC モード

#n88basic / #N88BASIC / #prompt で Include\N88BASIC\n88basic.idx(n88graph.abp と n88key.abp)を挿入する。
#prompt は ActiveBasic 互換の別名で、#n88basic と全く同じ動作。

  既定(64bit) -actba32
ライブラリ挿入 ○ ○
LINE / CIRCLE / LOCATE / PAINT 文 ○(パーサ) ○(同じパーサ)
Inkey$ / Input$(n) ○(n88key.abp) ○
640×480 黒窓 GUI ○(自動) ○
End → 窓待ち ○(N88_End) ○

N88 LINE(actba64 サブセット):

LINE (x1,y1)-(x2,y2)[,color][,B|BF]
LINE -(x2,y2)[,color][,B|BF]          ' 始点は LP(最終参照点)

N88 CIRCLE(actba64 サブセット):

CIRCLE (x,y),r[,color][,start][,end][,aspect][,F[,color2]]
CIRCLE STEP(x,y),r[,...]              ' 中心は LP からの相対
CIRCLE ,r[,...]                       ' 中心は LP

N88 LOCATE / PAINT:

LOCATE x, y                 ' または Locate(x, y) — 桁 x, 行 y(0 始まり。本家 N88 の 行,桁 1 始まりとは異なる)
PAINT (x, y), color1 [, color2]

色番号(0..7、N88 8 色):

値 色
0 黒
1 青
2 赤
3 マゼンタ
4 緑
5 シアン
6 黄
7 白(省略時)

サンプル: src/actba64/samples/n88_shapes.abp


2. 型

型 サイズ (32) サイズ (64) 備考
Byte 1 1  
Word 2 2  
Single 4 4 サイズと格納。汎用 IEEE 演算は未(Double を使う)
Long / DWord / Integer 4 4 別名あり
Double 8 8 IEEE 倍精度。加減乗除・比較・Function As Double / Double 仮引数、配列要素、UDT フィールドは 64bit と -actba32 の両方。32bit の値は edx:eax
Enum 名 4 4 列挙。メンバは整数定数(§3.4)
HANDLE / HWND 等 4 8 64bit は VoidPtr / *T(HFILE は 4 のまま)
*T 4 8 ポインタ
String ポインタ相当 (4) ポインタ相当 (8) 長さプレフィックス付きバイト列(AB4.20 互換)
Type 名 メンバ合計 メンバ合計 自然整列

条件式は「0 以外が真」。

2.1 配列

Dim a(N) As T           ' 1D: 添字 0..N(要素数 N+1)
Dim a[N] As T           ' 同上(バッファ記法)
Dim a(N, M) As T        ' 2D: 添字 (0..N, 0..M)
Dim a[N, M] As T        ' 同上(バッファ記法)
Dim a(N, M, L) As T     ' 3 次元以上。最大 8 次元

境界は定数式可(例: Dim Board(BOARD_SIZE - 1, BOARD_SIZE - 1) As Long)。
ELM(n) は要素数 n に対する上限で、値は n - 1。Dim a[ELM(10)] は Dim a[9] と同じ(添字 0..9)。
要素型は Byte / Long / Single / Double / String / UDT。UDT 配列は添字 1 個(平坦な通し番号)だけ。

レイアウト(first-index-fastest): 先頭添字が連続する。各次元の長さは上限+1。

flat = i0 + i1 * (N + 1)          ' Dim a(N, M) の a(i0, i1)
L0 = N + 1
L1 = M + 1
flat = i0 + i1 * L0 + i2 * L0 * L1   ' Dim a(N, M, L) の a(i0, i1, i2)

3 次元以上も同じで、k 番目の刻みはそれより前の長さの積。添字は括弧・角括弧とも可。個数は 1(平坦な通し番号)か、その配列の次元数。要素数は各長さの積。
境界は Dim のときに決まり、実行中には変えない。ReDim は Visual Basic の機能で、ActiveBasic 4.20 にも ActBa64 にも無い。

回帰: test/t_arr2d_rw.abp、test/t_arr2d_const.abp、test/t_arr3d_rw.abp。サンプル: samples/reversi.abp。

2.2 ユーザ定義型

Type COFF_HEADER
    Machine As Word
    NumberOfSections As Word
    TimeDateStamp As DWord
End Type

TypeDef HWND = Long
TypeDef PBYTE = *Byte
TypeDef PADD = *Function(a As Long, b As Long) As Long

メンバ参照: x.field / p->field / a(i) / a(i, j) / p[i](組み合わせ可)。
関数ポインタ型(*Function / *Sub)は §5.2.1。


3. 宣言

3.1 Const / Dim

Const MAX_PATH = 260
Const GENERIC_READ = &H80000000
Const NEXT = GENERIC_READ Or 1
Const MSG = "ok"
Const INC(n) = n + 1

Dim a As Long
Dim b = 3 As Long
Dim c As Integer = 4
Dim s$
Dim t As String
Dim p As *Byte
Dim buf[259] As Byte
Dim xs(7) As Long
Dim board(7, 7) As Long   ' 2D(§2.1)
Dim h As COFF_HEADER

3.2 手続き

Sub USAGE()
    Print "usage"
    End
End Sub

Function Add(a As Long, b As Long) As Long
    Add = a + b
End Function

Sub Inc(ByRef x As Long)
    x = x + 1
End Sub

3.3 Class

Class Counter
    Public
    value As Long

    Sub Counter(v As Long)      ' コンストラクタ
        value = v
    End Sub

    Sub ~Counter()               ' デストラクタ
    End Sub

    Sub Add(n As Long)
        value = value + n
    End Sub

    Function GetValue() As Long
        GetValue = value
    End Function
End Class

Dim c As Counter(10)            ' スタック上で ctor
c.Add(32)
x = c.GetValue()

Dim p As *Counter
p = New Counter(10)             ' ヒープ。失敗時は 0 で ctor は呼ばない
p = New [3] Counter(10)         ' 同じ引数で 3 要素
Delete p                        ' dtor を逆順に呼び、塊を解放。Delete 0 は何もしない

3.4 Enum

Enum Modes
    Off          ' 1
    On = 5       ' 5
    Max          ' 6(直前 + 1)
End Enum

Dim x As Modes   ' 4 バイト
x = On

4. 文

左辺 = 式
Let 左辺 = 式                 ' 省略可。`=` 代入と同じ
Name(引数...)                    ' Call キーワード不要(可なら無視)

If 式 Then
    ...
ElseIf 式 Then
    ...
Else
    ...
End If

If 式 Then 文[: 文...] [Else 文[: 文...]]   ' 単行 If。: の続きは Then / Else の一部

Select Case 式
    Case 定数
        ...
    Case Else
        ...
End Select

While 式
    ...
Wend

For i = 式 To 式 [Step 式]
    ...
Next

Do [While 式 | Until 式]
    ...
Loop [While 式 | Until 式]

Continue For | Continue While | Continue Do

With 式
    .field = ...
End With

Print [式 [{; | ,} 式 ...]] [;]  ' `;` は改行せず続ける。数値は符号桁と末尾空白
Print #番号, [式 [, ...]] [;]  ' ファイルへ。項目間はカンマ。`;` で改行抑制
Input ["prompt" ,|;] 変数 [, 変数 ...]  ' 1行をカンマで分割。足りなければ次の行
Input #番号, 変数 [, ...]  ' ファイルからフィールド読み取り
Open パス [For Input|Output|Append] As [#]番号
Close [[#]番号]     ' 省略時は全クローズ
Field #番号, バイト長
Get #番号, レコード, 文字列変数
Put #番号, レコード, 式
Write [#番号,] [式 [, ...]] [;]  ' カンマ区切り。番号省略は標準出力
Sleep(ミリ秒)       ' 待ち中もウインドウメッセージを処理
Beep
Cls [n]             ' コンソールをクリア。省略時 n は 1。負なら何もしない
MsgBox hWnd, text$ [, title$ [, type [, ans]]]
DelWnd hWnd
Window hWnd, hOwner, x, y, w, h, title$, style [, class$ [, id [, proc [, exStyle]]]]
ChDir path$
MkDir path$
Kill path$
Exec path$ [, cmd$]
Delete p            ' クラスポインタ。§3.3
End
ExitProcess(式)
文 対応
Let ○(省略可。= 代入と同じ)
If / ElseIf / 単行 If ○
Select Case ○
While / Wend ○
For ... To ... [Step] Next ○(負の Step 可)
Do ... Loop ○(While / Until を先頭または末尾に可)
With ... End With ○(ネスト可)
Exit Do / Exit While / Exit For ○
Continue For / Continue While / Continue Do ○(ループ先頭へ)
Print ○
Print # ○(BasicFile.abp。項目間カンマ。; で改行抑制)
Input ○(プロンプトの区切りは , または ;。変数が複数なら 1 行をカンマ分割し、項目が足りなければ次の行。数値は十進文字列 → IEEE)
Open / Close / Input # ○(BasicFile.abp。番号 1..16)
Field / Get # / Put # ○(ランダム。Open ... As + Field 長)
Write ○(画面または #番号。カンマ区切り)
Sleep(ms) ○(メッセージポンプ付き・default)
InsMenu ○(内部で InsertMenuA)
LINE / CIRCLE / LOCATE / PAINT(N88 文) ○(#n88basic)
Beep / Cls / MsgBox / Window / DelWnd ○(BasicCmd.abp。Cls はコンソール。Window はメッセージループを作らない)
ChDir / MkDir / Kill / Exec ○(BasicCmd.abp)
Delete ○(クラスポインタ)
行番号 ○(文頭の整数。無くてもよい。GoTo / GoSub の飛び先だけラベル)
*名前 ○(文頭のラベル。GoTo / GoSub の飛び先だけ。大小は区別する)
GoTo / GoSub / Return ○(行番号または *名前。Return は GoSub の復帰。Exit Function とは別)
Date$ / Time$ ○(yyyy/mm/dd と hh:mm:ss。月日時刻は 2 桁)
Inkey$ / Input$(n) ○(関数。#n88basic のみ。窓の WM_KEYDOWN)

命令の引数は同じ行のカンマ区切りで、次の文キーワードは引数にしない。

行番号は付けても付けなくてもよい。文の先頭の整数が行番号で、その行に文が無くてもエラーにしない。100 PRINT A は 100 の位置から PRINT へ落ちる。文の先頭の *名前 も同じ位置のラベルで、*start Print 1 は start から Print へ落ちる。改行して始まる *名前 はラベルで、_ で続けた行の * は掛け算のまま。同じ行番号や同じ名前が何度あっても、GoTo や GoSub の飛び先でなければエラーにしない。飛び先が無い、または二つ以上あるときはコンパイルエラー。名前の大小は区別する。GoTo と GoSub の飛び先は行番号か *名前。GoSub は戻る場所を覚えて移り、Return で戻る。GoSub していない Return は終了コード 1 でプロセスを終える。


5. 式

5.1 演算子

優先順位(高い順): As キャスト → ^ → 単項 - → * / \ Mod → + - & → << >> → 関係 → Not → And → Or → Xor。
* / \ どうし、および + - どうしは同優先で左から右へ評価。関係演算の値は真=-1 / 偽=0。

演算 対応
+ - *(整数) ○
+ - * /(Double) ○(IEEE。64bit と -actba32)
文字列 + / &(連結・同義) ○
単項 - / Not ○
= <> >< < > <= >= ○(値は -1/0。文字列は辞書順。Double 比較は 64bit と -actba32)
And / Or / Xor ○(ビット。条件の And/Or は短絡)
\ /(整数除算) ○(Long では同義)
Mod ○
<< / >>(>> は符号付き) ○
^(整数累乗) ○
expr As Type(切捨てキャスト) ○(String へ/からは不可)
+= -= *= /= \= <<= >>= / Mod= And= Or= Xor= ○
++ / -- ○

文字列の関係演算は辞書順(lstrcmpA)。長さが違い途中まで一致したら短い方が小さい。
As は例: &H12345678 As Word → &H5678。#strict 時は異なる型の変数代入、Long から Byte への代入(呼び出し結果を含む)、引数の String→VoidPtr と VoidPtr→Long が warning(As キャストで抑制)。

5.2 アドレス・サイズ・組込

VarPtr(左辺)
AddressOf(手続き名)
SizeOf(型名)
Len(文字列 | UDT変数)     ' 文字列長 or 構造体サイズ
Asc(s)
Left$(s, n) / Right$(s, n) / Mid$(s, start[, len])
Chr$(n) / Chr(n)
Str$(n)
Hex$(n) / Oct$(n)        ' 16進(小文字)と 8進。0 は "0"(StrUtils.abp)
ZeroString(n)            ' 長さ n のゼロ埋め。n が負なら空(StrUtils.abp)
StrD$(d)                 ' Double → 文字列(Include\default\DoubleStr.abp)
Date$() / Time$()        ' yyyy/mm/dd と hh:mm:ss(BasicCmd.abp)
Inkey$() / Input$(n)     ' #n88basic のみ(n88key.abp)
StrPtr(s)
MakeStr(p As *Byte)
RGB(r, g, b)
LOWORD(n)
ELM(n)                   ' 要素数 n の上限。値は n - 1
MakeIntResource(id)

5.2.1 関数ポインタ型

Dim p As *Function(a As Long, b As Long) As Long
Dim s As *Sub(n As Long)
TypeDef PADD = *Function(a As Long, b As Long) As Long
Dim q As PADD

p = AddressOf(Add)
r = p(1, 2)          ' 間接呼び出し(OP_CALL_RAX)
s = AddressOf(Show)
s(r)

5.2.2 リソース埋め込み

#resource "assets\payload.bin"

5.3 メモリ

p = malloc(n)
p = calloc(n)
p = realloc(p, n)
free(p)
memcpy(dst, src, n)
GetByte(p) / GetWord(p) / GetDWord(p) / GetSingle(p) / GetDouble(p)
SetByte(p, n) / SetWord(p, n) / SetDWord(p, n) / SetSingle(p, sng) / SetDouble(p, dbl)

(memcpy は RtlMoveMemory、FillMemory は RtlFillMemory へマップ)
realloc(p, n) は HeapReAlloc(GetProcessHeap(), 0, p, n)。戻り値は新しい先頭ポインタ。失敗時は 0 で、元のブロックはそのまま残る。
Get* / Set* は Memory.abp。ポインタが 0 のとき Get は 0、Set は何もしない。GetSingle / SetSingle は 4 バイト、GetDouble / SetDouble は 8 バイトの IEEE ビットをそのまま読む/書く。


6. 文字列

Chr$ / Right$ は組み込み。Hex$ / Oct$ / ZeroString / Val / InStr などはライブラリ側。Oct$ は 32bit のビット列を 8 進にし、先頭の 0 は付けない。ZeroString(n) の Len は n で、中身は 0(埋め込み NUL)。n が負のときは空文字。

_splitpath(path, drive, dir, fname, ext) は X:\ で始まる絶対パスを NUL 終端の各バッファへ分ける。drive は "C:"、dir は "\dir1\dir2\"、fname は拡張子を除いた名前、ext は ".txt"(最後の . から)。出力ポインタが 0 の欄は書き込まない。path が 0、または X:\ で始まらないときは何も書き込まない。2 バイト文字の先行バイトは区切りにしない。


7. プリプロセスと標準ヘッダ

7.1 行長上限

項目 値
定数 PP_LINE_MAX(Preproc.abp)
上限 1024 バイト / 行(終端 NUL・改行は含まない)
超過時 コンパイル失敗。error: …: too long line (実長 > 1024)

#include 展開・条件付きプリプロセス・With 書き換え後の行にも同じ上限がかかる。
長い条件式は _ 継続や括弧内改行で分割する(§1.2)。

7.2 #include

#include "Utils.abp"
#include <default/Windows.sbp>

循環・深さ超過はエラー。

7.3 自動 Include

Include\default\default.idx(Windows.sbp / WinConsts.sbp / WinTypes.sbp、Math.abp、CvtNum.abp、Pow.abp、Sleep.abp、Space.abp、DoubleStr.abp、BasicFile.abp、StringGc.abp、StrUtils.abp、Val.abp、Using.abp、SplitPath.abp、Memory.abp、BasicCmd.abp)は 常時 先頭へ挿入される。実体はリポジトリの src/Include 1 本。コンパイラは exe 隣 → 親〜3 階層上 → カレントの Include\ を順に探す(詳細は build.md)。
続けて Preproc が api.idx(主要 Win32 の Declare Lib)と UnicodeApi.sbp(Unicode 版 API)を挿入する。
加えてソースのディレクティブでプロファイルを挿入する:

ディレクティブ 挿入される idx
#console Include\console\console.idx
#n88basic / #prompt Include\N88BASIC\n88basic.idx

default.idx 自体には Declare Lib を載せない(型・定数・ヘルパのみ。Declare は api.idx / UnicodeApi.sbp)。不足 API はソース側で Declare Lib。

7.4 Math(IEEE Double)

角度はラジアン(Double)。Sin / Cos はマクローリン展開。円周率は MathPi()、ネイピア数は MathE()(Const は整数と文字列だけで、Double は持てないため Function)。

Dim hpi As Double
hpi = MathPi() / 2
Print Sin(hpi)        ' ≒ 1
Print Cos(0)          ' = 1
Print SinDeg(30)      ' ≒ 0.5
Print Log(MathE())    ' ≒ 1

主な関数: MathDiv / MathMod(整数ヘルパ) / Abs / Sgn / Min / Max / Sqr / Sin / Cos / Tan / SinDeg / CosDeg / Atn / Exp / Log / Log10 / Fix / Int / Rnd / Randomize / DegToRad / RadToDeg。 CDbl / CSng / CInt / CDWord は CvtNum.abp(CDbl は Double、CSng は Single、CInt は四捨五入した Long。0.5 は 0 から遠い側。CDWord は切捨て)。 ldexp / frexp / ipow / pow / rand / srand は Pow.abp。rand は 0..32767。Rnd とは種を共有しない。 Val は Val.abp で Double(空白、符号、小数、&H / &O)。String$(n, s) は s を n 回繰り返す。GetBasicColor(0..7) は N88 の 8 色。 Print Using "fmt"; expr は値 1 つ。# / .# は数値、文字列リテラルは @ と & &。同じ処理は Using$ / UsingStr$。

Space$(n) は Space.abp から常時利用可。ネストした関数呼び出しは対応する。複雑な入れ子は一時変数経由が安全。

StrD$(d As Double) は DoubleStr.abp から常時利用可。Print が Double 式を表示するとき自動で呼ばれる(64bit と -actba32)。

サンプル: src/actba64/samples/math_test.abp / テスト: test/t_math_*.abp / test/t_double_lit*.abp / test/t_dbl_func.abp

7.5 ファイル I/O(BasicFile.abp)

番号は 1..16。実体は src/Include/default/BasicFile.abp(default.idx で常時挿入)。

Open / Close

Open パス [For Input | Output | Append] As [#]番号
Close [[#]番号]          ' 省略時は 1..16 をすべて閉じる
For 動作
(省略) 読み書き(ランダム向け。OPEN_ALWAYS)
Input 読み取り専用。内容をメモリバッファへ(Input # 用)
Output 書き込み専用(新規作成)
Append 追記(末尾へシーク)

パス式の直後に文の As が続くため、パス側では As キャストを解釈しない(Open path As 1 可)。

Input #(順次)

Input #番号, 変数 [, 変数 ...]

カンマ/改行区切りの次フィールドを読む。変数は String / Long / Byte / Single / Double(数値は stdin Input と同じ変換)。

Field / Get # / Put #(ランダム)

Field #番号, フィールド長バイト
Get #番号, レコード番号, 文字列変数   ' レコードは 1 始まり
Put #番号, レコード番号, 式           ' 短い文字列は空白パディング、長い分は切り詰め

Open ... As(For 省略)で開いたうえで Field する。オフセットは (レコード - 1) * フィールド長。

Write / Print

Write [#番号,] [式 [, 式 ...]] [;]
Print #番号, [式 [, 式 ...]] [;]

Eof / Loc / Lof

Eof(番号)   ' 終端なら -1、それ以外 0
Loc(番号)   ' Input バッファ位置、またはランダムのレコード番号
Lof(番号)   ' Input バッファ長、またはファイルサイズ/レコード数

BasicFile.abp の関数(番号 1..16)。

未対応

Field の割り当て変数形式(BasicHelp の拡張形)は未実装。

回帰テスト例: src/actba64/test/t_open_close.abp、t_input_hash*.abp、t_field_get_put.abp、t_write_file.abp、t_print_file.abp、t_eof_loc_lof.abp

7.6 カメラと射影(dx_camera.sbp)

Include\d3d11\dx_camera.sbp。dx_graphics.sbp が取り込む。D3D11 には固定機能の変換がないので、結果は行優先 4×4 の g_dxProj / g_dxView(Single 16 個、D3DX の D3DMATRIX と同じ並び)に入る。

dx_SetProjection(angle, aspect, nearclip, backclip)
dx_SetCamera(pEye, pAt, pUp)
dx_SetCullMode(CullMode)

7.7 条件付きコンパイル

#define USE_FOO
#ifdef USE_FOO
    Print "on"
#else
    Print "off"
#endif

#ifndef _WIN64
#endif

8. WinAPI / IAT

api.idx / UnicodeApi.sbp の Declare(およびソース側 Declare)で認識した API はインポートテーブル(IAT)に載る。
Lib は 任意の DLL 名(システム DLL に限らない。自作・C/C++ 製も可)。拡張子省略時は .dll を付与(Lib "mydll" → mydll.dll)。

解決は 起動時の IAT(静的インポート)。LoadLibrary / GetProcAddress による実行時ロードは別途 API を Declare して自分で呼ぶ。

8.1 自作 DLL(C/C++)

Declare Function MyAdd Lib "mydll" Alias "MyAdd" (ByVal a As Long, ByVal b As Long) As Long
注意 内容
呼び出し規約 既定(64bit)は Microsoft x64、-actba32 は stdcall。DLL 側も合わせる
C++ 名前 extern "C" でエクスポートするか、マングル名を Alias に書く
配置 実行時に OS の DLL 検索パスで見つかること(通常は exe と同ディレクトリ)
回帰 t_decl_any_lib.abp(任意 Lib 登録)、t_decl_lib_norm.abp(拡張子正規化)

8.2 共通でよく使うもの(kernel32)

ExitProcess, GetCommandLineA, lstrlenA, lstrcpyA, lstrcatA,
CreateFileA, ReadFile, WriteFile, CloseHandle, GetFileSize, SetFilePointer,
GetFileAttributesA, GetProcessHeap, HeapAlloc, HeapFree, GetStdHandle

8.3 api.idx に既にあるもの / ソースで足すもの

GetModuleFileNameA, CreateProcessA, WaitForSingleObject, GetTickCount,
user32(MessageBoxA 等)、gdi32(CreatePen / Ellipse / BitBlt 等)、
PeekMessageA / MsgWaitForMultipleObjects などは api.idx の Declare で解決する。

不足 API・自作 DLL はソース側で Declare Lib。未登録かつ未 Declare の呼び出しはコンパイルエラーです。

8.4 DLL を出力する

.pj の #MODULETYPE=1 で PE DLL を出します。#MODULETYPE=0 と省略時は EXE です。

Function Export SampleFuncName() As Long
    SampleFuncName = 123456
End Function

Sub Export Init()
End Sub

Export を付けた Function / Sub だけがエクスポートされます。名前はソースの綴りのまま、名前表はバイト順の昇順です。上限は 64 個です。戻り値のある Function Export は、通常の Function と同じく値を返します。

入口は名前が DllMain の手続きです(大小無視。無いと error: DLL requires Function Export DllMain)。先頭の JMP がそこへ飛ぶので、ローダーが渡した引数はそのまま届きます。64bit は RCX / RDX / R8、32bit は stdcall のスタックです。サンプルの形は次のとおりです。

Function Export DllMain(hinstDLL As HINSTANCE, fdwReason As DWord, lpvReserved As VoidPtr) As Long
    Select Case fdwReason
        Case DLL_PROCESS_ATTACH
            _System_StartupProgram()
            DllMain = 1
    End Select
End Function

DLL_PROCESS_ATTACH(1)/ DLL_PROCESS_DETACH(0)/ DLL_THREAD_ATTACH(2)/ DLL_THREAD_DETACH(3)は WinConsts.sbp にあります。PROCESS_ATTACH では 0 以外を返します。0 だとローダーは DLL をアンロードします。

_System_StartupProgram をソースが定義していなければ、コンパイラが合成します。中身は EXE の main と同じ GC 初期化とモジュール文で、ExitProcess は呼ばず戻ります。ソースに同名の手続きがあるときは、それを使います。

優先イメージベースは EXE(64bit 0x140000000、32bit 0x400000)と重ならないよう、64bit DLL は 0x180000000、32bit(-actba32)は 0x10000000 です。

DLL には最後のセクションとして .reloc を付けます。32bit はコード中の絶対アドレス(ラベル、グローバル、文字列、IAT の call dword ptr [abs])を HIGHLOW で記録します。64bit のコードは RIP 相対なので、ローダーが移動できるよう空の再配置表だけ出します。EXE には .reloc を付けません。IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE は立てないので、優先ベースが空いていればそのアドレスのままマップされます。塞がっているときはローダーが .reloc を適用して別のアドレスへ移します。

src\actba64 からの例です。-o は #OUTPUT_RELEASE があるので省略できます。

actba64 samples\dllcall\dllsample\dllsample.pj
actba64 samples\dllcall\dllcall\dllcall.pj

dllsample.dll は dllcall.exe と同じディレクトリに置きます(出力先はそれぞれの .pj フォルダです)。回帰は test/t_dll_host.abp(静的な Declare Lib。優先ベースが空いている経路)と test/t_dll_reloc.abp(優先ベースを予約してから LoadLibraryA)です。どちらも ' Prebuild: で DLL を先に出します。


9. 32bit / 64bit ターゲット

同じ言語・同じパーサです。切替は CLI の -actba32 のみ。

観点 既定(64bit) -actba32
出力 PE32+ / AMD64 PE32 / i386
ポインタ / String / HANDLE 8 バイト 4 バイト
Long 4 4(LP64 ではない)
呼び出し Microsoft x64(RCX/RDX/R8/R9 + シャドウスペース) stdcall([esp+i*4]、呼び出し後は callee が pop)
Declare Lib ○ ○
DLL 優先ベース 0x180000000 0x10000000
DLL .reloc 空の再配置表(コードは RIP 相対) 絶対アドレスの HIGHLOW
Double 演算 ○(SSE。rax) ○(SSE2。値は edx:eax。配列要素・UDT フィールドも 8 バイト)
N88 LINE/CIRCLE 文 ○ ○
SizeOf(ポインタを含む UDT) ポインタ 8 バイト込み ポインタ 4 バイト込み(期待サイズを 64bit 決め打ちしない)

#PLATFORM=32 は AB4.20 の stage0 ホスト用で、この切替とは無関係です。


10. 非対応(意図的)


11. 最小例

#console

Dim s As String
Open "data.txt" For Input As 1
Input #1, s
Close 1
Print s
ExitProcess(0)
#console

Dim buf As String
Open "read.txt" As #1
Open "write.txt" As #2
Field #1, 10
Field #2, 10
Get #1, 1, buf
Put #2, 1, buf
Close
ExitProcess(0)
#console

Function Add(a As Long, b As Long) As Long
    Add = a + b
End Function

Dim x As Long
x = Add(3, 4)
ExitProcess(x)
#console

Type Point
    x As Long
    y As Long
End Type

Dim p As Point
p.x = 10
p.y = 20
ExitProcess(p.x + p.y)
#N88BASIC

Line(40, 40)-(180, 140), 7, B
Circle(320, 90), 50, 5
Sleep(300)
End

関連