607 lines
17 KiB
NASM
607 lines
17 KiB
NASM
name SMMU
|
||
title Scheme Memory Management Utilities
|
||
page 62,132
|
||
; =====> SMMU.ASM
|
||
;****************************************************************
|
||
;* TIPC Scheme '84 Memory Management Utilities *
|
||
;* *
|
||
;* (C) Copyright 1985, 1987 by Texas Instruments Incorporated. *
|
||
;* All rights reserved. *
|
||
;* *
|
||
;* Author: Herman Schuurman *
|
||
;* Date written: 26 August 1985 *
|
||
;* Last change: 17 September 1985 *
|
||
;* History: *
|
||
;* rb 4/ 5/87 "getbase" returns in carry flag a page's *
|
||
;* swap state *
|
||
;****************************************************************
|
||
.286c ;; Utilize the expanded 80286 instruction set
|
||
include pcmake.equ
|
||
include schemed.equ
|
||
include schemed.ref
|
||
include schemed.mac
|
||
|
||
DOS equ 021h
|
||
ExtAlloc equ 99 ;; # extended mem pages to allocate initially
|
||
;; (99 effectively removes barrier)
|
||
|
||
DGROUP group data
|
||
PGROUP group prog
|
||
|
||
data segment word public 'DATA'
|
||
assume ds:DGROUP
|
||
extrn page0:byte, page4:byte, page5:byte, page6:byte
|
||
extrn page7:byte, page8:byte
|
||
|
||
extrn _top:word, _paras:word,first_pa:word,first_dos:word
|
||
|
||
;; Age table
|
||
|
||
agetable label word
|
||
dw NUMPAGES dup (0)
|
||
|
||
AllocPag dw 0 ;; Allocated number of pages
|
||
;;
|
||
;; The following EQUates give the special bits within the page table,
|
||
;; mainly used for the CLOCK algorithm. Note that these equates are
|
||
;; also defined in SBIGEXT.C if modified.
|
||
|
||
SWAPPED equ 00000001b ;; Page is currently in extended memory
|
||
FIXED equ 10000000b ;; Fixed in memory (long pages)
|
||
|
||
PageBuf dw SWAPPED ;; Current available swap page (default 0)
|
||
|
||
public VMCycle
|
||
VMCycle dw 0 ;; Current VM cycle (modulo 65536)
|
||
|
||
;; public FAULTS
|
||
;;FAULTS dw 0 ;; Number of page faults
|
||
|
||
public GC_ING
|
||
GC_ING dw 0 ;; Indicate whether garbage collecting
|
||
|
||
m_lck_er db "[VM FATAL ERROR] Memory lock error - no page to swap",0Ah,0
|
||
m_pag_er db "[VM FATAL ERROR] Memory paging error number "
|
||
p_errnum db 30h
|
||
db 0Ah,0
|
||
|
||
;; Extended memory support structures....
|
||
|
||
DESC struc ;; Data segment descriptor
|
||
DESCLimit dw MIN_PAGESIZE ;; Segment limit (length)
|
||
DESCBaseL db 0 ;; Physical address - bits 7..0
|
||
DESCBaseM dw 0 ;; Physical address - bits 23..8
|
||
db 0 ;; Access rights byte
|
||
dw 0 ;; Intel reserved....
|
||
DESC ends
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; The GDT passed to INT 15h function 87h, is organized as follows :
|
||
;;
|
||
;; .-----------.
|
||
;; V |
|
||
;; [ES:SI] --> +00 .---------------. |
|
||
;; | Dummy | |
|
||
;; +08 |---------------| |
|
||
;; | GDT Loc |---'
|
||
;; +10 |---------------|
|
||
;; | Source GDT |
|
||
;; +18 |---------------|
|
||
;; | Target GDT |
|
||
;; +20 |---------------|
|
||
;; | BIOS code seg |
|
||
;; +28 |---------------|
|
||
;; | Stack segment |
|
||
;; `---------------'
|
||
;;
|
||
;;======================================================================
|
||
|
||
GDT label byte ;; Begin of global descriptor table
|
||
DESC <> ;; Dummy descriptor
|
||
|
||
DESC <> ;; GDT descriptor
|
||
|
||
Source DESC <,,,93h,> ;; Source area descriptor
|
||
|
||
Target DESC <,,,93h,> ;; Target area descriptor
|
||
|
||
DESC <> ;; BIOS code segment descriptor
|
||
|
||
DESC <> ;; Stack segment descriptor
|
||
|
||
data ends
|
||
|
||
prog segment byte public 'PROG'
|
||
assume cs:PGROUP
|
||
public _MMU,_%MMU
|
||
;; The following are here so link edit won't find urevolved refs
|
||
public _%MMU0,_%MMU1,_MMUCB
|
||
public getbase
|
||
public InitMem
|
||
|
||
extrn print_an:near ;; print_and_exit (truncated to 8 chars)
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; _MMU - Get page indicated on stack into real memory,
|
||
;; and return the paragraph address of it on the stack...
|
||
;;
|
||
;;======================================================================
|
||
Lcl_DS_Save dw data ;; Saved Data Segment
|
||
|
||
_%MMU proc far ;; Entry from PROGX segment
|
||
_%MMU0:
|
||
_%MMU1:
|
||
push AX
|
||
call _MMU
|
||
pop AX
|
||
ret
|
||
_%MMU endp
|
||
|
||
_MMU proc near ;; Normal Entry from PROG segment
|
||
_MMUCB:
|
||
push BP ;; Make stack accessable
|
||
mov BP,SP
|
||
push DS ;; Save Caller's DS
|
||
mov DS,CS:Lcl_DS_Save ;; and make our's available
|
||
push AX
|
||
push BX
|
||
mov BX,word ptr [bp+4] ;; Get pagetabl offset
|
||
mov AX,word ptr pagetabl+[BX] ;; Get (new) table indicator
|
||
cmp BX,PreAlloc*2 ;; If one of dedicated pages
|
||
jb M_RetPage ;; then jump
|
||
test byte ptr [pagetabl+BX],SWAPPED ;; If in extended memory
|
||
jne M_Swap ;; then go swap it in
|
||
|
||
;; Update age and return para address
|
||
|
||
M_Ret:
|
||
inc VMCycle ;; Time stamp
|
||
jnz M_Ret01 ;; On overflow
|
||
call PgSweep ;; Go sweep entire pagetabl
|
||
M_Ret01:
|
||
mov AX,VMCycle ;; Get time stamp
|
||
mov word ptr agetable+[BX],AX ;; Place in ageing table
|
||
|
||
mov AX,word ptr pagetabl+[BX] ;; Get paragraph address
|
||
xor AL,AL
|
||
M_RetPage:
|
||
mov word ptr [BP+4],AX ;; Set return value
|
||
pop BX
|
||
pop AX
|
||
pop DS
|
||
pop BP
|
||
ret
|
||
|
||
;; Retrieve page from extended memory
|
||
|
||
M_Swap:
|
||
pusha ;; Save all registers
|
||
push ES ;; including ES
|
||
push AX ;; Save page number on stack
|
||
push BX ;; Save the page table entry
|
||
call FndPage ;; Find a page for swapping
|
||
pop DI ;; Retrieve final destination
|
||
mov AX,PageBuf ;; Set swapped page address
|
||
xchg pagetabl+[BX],AX ;; Get the current page contents
|
||
xor AL,AL ;; Remove attribute bits
|
||
mov pagetabl+[DI],AX
|
||
mov BX,PageBuf ;; Get the page buffer address
|
||
shr BX,2 ;; Adjust the page base address
|
||
add BH,10h ;; and raise above 1MByte
|
||
shr AX,4 ;; Create a correct address
|
||
push AX ;; Save source as next destination
|
||
call MovePage ;; Swap old page out
|
||
pop BX ;; Set next destination
|
||
pop AX ;; and old source
|
||
mov PageBuf,AX ;; Set new swap page
|
||
shr AX,2
|
||
add AH,10h
|
||
call MovePage ;; Swap new page in
|
||
pop ES ;; Restore all registers
|
||
popa ;; including ES
|
||
|
||
;; inc FAULTS ;; update page fault count
|
||
|
||
jmp M_Ret
|
||
|
||
_MMU endp
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; PgSweep - page table clocked sweep routine.
|
||
;; This routine cleans up the current page table after a full
|
||
;; reference cycle (253 counts).
|
||
;;
|
||
;;======================================================================
|
||
|
||
public PgSweep
|
||
PgSweep proc near
|
||
push AX
|
||
push BX
|
||
push CX
|
||
mov BX,offset agetable[PreAlloc*2] ;; Don't bother with the
|
||
mov CX,AllocPag ;; dedicated pages in the table
|
||
xor AX,AX ;; Clear AX register
|
||
PgSwp$0:
|
||
mov AL,byte ptr [BX+1] ;; Get the current high byte
|
||
mov word ptr [BX],AX
|
||
add BX,2
|
||
loop PgSwp$0 ;; Continue with next sweep
|
||
mov VMCycle,100h ;; Set next cycle
|
||
pop CX
|
||
pop BX
|
||
pop AX
|
||
ret
|
||
|
||
PgSweep endp
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; FndPage - Find a swappable page in the page table.
|
||
;; This routine scans the page table (non-dedicated pages only),
|
||
;; for swappable pages. The least recently used page NOT USED
|
||
;; IN THE CURRENT VM INSTRUCTION is selected...
|
||
;;
|
||
;; As an added bonus, the current code page can not be swapped
|
||
;; either.....
|
||
;;
|
||
;;======================================================================
|
||
|
||
FndPage proc near
|
||
mov BX,cb_pag ;; Get entry into current code page
|
||
cmp BX,PreAlloc*2 ;; Check against permanent pages
|
||
jb FndPag$1 ;; Don't worry...it'll stay around
|
||
cmp pagetabl+[BX],FIXED ;; Check for fixed page
|
||
jbe FndPag$1 ;; which will stay too
|
||
mov AX,VMCycle ;; Set to current cycle
|
||
mov agetable+[BX],AX ;; Try to keep page in memory
|
||
FndPag$1:
|
||
mov BX,PreAlloc*2 ;; Don't bother with the
|
||
mov CX,AllocPag ;; dedicated pages in the table
|
||
xor DX,DX ;; Set initial distance
|
||
|
||
FndPag$2:
|
||
test byte ptr [BX+pagetabl],FIXED+SWAPPED
|
||
jne FndPag$3 ;; Fixed,Swapped,Noswap pages are exempt
|
||
mov AX,VMCycle ;; Check against current cycle
|
||
sub AX,agetable+[BX]
|
||
cmp DX,AX
|
||
jae FndPag$3 ;; Already found a better page
|
||
mov SI,BX ;; Save the page address
|
||
mov DX,AX ;; and its value
|
||
FndPag$3:
|
||
add BX,2
|
||
loop FndPag$2 ;; Continue with next sweep
|
||
|
||
;; Completed the sweep..the most desirable page should
|
||
;; be in SI now, unless DX is still 0....
|
||
|
||
cmp DX,0 ;; See if we found a page
|
||
je FndPag$4 ;; No...error
|
||
mov BX,SI ;; Return its number
|
||
ret
|
||
|
||
public FndPag$4
|
||
|
||
FndPag$4:
|
||
lea BX,m_lck_er ;; Indicate a lock error
|
||
FatalError:
|
||
push BX ;; Save the error message
|
||
mov AX,DS
|
||
mov ES,AX ;; Make sure ES is Ok...
|
||
C_call print_an ;; Print the message and quit
|
||
|
||
FndPage endp
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; Get page base address without forcing a page fault.
|
||
;; For debugging purposes only (SDUMP.C)....
|
||
;;
|
||
;; On exit, set carry if page is swapped out, else clear carry (used by XLI)
|
||
;;
|
||
;;======================================================================
|
||
|
||
getbase proc near
|
||
push BP
|
||
mov BP,SP
|
||
mov BX,word ptr [BP+4]
|
||
mov AX,word ptr [BX+pagetabl] ;; Get table indicator
|
||
|
||
test AX,SWAPPED ; is page swapped out?
|
||
jz getb_10 ; no, jump
|
||
stc ; page is swapped out, set carry
|
||
jmp short getb_20
|
||
getb_10: clc ; page is in memory, clear carry
|
||
|
||
getb_20: pop BP
|
||
ret
|
||
|
||
getbase endp
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; Swap page to extended memory
|
||
;; Used in FIND_BIG_BLOCK in SBIGMEM.C
|
||
;;
|
||
;;======================================================================
|
||
|
||
public move_pag
|
||
move_pag proc near
|
||
push BP
|
||
mov BP,SP
|
||
pusha ;; Save all registers
|
||
push ES ;; including ES
|
||
|
||
mov DI,[BP+6] ;; Extended memory page to swap
|
||
mov AX,word ptr pagetabl+[DI] ;; AX <= Extended memory address
|
||
|
||
mov BX,[BP+4] ;; Real memory page to swap
|
||
xchg pagetabl+[BX],AX ;; Update its pagetabl entry
|
||
xor AL,AL ;; AX <= para address of page to swap
|
||
push DI
|
||
push AX
|
||
|
||
mov BX,word ptr [BX+pagetabl] ;; Extended page address (destination)
|
||
shr BX,2 ;; Adjust page base address
|
||
add BH,10h ;; and raise above 1mb address
|
||
shr AX,4 ;; Real page address (source)
|
||
call MovePage ;; Move it
|
||
|
||
pop AX ;; Reload paragraph address
|
||
or AL,FIXED ;; Fixed attribute
|
||
pop DI ;; Reload page number
|
||
mov word ptr pagetabl+[DI],AX ;; Update pagetabl entry
|
||
|
||
pop ES ;; Restore all regs
|
||
popa ;; including ES
|
||
|
||
pop BP ;; restore base ptr
|
||
ret
|
||
|
||
move_pag endp
|
||
|
||
subttl Extended memory support
|
||
page
|
||
;;======================================================================
|
||
;;
|
||
;; Extended memory I/O routine
|
||
;;
|
||
;; Source address is in AX, destination in BX.
|
||
;; The high byte of each register contains the upper 8 bits of
|
||
;; the real address (bits 16..23). The low byte contains the
|
||
;; next 8 bits of the real address (bits 8..15)...
|
||
;;
|
||
;;======================================================================
|
||
|
||
MovePage proc near
|
||
mov SI,SS
|
||
mov CX,SP ; Save the original stack in SI:CX
|
||
cli
|
||
mov DX,CS
|
||
mov SS,DX
|
||
mov SP,offset PGROUP:ExtMemStack
|
||
sti
|
||
push SI
|
||
push CX ; Save old stack info
|
||
mov Source.DESCBaseM,AX
|
||
mov Target.DESCBaseM,BX
|
||
mov CX,MIN_PAGESIZE/2 ;; Reduce pagesize to word count
|
||
push DS
|
||
pop ES
|
||
mov SI,offset DGROUP:GDT
|
||
mov AH,87h ; Perform a block move
|
||
int 15h
|
||
|
||
; kludge to fix hanging keyboard
|
||
mov AL,0AEh ; ensure keyboard enabled
|
||
out 64h,AL ; output to 8042 controller
|
||
|
||
pop CX
|
||
pop BX
|
||
cli
|
||
mov ss,BX ; Restore the original stack
|
||
mov sp,CX
|
||
sti
|
||
jz MovRet ; If successful, return
|
||
or AH,AH ; Return status non-zero?
|
||
jnz MovePage$1 ; Yes...error
|
||
MovRet:
|
||
ret
|
||
|
||
;; Error detected durin paging ....as fatal as can be....
|
||
|
||
MovePage$1:
|
||
or p_errnum,AH ; Set error indicator
|
||
lea BX,m_pag_er ; Load up Error message
|
||
jmp FatalError ; Abort
|
||
|
||
MovePage endp
|
||
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; InitMem()
|
||
;; Initialize all the memory tables correctly. Return the
|
||
;; total number of pages (excluding the dedicated ones) we've
|
||
;; been able to allocate.
|
||
;;
|
||
;;======================================================================
|
||
|
||
InitMem proc near
|
||
mov BX,DS
|
||
mov CS:Lcl_DS_Save,BX ;; Save DS for manager above
|
||
mov ES,BX ;; Ensure ES = DS
|
||
|
||
;; Convert offset within pagetabl[0] into paragraph address
|
||
|
||
mov DI,offset pagetabl
|
||
mov AX,word ptr [DI]
|
||
mov CX,4
|
||
shr AX,CL
|
||
add AX,BX
|
||
mov word ptr [DI],AX
|
||
|
||
;; Same for pagetabl[4] through pagetabl[8]
|
||
|
||
mov DX,5
|
||
mov DI,offset pagetabl[8]
|
||
EmmP$0:
|
||
mov AX,word ptr [DI]
|
||
shr AX,CL
|
||
add AX,BX
|
||
mov word ptr [DI],AX
|
||
add DI,2
|
||
dec DX
|
||
jnz EmmP$0
|
||
|
||
;; Compute first page paragraph address
|
||
;; (In the process, allocate all the memory that DOS will give us.)
|
||
|
||
mov BX,0FFFFh ;; first ask for too much
|
||
mov AH,048h
|
||
int DOS ;; DOS gets an error, but tells us
|
||
;; in BX how much we CAN get
|
||
mov AH,048h
|
||
int DOS ;; reissue allocation request
|
||
mov first_dos,AX ;; save address for returning it to DOS
|
||
add AX,(MIN_PAGESIZE shr 4) - 1 ;; Move to page boundary
|
||
and AX,not ((MIN_PAGESIZE shr 4) - 1)
|
||
mov first_pa,AX ;; first page paragraph address
|
||
|
||
|
||
;; Initialize page management table with pages available in real memory
|
||
|
||
mov DX,nextpage
|
||
mov freepage,DX ;; freepage = nextpage
|
||
mov DI,_paras ;; Get maximum number of paragraphs
|
||
sub DI,(MIN_PAGESIZE shr 4) ;; Get address of last paragraph
|
||
xor CX,CX ;; Keep number of pages in CX
|
||
InitM$1:
|
||
cmp DI,AX ;; Did we reach it
|
||
jb InitM$2 ;; Yes...no more
|
||
cmp DX,NUMPAGES ;; See if we have filled the table
|
||
jae InitM$2
|
||
mov BX,DX
|
||
shl BX,1
|
||
mov word ptr [BX+pagetabl],AX
|
||
and word ptr [BX+attrib],not NOMEMORY
|
||
inc DX
|
||
mov word ptr [BX+pagelink],DX
|
||
mov word ptr [BX+nextcell],0
|
||
inc CX ;; page_count++
|
||
add AX,(MIN_PAGESIZE shr 4)
|
||
jmp InitM$1
|
||
|
||
;;
|
||
;; At this time, DX <= next avail page number, CX <= current page count
|
||
;;
|
||
;; Now Lets see if this is a 286 machine
|
||
;;
|
||
|
||
InitM$2:
|
||
mov nextpage,DX ;; Save next available page
|
||
xor AX,AX
|
||
|
||
mov BX,PC_MAKE ;; Get pc type
|
||
cmp BX,1 ;; Is it TIPC?
|
||
jne InitM$20 ;; No, go check for 286/386
|
||
push DS ;; Yes,lets check for a Bus Pro
|
||
mov DS,AX ;; DS <= 0 for addressing low mem
|
||
mov BX,DS:word ptr [01A2h] ;; Checkout vector 68 bytes 2 & 3
|
||
pop DS
|
||
add BL,BH
|
||
cmp BL,0F0h ;; If AL==F0 then TIPC=Business Pro
|
||
je InitM$21
|
||
jne InitM$Ret
|
||
InitM$20:
|
||
cmp BX,IBMAT ;; Is it IBM AT?
|
||
;; (includes XT/286, PS/2-50,-60)
|
||
je InitM$21 ;; yes, jump
|
||
cmp BX,IBM80 ;; Is it IBM PS/2 Model 80?
|
||
jne InitM$Ret ;; no, jump
|
||
|
||
;; Fill out rest of page table with extended memory pages. Only allocate
|
||
;; the first 512kb of extended memory; the rest is allocated but marked
|
||
;; marked as unallocated in the page tables (ie, ATTRIB and PAGELINK). This
|
||
;; will force the memory allocation to work (at least initially) in real
|
||
;; memory and the first 512k of extended memory until an "out of memory".
|
||
;; At that time, NEXTPAGE will be updated, and some more pages in extended
|
||
;; memory will then be marked as allocated (ie, ATTRIB and PAGELINK). This
|
||
;; scenario will be repeated until all of extended memory is actually used.
|
||
;; The upper limit will be help in LASTPAGE. Also see out_of_memory in
|
||
;; SMEMORY.C
|
||
;;
|
||
;; This should help performance for those applications which generate a
|
||
;; lot of garbage, but don't have to use the full extent of the extended
|
||
;; memory.
|
||
|
||
InitM$21:
|
||
push CX ;; Save current count
|
||
mov AH,88h ;; Get number of contiguous 1k
|
||
int 15h ;; blocks starting at 1MByte
|
||
add ax,((MIN_PAGESIZE shr 10) - 1)
|
||
and ax,not ((MIN_PAGESIZE shr 10) - 1)
|
||
xor DX,DX
|
||
mov CX,(MIN_PAGESIZE shr 10);; Number 1K blocks per page
|
||
idiv CX ;; Reduce to # of pages
|
||
mov DX,nextpage ;; Retrieve next available page number
|
||
mov CX,0101h ;; Count the extended pages
|
||
xor DI,DI
|
||
InitM$3:
|
||
dec AX ;; Check for last extended memory page
|
||
jle InitM$4 ;; Yes...no more
|
||
cmp DX,NUMPAGES ;; See if we have filled the table
|
||
jae InitM$4
|
||
mov BX,DX ;; DX = page number
|
||
shl BX,1 ;; BX = page table offset
|
||
inc DX ;; DX = next page number
|
||
mov word ptr [BX+pagetabl],CX ;; Page's address
|
||
mov word ptr [BX+nextcell],0 ;; Nextcell in page = 0
|
||
cmp CH,ExtAlloc ;; 512kb allocated?
|
||
jb InitM$33 ;; below, mark as allocated
|
||
ja InitM$35 ;; above, skip allocation
|
||
mov DI,DX ;; equal, EXT MEM LIMIT
|
||
InitM$33:
|
||
and word ptr [BX+attrib],not NOMEMORY
|
||
mov word ptr [BX+pagelink],DX ;; No, update pagelink info
|
||
InitM$35:
|
||
inc CH ;; Next extended memory page
|
||
jmp InitM$3 ;; Go allocate next page
|
||
|
||
;; At this time, DX <= last page number, CH <= # extended memory pages
|
||
|
||
|
||
InitM$4:
|
||
mov lastpage,DX ; last page number
|
||
mov nextpage,DX ; default nextpage to lastpage
|
||
or DI,DI ; Did we get our extended mem limit?
|
||
jz InitM$45 ; no, lastpage=nextpage, jump
|
||
mov nextpage,DI ; yes, lets use that limit
|
||
InitM$45:
|
||
xor AH,AH
|
||
mov AL,CH ; Get extended memory count
|
||
dec AX ; Don't count the swapping page
|
||
pop CX ; Retrieve real memory count
|
||
InitM$Ret:
|
||
add AX,CX ; Total Page count
|
||
mov AllocPag,AX ; Save allocated pages for later
|
||
ret
|
||
|
||
InitMem endp
|
||
|
||
;;======================================================================
|
||
;;
|
||
;; Temporary stack during extended memory operations...
|
||
;;
|
||
;;======================================================================
|
||
|
||
db 10 dup ("ExtStack")
|
||
ExtMemStack label word ;; Extended memory support stack
|
||
|
||
prog ends
|
||
|
||
end
|
||
|