534 lines
14 KiB
NASM
534 lines
14 KiB
NASM
|
name EXPSMMU
|
|||
|
title Scheme Memory Management Utilities for Expanded Memory
|
|||
|
page 62,132
|
|||
|
; =====> EXPSMMU.ASM
|
|||
|
;****************************************************************
|
|||
|
;* TIPC Scheme '84 Memory Management Utilities *
|
|||
|
;* *
|
|||
|
;* (C) Copyright 1985 by Texas Instruments Incorporated. *
|
|||
|
;* All rights reserved. *
|
|||
|
;* *
|
|||
|
;* Author: Terry Caudill *
|
|||
|
;* Date written: 18 March 1986 *
|
|||
|
;* Modifications: *
|
|||
|
;* tc 3/16/87 Better error handling for mapping errors and *
|
|||
|
;* fix to requiring page frame on 64k boundary *
|
|||
|
;* rb 4/5/87 "getbase" modified to return a page's swap *
|
|||
|
;* status in the carry bit *
|
|||
|
;****************************************************************
|
|||
|
include schemed.equ
|
|||
|
include schemed.ref
|
|||
|
include schemed.mac
|
|||
|
|
|||
|
DOS equ 021h
|
|||
|
EMM_DSR equ 67h ;; EMM DSR Interrupt
|
|||
|
|
|||
|
;; EMM DSR Function Requests
|
|||
|
|
|||
|
EMM_Status equ 40h ;; Get status of EMM
|
|||
|
EMM_FrameAddr equ 41h ;; Get segment of page frame
|
|||
|
EMM_PageCount equ 42h ;; How many pages available
|
|||
|
EMM_Allocate equ 43h ;; Allocate pages
|
|||
|
EMM_MapPage equ 44h ;; Map page into page frame
|
|||
|
EMM_Dealloc equ 45h ;; Deallocate PCS'S expanded mem pages
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
Emm_Handle dw 0 ;; Handle returned by EMM
|
|||
|
PageFrame dw 0 ;; Segment address for EMM Mapping
|
|||
|
|
|||
|
EmmAvail db 0 ;; Emm available
|
|||
|
public FirstEmmPage
|
|||
|
FirstEmmPage db 0 ;; First page number of Expanded Memory
|
|||
|
public EmmPageNum,EmmPage,CodeIn
|
|||
|
EmmPageNum db 2 ;; Emm Physical page number to map
|
|||
|
EmmPage equ $
|
|||
|
EmmPage0 db 0 ;; Table to map Emm Physical page
|
|||
|
EmmPage1 db 0 ;; to actual pagetable offset
|
|||
|
EmmPage2 db 0
|
|||
|
CodeIn db 0 ;; Code block currently mapped
|
|||
|
public GC_ING
|
|||
|
GC_ING dw 0
|
|||
|
|
|||
|
EmmDeviceName db "EMMXXXX0"
|
|||
|
m_ems_er db "[VM FATAL ERROR] Expanded Memory Manager error "
|
|||
|
db 38h
|
|||
|
p_errnum db 30h
|
|||
|
db 0Ah,0
|
|||
|
|
|||
|
data ends
|
|||
|
|
|||
|
|
|||
|
prog segment byte public 'PROG'
|
|||
|
assume cs:PGROUP
|
|||
|
public _MMU,_%MMU
|
|||
|
public _%MMU0,_%MMU1,_MMUCB
|
|||
|
public gcclean
|
|||
|
public getbase
|
|||
|
public InitMem
|
|||
|
public rlsexp
|
|||
|
|
|||
|
extrn print_an:near ;; print_and_exit (truncated to 8 chars)
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; _MMU - Take page passed on stack, and return its paragraph address
|
|||
|
;; on the stack. If page in conventional memory, just get its
|
|||
|
;; paragraph address from pagetabl. If in expanded memory and
|
|||
|
;; already mapped in, return the PageFrame, otherwise request
|
|||
|
;; the EMM to map the page into the PageFrame.
|
|||
|
;;
|
|||
|
;; NOTE: If an expanded memory page is requested which is greater
|
|||
|
;; than the normal page size, Emm Pages 0 and 1 are loaded
|
|||
|
;; automatically and address of page 0 returned.
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
|
|||
|
;**************************************************************************
|
|||
|
; *
|
|||
|
; W A R N I N G *
|
|||
|
; Any references to data normally addressed by the data segment register *
|
|||
|
; should be prefixed with SS: (segment override) because the DS register *
|
|||
|
; may not contain the address of the current data segment. *
|
|||
|
; *
|
|||
|
;**************************************************************************
|
|||
|
|
|||
|
|
|||
|
_MMU proc near ;; Normal Entry from PROG segment
|
|||
|
push BP
|
|||
|
mov BP,SP ;; Make stack accessable
|
|||
|
push BX
|
|||
|
mov BX,word ptr [bp+4] ;; BX <= Page number
|
|||
|
cmp BL,SS:FirstEmmPage ;; Page in real memory?
|
|||
|
jb _MMUPageRet0 ;; Yes..return
|
|||
|
_MMU$0:
|
|||
|
push AX ;; Save caller's regs
|
|||
|
|
|||
|
mov AX,2 ;; DX <= Emm Physical page #
|
|||
|
cmp BL,SS:EmmPage2 ;; Mapped in Emm page 2?
|
|||
|
je _MMU$00 ;; Yes ...jump
|
|||
|
dec AX
|
|||
|
cmp BL,SS:EmmPage1 ;; Mapped in Emm page 1?
|
|||
|
je _MMU$00 ;; Yes ...jump
|
|||
|
dec AX
|
|||
|
cmp BL,SS:EmmPage0 ;; Mapped in Emm page 0?
|
|||
|
jne _MMU$01 ;; Yes ...jump
|
|||
|
_MMU$00:
|
|||
|
mov SS:EmmPageNum,AL ;; Mark as last page mapped
|
|||
|
jmp _MMUP$10
|
|||
|
|
|||
|
; If large page object, load 2 consecutive pages
|
|||
|
_MMU$01:
|
|||
|
cmp [SS:psize+BX],MIN_PAGESIZE ;; Normal sized page?
|
|||
|
je _MMU$1 ;; Yes...jump
|
|||
|
pop AX ;; Restore AX register
|
|||
|
mov SS:EmmPageNum,0 ;; Map Page 0 with 1st page
|
|||
|
push BX ;; Push Page number
|
|||
|
call _MMUPage ;; Go map it
|
|||
|
inc SS:EmmPageNum ;; Map Page 1 with 2nd page
|
|||
|
add BX,2 ;; Get next page number
|
|||
|
push BX ;; Push as argument
|
|||
|
call _MMUPage ;; Go map it
|
|||
|
pop BX ;; Ignore Para address of 2nd page
|
|||
|
pop BX ;; Return Para address of 1st page
|
|||
|
jmp _MMUPageRet
|
|||
|
|
|||
|
|
|||
|
; Page not currently mapped - Lets map it
|
|||
|
_MMU$1:
|
|||
|
mov AL,SS:EmmPageNum ;; Last Emm physical page mapped
|
|||
|
inc AL ;; Get next
|
|||
|
cmp AL,3 ;;
|
|||
|
jl _MMU$2 ;; If code block page
|
|||
|
xor AL,AL ;; then wrap to zero
|
|||
|
_MMU$2:
|
|||
|
mov SS:EmmPageNum,AL ;; Update Emm Page last mapped
|
|||
|
jmp _MMUP$1
|
|||
|
|
|||
|
_MMU endp
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; _MMUPage - Load Expanded page number specified in EmmPageNum.
|
|||
|
;; Emm Page 3 should only be used for the currently
|
|||
|
;; executing code block (via LoadCode macro).
|
|||
|
;;
|
|||
|
;; NOTE: EmmPageNum must be set before this routine is called.
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
|
|||
|
_MMUPage proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
push BX
|
|||
|
mov BX,word ptr [bp+4] ;; Get page to map
|
|||
|
cmp BL,SS:FirstEmmPage ;; Page in real memory?
|
|||
|
jae _MMUP$0 ;; No...go map it
|
|||
|
cmp SS:EmmPageNum,3 ;; Loading a code block?
|
|||
|
jne _MMUPageRet0 ;; No...return page
|
|||
|
mov SS:CodeIn,BL ;; Note code block
|
|||
|
_MMUPageRet0:
|
|||
|
mov BX,word ptr [BX+SS:pagetabl]
|
|||
|
_MMUPageRet:
|
|||
|
mov word ptr [bp+4],BX ;; return it
|
|||
|
pop BX
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
_MMUP$0:
|
|||
|
push AX
|
|||
|
xor AH,AH
|
|||
|
mov AL,SS:EmmPageNum ;; Get page number to map
|
|||
|
_MMUP$1:
|
|||
|
xchg AX,BX ;; Note page number in table
|
|||
|
mov byte ptr [SS:EmmPage+BX],AL
|
|||
|
xchg AX,BX
|
|||
|
|
|||
|
;; Map Page from Expanded memory
|
|||
|
|
|||
|
push AX ;; Save accross call
|
|||
|
push DX
|
|||
|
mov AH,EMM_MapPage ;; Map Page Function
|
|||
|
sub BL,SS:FirstEmmPage ;; Convert page to map
|
|||
|
shr BX,1 ;; to EMM Logical Page
|
|||
|
mov DX,SS:Emm_Handle ;; EMM Handle
|
|||
|
int EMM_DSR
|
|||
|
pop DX ;; Restore saved regs
|
|||
|
pop BX
|
|||
|
or AH,AH ;; Error doing map page?
|
|||
|
jnz Emm_Fatal_Map ;; Yes, fatal
|
|||
|
mov AX,BX ;; restore AX
|
|||
|
_MMUP$10:
|
|||
|
mov BX,SS:PageFrame ;; Get current page frame
|
|||
|
shl AL,1 ;; Convert to offset
|
|||
|
shl AL,1
|
|||
|
add BH,AL ;; and add to page frame
|
|||
|
pop AX
|
|||
|
jmp _MMUPageRet
|
|||
|
|
|||
|
Emm_Fatal_Map:
|
|||
|
jmp Emm_Fatal_Error
|
|||
|
|
|||
|
_MMUPage endp
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; Alternate Entry points
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
|
|||
|
;; Return Paragraph address of page number
|
|||
|
|
|||
|
_%MMU proc far ;; Entry from PROGX segment
|
|||
|
push AX
|
|||
|
call _MMU
|
|||
|
pop AX
|
|||
|
ret
|
|||
|
_%MMU endp
|
|||
|
|
|||
|
;; Load Emm Page 0 - Called from garbage compactor
|
|||
|
|
|||
|
_%MMU0 proc far ;; Entry from PROGX segment
|
|||
|
push AX
|
|||
|
mov SS:EmmPageNum,0
|
|||
|
call _MMUPAGE
|
|||
|
pop AX
|
|||
|
ret
|
|||
|
_%MMU0 endp
|
|||
|
|
|||
|
;; Load Emm Page 1 - Called from garbage compactor
|
|||
|
|
|||
|
_%MMU1 proc far ;; Entry from PROGX segment
|
|||
|
push AX
|
|||
|
mov SS:EmmPageNum,1
|
|||
|
call _MMUPAGE
|
|||
|
pop AX
|
|||
|
ret
|
|||
|
_%MMU1 endp
|
|||
|
|
|||
|
;; Load Code Block into Emm Page 3 - Entry from PROG segment
|
|||
|
|
|||
|
_MMUCB proc near
|
|||
|
mov SS:EmmPageNum,3
|
|||
|
jmp _MMUPage
|
|||
|
|
|||
|
_MMUCB endp
|
|||
|
|
|||
|
|
|||
|
;**************************************************************************
|
|||
|
; *
|
|||
|
; W A R N I N G *
|
|||
|
; Any above references to data normally addressed by the data segment *
|
|||
|
; register should be prefixed with SS: (segment override) because the *
|
|||
|
; DS register may not contain the address of the current data segment. *
|
|||
|
; *
|
|||
|
;**************************************************************************
|
|||
|
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; Get page base address without forcing a page fault.
|
|||
|
;; For debugging purposes only (SDUMP.C)....
|
|||
|
;;
|
|||
|
;; On exit, carry set if page is swapped out, else it's clear (used by XLI).
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
|
|||
|
getbase proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
push BX
|
|||
|
mov BX,word ptr [BP+4]
|
|||
|
cmp BL,SS:FirstEmmPage
|
|||
|
jae gc_00
|
|||
|
mov AX,word ptr [BX+SS:pagetabl] ;; Get paragraph address
|
|||
|
clc
|
|||
|
jmp gb_quit
|
|||
|
gc_00:
|
|||
|
mov AX,2
|
|||
|
cmp BL,SS:EmmPage0
|
|||
|
je gb_5
|
|||
|
dec AX
|
|||
|
cmp BL,SS:EmmPage1
|
|||
|
je gb_5
|
|||
|
dec AX
|
|||
|
cmp BL,SS:EmmPage2
|
|||
|
je gb_5
|
|||
|
dec AX
|
|||
|
cmp BL,SS:CodeIn
|
|||
|
stc
|
|||
|
jne gb_quit
|
|||
|
mov AX,3
|
|||
|
gb_5:
|
|||
|
shl AL,1
|
|||
|
shl AL,1
|
|||
|
or AL,byte ptr [SS:PageFrame+1]
|
|||
|
xchg AL,AH
|
|||
|
clc
|
|||
|
gb_quit:
|
|||
|
pop BX
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
|
|||
|
getbase endp
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; exppage()
|
|||
|
;; This routine returns the first emm page number
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
public exppage
|
|||
|
exppage proc near
|
|||
|
xor AH,AH
|
|||
|
mov AL,FirstEmmPage
|
|||
|
shr AL,1
|
|||
|
ret
|
|||
|
exppage endp
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; gcclean()
|
|||
|
;; This routine must be called after garbage collection and
|
|||
|
;; compaction to clean up the pagetabl and EmmPage table.
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
gcclean proc near
|
|||
|
mov byte ptr EmmPageNum,0 ;; Reset EmmPage indicator
|
|||
|
mov word ptr EmmPage,0
|
|||
|
mov byte ptr EmmPage2,0
|
|||
|
ret
|
|||
|
gcclean endp
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; InitMem()
|
|||
|
;; Check to see if expanded memory manager is present and set up
|
|||
|
;; the memory tables. Return the total number of pages (excluding
|
|||
|
;; the dedicated ones) we've been able to allocate.
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
Lcl_DS_Save dw data ;; Local copy of data segment
|
|||
|
|
|||
|
InitMem proc near
|
|||
|
mov BX,DS
|
|||
|
mov CS:Lcl_DS_Save,DS ;; 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
|
|||
|
EmmP$1:
|
|||
|
cmp DI,AX ;; Did we reach it
|
|||
|
jb EmmP$2 ;; Yes...no more
|
|||
|
cmp DX,NUMPAGES ;; See if we have filled the table
|
|||
|
jae EmmP$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 EmmP$1
|
|||
|
EmmP$2:
|
|||
|
push CX ;; Save # real memory pages
|
|||
|
shl DX,1
|
|||
|
mov FirstEmmPage,DL ;; Save first exp mem page number
|
|||
|
|
|||
|
mov AH,35H ;; Get Interrupt Vector
|
|||
|
mov AL,67H ;; "Vector"
|
|||
|
int 21H
|
|||
|
mov DI,000AH ;; ES:DI points to device name field
|
|||
|
lea SI,EmmDeviceName ;; DS:SI points to device name
|
|||
|
mov CX,8
|
|||
|
cld
|
|||
|
repe CMPSB ;; Compare the two strings
|
|||
|
je EmmPres ;; Jump if EMM present
|
|||
|
mov ES,CS:Lcl_DS_Save ;; Restore ES
|
|||
|
xor BX,BX ;; No EMM pages available
|
|||
|
jmp EmmP$2A ;; Skip talking to Emm Manager
|
|||
|
EmmPres:
|
|||
|
mov ES,CS:Lcl_DS_Save ;; Restore ES
|
|||
|
mov AH,EMM_FrameAddr ;; Get Page Frame Address
|
|||
|
int EMM_DSR
|
|||
|
or AH,AH
|
|||
|
jnz Emm_Fatal_Error
|
|||
|
EmmP$:
|
|||
|
mov PageFrame,BX ;; Save page frame address
|
|||
|
|
|||
|
mov AH,EMM_PageCount ;; Get Unallocated Pages Count
|
|||
|
int EMM_DSR ;; (returned in BX)
|
|||
|
or AH,AH
|
|||
|
jnz EMM_Fatal_Error
|
|||
|
EmmP$2A:
|
|||
|
cmp BX,0 ;; Are there any pages available?
|
|||
|
je EmmP$2B ;; No, jump
|
|||
|
mov EmmAvail,1 ;; Yes, note pages available
|
|||
|
EmmP$2B:
|
|||
|
mov AX,BX ;; Number exp mem pages available
|
|||
|
xor DX,DX
|
|||
|
mov DL,FirstEmmPage ;; Restore first exp mem page
|
|||
|
shr DX,1 ;; Convert to number
|
|||
|
xor CX,CX ;; Page count
|
|||
|
|
|||
|
;; Why was this here? mov SI,PageFrame
|
|||
|
|
|||
|
EmmP$3:
|
|||
|
cmp CX,AX ;; Last expanded memory page?
|
|||
|
je EmmP$4 ;; Yes...no more
|
|||
|
cmp DX,NUMPAGES ;; Filled the table?
|
|||
|
jae EmmP$4 ;; Yes...no more
|
|||
|
mov BX,DX
|
|||
|
shl BX,1
|
|||
|
mov word ptr [BX+pagetabl],0
|
|||
|
and word ptr [BX+attrib],not NOMEMORY
|
|||
|
inc DX
|
|||
|
mov word ptr [BX+pagelink],DX
|
|||
|
mov word ptr [BX+nextcell],0
|
|||
|
inc CX
|
|||
|
jmp EmmP$3
|
|||
|
|
|||
|
EmmP$4:
|
|||
|
mov nextpage,DX ;; nextpage = lastpage
|
|||
|
mov lastpage,DX ;;
|
|||
|
jcxz EmmP$Ret ;; Return if no pages allocated
|
|||
|
|
|||
|
mov AH,EMM_Allocate ;; Allocate Pages
|
|||
|
mov BX,CX ;; Number of pages
|
|||
|
int EMM_DSR
|
|||
|
or AH,AH
|
|||
|
jnz Emm_Fatal_Error
|
|||
|
mov emm_handle,DX ;; Save Handle returned
|
|||
|
EmmP$Ret:
|
|||
|
mov AX,CX ;; Get extended memory count
|
|||
|
pop CX ;; Retrieve real memory count
|
|||
|
add AX,CX ;; and return combination
|
|||
|
ret
|
|||
|
|
|||
|
Emm_Fatal_Error:
|
|||
|
mov BX,DS ;; Lattice needs ES=DS
|
|||
|
mov ES,BX
|
|||
|
and AH,0Fh ;; isolate low order nibble of error
|
|||
|
add AH,'0' ;; convert to ascii
|
|||
|
cmp AH,'9' ;; is it 0-9?
|
|||
|
jbe Emm_Fat01 ;; yes, jump
|
|||
|
add AH,'A'-'9'-1 ;; add fudge factor for A-F
|
|||
|
Emm_Fat01:
|
|||
|
mov byte ptr ss:p_errnum,AH ;; Set error indicator
|
|||
|
lea BX,ss:m_ems_er ;; Fatal Error Message
|
|||
|
push BX
|
|||
|
C_call print_an
|
|||
|
|
|||
|
|
|||
|
InitMem endp
|
|||
|
|
|||
|
|
|||
|
;;======================================================================
|
|||
|
;;
|
|||
|
;; rlsexp - Release Expanded Memory Pages
|
|||
|
;;
|
|||
|
;;======================================================================
|
|||
|
rlsexp proc near
|
|||
|
cmp EmmAvail,0 ;; Emm being used?
|
|||
|
je rlsret ;; No, Return
|
|||
|
mov AH,EMM_Dealloc ;; Yes, Deallocate pages
|
|||
|
mov DX,EMM_Handle
|
|||
|
int EMM_DSR
|
|||
|
rlsret:
|
|||
|
ret
|
|||
|
rlsexp endp
|
|||
|
|
|||
|
prog ends
|
|||
|
|
|||
|
end
|
|||
|
|