ReC98/libs/master.lib/memheap.asm

310 lines
7.6 KiB
NASM
Raw Normal View History

; master library
;
; Description:
; ヒープ型メモリマネージャ
;
; Functions:
; unsigned hmem_allocbyte( unsigned bytesize ) ;
; unsigned hmem_alloc( unsigned parasize ) ;
; void hmem_free( unsigned memseg ) ;
;
; Returns:
; unsigned hmem_alloc: (cy=0) 確保したセグメント
; 0(cy=1) 管理メモリ不足
;
; Notes:
; hmem_alloc 実行後は、mem_AllocID は 0になります。
; hmem_alloc(0)として呼び出すと、メモリ不足を返します。
;
; ヒープの構造
; 管理情報: 16bytes
; Using 2bytes 使用中なら1, 未使用なら 0
; NextSeg 2bytes 次のメモリブロックの先頭
; 末尾なら mem_OutSegと同じ値
; ID 2bytes 用途ID(確保時のmem_AllocIDの値を転写)
; 続くパラグラフがデータ部
;
; Assembly Language Note:
; AX以外の全てのレジスタを保存します。
;
; Running Target:
; MS-DOS
;
; Author:
; 恋塚昭彦
;
; Rebision History:
; 93/ 3/20 Initial
; 93/ 5/ 3 bugfix: hmem_allocbyte(2倍の確保がされていた(^^;)
; 93/11/ 7 [M0.21] a=hmem_alloc(x); b=hmem_alloc(y);
; hmem_free(a); hmem_free(b); としたときに
; mem_TopHeapが異常になるバグ修正(hmem_freeのbug)
; (末尾がholeでその直前で唯一のブロックを開放したとき)
; 93/11/ 7 [M0.21] hmem_freeが、holeが連接したときに接続する部分が
; おかしかった(;_;)
; 93/12/27 [M0.22] hmem_freeの連接接続が完全ではなかった
; 93/12/29 [M0.22] hmem_freeに渡される値の検査を少しいれた
; (最低値より小さいときと、using flagが1でないときは
; 何もせずreturnするようにした)
; 95/ 2/14 [M0.22k] mem_AllocID対応
; 95/ 3/19 [M0.22k] 確保サイズが(mem_OutSeg-mem_EndMark)以上なら
; 先に失敗を返すようにした。
; 95/ 3/21 [M0.22k] BUGFIX hmem_free() 先頭のブロックを開放したときに
; 直後がフリーブロックで、その次のフリーブロックが最終
; ブロックのときに、その最終フリーブロックを見失って
; mem_FirstHoleを0にしていた。
;  このため、次に末尾から2番目のブロックを開放しても
; 最初の空きを作ったと思って連接作業を行わず、
; 末尾の"忘れ去られた"フリーブロックと連接しなかった。
;
MEMHEAD STRUC
using dw ?
nextseg dw ?
mem_id dw ?
MEMHEAD ENDS
func HMEM_ALLOCBYTE ; hmem_allocbyte() {
push BX
mov BX,SP
;
bytesize = (RETSIZE+1)*2
mov BX,SS:[BX+bytesize]
add BX,15
rcr BX,1
shr BX,1
shr BX,1
shr BX,1
jmp short hmem_allocb
endfunc ; }
func HMEM_ALLOC ; hmem_alloc() {
push BX
mov BX,SP
;
parasize = (RETSIZE+1)*2
mov BX,SS:[BX+parasize]
hmem_allocb:
cmp mem_TopSeg,0 ; house keeping
jne short A_S
call MEM_ASSIGN_ALL
A_S:
push CX
push ES
test BX,BX
jz short NO_MEMORY ; house keeping
mov AX,mem_OutSeg
sub AX,mem_EndMark
cmp BX,AX
jae short NO_MEMORY ; house keeping (add 95/3/19)
inc BX
mov AX,mem_FirstHole
test AX,AX
jz short ALLOC_CENTER ; 中心から取るのね
; フリーブロックから探す
SEARCH_HOLE_S:
mov CX,mem_OutSeg
SEARCH_HOLE:
mov ES,AX
mov AX,ES:[0].nextseg
cmp ES:[0].using,0
jne short SEARCH_HOLE_E
mov CX,ES
add CX,BX
jc short SEARCH_HOLE_E0 ; 95/3/22
cmp CX,AX ; now+size <= next
jbe short FOUND_HOLE
SEARCH_HOLE_E0:
mov CX,mem_OutSeg
SEARCH_HOLE_E:
cmp AX,CX
jne short SEARCH_HOLE
; フリーブロックにはめぼしいものはなかった
ALLOC_CENTER:
; 中心から確保するのね
mov AX,mem_TopHeap
mov CX,AX
sub AX,BX
jc short NO_MEMORY ; 95/3/22
cmp AX,mem_EndMark
jb short NO_MEMORY
mov mem_TopHeap,AX
mov ES,AX
mov ES:[0].nextseg,CX
mov ES:[0].using,1
mov BX,AX
jmp short RETURN
NO_MEMORY: ; こんなとこに
mov AX,0
mov mem_AllocID,AX
stc
pop ES
pop CX
pop BX
ret 2
; ES=now
; AX=next
; CX=now+size
FOUND_HOLE:
; いいフリーブロックが見つかったので
; 前の必要部分だけを切り取るのだ
sub AX,CX
cmp AX,1 ; 新しい穴が 0〜1パラの大きさしかないなら穴を占有する
jbe short JUST_FIT
add AX,CX
mov ES:[0].using,1
mov ES:[0].nextseg,CX
mov BX,ES
mov ES,CX
mov ES:[0].nextseg,AX
mov ES:[0].using,0
cmp BX,mem_FirstHole
jne short RETURN
mov mem_FirstHole,CX ; 今のが先頭フリーブロックだったら更新だ
jmp short RETURN
; ちょうどいい按配でフリーブロックが見つかったのね
JUST_FIT:
mov ES:[0].using,1
mov BX,ES
cmp BX,mem_FirstHole
jne short RETURN
; 今つぶしたのが先頭フリーブロックだったのなら検索だ
mov AX,mem_OutSeg
mov CX,BX
push BX
SEARCH_NEXT_HOLE:
les CX,ES:[0] ; CX=using, ES=nextseg
jcxz short FOUND_NEXT_HOLE
mov BX,ES
cmp BX,AX
jb short SEARCH_NEXT_HOLE
xor BX,BX
FOUND_NEXT_HOLE:
mov mem_FirstHole,BX
pop BX
; jmp short RETURN
; in: BX = 確保できたメモリの管理ブロックのsegment
RETURN: mov ES,BX
mov AX,0
xchg AX,mem_AllocID
mov ES:[0].mem_id,AX
lea AX,[BX+1]
clc
NO_THANKYOU: ; hmem_freeのエラーはここにくる(笑)
pop ES
pop CX
pop BX
ret 2
endfunc ; }
func HMEM_FREE ; hmem_free() {
push BX
mov BX,SP
push CX
push ES
;
memseg = (RETSIZE+1)*2
mov BX,SS:[BX+memseg]
dec BX
mov ES,BX
cmp BX,mem_TopHeap
je short EXPAND_CENTER ; 先頭のブロックなら専用処理へ
jb short NO_THANKYOU ; mem_TopHeapより小さいなら無効
; 先頭ではないブロックの開放処理
xor BX,BX
cmp ES:[BX].using,1
jne short NO_THANKYOU ; usingが1でなければ無効
mov ES:[BX].using,BX ; using <- 0
mov CX,mem_FirstHole
mov AX,ES
mov mem_FirstHole,AX
jcxz short FREE_RETURN ; 穴がなかったのならすぐOKだ
cmp AX,CX
jb short CONNECT_START
mov AX,CX ; 以前の最初の穴と現在位置のどちらか低い方を
mov mem_FirstHole,AX ; 新しい最初の穴に。
CONNECT_START:
mov CX,AX
mov AX,ES:[BX].nextseg ; 終了地点は、目的ブロックの次だけど
cmp AX,mem_OutSeg
jne short NO_TAIL
mov AX,ES ; 末尾だったら、終了地点をひとつ前へ
NO_TAIL:
; ・連接してしまったフリーブロックを接続する作業
push DS
CONNECT_FREE:
; 空きを探すループ
mov DS,CX ; DS<-CX
mov CX,[BX].nextseg ; CX=next seg
cmp CX,AX
ja short CONNECT_SKIP_OVER ; nextsegが最終segより大きければ終わり
cmp [BX].using,BX
jne short CONNECT_FREE ; フリーブロックになるまで進める
; 連続した空きを繋ぐループ
CONNECT_FREE2:
mov ES,CX ; ES<-CX(=next seg)
cmp ES:[BX].using,BX ; 連接したフリーまで進める
jne short CONNECT_FREE
; DSとESのフリーブロックが連接している
mov CX,ES:[BX].nextseg ; CX=新しいnext seg
mov [BX].nextseg,CX ; 接続
cmp CX,AX
jbe short CONNECT_FREE2 ; CXが最終seg以内ならまた次を見る
CONNECT_SKIP_OVER:
pop DS
jmp short FREE_RETURN
EVEN
EXPAND_CENTER: ; 先頭ブロックの開放
xor BX,BX
mov AX,ES:[BX].nextseg
mov mem_TopHeap,AX
cmp AX,mem_OutSeg
je short FREE_RETURN ; 末尾だったら終り
mov ES,AX
cmp ES:[BX].using,BX
jne short FREE_RETURN ; 次のブロックが使用中なら終り
; すぐ次(ES:)がフリーブロックなのでそこも詰める。
mov AX,ES:[BX].nextseg
mov mem_TopHeap,AX
; 以後の最初のフリーブロックを検索し、その場所をmem_FirstHoleに入れる
mov CX,mem_OutSeg
cmp AX,CX
je short FREE_LOST_HOLE ; それが最終ブロックなら飛ぶ
jmp short X_SEARCH_HOLE
EVEN
X_SEARCH_NEXT_HOLE:
mov AX,ES:[BX].nextseg
cmp AX,CX
je short FREE_LOST_HOLE
X_SEARCH_HOLE:
mov ES,AX
cmp ES:[BX].using,BX
jne short X_SEARCH_NEXT_HOLE
mov BX,ES
FREE_LOST_HOLE:
mov mem_FirstHole,BX
FREE_RETURN:
clc
pop ES
pop CX
pop BX
ret 2
endfunc ; }