shmalloc.asm
来自「ART OF Assembly Language Programming, 很不」· 汇编 代码 · 共 819 行 · 第 1/2 页
ASM
819 行
; is the first and last item), but this code handles that case without any
; problems.
Free1st: mov ax, [bx].Next
mov ResidentSeg:AllocatedList, ax
jmp AddFree
; If we're removing the last guy in the chain, simply set the next field
; of the previous node in the list to NULL.
FreeLast: mov di, [bx].Prev
mov [di].Next, NULL
; Okay, now we've got to put the freed block onto the free block list.
; The free block list is sorted according to address. We have to search
; for the first free block whose address is greater than the block we've
; just freed and insert the new free block before that one. If the two
; blocks are adjacent, then we've got to merge them into a single free
; block. Also, if the block before is adjacent, we must merge it as
; well. This will coalesce all free blocks on the free list so there
; are as few free blocks as possible and those blocks are as large as
; possible.
AddFree: mov ax, ResidentSeg:FreeList
test ax, ax ;Empty list?
jne SrchPosn
; If the list is empty, stick this guy on as the only entry.
mov ResidentSeg:FreeList, bx
mov [bx].Next, NULL
mov [bx].Prev, NULL
jmp FreeDone
; If the free list is not empty, search for the position of this block
; in the free list:
SrchPosn: mov di, ax
cmp bx, di
jb FoundPosn
mov ax, [di].Next
test ax, ax ;At end of list?
jne SrchPosn
; If we fall down here, the free block belongs at the end of the list.
; See if we need to merge the new block with the old one.
mov ax, di
add ax, [di].BlkSize ;Compute address of 1st byte
add ax, 8 ; after this block.
cmp ax, bx
je MergeLast
; Okay, just add the free block to the end of the list.
mov [di].Next, bx
mov [bx].Prev, di
mov [bx].Next, NULL
jmp FreeDone
; Merge the freed block with the block DI points at.
MergeLast: mov ax, [di].BlkSize
add ax, [bx].BlkSize
add ax, 8
mov [di].BlkSize, ax
jmp FreeDone
; If we found a free block before which we are supposed to insert
; the current free block, drop down here and handle it.
FoundPosn: mov ax, bx ;Compute the address of the
add ax, [bx].BlkSize ; next block in memory.
add ax, 8
cmp ax, di ;Equal to this block?
jne DontMerge
; The next free block is adjacent to the one we're freeing, so just
; merge the two.
mov ax, [di].BlkSize ;Merge the sizes together.
add ax, 8
add [bx].BlkSize, ax
mov ax, [di].Next ;Tweak the links.
mov [bx].Next, ax
mov ax, [di].Prev
mov [bx].Prev, ax
jmp TryMergeB4
; If the blocks are not adjacent, just link them together here.
DontMerge: mov ax, [di].Prev
mov [di].Prev, bx
mov [bx].Prev, ax
mov [bx].Next, di
; Now, see if we can merge the current free block with the previous free blk.
TryMergeB4: mov di, [bx].Prev
mov ax, di
add ax, [di].BlkSize
add ax, 8
cmp ax, bx
je CanMerge
pop bx
pop di ;Nothing allocated, just
pop ds ; return to caller.
iret
; If we can merge the previous and current free blocks, do that here:
CanMerge: mov ax, [bx].Next
mov [di].Next, ax
mov ax, [bx].BlkSize
add ax, 8
add [di].BlkSize, ax
pop bx
pop di
pop ds
iret
assume ds:nothing
assume bx:nothing
assume di:nothing
; Here's where we handle the shared memory initializatin (SHMINIT) function.
; All we got to do is create a single block on the free list (which is all
; available memory), empty out the allocated list, and then zero out all
; shared memory.
Tryshminit: cmp al, 13h
jne TryShmAttach
; Reset the memory allocation area to contain a single, free, block of
; memory whose size is 0FFF8h (need to reserve eight bytes for the block's
; data structure).
push es
push di
push cx
mov ax, SharedMemory ;Zero out the shared
mov es, ax ; memory segment.
mov cx, 32768
xor ax, ax
mov di, ax
rep stosw
; Note: the commented out lines below are unnecessary since the code above
; has already zeroed out the entire shared memory segment.
; Note: we cannot put the first record at offset zero because offset zero
; is the special value for the NULL pointer. We'll use 4 instead.
mov di, 4
; mov es:[di].Region.Key, 0 ;Key is arbitrary.
; mov es:[di].Region.Next, 0 ;No other entries.
; mov es:[di].Region.Prev, 0 ; Ditto.
mov es:[di].Region.BlkSize, 0FFF8h ;Rest of segment.
mov ResidentSeg:FreeList, di
pop cx
pop di
pop es
mov ax, 0 ;Return no error.
iret
; Handle the SHMATTACH function here. On entry, DX contains a key number.
; Search for an allocated block with that key number and return a pointer
; to that block (if found) in ES:DI. Return an error code (AX=3) if we
; cannot find the block.
TryShmAttach: cmp al, 14h ;Attach opcode.
jne IllegalOp
mov ax, SharedMemory
mov es, ax
mov di, ResidentSeg:AllocatedList
FindOurs: cmp dx, es:[di].Region.Key
je FoundOurs
mov di, es:[di].Region.Next
test di, di
jne FoundOurs
mov ax, 3 ;Can't find the key.
iret
FoundOurs: add di, 8 ;Point at actual data.
mov ax, 0 ;No error.
iret
; They called us with an illegal subfunction value. Try to do as little
; damage as possible.
IllegalOp: mov ax, 0 ;Who knows what they were thinking?
iret
MyInt2F endp
assume ds:nothing
ResidentSeg ends
; Here's the segment that will actually hold the shared data.
SharedMemory segment para public 'Shared'
db 0FFFFh dup (?)
SharedMemory ends
cseg segment para public 'code'
assume cs:cseg, ds:ResidentSeg
; SeeIfPresent- Checks to see if our TSR is already present in memory.
; Sets the zero flag if it is, clears the zero flag if
; it is not.
SeeIfPresent proc near
push es
push ds
push di
mov cx, 0ffh ;Start with ID 0FFh.
IDLoop: mov ah, cl
push cx
mov al, 0 ;Verify presence call.
int 2Fh
pop cx
cmp al, 0 ;Present in memory?
je TryNext
strcmpl
byte "Dynamic Shared Memory TSR",0
je Success
TryNext: dec cl ;Test USER IDs of 80h..FFh
js IDLoop
cmp cx, 0 ;Clear zero flag.
Success: pop di
pop ds
pop es
ret
SeeIfPresent endp
; FindID- Determines the first (well, last actually) TSR ID available
; in the multiplex interrupt chain. Returns this value in
; the CL register.
;
; Returns the zero flag set if it locates an empty slot.
; Returns the zero flag clear if failure.
FindID proc near
push es
push ds
push di
mov cx, 0ffh ;Start with ID 0FFh.
IDLoop: mov ah, cl
push cx
mov al, 0 ;Verify presence call.
int 2Fh
pop cx
cmp al, 0 ;Present in memory?
je Success
dec cl ;Test USER IDs of 80h..FFh
js IDLoop
xor cx, cx
cmp cx, 1 ;Clear zero flag
Success: pop di
pop ds
pop es
ret
FindID endp
Main proc
meminit
mov ax, ResidentSeg
mov ds, ax
mov ah, 62h ;Get this program's PSP
int 21h ; value.
mov PSP, bx
; Before we do anything else, we need to check the command line
; parameters. If there is one, and it is the word "REMOVE", then remove
; the resident copy from memory using the multiplex (2Fh) interrupt.
argc
cmp cx, 1 ;Must have 0 or 1 parms.
jb TstPresent
je DoRemove
Usage: print
byte "Usage:",cr,lf
byte " shmalloc",cr,lf
byte "or shmalloc REMOVE",cr,lf,0
ExitPgm
; Check for the REMOVE command.
DoRemove: mov ax, 1
argv
stricmpl
byte "REMOVE",0
jne Usage
call SeeIfPresent
je RemoveIt
print
byte "TSR is not present in memory, cannot remove"
byte cr,lf,0
ExitPgm
RemoveIt: mov MyTSRID, cl
printf
byte "Removing TSR (ID #%d) from memory...",0
dword MyTSRID
mov ah, cl
mov al, 1 ;Remove cmd, ah contains ID
int 2Fh
cmp al, 1 ;Succeed?
je RmvFailure
print
byte "removed.",cr,lf,0
ExitPgm
RmvFailure: print
byte cr,lf
byte "Could not remove TSR from memory.",cr,lf
byte "Try removing other TSRs in the reverse order "
byte "you installed them.",cr,lf,0
ExitPgm
; Okay, see if the TSR is already in memory. If so, abort the
; installation process.
TstPresent: call SeeIfPresent
jne GetTSRID
print
byte "TSR is already present in memory.",cr,lf
byte "Aborting installation process",cr,lf,0
ExitPgm
; Get an ID for our TSR and save it away.
GetTSRID: call FindID
je GetFileName
print
byte "Too many resident TSRs, cannot install",cr,lf,0
ExitPgm
; Things look cool so far, so install the interrupts
GetFileName: mov MyTSRID, cl
print
byte "Installing interrupts...",0
; Patch into the INT 2Fh interrupt chain.
cli ;Turn off interrupts!
mov ax, 0
mov es, ax
mov ax, es:[2Fh*4]
mov word ptr OldInt2F, ax
mov ax, es:[2Fh*4 + 2]
mov word ptr OldInt2F+2, ax
mov es:[2Fh*4], offset MyInt2F
mov es:[2Fh*4+2], seg ResidentSeg
sti ;Okay, ints back on.
; We're hooked up, the only thing that remains is to initialize the shared
; memory segment and then terminate and stay resident.
printf
byte "Installed, TSR ID #%d.",cr,lf,0
dword MyTSRID
mov ah, MyTSRID ;Initialization call.
mov al, 13h
int 2Fh
mov dx, EndResident ;Compute size of program.
sub dx, PSP
mov ax, 3100h ;DOS TSR command.
int 21h
Main endp
cseg ends
sseg segment para stack 'stack'
stk db 256 dup (?)
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes db 16 dup (?)
zzzzzzseg ends
end Main
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?