; 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 ; }