a4_log07
の編集
https://essen.osask.jp/?a4_log07
[
トップ
] [
編集
|
差分
|
バックアップ
|
添付
|
リロード
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
BracketName
EssenRev4
FormattingRules
FrontPage
Help
InterWiki
InterWikiName
InterWikiSandBox
K
MenuBar
PHP
PukiWiki
PukiWiki/1.4
PukiWiki/1.4/Manual
PukiWiki/1.4/Manual/Plugin
PukiWiki/1.4/Manual/Plugin/A-D
PukiWiki/1.4/Manual/Plugin/E-G
PukiWiki/1.4/Manual/Plugin/H-K
PukiWiki/1.4/Manual/Plugin/L-N
PukiWiki/1.4/Manual/Plugin/O-R
PukiWiki/1.4/Manual/Plugin/S-U
PukiWiki/1.4/Manual/Plugin/V-Z
RecentDeleted
SDL2_01
SandBox
WikiEngines
WikiName
WikiWikiWeb
YukiWiki
a21
a21_acl01
a21_bbs01
a21_challengers
a21_count
a21_edu01
a21_edu02
a21_edu03
a21_edu04
a21_edu05
a21_edu06
a21_edu07
a21_edu08
a21_edu09
a21_edu10
a21_edu11
a21_hlx000
a21_hlx001
a21_hlx001_1
a21_hlx001_2
a21_hlx001_3
a21_hlx002
a21_hlx002_1
a21_hlx003
a21_hlx003_1
a21_hlx004_1
a21_memo01
a21_opt
a21_opt02
a21_opt03
a21_p01
a21_special
a21_tl9a
a21_todo
a21_txt01
a21_txt01_10
a21_txt01_1a
a21_txt01_2
a21_txt01_2a
a21_txt01_2b
a21_txt01_3
a21_txt01_4
a21_txt01_5
a21_txt01_6
a21_txt01_6a
a21_txt01_7
a21_txt01_8
a21_txt01_8a
a21_txt01_9
a21_txt01_9a
a21_txt02
a21_txt02_10
a21_txt02_10a
a21_txt02_10b
a21_txt02_11
a21_txt02_11a
a21_txt02_12
a21_txt02_12a
a21_txt02_12b
a21_txt02_1a
a21_txt02_1b
a21_txt02_2
a21_txt02_2a
a21_txt02_3
a21_txt02_3a
a21_txt02_4
a21_txt02_4a
a21_txt02_5
a21_txt02_5a
a21_txt02_6
a21_txt02_6a
a21_txt02_6b
a21_txt02_6b_rev0
a21_txt02_6x
a21_txt02_7
a21_txt02_7a
a21_txt02_8
a21_txt02_8a
a21_txt02_9
a21_txt02_9a
a22_acl2_01
a22_acl2_02
a22_edu12
a22_intro01
a22_intro02
a22_intro03
a22_memman01
a22_memman02
a22_memman03
a22_memman04
a22_memman05
a22_memman06
a22_memman07
a22_memo01
a22_mingw_debug
a22_txt03
a22_txt03_1a
a22_txt03_1b
a22_txt03_2
a22_txt03_2a
a22_ufcs01
a23_bbs
a23_ec001
a23_ec002
a23_intro00
a23_intro000
a23_intro01
a23_intro02
a23_intro03
a23_intro04
a23_intro05
a23_intro06
a23_intro07
a23_intro08
a23_intro09
a23_intro10
a23_intro10wk1
a23_intro10wk2
a23_intro10wk3
a23_intro11
a23_intro12
a23_intro13
a23_intro13wk1
a23_intro14
a23_intro15
a23_intro16
a23_intro17
a23_intro17wk1
a23_intro18
a23_intro19
a23_intro90
a23_intro91
a23_neopixel1
a23_os01
a23_useSelfMade
a23_usm001
a23_usm002
a23_usm003
a23_usm004
a23_usm005
a23_usm006
a23_usm007
a23_usm008
a23_usm009
a24_AMap11
a24_AMapSim11
a24_AMemFile
a24_AMemMan
a24_aErrExit
a24_aFnv
a24_aOsFunc
a24_aQSort
a24_aXorShift32
a24_acl1T_doc01
a24_acl1Tiny
a24_acpp0
a24_buntan01
a24_cMin
a24_getTyp
a24_goodvalues
a24_idea001
a24_longdef
a24_memo01
a24_memo02
a24_osc20240310
a24_osc20241026
a24_picoLcd13
a24_picoTrain1
a24_programs
a24_raspberrypi01
a24_raspberrypi02
a24_schedule
a24_spc2tab
a24_tab2spc
a24_useSelfMade
a25_acl3
a25_acl4
a25_acl4_001
a25_acl4_002
a25_acl4_003
a25_acl4_log01
a25_acl4_log02
a25_acl4_log03
a25_buntan02
a25_buntan03
a25_buntan04
a25_buntan05
a25_kcas01
a25_kharc01
a25_kharc02
a25_kharc03
a25_kharc04
a25_kharc05
a25_kharc06
a25_kharcs1
a25_kharcs2
a25_kharcs3
a25_kharcs4
a25_kharcs5
a25_kharcs6
a25_kharcs7
a25_kharcs8
a25_kharcs9
a4_0001
a4_0002
a4_0003
a4_0004
a4_0005
a4_0006
a4_0007
a4_0008
a4_0009
a4_0010
a4_0011
a4_0012
a4_0013
a4_0014
a4_comments
a4_d0001
a4_d0002
a4_d0003
a4_i01
a4_links
a4_log
a4_log00
a4_log04
a4_log05
a4_log06
a4_log07
a4_log08
a4_p0001
a4_p0002
a4_p0003
a4_p0004
a4_p0005
a4_p0006
aclib00
aclib01
aclib02
aclib03
aclib04
aclib05
aclib06
aclib07
aclib08
aclib09
aclib10
aclib11
aclib12
aclib13
aclib14
aclib15
aclib16
aclib17
aclib18
aclib19
aclib20
aclib21
aclib22
aclib23
aclib24
aclib25
aclib_bbs
arm64_01
avm0001
edu0001
edu0002
edu0003
esb02b_hrb
esb_dbg
esbasic0001
esbasic0002
esbasic0003
esbasic0004
esbasic0005
esbasic0006
esbasic0007
esbasic0008
esbasic0009
esbasic0010
esbasic0011
esbasic0012
esbasic0013
esbasic0014
esbasic0015
esbasic0016
esbasic0017
esbasic02a
esc0001
escm0001
essen_hist
esvm0001
esvm0002
esvm0003
esvm0004
esvm0005
esvm0006
esvm_i0
hh4a
idea0001
idea0002
idea0003
impressions
jck_0000
jck_0001
kawai
kbcl0_0000
kbcl0_0001
kbcl0_0002
kbcl0_0003
kbcl0_0004
kbcl0_0005
kbcl0_0006
kbcl0_0007
kclib1_0000
kclib1_0001
kclib1_0002
kclib1_0003
kclib1_0004
kclib1_0005
kclib1_0006
kclib1_0007
kclib1_0008
kclib1_0009
kclib1_0010
kpap0001
members
memo0001
osask4g
osask4g_r2
p20200311a
p20200610a
p20200610b
p20200624a
p20200711a
p20200716a
p20250813a
p20250813b
p20250813c
p20250815a
p20250903a
p20251006a
page0001
page0002
page0003
page0004
page0005
page0006
page0007
page0008
page0009
page0010
page0011
page0012
page0013
page0014
page0015
page0016
page0017
page0018
page0019
page0020
page0021
page0022
page0023
populars
seccamp
seccamp2019
sechack
sechack2019
seclang01
sh3_2020
sh3_2020_kw
sh3_2020_nk
sh3_2021_kw
sh3_2021_nk
sh3_2022_kw
sh3_2023_kw
sh3_2024_kw
sh3_2025_kw
sh3_2026_kw
sh3_kw_hist
termux001
termux002
text0001
text0001a
text0002
text0002a
text0003
text0004
text0005
text0006
text0006a
text0007
text0008
text0010
text0011
text0012
text0013
text0014
text0015
text0016
text0017
text0018
text0019
text0020
text0021
tl1c
tl2c
tl3c
tl3d
* acl4の開発ログ #07 -(by [[K]], 2026.03.04) -''acl4開発のもくじ → [[a4_i01]]'' ** 2026.03.04(水) #0 -今日からインタプリタの基礎部分を作ります。 ** 2026.03.04(水) #1 -このプログラムを実行すると、私の好きなマンデルブロー集合の画像が出ます(出るはずです)。 -だからこれを動かしたいです。 #define x R00 #define y R01 #define c R02 #define sx R03 #define sy R04 #define cx R05 #define cy R06 #define zx R07 #define zy R08 #define xx R09 #define yy R0a #define n R0b #define sn R0c #define t0 c #define lp0 L0000 #define lp1 L0001 #define lp2 L0002 #define lp3 L0003 #define lp4 L0004 #define sk0 L0005 #define sk1 L0006 Lod_RI(x, 512); Lod_RI(y, 384); Dbg_OpenWin_RR(x, y); Lod_RI(y, 0); Lbl_T(lp0); // for (y = 0; y < 384; y++) { Lod_RI(x, 0); Lbl_T(lp1); // for (x = 0; x < 512; x++) { Lod_RI(sn, 0); Lod_RI(sx, 0); Lbl_T(lp2); // for (sx = 0; sx < 4; sx++) { Lod_RR(cx, x); Mul_RI(cx, 4); Add_RR(cx, sx); Mul_RI(cx, 56); Add_RI(cx, 4673536); Lod_RI(sy, 0); Lbl_T(lp3); // for (sy = 0; sy < 4; sy++) { Lod_RR(cy, y); Mul_RI(cy, 4); Add_RR(cy, sy); Mul_RI(cy, -56); Add_RI(cy, -124928); Lod_RR(zx, cx); Lod_RR(zy, cy); Lod_RI(n, 1); Lbl_T(lp4); // for (n = 1; n < 447; n++) { Mul64Shr_RRRI(xx, zx, zx, 24); Mul64Shr_RRRI(yy, zy, zy, 24); Lod_RR(t0, xx); Add_RR(t0, yy); Jgt_RIT(t0, 0x4000000, sk0); Mul64Shr_RRRI(zy, zy, zx, 23); Lod_RR(zx, xx); Add_RR(zx, cx); Sub_RR(zx, yy); Add_RR(zy, cy); Add_RI(n, 1); Jlt_RIT(n, 447, lp4); Lbl_T(sk0); Add_RR(sn, n); Add_RI(sy, 1); Jlt_RIT(sy, 4, lp3); Add_RI(sx, 1); Jlt_RIT(sx, 4, lp2); Shr_RI(sn, 4); Lod_RR(c, sn); Mul_RI(c, 256); Jlt_RIT(sn, 256, sk1); // if (sn >= 256) { Lod_RI(c, 0); Jge_RIT(sn, 447, sk1); // if (sn < 447) { Lod_RR(c, sn); Add_RI(c, 0xfe01); Lbl_T(sk1); Mul_RI(c, 256); Dbg_SetPix_RRR(x, y, c); Add_RI(x, 1); Jlt_RIT(x, 512, lp1); Add_RI(y, 1); Jlt_RIT(y, 384, lp0); Dbg_Ret_I(0); -ちなみにこれは以下の定義を使えばC言語として動きます(この方法でデバッグしました)。 #define Lbl_T(t) t: #define Lod_RI(r, i) r=i #define Lod_RR(r, s) r=s #define Add_RI(r, i) r+=i #define Add_RR(r, s) r+=s #define Sub_RR(r, s) r-=s #define Mul_RI(r, i) r*=i #define Shr_RI(r, i) r>>=i #define Jlt_RIT(r, i, t) if(r< i)goto t #define Jge_RIT(r, i, t) if(r>=i)goto t #define Jgt_RIT(r, i, t) if(r> i)goto t #define Mul64Shr_RRRI(r, s, t, i) r=(intptr_t)(((int64_t)s*(int64_t)t)>>i) #define Dbg_OpenWin_RR(x, y) a_Win win[1]; a_Win_ini(_arg_ win, x, y, "graphic", 0) #define Dbg_SetPix_RRR(x, y, c) setPix0(win, x, y, c) #define Dbg_Ret_I(i) a_Win_flushAll0(win); return i ** 2026.03.04(水) #2 -拡張性重視だと速度が落ちて、速度重視だと拡張性が落ちるので、どうしたらいいかいろいろ試しています。 ** 2026.03.04(水) #3 -とりあえず、これならいいかなと思える程度のものはできました。 -でも現状ではmandelくらいのことしかできないので、ここからメモリアクセスとかポインタ演算とかを追加していく必要があります。 ** 2026.03.05(木) #0 -昨日作ったものはこんな感じです。 -[1] Windowsでグラフィックを出すための雑なプログラム: a4_t0001.c // #include <windows.h> してあることが前提. a_class(a_Win) { char title[256]; int xsz, ysz; uint32_t *buf; HWND hWin; HINSTANCE hInst; BITMAPINFO bmi; }; a_static a_Win *a_Win_opened = NULL; a_static LRESULT CALLBACK a_Win_wndProc(HWND hw, unsigned int msg, WPARAM wp, LPARAM lp) { if (msg == WM_CLOSE) return 0; // closeボタンを無視. if (msg == WM_DESTROY) { PostQuitMessage(0); return 0; } a_Win *w = a_Win_opened; if (w == NULL || w->hWin != hw) return DefWindowProc(hw, msg, wp, lp); if (msg == WM_PAINT) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hw, &ps); SetDIBitsToDevice(hdc, 0, 0, w->xsz, w->ysz, 0, 0, 0, w->ysz, w->buf, &w->bmi, DIB_RGB_COLORS); EndPaint(hw, &ps); return 0; } return DefWindowProc(hw, msg, wp, lp); } a_static int a_Win_winThread(a_Win *w) { WNDCLASSEX wc; RECT r; int i; MSG msg; wc.cbSize = sizeof (WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = a_Win_wndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = w->hInst; wc.hIcon = (HICON) LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hIconSm = wc.hIcon; wc.hCursor = (HCURSOR) LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hbrBackground = (HBRUSH) COLOR_APPWORKSPACE; wc.lpszMenuName = NULL; wc.lpszClassName = w->title; if (RegisterClassEx(&wc) == 0) return 1; r.left = 0; r.top = 0; r.right = w->xsz; r.bottom = w->ysz; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, FALSE); w->hWin = CreateWindowA(wc.lpszClassName, w->title, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, NULL, NULL, w->hInst, NULL); if (w->hWin == NULL) return 1; ShowWindow(w->hWin, SW_SHOW); UpdateWindow(w->hWin); for (;;) { i = GetMessage(&msg, NULL, 0, 0); if (i == 0 || i == -1) break; // エラーもしくは終了メッセージ. // そのほかはとりあえずデフォルト処理で. TranslateMessage(&msg); DispatchMessage(&msg); } UnregisterClass(w->title, w->hInst); return 0; } a_static void a_Win_ini(_argDef_ a_Win *w, int xsz, int ysz, const char *title, uint32_t c) { #if (a_DbgLv >= 2) if (xsz < 192 || ysz < 64) a_errExit("%s(%d): Win_ini: bad window size: xsz=%d, ysz=%d", a_fil, a_lin, xsz, ysz); if (title == NULL || strlen(title) == 0 || strlen(title) >= 256) a_errExit("%s(%d): Win_ini: bad window title", a_fil, a_lin); #endif w->buf = a_malloc(_arg_ xsz * ysz * sizeof (uint32_t)); int i; for (i = 0; i < xsz * ysz; i++) { w->buf[i] = c; } w->xsz = xsz; w->ysz = ysz; strcpy(w->title, title); w->bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); w->bmi.bmiHeader.biWidth = xsz; w->bmi.bmiHeader.biHeight = - ysz; w->bmi.bmiHeader.biPlanes = 1; w->bmi.bmiHeader.biBitCount = 32; w->bmi.bmiHeader.biCompression = BI_RGB; a_Win_opened = w; CreateThread(NULL, 0, (void *) a_Win_winThread, (void *) w, 0, (void *) &i); Sleep(128); } a_static void a_Win_flushAll0(a_Win *w) { InvalidateRect(w->hWin, NULL, FALSE); UpdateWindow(w->hWin); } -[2] 上記の mandel くらいなら余裕で動かせるVM: tt0001a.c #define a_Version 1 #include <acl4.c> #include <windows.h> #include "a4_t0001.c" void myPut(Preprocessor_Put0 *w, VecChr *lin) // t0013b.cで作ったやつと同じ. { if (Preprocessor_isDirective(lin->p, lin->p + lin->n) != 0) fprintf(stderr, "[err] %.*s", (int) lin->n, lin->p); else VecChr_puts(w->dst, lin->p, lin->p + lin->n); } int main(int argc, const char **argv) { if (argc < 2) return 1; Preprocessor pp[1]; Preprocessor_ini(pp); // プリプロセッサの初期化. pp->put = (void *) myPut; // 出力関数の変更. Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 入力ファイルを指定. Preprocessor_SourceFiles_addFile(pp->sfs, "a4vm-asm-tt0001.h", 17); // 最初にこれをインクロードさせる. VecChr vc[1]; VecChr_ini(vc); pp->put0->dst = vc; // 出力先のオブジェクトを指定. Preprocessor_main(pp); Preprocessor_din(pp); // プリプロセッサ処理. およびメモリ開放. Token0 t0[1]; Token0_ini1(t0); t0->s = vc->p; t0->s1 = vc->p + vc->n; Preprocessor_Eval ev[1]; ev->err = 0; VecChr bin[1]; VecChr_ini(bin); for (;;) { intptr_t i = Preprocessor_eval(ev, t0, 0x7fff); if (ev->err != 0) break; VecChr_resizeDiff(_arg_ bin, sizeof (intptr_t)); ((intptr_t *) (bin->p + bin->n))[-1] = i; Token1_get(t0); if (t0->c != ',' && t0->c != ';') break; } intptr_t *bp = (intptr_t *) bin->p, bn = bin->n / sizeof (intptr_t), reg0[256], *reg, lab[256], pc; for (pc = 0; pc < bn; pc += 4) { if (bp[pc] == 0x01) lab[bp[pc + 1]] = pc + 4; // Lbl_T(t)命令の次の命令を指す. } a_Win win[1]; win->buf = NULL; clock_t tm0 = clock(); reg = reg0 + 256 - 16; for (pc = 0;;) { intptr_t op = bp[pc], a = bp[pc + 1], b = bp[pc + 2], c = bp[pc + 3], d, e, f; pc += 4; switch (op) { case 0x01: continue; // Lbl_T(t) case 0x02: reg[a] = b; continue; // Lod_RI(r, i) case 0x03: reg[a] = reg[b]; continue; // Lod_RR(r, s) case 0x18: reg[a] <<= b; continue; // Shl_RI(r, i) case 0x19: reg[a] <<= reg[b]; continue; // Shl_RR(r, s) case 0x1a: reg[a] >>= b; continue; // Shr_RI(r, i) case 0x1b: reg[a] >>= reg[b]; continue; // Shr_RR(r, s) case 0x20: reg[a] += b; continue; // Add_RI(r, i) case 0x21: reg[a] += reg[b]; continue; // Add_RR(r, s) case 0x22: reg[a] -= b; continue; // Sub_RI(r, i) case 0x23: reg[a] -= reg[b]; continue; // Sub_RR(r, s) case 0x24: reg[a] *= b; continue; // Mul_RI(r, i) case 0x25: reg[a] *= reg[b]; continue; // Mul_RR(r, s) case 0x38: if (reg[b] < c) { pc = lab[a]; } continue; // Jlt_RI(r, i, t) case 0x39: if (reg[b] < reg[c]) { pc = lab[a]; } continue; // Jlt_RR(r, s, t) case 0x3a: if (reg[b] >= c) { pc = lab[a]; } continue; // Jge_RIT(r, i, t) case 0x3b: if (reg[b] >= reg[c]) { pc = lab[a]; } continue; // Jge_RRT(r, s, t) case 0x3c: if (reg[b] <= c) { pc = lab[a]; } continue; // Jle_RIT(r, i, t) case 0x3d: if (reg[b] <= reg[c]) { pc = lab[a]; } continue; // Jle_RRT(r, s, t) case 0x3e: if (reg[b] > c) { pc = lab[a]; } continue; // Jgt_RIT(r, i, t) case 0x3f: if (reg[b] > reg[c]) { pc = lab[a]; } continue; // Jgt_RRT(r, s, t) case 0x04: goto fin; // Dbg_Ret_I(i) case 0x06: d = bp[pc + 1]; pc += 4; reg[a] = (intptr_t)(((int64_t)reg[b]*(int64_t)reg[c])>>d); continue; // Mul64Shr_RRRI(r, s, t, i) case 0x08: a_Win_ini(_arg_ win, reg[a], reg[b], "graphic", 0); tm0 = clock(); continue; // Dbg_OpenWin_RR(x, y) case 0x09: win->buf[reg[b] * win->xsz + reg[a]] = reg[c]; continue; // Dbg_SetPix_RRR(x, y, c) case 0x36: reg[b]++; if (reg[b] < c) { pc = lab[a]; } continue; // IncJlt_RIT(r, i, t) case 0x00: case 0x05: case 0x07: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x37: continue; // すべてのcaseを書くと速くなる(コンパイラの最適化の都合). } } fin: printf("tm=%d\n", clock() - tm0); if (win->buf != NULL) { a_Win_flushAll0(win); for (;;) { Sleep(1000); } // Ctrl-C で終了させる. } VecChr_din4(vc, bin, 0, 0); a_malloc_debugList(_arg); return 0; } -[3] 上記のために作った設定ファイル: a4vm-asm-tt0001.h #define R00 0 #define R01 1 #define R02 2 #define R03 3 #define R04 4 #define R05 5 #define R06 6 #define R07 7 #define R08 8 #define R09 9 #define R0a 10 #define R0b 11 #define R0c 12 #define R0d 13 #define R0e 14 #define R0f 15 #define L0000 0 #define L0001 1 #define L0002 2 #define L0003 3 #define L0004 4 #define L0005 5 #define L0006 6 #define L0007 7 #define Lbl_T(t) 0x0001, t, 0, 0 #define Lod_RI(r, i) 0x0002, r, i, 0 #define Lod_RR(r, s) 0x0003, r, s, 0 #define Or__RI(r, i) 0x0010, r, i, 0 #define Or__RR(r, s) 0x0011, r, s, 0 #define Xor_RI(r, i) 0x0012, r, i, 0 #define Xor_RR(r, s) 0x0013, r, s, 0 #define And_RI(r, i) 0x0014, r, i, 0 #define And_RR(r, s) 0x0015, r, s, 0 #define Shl_RI(r, i) 0x0018, r, i, 0 #define Shl_RR(r, s) 0x0019, r, s, 0 #define Shr_RI(r, i) 0x001a, r, i, 0 #define Shr_RR(r, s) 0x001b, r, s, 0 #define Add_RI(r, i) 0x0020, r, i, 0 #define Add_RR(r, s) 0x0021, r, s, 0 #define Sub_RI(r, i) 0x0022, r, i, 0 #define Sub_RR(r, s) 0x0023, r, s, 0 #define Mul_RI(r, i) 0x0024, r, i, 0 #define Mul_RR(r, s) 0x0025, r, s, 0 #define Div_RI(r, i) 0x0028, r, i, 0 #define Div_RR(r, s) 0x0029, r, s, 0 #define Mod_RI(r, i) 0x002a, r, i, 0 #define Mod_RR(r, s) 0x002b, r, s, 0 #define Jeq_RIT(r, i, t) 0x0030, t, r, i #define Jeq_RRT(r, s, t) 0x0031, t, r, s #define Jne_RIT(r, i, t) 0x0032, t, r, i #define Jne_RRT(r, s, t) 0x0033, t, r, s #define Jlt_RIT(r, i, t) 0x0038, t, r, i #define Jlt_RRT(r, s, t) 0x0039, t, r, s #define Jge_RIT(r, i, t) 0x003a, t, r, i #define Jge_RRT(r, s, t) 0x003b, t, r, s #define Jle_RIT(r, i, t) 0x003c, t, r, i #define Jle_RRT(r, s, t) 0x003d, t, r, s #define Jgt_RIT(r, i, t) 0x003e, t, r, i #define Jgt_RRT(r, s, t) 0x003f, t, r, s #define Dbg_Ret_I(i) 0x0004, i, 0, 0 #define Mul64Shr_RRRI(r, s, t, i) 0x0006, r, s, t, 0x0, i, 0, 0 #define Dbg_OpenWin_RR(x, y) 0x0008, x, y, 0 #define Dbg_SetPix_RRR(x, y, c) 0x0009, x, y, c #define IncJlt_RIT(r, i, t) 0x0036, t, r, i -実行結果: >tt0001a mandel_asm.txt --https://essen.osask.jp/files/pic20260306a.png ** 2026.03.05(木) #1 -次にやるべきことを考えました。 -まず考えなきゃいけないのは、背伸びしすぎないことです。あんなことやこんなことができるすごいVMを作りたい衝動が毎日来るわけですが(笑)、そんなものは今はいらなくて、今は作りやすくて理解しやすいVMです。すごいのを作るのはその先の話のです。 -とりあえず、関数を記述できるようになるべきですね。それが次の一歩になりそうです。 ** 2026.03.05(木) #2 -[Q] 確かCコンパイラを作りたいんですよね?こんな自作仮想マシンを作っているのは遠回りじゃないですか? --[A] C言語のソースコードを、自作仮想マシンのアセンブラに「コンパイル」することを考えています。 --それができさえすれば、自作仮想マシンから実際のCPUのアセンブラや機械語に変換するのはかなり簡単です。 #define Lod_RI(r, i) X86_MOV_M32EbpDispI(r*4, i); // MOV DWORD [EBP+r*4],i #define Lod_RR(r, s) X86_MOV_R32M32EbpDisp(EAX,s*4); X86_M32EbpDispR32(r*4,EAX); --だから遠回りのようでいて、実はそうではないのです。 --このようなVMコードを経由しないでコンパイラを書こうとすると、CPUが変わるたびに作り直ししなければいけません。 --コード生成部をきれいに切り分けておけば、複数のCPUに対応するのはほとんど手間がかからなくなります(最適化はやりにくくなる可能性はあります)。 ** 2026.03.06(金) #0 -a4vmの仕様: --レジスタマシン。整数レジスタ・ポインタレジスタは区別がない(汎用レジスタモデル)。 --レジスタは intptr_t と同じ幅を持つ。だから環境によってレジスタの幅が変わりうる。 --intptr_t はポインタを格納するのに十分な大きさの整数型ということになっているため、16ビット環境では16ビットになる可能性があると思われるが、とりあえず今のバージョンでは intptr_t が16ビットになる場合を想定せず、32ビット以上だと仮定する。 ---16ビットの場合は、将来ちゃんと考えなおす。今から考えると考えることが多くなりすぎて手に負えなくなりそうだから。 --レジスタは関数ごとにローカルな存在で、関数呼び出しによって破壊されないし、またレジスタ数も関数宣言時に好きな数に宣言できる。8や16でもいいし、100とか1000でもいい。 ---実際のCPUではレジスタはグローバルな存在だけど、a4vmの中ではローカルとして扱う。その差異は、実CPUの命令列に置き換えるときに埋める。 ---関数の戻り値をどう返すかは、後で考える必要がある。レジスタが完全に関数ローカルだと返せないから。 ---これらのレジスタを実際のレジスタに対応させるかどうかは、処理系依存。実際はスタック変数であってもかまわない。 ---レジスタ数に上限を設けないほうが、コンパイラを作りやすくなるだろうと考えた。 --浮動小数点レジスタもあとで考えるけど、たぶん関数ローカルで、個数を関数宣言時に決められるのは同じにすると思う。 -構想: --関数の引数でレジスタ渡しを認めず、全部スタック渡しにすれば、たぶん話はシンプルになるからそれがいいかもしれない。 --戻り値についても、スタックの所定の場所に書いておくっていうやり方が可能ならそれでいいかもしれない。 --これだと関数間のデータのやり取りは全部メモリ経由になってシンプルだともいえるし、返す値の個数を2個以上にする余地も生まれる。 ** 2026.03.09(月) #0 -C言語では、スタックに引数を積んで関数を呼び出すという「スタック渡し」が一般的です。 -C言語がはやる前、機械語やアセンブラが主流の時は、レジスタに値を代入して呼び出す、レジスタ渡しが一般的でした。今でもBIOSの呼び出しなどにその名残りを見ることができます。 -しかしx86-64時代になって、使えるレジスタが増えてくると、C言語も一部の引数をレジスタで渡すようになりました。 -結局のところ、できればレジスタ渡しが理想なんでしょう。・・・呼び出された関数のほうから見れば、スタック上に引数があってもそのままでは利用できず、レジスタに読み込んで使うしかないからです。それなら最初からレジスタに入れて呼べばいいじゃないか、とまあそういう話になるわけです。 -ではなぜ以前はスタック渡しが主流だったのかと言えば、それはx86のレジスタが足りなかったからです。x86-64でも引数の数が既定の数を超えれば、超えた分はスタック渡しで渡されます。 -C言語では引数の個数に上限などは規定されていないので、引数がレジスタ数よりも多くなることはあり得ることです。スタックならかなり大きくて余裕があるので、引数がかなり長くなっても余裕で対応できるというわけです。 -2000年ごろ、私はOSASKを作っていたのですが、関数呼び出しのたびにスタックに値を書き込んでから関数を呼び出すという方式に素朴な疑問を持ちました。レジスタならプログラムで値をロードしなければ意図した値をセットすることはできませんが、メモリならデータ領域に書いておけば、プログラム側では何もしなくても、意図した値にしておくことができるわけです。 -スタックなんていう「初期値の設定できない場所」に引数を書け、というルールがダメだと直感で思いました。そこで「引数列はメモリ上のどこにおいてもよくて、どこに引数列があるのかをEBXレジスタで渡す」というポインタ渡し方式を採用しました。 -この方法はスタック渡しとも相性がいいのです。なぜならスタックに積んだ後で EBX=ESP; を実行して、それから関数を呼び出せばいいからです。たった1命令のオーバーヘッドです。 -逆はできません。つまりスタック渡しでしか受け取れないC言語の関数は、ポインタ渡しをされても、結局引数列をスタックに積みなおすしか受け取り手段がなくて、それはかなり大きなオーバヘッドになってしまうのです。 -今では高速化のためにレジスタ渡しをするわけですが、レジスタ渡しとポインタ渡しはどっちがいいのでしょうか。かつての私はそれも考えました。私はポインタ渡しがレジスタ渡しよりも優れていると思います。 -え?レジスタに入れておけば、関数側で値を読み込む手間がなくなるのに、それよりもいいことなんてあるもんか、と思うでしょう。でもそれがあるんです。そもそも呼び出し側ではメモリに書き込む操作すらしてないのです。だから関数側がメモリからレジスタに入れる処理をしても、(レジスタ渡しと比較して)追加のオーバーヘッドにはなってないのです。 -・・・ということが私にはすでに分かっているので、自作のCコンパイラでも関数呼び出しはポインタ渡しにしようかなあと少し思うのですが、そうやって独自拡張をやると「理解しにくい」コンパイラになってしまうので、やっぱり今回はやらないことにします。 -将来のバージョンで検討します。 ** 2026.03.11(水) #0 -2026.03.06(金) #0 の時に考えた「戻り値もスタックに書き込むことで返す」っていうのは、結構うまいと思うようになりました。EAXやRAXなどで返す方法だと、構造体とかは返せないので、その場合だけ工夫しなければいけません。でもメモリで返す方法なら、サイズの制限とかはないので、シンプルでいいなと思いました。 ** 2026.03.12(木) #0 -理想的なレジスタの使い方を考えています、なんとなく。 --スタックポインタ: これはほしいです。 --引数ポインタ: 今はいらないけど、将来的には欲しいです。 --返値ポインタ: 今はいらないけど、将来的には欲しいです。 --thisポインタ: これはほしいです。 -仮想マシンの仕様をうまく工夫したら、引数のいろんな渡し方を統合できないかなあ。 ** 2026.03.13(金) #0 -a4vmのメモリアクセスをどうするかずっと考えていました。a4vmでは引数を受け取るにはメモリアクセスが(とりあえず)必須なので、ここが決まらないと何も始まらないわけです。 -それで結局、データサイズとディスプレースメントをそのまま指定する、普通のCPUと同じようなモデルを採用することにします。 -sizeof (int)がどうだとか、構造体がどうだとかの情報をa4vmは管理せず、その上のレイヤで解決してもらおうというものです。 -これでメモリアクセスのための命令の設計ができそうです。 ** 2026.03.13(金) #1 -R00だけ例外的に(実CPUのAX/EAX/RAXのような)関数においてreturn値を返すレジスタに強制的に対応させるというルールを作れば、「a4vmから普通のコンパイラで作ったC関数が呼べない」という問題を解決できそうです。 --ここでずっと悩んでいました。 -あとは(x86-64みたいな)一部の引数をレジスタ渡しするような仕様をどう吸収するかです。 --まあそうでなくてもレジスタ渡しの __fastcall というやつはあるので、厳密にいえば x86 でも解決すべき問題ではあります。 --結局、実レジスタを指定して代入する命令も必要だということですね。 ** 2026.03.13(金) #2 -まだ自分が納得できていないので考えます。 -第一にa4vmの目的は、実CPUに依存しない中間コードを扱うことでした。こうすることでコンパイラが楽できるはずだと考えました。 -だからまずはレジスタを抽象化して、コンパイラの都合でいくつでも使えることにしました。 -一方で、C言語の引数渡しの方法はかなり多様性があります-。 -これに対応するには引数の渡し方をコンパイラ側が関知せずに、a4vm側で処理する必要があるでしょう。 -そうすると引数の型をa4vmは把握しなければいけません。スタック渡し(=メモリ経由)になる可能性があるからです。 -[呼び出す場合] 引数はすべて定数もしくはレジスタ(=a4vmにおける仮想レジスタ)で指定するとして、返値はレジスタで受け取るということにすれば、まあそれほど複雑にならずに済むかもしれません。 -[呼ばれる場合] やはり引数はレジスタで受け取るということにするべきでしょうか。なんかオーバーヘッドがあるような気がしますが、まあでもしょうがないかもしれません。 -a4vmで書かれた関数をa4vmから呼ぶ場合についても、上記のやり方でやることができますが、a4vm間呼び出しというのがあるべきです。それだと高速にできたり、高度なことができるといいです。 -a4vm間の呼び出しについての仕様については今決める必要はなくて、まずは外部とのやり取りが可能な形式だけ設計すればよさそうです。 --a4vm間呼び出しでは、引数も返値もメモリ経由になる。ポインタ渡しになって、高速化もされる。私の理想を好きなだけ実験して決めればいい。 ** 2026.03.13(金) #3 -手元では引数の受け取りと、a4vmからprintf関数の呼び出しができましたー。 -次は再帰を頑張ります。 ** 2026.03.13(金) #4 -再起呼び出しもできました。それぞれ動いてきちんと答えが出ました。 // tt0002c.txt: 階乗計算. #define i R00 #define fnc R01 #define j R02 Arg2_RR(fnc, i); // fncは自分自身への関数ポインタ. 呼び出し元が与えている. Jle_RIT(i, 1, L0000); Add_RI(i, -1); Afn_RR_RR(j, fnc, fnc, i); // j = fnc(fnc, i); Add_RI(i, +1); // iを元に戻す. Mul_RR(i, j); Dbg_Ret_R(i); Lbl_T(L0000); Dbg_Ret_I(1); // tt0002d.txt: フィボナッチ数. #define i R00 #define fnc R01 #define j R02 Arg2_RR(fnc, i); // fncは自分自身への関数ポインタ. 呼び出し元が与えている. Jle_RIT(i, 1, L0000); Add_RI(i, -2); Afn_RR_RR(j, fnc, fnc, i); // j = fnc(fnc, i); Add_RI(i, +1); Afn_RR_RR(i, fnc, fnc, i); // i = fnc(fnc, i); Add_RR(i, j); Lbl_T(L0000); Dbg_Ret_R(i); -これだけ部品がそろえば、そろそろCコンパイラも作れそうです。 --直接a4vmのコードを書くのもちょっと飽きてきました(笑)。 ** 2026.03.13(金) #5 -ちょっとメモ: -呼び出し規約ABI比較:System V vs Windows ABI --https://qiita.com/CRUD5th/items/8eec9ee022b843c05e99 -x64 呼び出し規約のシャドウストアは何に使われるのか (Windows) --https://yaya.lsv.jp/shadow_store/ * こめんと欄 #comment
タイムスタンプを変更しない
* acl4の開発ログ #07 -(by [[K]], 2026.03.04) -''acl4開発のもくじ → [[a4_i01]]'' ** 2026.03.04(水) #0 -今日からインタプリタの基礎部分を作ります。 ** 2026.03.04(水) #1 -このプログラムを実行すると、私の好きなマンデルブロー集合の画像が出ます(出るはずです)。 -だからこれを動かしたいです。 #define x R00 #define y R01 #define c R02 #define sx R03 #define sy R04 #define cx R05 #define cy R06 #define zx R07 #define zy R08 #define xx R09 #define yy R0a #define n R0b #define sn R0c #define t0 c #define lp0 L0000 #define lp1 L0001 #define lp2 L0002 #define lp3 L0003 #define lp4 L0004 #define sk0 L0005 #define sk1 L0006 Lod_RI(x, 512); Lod_RI(y, 384); Dbg_OpenWin_RR(x, y); Lod_RI(y, 0); Lbl_T(lp0); // for (y = 0; y < 384; y++) { Lod_RI(x, 0); Lbl_T(lp1); // for (x = 0; x < 512; x++) { Lod_RI(sn, 0); Lod_RI(sx, 0); Lbl_T(lp2); // for (sx = 0; sx < 4; sx++) { Lod_RR(cx, x); Mul_RI(cx, 4); Add_RR(cx, sx); Mul_RI(cx, 56); Add_RI(cx, 4673536); Lod_RI(sy, 0); Lbl_T(lp3); // for (sy = 0; sy < 4; sy++) { Lod_RR(cy, y); Mul_RI(cy, 4); Add_RR(cy, sy); Mul_RI(cy, -56); Add_RI(cy, -124928); Lod_RR(zx, cx); Lod_RR(zy, cy); Lod_RI(n, 1); Lbl_T(lp4); // for (n = 1; n < 447; n++) { Mul64Shr_RRRI(xx, zx, zx, 24); Mul64Shr_RRRI(yy, zy, zy, 24); Lod_RR(t0, xx); Add_RR(t0, yy); Jgt_RIT(t0, 0x4000000, sk0); Mul64Shr_RRRI(zy, zy, zx, 23); Lod_RR(zx, xx); Add_RR(zx, cx); Sub_RR(zx, yy); Add_RR(zy, cy); Add_RI(n, 1); Jlt_RIT(n, 447, lp4); Lbl_T(sk0); Add_RR(sn, n); Add_RI(sy, 1); Jlt_RIT(sy, 4, lp3); Add_RI(sx, 1); Jlt_RIT(sx, 4, lp2); Shr_RI(sn, 4); Lod_RR(c, sn); Mul_RI(c, 256); Jlt_RIT(sn, 256, sk1); // if (sn >= 256) { Lod_RI(c, 0); Jge_RIT(sn, 447, sk1); // if (sn < 447) { Lod_RR(c, sn); Add_RI(c, 0xfe01); Lbl_T(sk1); Mul_RI(c, 256); Dbg_SetPix_RRR(x, y, c); Add_RI(x, 1); Jlt_RIT(x, 512, lp1); Add_RI(y, 1); Jlt_RIT(y, 384, lp0); Dbg_Ret_I(0); -ちなみにこれは以下の定義を使えばC言語として動きます(この方法でデバッグしました)。 #define Lbl_T(t) t: #define Lod_RI(r, i) r=i #define Lod_RR(r, s) r=s #define Add_RI(r, i) r+=i #define Add_RR(r, s) r+=s #define Sub_RR(r, s) r-=s #define Mul_RI(r, i) r*=i #define Shr_RI(r, i) r>>=i #define Jlt_RIT(r, i, t) if(r< i)goto t #define Jge_RIT(r, i, t) if(r>=i)goto t #define Jgt_RIT(r, i, t) if(r> i)goto t #define Mul64Shr_RRRI(r, s, t, i) r=(intptr_t)(((int64_t)s*(int64_t)t)>>i) #define Dbg_OpenWin_RR(x, y) a_Win win[1]; a_Win_ini(_arg_ win, x, y, "graphic", 0) #define Dbg_SetPix_RRR(x, y, c) setPix0(win, x, y, c) #define Dbg_Ret_I(i) a_Win_flushAll0(win); return i ** 2026.03.04(水) #2 -拡張性重視だと速度が落ちて、速度重視だと拡張性が落ちるので、どうしたらいいかいろいろ試しています。 ** 2026.03.04(水) #3 -とりあえず、これならいいかなと思える程度のものはできました。 -でも現状ではmandelくらいのことしかできないので、ここからメモリアクセスとかポインタ演算とかを追加していく必要があります。 ** 2026.03.05(木) #0 -昨日作ったものはこんな感じです。 -[1] Windowsでグラフィックを出すための雑なプログラム: a4_t0001.c // #include <windows.h> してあることが前提. a_class(a_Win) { char title[256]; int xsz, ysz; uint32_t *buf; HWND hWin; HINSTANCE hInst; BITMAPINFO bmi; }; a_static a_Win *a_Win_opened = NULL; a_static LRESULT CALLBACK a_Win_wndProc(HWND hw, unsigned int msg, WPARAM wp, LPARAM lp) { if (msg == WM_CLOSE) return 0; // closeボタンを無視. if (msg == WM_DESTROY) { PostQuitMessage(0); return 0; } a_Win *w = a_Win_opened; if (w == NULL || w->hWin != hw) return DefWindowProc(hw, msg, wp, lp); if (msg == WM_PAINT) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hw, &ps); SetDIBitsToDevice(hdc, 0, 0, w->xsz, w->ysz, 0, 0, 0, w->ysz, w->buf, &w->bmi, DIB_RGB_COLORS); EndPaint(hw, &ps); return 0; } return DefWindowProc(hw, msg, wp, lp); } a_static int a_Win_winThread(a_Win *w) { WNDCLASSEX wc; RECT r; int i; MSG msg; wc.cbSize = sizeof (WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = a_Win_wndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = w->hInst; wc.hIcon = (HICON) LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hIconSm = wc.hIcon; wc.hCursor = (HCURSOR) LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hbrBackground = (HBRUSH) COLOR_APPWORKSPACE; wc.lpszMenuName = NULL; wc.lpszClassName = w->title; if (RegisterClassEx(&wc) == 0) return 1; r.left = 0; r.top = 0; r.right = w->xsz; r.bottom = w->ysz; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, FALSE); w->hWin = CreateWindowA(wc.lpszClassName, w->title, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, NULL, NULL, w->hInst, NULL); if (w->hWin == NULL) return 1; ShowWindow(w->hWin, SW_SHOW); UpdateWindow(w->hWin); for (;;) { i = GetMessage(&msg, NULL, 0, 0); if (i == 0 || i == -1) break; // エラーもしくは終了メッセージ. // そのほかはとりあえずデフォルト処理で. TranslateMessage(&msg); DispatchMessage(&msg); } UnregisterClass(w->title, w->hInst); return 0; } a_static void a_Win_ini(_argDef_ a_Win *w, int xsz, int ysz, const char *title, uint32_t c) { #if (a_DbgLv >= 2) if (xsz < 192 || ysz < 64) a_errExit("%s(%d): Win_ini: bad window size: xsz=%d, ysz=%d", a_fil, a_lin, xsz, ysz); if (title == NULL || strlen(title) == 0 || strlen(title) >= 256) a_errExit("%s(%d): Win_ini: bad window title", a_fil, a_lin); #endif w->buf = a_malloc(_arg_ xsz * ysz * sizeof (uint32_t)); int i; for (i = 0; i < xsz * ysz; i++) { w->buf[i] = c; } w->xsz = xsz; w->ysz = ysz; strcpy(w->title, title); w->bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); w->bmi.bmiHeader.biWidth = xsz; w->bmi.bmiHeader.biHeight = - ysz; w->bmi.bmiHeader.biPlanes = 1; w->bmi.bmiHeader.biBitCount = 32; w->bmi.bmiHeader.biCompression = BI_RGB; a_Win_opened = w; CreateThread(NULL, 0, (void *) a_Win_winThread, (void *) w, 0, (void *) &i); Sleep(128); } a_static void a_Win_flushAll0(a_Win *w) { InvalidateRect(w->hWin, NULL, FALSE); UpdateWindow(w->hWin); } -[2] 上記の mandel くらいなら余裕で動かせるVM: tt0001a.c #define a_Version 1 #include <acl4.c> #include <windows.h> #include "a4_t0001.c" void myPut(Preprocessor_Put0 *w, VecChr *lin) // t0013b.cで作ったやつと同じ. { if (Preprocessor_isDirective(lin->p, lin->p + lin->n) != 0) fprintf(stderr, "[err] %.*s", (int) lin->n, lin->p); else VecChr_puts(w->dst, lin->p, lin->p + lin->n); } int main(int argc, const char **argv) { if (argc < 2) return 1; Preprocessor pp[1]; Preprocessor_ini(pp); // プリプロセッサの初期化. pp->put = (void *) myPut; // 出力関数の変更. Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 入力ファイルを指定. Preprocessor_SourceFiles_addFile(pp->sfs, "a4vm-asm-tt0001.h", 17); // 最初にこれをインクロードさせる. VecChr vc[1]; VecChr_ini(vc); pp->put0->dst = vc; // 出力先のオブジェクトを指定. Preprocessor_main(pp); Preprocessor_din(pp); // プリプロセッサ処理. およびメモリ開放. Token0 t0[1]; Token0_ini1(t0); t0->s = vc->p; t0->s1 = vc->p + vc->n; Preprocessor_Eval ev[1]; ev->err = 0; VecChr bin[1]; VecChr_ini(bin); for (;;) { intptr_t i = Preprocessor_eval(ev, t0, 0x7fff); if (ev->err != 0) break; VecChr_resizeDiff(_arg_ bin, sizeof (intptr_t)); ((intptr_t *) (bin->p + bin->n))[-1] = i; Token1_get(t0); if (t0->c != ',' && t0->c != ';') break; } intptr_t *bp = (intptr_t *) bin->p, bn = bin->n / sizeof (intptr_t), reg0[256], *reg, lab[256], pc; for (pc = 0; pc < bn; pc += 4) { if (bp[pc] == 0x01) lab[bp[pc + 1]] = pc + 4; // Lbl_T(t)命令の次の命令を指す. } a_Win win[1]; win->buf = NULL; clock_t tm0 = clock(); reg = reg0 + 256 - 16; for (pc = 0;;) { intptr_t op = bp[pc], a = bp[pc + 1], b = bp[pc + 2], c = bp[pc + 3], d, e, f; pc += 4; switch (op) { case 0x01: continue; // Lbl_T(t) case 0x02: reg[a] = b; continue; // Lod_RI(r, i) case 0x03: reg[a] = reg[b]; continue; // Lod_RR(r, s) case 0x18: reg[a] <<= b; continue; // Shl_RI(r, i) case 0x19: reg[a] <<= reg[b]; continue; // Shl_RR(r, s) case 0x1a: reg[a] >>= b; continue; // Shr_RI(r, i) case 0x1b: reg[a] >>= reg[b]; continue; // Shr_RR(r, s) case 0x20: reg[a] += b; continue; // Add_RI(r, i) case 0x21: reg[a] += reg[b]; continue; // Add_RR(r, s) case 0x22: reg[a] -= b; continue; // Sub_RI(r, i) case 0x23: reg[a] -= reg[b]; continue; // Sub_RR(r, s) case 0x24: reg[a] *= b; continue; // Mul_RI(r, i) case 0x25: reg[a] *= reg[b]; continue; // Mul_RR(r, s) case 0x38: if (reg[b] < c) { pc = lab[a]; } continue; // Jlt_RI(r, i, t) case 0x39: if (reg[b] < reg[c]) { pc = lab[a]; } continue; // Jlt_RR(r, s, t) case 0x3a: if (reg[b] >= c) { pc = lab[a]; } continue; // Jge_RIT(r, i, t) case 0x3b: if (reg[b] >= reg[c]) { pc = lab[a]; } continue; // Jge_RRT(r, s, t) case 0x3c: if (reg[b] <= c) { pc = lab[a]; } continue; // Jle_RIT(r, i, t) case 0x3d: if (reg[b] <= reg[c]) { pc = lab[a]; } continue; // Jle_RRT(r, s, t) case 0x3e: if (reg[b] > c) { pc = lab[a]; } continue; // Jgt_RIT(r, i, t) case 0x3f: if (reg[b] > reg[c]) { pc = lab[a]; } continue; // Jgt_RRT(r, s, t) case 0x04: goto fin; // Dbg_Ret_I(i) case 0x06: d = bp[pc + 1]; pc += 4; reg[a] = (intptr_t)(((int64_t)reg[b]*(int64_t)reg[c])>>d); continue; // Mul64Shr_RRRI(r, s, t, i) case 0x08: a_Win_ini(_arg_ win, reg[a], reg[b], "graphic", 0); tm0 = clock(); continue; // Dbg_OpenWin_RR(x, y) case 0x09: win->buf[reg[b] * win->xsz + reg[a]] = reg[c]; continue; // Dbg_SetPix_RRR(x, y, c) case 0x36: reg[b]++; if (reg[b] < c) { pc = lab[a]; } continue; // IncJlt_RIT(r, i, t) case 0x00: case 0x05: case 0x07: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x37: continue; // すべてのcaseを書くと速くなる(コンパイラの最適化の都合). } } fin: printf("tm=%d\n", clock() - tm0); if (win->buf != NULL) { a_Win_flushAll0(win); for (;;) { Sleep(1000); } // Ctrl-C で終了させる. } VecChr_din4(vc, bin, 0, 0); a_malloc_debugList(_arg); return 0; } -[3] 上記のために作った設定ファイル: a4vm-asm-tt0001.h #define R00 0 #define R01 1 #define R02 2 #define R03 3 #define R04 4 #define R05 5 #define R06 6 #define R07 7 #define R08 8 #define R09 9 #define R0a 10 #define R0b 11 #define R0c 12 #define R0d 13 #define R0e 14 #define R0f 15 #define L0000 0 #define L0001 1 #define L0002 2 #define L0003 3 #define L0004 4 #define L0005 5 #define L0006 6 #define L0007 7 #define Lbl_T(t) 0x0001, t, 0, 0 #define Lod_RI(r, i) 0x0002, r, i, 0 #define Lod_RR(r, s) 0x0003, r, s, 0 #define Or__RI(r, i) 0x0010, r, i, 0 #define Or__RR(r, s) 0x0011, r, s, 0 #define Xor_RI(r, i) 0x0012, r, i, 0 #define Xor_RR(r, s) 0x0013, r, s, 0 #define And_RI(r, i) 0x0014, r, i, 0 #define And_RR(r, s) 0x0015, r, s, 0 #define Shl_RI(r, i) 0x0018, r, i, 0 #define Shl_RR(r, s) 0x0019, r, s, 0 #define Shr_RI(r, i) 0x001a, r, i, 0 #define Shr_RR(r, s) 0x001b, r, s, 0 #define Add_RI(r, i) 0x0020, r, i, 0 #define Add_RR(r, s) 0x0021, r, s, 0 #define Sub_RI(r, i) 0x0022, r, i, 0 #define Sub_RR(r, s) 0x0023, r, s, 0 #define Mul_RI(r, i) 0x0024, r, i, 0 #define Mul_RR(r, s) 0x0025, r, s, 0 #define Div_RI(r, i) 0x0028, r, i, 0 #define Div_RR(r, s) 0x0029, r, s, 0 #define Mod_RI(r, i) 0x002a, r, i, 0 #define Mod_RR(r, s) 0x002b, r, s, 0 #define Jeq_RIT(r, i, t) 0x0030, t, r, i #define Jeq_RRT(r, s, t) 0x0031, t, r, s #define Jne_RIT(r, i, t) 0x0032, t, r, i #define Jne_RRT(r, s, t) 0x0033, t, r, s #define Jlt_RIT(r, i, t) 0x0038, t, r, i #define Jlt_RRT(r, s, t) 0x0039, t, r, s #define Jge_RIT(r, i, t) 0x003a, t, r, i #define Jge_RRT(r, s, t) 0x003b, t, r, s #define Jle_RIT(r, i, t) 0x003c, t, r, i #define Jle_RRT(r, s, t) 0x003d, t, r, s #define Jgt_RIT(r, i, t) 0x003e, t, r, i #define Jgt_RRT(r, s, t) 0x003f, t, r, s #define Dbg_Ret_I(i) 0x0004, i, 0, 0 #define Mul64Shr_RRRI(r, s, t, i) 0x0006, r, s, t, 0x0, i, 0, 0 #define Dbg_OpenWin_RR(x, y) 0x0008, x, y, 0 #define Dbg_SetPix_RRR(x, y, c) 0x0009, x, y, c #define IncJlt_RIT(r, i, t) 0x0036, t, r, i -実行結果: >tt0001a mandel_asm.txt --https://essen.osask.jp/files/pic20260306a.png ** 2026.03.05(木) #1 -次にやるべきことを考えました。 -まず考えなきゃいけないのは、背伸びしすぎないことです。あんなことやこんなことができるすごいVMを作りたい衝動が毎日来るわけですが(笑)、そんなものは今はいらなくて、今は作りやすくて理解しやすいVMです。すごいのを作るのはその先の話のです。 -とりあえず、関数を記述できるようになるべきですね。それが次の一歩になりそうです。 ** 2026.03.05(木) #2 -[Q] 確かCコンパイラを作りたいんですよね?こんな自作仮想マシンを作っているのは遠回りじゃないですか? --[A] C言語のソースコードを、自作仮想マシンのアセンブラに「コンパイル」することを考えています。 --それができさえすれば、自作仮想マシンから実際のCPUのアセンブラや機械語に変換するのはかなり簡単です。 #define Lod_RI(r, i) X86_MOV_M32EbpDispI(r*4, i); // MOV DWORD [EBP+r*4],i #define Lod_RR(r, s) X86_MOV_R32M32EbpDisp(EAX,s*4); X86_M32EbpDispR32(r*4,EAX); --だから遠回りのようでいて、実はそうではないのです。 --このようなVMコードを経由しないでコンパイラを書こうとすると、CPUが変わるたびに作り直ししなければいけません。 --コード生成部をきれいに切り分けておけば、複数のCPUに対応するのはほとんど手間がかからなくなります(最適化はやりにくくなる可能性はあります)。 ** 2026.03.06(金) #0 -a4vmの仕様: --レジスタマシン。整数レジスタ・ポインタレジスタは区別がない(汎用レジスタモデル)。 --レジスタは intptr_t と同じ幅を持つ。だから環境によってレジスタの幅が変わりうる。 --intptr_t はポインタを格納するのに十分な大きさの整数型ということになっているため、16ビット環境では16ビットになる可能性があると思われるが、とりあえず今のバージョンでは intptr_t が16ビットになる場合を想定せず、32ビット以上だと仮定する。 ---16ビットの場合は、将来ちゃんと考えなおす。今から考えると考えることが多くなりすぎて手に負えなくなりそうだから。 --レジスタは関数ごとにローカルな存在で、関数呼び出しによって破壊されないし、またレジスタ数も関数宣言時に好きな数に宣言できる。8や16でもいいし、100とか1000でもいい。 ---実際のCPUではレジスタはグローバルな存在だけど、a4vmの中ではローカルとして扱う。その差異は、実CPUの命令列に置き換えるときに埋める。 ---関数の戻り値をどう返すかは、後で考える必要がある。レジスタが完全に関数ローカルだと返せないから。 ---これらのレジスタを実際のレジスタに対応させるかどうかは、処理系依存。実際はスタック変数であってもかまわない。 ---レジスタ数に上限を設けないほうが、コンパイラを作りやすくなるだろうと考えた。 --浮動小数点レジスタもあとで考えるけど、たぶん関数ローカルで、個数を関数宣言時に決められるのは同じにすると思う。 -構想: --関数の引数でレジスタ渡しを認めず、全部スタック渡しにすれば、たぶん話はシンプルになるからそれがいいかもしれない。 --戻り値についても、スタックの所定の場所に書いておくっていうやり方が可能ならそれでいいかもしれない。 --これだと関数間のデータのやり取りは全部メモリ経由になってシンプルだともいえるし、返す値の個数を2個以上にする余地も生まれる。 ** 2026.03.09(月) #0 -C言語では、スタックに引数を積んで関数を呼び出すという「スタック渡し」が一般的です。 -C言語がはやる前、機械語やアセンブラが主流の時は、レジスタに値を代入して呼び出す、レジスタ渡しが一般的でした。今でもBIOSの呼び出しなどにその名残りを見ることができます。 -しかしx86-64時代になって、使えるレジスタが増えてくると、C言語も一部の引数をレジスタで渡すようになりました。 -結局のところ、できればレジスタ渡しが理想なんでしょう。・・・呼び出された関数のほうから見れば、スタック上に引数があってもそのままでは利用できず、レジスタに読み込んで使うしかないからです。それなら最初からレジスタに入れて呼べばいいじゃないか、とまあそういう話になるわけです。 -ではなぜ以前はスタック渡しが主流だったのかと言えば、それはx86のレジスタが足りなかったからです。x86-64でも引数の数が既定の数を超えれば、超えた分はスタック渡しで渡されます。 -C言語では引数の個数に上限などは規定されていないので、引数がレジスタ数よりも多くなることはあり得ることです。スタックならかなり大きくて余裕があるので、引数がかなり長くなっても余裕で対応できるというわけです。 -2000年ごろ、私はOSASKを作っていたのですが、関数呼び出しのたびにスタックに値を書き込んでから関数を呼び出すという方式に素朴な疑問を持ちました。レジスタならプログラムで値をロードしなければ意図した値をセットすることはできませんが、メモリならデータ領域に書いておけば、プログラム側では何もしなくても、意図した値にしておくことができるわけです。 -スタックなんていう「初期値の設定できない場所」に引数を書け、というルールがダメだと直感で思いました。そこで「引数列はメモリ上のどこにおいてもよくて、どこに引数列があるのかをEBXレジスタで渡す」というポインタ渡し方式を採用しました。 -この方法はスタック渡しとも相性がいいのです。なぜならスタックに積んだ後で EBX=ESP; を実行して、それから関数を呼び出せばいいからです。たった1命令のオーバーヘッドです。 -逆はできません。つまりスタック渡しでしか受け取れないC言語の関数は、ポインタ渡しをされても、結局引数列をスタックに積みなおすしか受け取り手段がなくて、それはかなり大きなオーバヘッドになってしまうのです。 -今では高速化のためにレジスタ渡しをするわけですが、レジスタ渡しとポインタ渡しはどっちがいいのでしょうか。かつての私はそれも考えました。私はポインタ渡しがレジスタ渡しよりも優れていると思います。 -え?レジスタに入れておけば、関数側で値を読み込む手間がなくなるのに、それよりもいいことなんてあるもんか、と思うでしょう。でもそれがあるんです。そもそも呼び出し側ではメモリに書き込む操作すらしてないのです。だから関数側がメモリからレジスタに入れる処理をしても、(レジスタ渡しと比較して)追加のオーバーヘッドにはなってないのです。 -・・・ということが私にはすでに分かっているので、自作のCコンパイラでも関数呼び出しはポインタ渡しにしようかなあと少し思うのですが、そうやって独自拡張をやると「理解しにくい」コンパイラになってしまうので、やっぱり今回はやらないことにします。 -将来のバージョンで検討します。 ** 2026.03.11(水) #0 -2026.03.06(金) #0 の時に考えた「戻り値もスタックに書き込むことで返す」っていうのは、結構うまいと思うようになりました。EAXやRAXなどで返す方法だと、構造体とかは返せないので、その場合だけ工夫しなければいけません。でもメモリで返す方法なら、サイズの制限とかはないので、シンプルでいいなと思いました。 ** 2026.03.12(木) #0 -理想的なレジスタの使い方を考えています、なんとなく。 --スタックポインタ: これはほしいです。 --引数ポインタ: 今はいらないけど、将来的には欲しいです。 --返値ポインタ: 今はいらないけど、将来的には欲しいです。 --thisポインタ: これはほしいです。 -仮想マシンの仕様をうまく工夫したら、引数のいろんな渡し方を統合できないかなあ。 ** 2026.03.13(金) #0 -a4vmのメモリアクセスをどうするかずっと考えていました。a4vmでは引数を受け取るにはメモリアクセスが(とりあえず)必須なので、ここが決まらないと何も始まらないわけです。 -それで結局、データサイズとディスプレースメントをそのまま指定する、普通のCPUと同じようなモデルを採用することにします。 -sizeof (int)がどうだとか、構造体がどうだとかの情報をa4vmは管理せず、その上のレイヤで解決してもらおうというものです。 -これでメモリアクセスのための命令の設計ができそうです。 ** 2026.03.13(金) #1 -R00だけ例外的に(実CPUのAX/EAX/RAXのような)関数においてreturn値を返すレジスタに強制的に対応させるというルールを作れば、「a4vmから普通のコンパイラで作ったC関数が呼べない」という問題を解決できそうです。 --ここでずっと悩んでいました。 -あとは(x86-64みたいな)一部の引数をレジスタ渡しするような仕様をどう吸収するかです。 --まあそうでなくてもレジスタ渡しの __fastcall というやつはあるので、厳密にいえば x86 でも解決すべき問題ではあります。 --結局、実レジスタを指定して代入する命令も必要だということですね。 ** 2026.03.13(金) #2 -まだ自分が納得できていないので考えます。 -第一にa4vmの目的は、実CPUに依存しない中間コードを扱うことでした。こうすることでコンパイラが楽できるはずだと考えました。 -だからまずはレジスタを抽象化して、コンパイラの都合でいくつでも使えることにしました。 -一方で、C言語の引数渡しの方法はかなり多様性があります-。 -これに対応するには引数の渡し方をコンパイラ側が関知せずに、a4vm側で処理する必要があるでしょう。 -そうすると引数の型をa4vmは把握しなければいけません。スタック渡し(=メモリ経由)になる可能性があるからです。 -[呼び出す場合] 引数はすべて定数もしくはレジスタ(=a4vmにおける仮想レジスタ)で指定するとして、返値はレジスタで受け取るということにすれば、まあそれほど複雑にならずに済むかもしれません。 -[呼ばれる場合] やはり引数はレジスタで受け取るということにするべきでしょうか。なんかオーバーヘッドがあるような気がしますが、まあでもしょうがないかもしれません。 -a4vmで書かれた関数をa4vmから呼ぶ場合についても、上記のやり方でやることができますが、a4vm間呼び出しというのがあるべきです。それだと高速にできたり、高度なことができるといいです。 -a4vm間の呼び出しについての仕様については今決める必要はなくて、まずは外部とのやり取りが可能な形式だけ設計すればよさそうです。 --a4vm間呼び出しでは、引数も返値もメモリ経由になる。ポインタ渡しになって、高速化もされる。私の理想を好きなだけ実験して決めればいい。 ** 2026.03.13(金) #3 -手元では引数の受け取りと、a4vmからprintf関数の呼び出しができましたー。 -次は再帰を頑張ります。 ** 2026.03.13(金) #4 -再起呼び出しもできました。それぞれ動いてきちんと答えが出ました。 // tt0002c.txt: 階乗計算. #define i R00 #define fnc R01 #define j R02 Arg2_RR(fnc, i); // fncは自分自身への関数ポインタ. 呼び出し元が与えている. Jle_RIT(i, 1, L0000); Add_RI(i, -1); Afn_RR_RR(j, fnc, fnc, i); // j = fnc(fnc, i); Add_RI(i, +1); // iを元に戻す. Mul_RR(i, j); Dbg_Ret_R(i); Lbl_T(L0000); Dbg_Ret_I(1); // tt0002d.txt: フィボナッチ数. #define i R00 #define fnc R01 #define j R02 Arg2_RR(fnc, i); // fncは自分自身への関数ポインタ. 呼び出し元が与えている. Jle_RIT(i, 1, L0000); Add_RI(i, -2); Afn_RR_RR(j, fnc, fnc, i); // j = fnc(fnc, i); Add_RI(i, +1); Afn_RR_RR(i, fnc, fnc, i); // i = fnc(fnc, i); Add_RR(i, j); Lbl_T(L0000); Dbg_Ret_R(i); -これだけ部品がそろえば、そろそろCコンパイラも作れそうです。 --直接a4vmのコードを書くのもちょっと飽きてきました(笑)。 ** 2026.03.13(金) #5 -ちょっとメモ: -呼び出し規約ABI比較:System V vs Windows ABI --https://qiita.com/CRUD5th/items/8eec9ee022b843c05e99 -x64 呼び出し規約のシャドウストアは何に使われるのか (Windows) --https://yaya.lsv.jp/shadow_store/ * こめんと欄 #comment
テキスト整形のルールを表示する