!************************************************************************** !* !* Network driver interface for netboot bootrom !* !* Module: memory.S !* Purpose: Memory management functions for DOS simulator !* Entries: dos48, dos49, dos4A, freeall, checkmcb !* !************************************************************************** !* !* Copyright (C) 1995-2003 Gero Kuhlmann !* !* This program is free software; you can redistribute it and/or modify !* it under the terms of the GNU General Public License as published by !* the Free Software Foundation; either version 2 of the License, or !* any later version. !* !* This program is distributed in the hope that it will be useful, !* but WITHOUT ANY WARRANTY; without even the implied warranty of !* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !* GNU General Public License for more details. !* !* You should have received a copy of the GNU General Public License !* along with this program; if not, write to the Free Software !* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. !* !* $Id: memory.S,v 1.5 2003/01/25 23:29:41 gkminix Exp $ !* ! !************************************************************************** ! ! Include assembler macros: ! #include #include #include "dospriv.inc" ! !************************************************************************** ! ! BSS segment ! .bss extrn curr_psp ! current PSP segment .comm first_mcb,2 ! pointer to first MCB ! !************************************************************************** ! ! Start code segment. ! .text public dos48 ! define entry points public dos49 public dos4A public dos58 public freeall public checkmcb extrn txtseg ! !************************************************************************** ! ! Allocate memory block. This routine is very easy in that it allocates ! the first possible block it can find. This is not very economical, but ! sufficient for the DOS simulator. ! Note that this routine might also get called from outside a DOS interrupt, ! so it should not use any reference to the DOS stack structure! ! Input: BX - number of paragraphs ! Output: AX - segment address or error code, if carry flag set ! BX - maximum available number of paragraphs ! Carry flag - set if error ! Registers changed: AX, BX ! dos48: pushfs push dx xor dx,dx ! size of largest available block #ifdef IS386 mov fs,[first_mcb] ! start looking at first MCB #else mov es,[first_mcb] ! start looking at first MCB #endif call checkmcb ! check the first MCB jc dos489 dos481: segfs mov ax,[mcb_owner] or ax,ax ! is block available? jz dos483 dos484: call nextmcb ! no, jump to next MCB jnc dos481 or ax,ax jnz dos482 ! serious error mov bx,dx ! end of list reached, return largest mov ax,#ERR_NOMEM ! available memory block size dos482: stc ! return with error jmp dos489 dos483: segfs mov ax,[mcb_size] cmp bx,#$FFFF ! FFFFh size means: determine amount of je dos487 ! free memory. never get that much. cmp ax,bx ! check size of free memory block jae dos485 dos487: cmp ax,dx ! not sufficient, save in DX if greater jbe dos484 ! than any preceding block mov dx,ax jmp dos484 ! continue with next memory block dos485: mov dx,ax ! block is large enough, save its old segfs ! size and then set the new one mov [mcb_size],bx mov ax,[curr_psp] or ax,ax ! is a process running already jnz dos486 mov ax,#$FFFF ! dummy PSP address dos486: segfs mov [mcb_owner],ax ! save owner of memory block sub dx,bx ! compute size of remaining free block jbe dos488 ! nothing left #ifdef IS386 mov ax,fs #else mov ax,es #endif add ax,bx ! compute address of new MCB inc ax dec dx ! adjust size of remaining memory block mov bl,#MCB_MEM_ID segfs xchg bl,[mcb_id] ! remember old ID and set new ID push ds mov ds,ax mov byte ptr [mcb_id],bl mov word ptr [mcb_size],dx ! setup new free memory block mov word ptr [mcb_owner],#0 pop ds segfs mov bx,[mcb_size] ! restore BX register call cleanup ! cleanup free memory blocks dos488: #ifdef IS386 mov ax,fs #else mov ax,es ! return address of new memory block #endif inc ax ! to caller clc dos489: pop dx popfs ret ! !************************************************************************** ! ! Free a memory block. ! Note that this routine might also get called from outside a DOS interrupt, ! so it should not use any reference to the DOS stack structure! ! Input: ES - segment of memory block to free ! Output: AX - error code if carry flag set ! Carry flag - set if error ! Registers changed: AX ! dos49: pushfs push dx push ax mov dx,es dec dx ! convert memory block address into call findmcb ! MCB address and find MCB jc dos497 segfs mov word ptr [mcb_owner],#0 ! free memory block call cleanup ! integrate the new free memory block dos497: pop dx jc dos499 ! on error dont restore AX register mov ax,dx dos499: pop dx popfs ret ! !************************************************************************** ! ! Resize memory block. ! Note that this routine might also get called from outside a DOS interrupt, ! so it should not use any reference to the DOS stack structure! ! Input: BX - new size of memory block in paragraphs ! ES - segment of memory block ! Output: BX - maximum available memory space ! AX - error code if carry flag set ! Carry flag - set if error ! Registers changed: AX, BX ! dos4A: pushfs push dx push ax mov dx,es dec dx ! convert memory block address into call findmcb ! MCB address and find MCB jc dos4A7 segfs mov dx,[mcb_size] ! used at label dos4A4 cmp bx,dx je dos4A8 ! nothing changes jb dos4A4 ! make it smaller ! Make the memory block larger. To do this, first check if the next ! block has enough memory space available. Then include it into the ! current block. #ifdef IS386 mov dx,fs #else mov dx,es #endif call nextmcb ! check next MCB jnc dos4A2 or ax,ax jnz dos4A7 dos4A1: mov ax,#ERR_NOMEM ! when at the end of the memory, jmp dos4A7 ! there is no more available dos4A2: segfs cmp word ptr [mcb_owner],#0 ! is the next memory block free? jne dos4A1 push dx segfs mov ax,[mcb_size] ! load size of free memory block segfs mov dl,[mcb_id] ! load ID of free memory block #ifdef IS386 pop fs #else pop es #endif segfs add ax,[mcb_size] ! compute size of both memory blocks inc ax ! combined cmp ax,bx ! is it enough jae dos4A3 mov bx,ax ! return maximum size to caller jmp dos4A1 dos4A3: segfs mov [mcb_size],ax ! set size of new combined segment segfs mov [mcb_id],dl ! set ID of new segment cmp ax,bx ! if the size is exactly what has je dos4A8 ! been requested, we dont need to split mov dx,ax ! Make the memory block smaller. dos4A4: sub dx,bx ! compute size of remaining memory block dec dx jz dos4A8 ! new memory block is zero size segfs mov [mcb_size],bx ! set new size of memory block #ifdef IS386 mov ax,fs #else mov ax,es #endif add ax,bx ! compute address of new MCB inc ax mov bl,#MCB_MEM_ID segfs xchg bl,[mcb_id] ! remember old ID and set new ID push ds mov ds,ax mov byte ptr [mcb_id],bl mov word ptr [mcb_size],dx ! setup new free memory block mov word ptr [mcb_owner],#0 pop ds segfs mov bx,[mcb_size] ! restore BX register call cleanup ! cleanup free memory blocks jnc dos4A8 ! Termination with or without error. dos4A7: stc ! return with error pop dx ! dont restore AX jmp dos4A9 dos4A8: clc ! no error pop ax dos4A9: pop dx popfs ret ! !************************************************************************** ! ! Set or get the DOS memory allocation strategy. ! Since we always use the same allocation strategy, this function always ! returns the same value. In addition to the original DOS we introduce ! another function to return the segment of the first MCB. ! Input: AL - Function code ! BL - New memory allocation strategy ! Output: AX - Old memory allocation strategy ! Carry flag - set if error ! Registers changed: AX ! dos58: xor ah,ah or al,al ! return current allocation strategy, which jz dos589 ! is always 0 (e.g. low memory first fit) cmp al,#$63 ! our own function to return first MCB seg jne dos588 mov ax,[first_mcb] ! return first MCB segment jmp dos589 dos588: stc mov al,#1 ! function not supported dos589: ret ! !************************************************************************** ! ! Free all memory blocks of an owner. This routine is called internally ! by the process management code. ! Input: DX - PSP segment of owner ! Output: AX - error code if carry flag set ! CX - number of MCBs freed ! Carry flag - set if error ! Registers changed: AX, CX ! freeall: pushfs xor cx,cx #ifdef IS386 mov fs,[first_mcb] ! start at first MCB #else mov es,[first_mcb] ! start at first MCB #endif call checkmcb ! check that first MCB jc free9 free1: segfs cmp dx,[mcb_owner] ! found memory block of owner jne free2 segfs mov word ptr [mcb_owner],#0 ! free current memory block inc cx free2: call nextmcb ! continue with next MCB jnc free1 or ax,ax ! reached end of MCB list jz free8 stc jmp free9 free8: call cleanup ! cleanup free memory blocks free9: popfs ret ! !************************************************************************** ! ! Find an MCB in the list. ! Input: DX - MCB to search for ! Output: ES - segment of MCB (register FS on 386+) ! AX - error code ! Carry flag - set if error ! Registers changed: AX, ES ! findmcb: #ifdef IS386 mov fs,[first_mcb] ! start at first MCB #else mov es,[first_mcb] ! start at first MCB #endif call checkmcb ! check that first MCB jc findm3 findm1: #ifdef IS386 mov ax,fs #else mov ax,es #endif cmp ax,dx ! compare MCB addresses je findm3 ! found it call nextmcb ! proceed with next MCB jnc findm1 or ax,ax ! found an error? jnz findm2 mov ax,#ERR_INVMEM ! just got to end of list findm2: stc findm3: ret ! !************************************************************************** ! ! Cleanup MCB list by concatenating adjacent free MCBs. ! Input: none ! Output: AX - error code ! Carry flag - set if error ! Registers changed: AX ! cleanup: #ifdef IS386 push fs ! we really have to save FS here #else push es #endif push bx push dx #ifdef IS386 mov fs,[first_mcb] ! start at first MCB #else mov es,[first_mcb] ! start at first MCB #endif call checkmcb ! check that first MCB jc clean9 clean1: segfs mov ax,[mcb_owner] or ax,ax ! concatenation possible if current jz clean2 ! MCB is free clean5: call nextmcb ! otherwise check next MCB if its jnc clean1 ! free jmp clean4 ! handle error clean2: #ifdef IS386 mov dx,fs #else mov dx,es #endif clean6: call nextmcb ! get next MCB jnc clean3 clean4: or ax,ax ! at end of list? this 'or' also jz clean9 ! clears the carry flag stc ! return with error jmp clean9 clean3: segfs mov ax,[mcb_owner] ! is next MCB free? or ax,ax jnz clean5 ! no, continue with next one segfs mov bl,[mcb_id] segfs mov ax,[mcb_size] inc ax ! get total size of next block #ifdef IS386 mov fs,dx #else mov es,dx #endif segfs add [mcb_size],ax ! add size to preceding MCB segfs mov [mcb_id],bl ! copy ID from old to new MCB jmp clean6 ! continue with the next MCB clean9: pop dx pop bx #ifdef IS386 pop fs #else pop es #endif ret ! !************************************************************************** ! ! Proceed to next MCB. ! Input: ES - Segment of old MCB (register FS for 386+) ! Output: ES - Segment of new MCB (register FS for 386+) ! AX - error code (0 if end of MCB list) ! Carry Flag - set if error ! Registers changed: AX, ES ! nextmcb: xor ax,ax segfs cmp byte ptr [mcb_id],#MCB_END_ID je check8 #ifdef IS386 mov ax,fs #else mov ax,es #endif segfs add ax,[mcb_size] ! add size to current MCB pointer jc check7 ! should never happen inc ax seg cs cmp ax,[txtseg] ! check for upper limit jae check7 cmp ax,[first_mcb] ! check for lower limit jb check7 #ifdef IS386 mov fs,ax #else mov es,ax #endif xor ax,ax ! fall through to MCB checking ! !************************************************************************** ! ! Check if MCB is correct. ! Input: ES - segment of MCB (register FS for 386+) ! Output: AX - error code (unchanged if no error) ! Carry flag - set if error ! Registers changed: AX ! checkmcb: segfs cmp byte ptr [mcb_id],#MCB_MEM_ID je check9 segfs cmp byte ptr [mcb_id],#MCB_END_ID je check9 check7: mov ax,#ERR_INVMCB check8: stc ! error check9: ret ! !************************************************************************** ! end