x86リアルモードでモニターを作ろうシリーズです。
Windows11+wsl2で構築しています。
発端
なんかけっこう長くやってる気がするんですが、動くコマンドはechoの一つだけという現状。とってもさみしいです。ここはコマンドを増やしたいところ。
ファイルシステムまわりのプログラムの勉強と修行を兼ねてlsなんかはどうだろうか、とか思うわけです。
ルートディレクトリのLBAは保存してあるし、ディレクトリエントリをファイル名でサーチする処理はファイルロードで作ったし、ちょっと流用すればできそうな気がするんです。
ええと、ルートディレクトリをバッファに読み込んで……
バッファのサイズはどうしよう……
位置はどこにあればいいんだろう……
ほかの処理とかち合わないようにしないと……
ええと、ええと、ええと……
えぇい!面倒くさい!使い捨てのバッファなんか、いちいち細かいところなんか考えてられるかっ!!
というわけでメモリ・アロケータを作成することになるのでした。
仕様っぽなにか
もうね、使えるメモリは全部対象にしてしまおうと思うんです。面倒くさいので。
0x10000~0xA0000までをアプリケーションエリアとしているので、ここが対象です。
管理はビットマップで行きましょう。対象が(今どきのメモリサイズと比べて)大きくないので、それほどエリアコストになりません。
ページサイズは4KByteで。4KByteは大きすぎる気もしますが(今どきのメモリと比べれば)大した問題にならないでしょう
ビットマップそのものは最初のユーザーとしましょう。対象の1ページ目はビットマップスペースです。
システムコールで取得、解放できるように。
ビットマップのビットが使えるかどうかのテスト、そしてセットとクリアはマクロにします。処理が小さいこと、呼び出しコストがもったいないことを考慮しました。
厳密な排他処理は考慮しません。所詮はシングルタスク、シングルユーザのシステムですし。
こんなもんでしょうか。
マクロ
マップのビットが立っているかどうかを確認するマクロはこんな感じです。
bxで指定したbitの内容を8bitレジスタに返します。
%macro BIT_TEST 1
push cx
mov di, bx
shr di, 3
mov al, [es:di]
mov cl, bl
and cl, 7
shr al, cl
and al, 1
mov %1, al
pop cx
%endmacro
bitをセットするマクロです。bxで指定されたbitをセットします。
%macro BIT_SET 0
push cx
mov di, bx
shr di, 3
mov al, [es:di]
mov cl, bl
and cl, 7
mov ah, 1
shl ah, cl
or al, ah
mov [es:di], al
pop cx
%endmacro
bitをクリアするマクロはこう。bxで指定されたbitをクリアします。
%macro BIT_CLR 0
push cx
mov di, bx
shr di, 3
mov al, [es:di]
mov cl, bl
and cl, 7
mov ah, 1
shl ah, cl
not ah
and al, ah
mov [es:di], al
pop cx
%endmacro
初期化・アロケート・フリー
初期化はこうなります。何はともあれまず最初にこれを実行します。
自分自身の登録もしています。
; ---------------------------------
; page_init
; DX:AX = managed_end physical address
; ---------------------------------
page_init:
push ax
push dx
push bx
push cx
push di
push es
mov bx, MANAGED_SEG
mov [cs:bitmap_seg], bx
mov es, bx
and ax, 0xF000
mov [cs:g_managed_end], ax
mov [cs:g_managed_end+2], dx
mov bx, dx
shl bx, 4
mov cx, ax
shr cx, 12
or bx, cx
sub bx, MANAGED_BASE_PAGE
mov [cs:g_num_pages], bx
mov cx, bx
add cx, 7
shr cx, 3
xor di, di
.clear:
mov byte [es:di], 0
inc di
loop .clear
mov si, [MHS_OFF]
mov ax, [si + MHS.cnt]
cmp ax, 0
je .free_skip
mov cx, ax
.free_loop:
mov bx, cx
shl bx, 1
mov ax, [si + MHS.AXs + bx]
mov dx, [si + MHS.DXs + bx]
mov bx, ax
shl dx, 12
mov ah, svc_page_free
int 0x80
loop .free_loop
.free_skip:
xor bx, bx
BIT_SET
xor ax, ax
mov [cs:g_scan_hint], ax
pop es
pop di
pop cx
pop bx
pop dx
pop ax
ret
アロケート処理です。失敗するとdx:axが0、Cフラグが立ちます。
; ---------------------------------
; page_alloc
; ---------------------------------
page_alloc:
push bx
push cx
push di
push es
mov ax, [cs:bitmap_seg]
mov es, ax
mov bx, [cs:g_scan_hint]
mov cx, [cs:g_num_pages]
.scan1:
cmp bx, cx
jae .scan2
BIT_TEST al
cmp al, 0
jne .next1
BIT_SET
mov ax, bx
inc ax
mov [cs:g_scan_hint], ax
mov ax, bx
add ax, MANAGED_BASE_PAGE
mov dx, ax
shl ax, 12
shr dx, 4
jmp .ok
.next1:
inc bx
jmp .scan1
.scan2:
xor bx, bx
.scan2_loop:
cmp bx, [cs:g_scan_hint]
jae .fail
BIT_TEST al
cmp al, 0
jne .next2
BIT_SET
mov ax, bx
inc ax
mov [cs:g_scan_hint], ax
mov ax, bx
add ax, MANAGED_BASE_PAGE
mov dx, ax
shl ax, 12
shr dx, 4
clc
jmp .ok
.next2:
inc bx
jmp .scan2_loop
.fail:
xor ax, ax
xor dx, dx
stc
.ok:
pop es
pop di
pop cx
pop bx
ret
フリー処理です。失敗するとaxに1、Cフラグが立ちます。
; ---------------------------------
; page_free
; 入力:DX:BX = ページ先頭物理アドレス
; 出力:AX=0 成功 / AX!=0 エラー
; ---------------------------------
page_free:
push bx
push cx
push di
push es
mov si, bx ; BXを保存(AXの代わり)
mov ax, [cs:bitmap_seg]
mov es, ax
; range check: addr >= 0x0001:0000 (0x10000)
cmp dx, 0x0001
jb .err
jne .chk_align
cmp si, 0x0000
jb .err
.chk_align:
; 4KB align check: low 12 bits must be 0
test si, 0x0FFF
jnz .err
; page_num = addr >> 12 = (DX<<4) | (BX>>12)
mov bx, dx
shl bx, 4
mov cx, si
shr cx, 12
or bx, cx ; BX = page_num (absolute)
; index = page_num - MANAGED_BASE_PAGE
sub bx, MANAGED_BASE_PAGE
; index 0 is reserved
cmp bx, 0
je .err
; index < num_pages ?
cmp bx, [cs:g_num_pages]
jae .err
; double-free check: must be allocated
BIT_TEST al
cmp al, 1
jne .err
BIT_CLR
xor ax, ax
clc
jmp .ok
.err:
mov ax, 1
stc
.ok:
pop es
pop di
pop cx
pop bx
ret
定数、変数はこうです。
PAGE_SHIFT equ 12
PAGE_SIZE equ (1 << PAGE_SHIFT)
MANAGED_BASE equ 0x10000
MANAGED_BASE_PAGE equ (MANAGED_BASE >> PAGE_SHIFT) ; = 0x10
MANAGED_SEG equ (MANAGED_BASE >> 4) ; = 0x1000
; ---- globals (Monitor常駐) ----
g_managed_end dd 0
g_num_pages dw 0
g_scan_hint dw 0
bitmap_seg dw 0
おわりに
既存のloadappにも組み込みました。monitorが動かないとシステムコールが使えないので、boot1から呼ばれたのかTTSから呼ばれたのかフラグで判定しました。
これでこれからは配置とか気にせずにメモリを使うことができます。
このソースはgithubで公開しています。よろしければどうぞ。
https://github.com/CbWB-Inc/software/tree/main/laboratory/lab02/hyfax-08-alloc
今更ですがalloct.asmはテスト用のドライバアプリです。TTSがコマンド待ちになったら『alloct』と打ち込めば実行されます。
《2026/01/21 22:03:47》