786 lines
19 KiB
NASM
786 lines
19 KiB
NASM
;**********************************************************************
|
||
;
|
||
; Copyrigth (C) 1984 Logitech. All Rights Reserved.
|
||
;
|
||
; Permission is hereby granted to registered users to use or
|
||
; abstract the following program in the implementation of
|
||
; customized versions. This permission does not include the
|
||
; right to redistribute the source code of this program.
|
||
;
|
||
;
|
||
; RTS - Mainline of Modula-2/86(tm) Run Time System
|
||
;
|
||
; 8308.09 converted to PC-DOS 1.1
|
||
; 8312.28 function 26H and trapping of interrupt 23H (break)
|
||
; 8401.17 default programname is COMINT; init interrupt mask in PD
|
||
;
|
||
CGROUP group code
|
||
DGROUP group data,stack,memory
|
||
|
||
include RTS.INC
|
||
|
||
data segment public
|
||
|
||
public START_MEM, MEM_SIZE
|
||
public CUR_PROCESS, RTS_PROCESS, CUR_P_PTR
|
||
public BASE_PAGE_PTR
|
||
public SAVED_DISK, RTS_DISK
|
||
public FILE_SPEC, FILE_HANDLE
|
||
|
||
BASE_PAGE db 100H dup (?) ; required for the Main-Module
|
||
TOP_OF_MEMORY equ word ptr BASE_PAGE+2 ; last free paragraph, +1
|
||
TRANS_COM_SIZE equ 440H ; transient part of COMMAND.COM (in parag)
|
||
START_MEM dw ? ; first free paragraph
|
||
MEM_SIZE dw ? ; number of free paragraphs at START_MEM
|
||
DOS dd ? ; jump vector to DOS
|
||
START_ADDR dd ? ; start address of .LOD program
|
||
|
||
; - saved interrupt vectors -
|
||
OLD_RTS_VECTOR dd ?
|
||
OLD_DIV0_VECTOR dd ?
|
||
OLD_INTO_VECTOR dd ?
|
||
OLD_BREAK_VECTOR dd ?
|
||
|
||
; This is a copy of the descriptor of the current process:
|
||
CUR_PROCESS ProcessDescriptor <>
|
||
; Workspace of the MAIN process, starting with RTS:
|
||
RTS_PROCESS ProcessDescriptor <>
|
||
CUR_P_PTR dd RTS_PROCESS ; pointer to current process descr.
|
||
BASE_PAGE_PTR dd BASE_PAGE ; ptr to program segment prefix
|
||
MAIN_SP dw ?
|
||
MAIN_SS dw ?
|
||
|
||
START_DISK db ?
|
||
SAVED_DISK db ?
|
||
RTS_DISK db ?
|
||
DEFAULT_NAME db 'COMINT ' ; default file to load
|
||
DEFAULT_TYPE db 'LOD' ; default filetype for loading
|
||
DEFAULT_PATH db '\M2LOD\' ; secondary directory to search
|
||
DEF_PATH_LENGTH equ 7H
|
||
FILE_SPEC db 64H dup(?)
|
||
FILE_MSG1 db ' '
|
||
FILE_MSG2 db ' not found in current directory or in \M2LOD$'
|
||
FILE_HANDLE dw ?
|
||
RES_FN db '?:????????.???$' ; for writing filespecs
|
||
NO_FILE db 'File not found: $'
|
||
NO_MEMORY db 'Insufficient Memory: $'
|
||
SOME_ERROR DB ' --- $'
|
||
|
||
NORMAL_MSG DB 'normal termination$'
|
||
WARNED_MSG DB 'warning$'
|
||
STOP_MSG DB 'stopped$'
|
||
ASSERT_MSG DB 'wrong assertion$'
|
||
HALT_MSG DB 'HALT called$'
|
||
CASE_MSG DB 'case-tag error$'
|
||
STACK_MSG DB 'stack overflow$'
|
||
HEAP_MSG DB 'heap overflow$'
|
||
FCT_ERR_MSG DB 'function return error$'
|
||
ADDR_OVF_MSG DB 'address overflow$'
|
||
REAL_OVF_MSG DB 'real overflov$'
|
||
CARD_OVF_MSG DB 'cardinal overflow$'
|
||
INTEGER_OVF_MSG DB 'integer overflow$'
|
||
RANGE_ERR_MSG DB 'range error$'
|
||
ZERO_DIV_MSG DB 'divison by zero$'
|
||
PROC_END_MSG DB 'coroutine end$'
|
||
LOAD_MSG DB 'cannot load$'
|
||
CALL_MSG DB 'unsuccessfull program call$'
|
||
NO_PROG_MSG DB 'program not found$'
|
||
NO_MOD_MSG DB 'module not found$'
|
||
INCOMPAT_MSG DB 'incompatible module keys$'
|
||
BAD_FILE_MSG DB 'bad structure in file$'
|
||
ILL_INSTR_MSG DB 'illegal instruction encountered$'
|
||
ILL_FCT_MSG DB 'illegal RTS call$'
|
||
NO_MORE_ISR DB 'too many concurrent IO-Processes$'
|
||
|
||
even
|
||
STATUS_MSG DW NORMAL_MSG, WARNED_MSG, STOP_MSG, ASSERT_MSG
|
||
DW HALT_MSG, CASE_MSG, STACK_MSG, HEAP_MSG
|
||
DW FCT_ERR_MSG, ADDR_OVF_MSG, REAL_OVF_MSG,CARD_OVF_MSG
|
||
DW INTEGER_OVF_MSG, RANGE_ERR_MSG, ZERO_DIV_MSG
|
||
DW PROC_END_MSG, LOAD_MSG, CALL_MSG
|
||
DW NO_PROG_MSG, NO_MOD_MSG, INCOMPAT_MSG, BAD_FILE_MSG
|
||
DW ILL_INSTR_MSG, ILL_FCT_MSG, NO_MORE_ISR
|
||
|
||
data ends
|
||
|
||
stack segment stack
|
||
db 100h dup (?) ; loader will set up stack for us
|
||
stack ends
|
||
|
||
|
||
code segment public
|
||
; Upon entry, we assume CS, IP and DS to be set correctly.
|
||
; We return to DOS through a jump to location 0 of the Program Segment Prefix
|
||
; There is no explicit release of memory or stack reset.
|
||
|
||
extrn LoadProg:NEAR ; resident loader
|
||
extrn RTS_BRANCH:NEAR ; interrupt dispatcher
|
||
extrn REST_I_V:NEAR ; restore interrupt vectors
|
||
extrn STACK_OVF:NEAR ; stack overflow
|
||
extrn DIV_BY_ZERO:NEAR ; divide by zero handler
|
||
extrn STOPPED:NEAR ; break handler
|
||
extrn GET_INTERRUPT_MASK:NEAR ; reads the current interrupt mask
|
||
|
||
public AFTER_RESIDENT
|
||
public RTS_DS
|
||
|
||
assume CS:code
|
||
public RTS_DS
|
||
RTS_DS DW ? ; We need a way to set the DS later on
|
||
|
||
main proc near
|
||
RTS_START:
|
||
push DS ; base of PSP
|
||
mov ax,data
|
||
mov ES,ax ; point to data segment
|
||
mov RTS_DS,ax ; (make it easy to access later, in ISR's)
|
||
mov di,offset BASE_PAGE
|
||
mov si,0
|
||
mov cx,size BASE_PAGE
|
||
cld
|
||
rep movsb ; copy PSP into BASE_PAGE
|
||
mov DS,ax ; now switch to RTS data segment
|
||
assume DS:data,ES:data
|
||
pop word ptr DOS+2 ; set up exit vector, which
|
||
mov word ptr DOS,0 ; goes to PSP:0
|
||
;
|
||
STI ; Allow interruptions
|
||
|
||
;******************************************************
|
||
; Initial Memory Allocation
|
||
;******************************************************
|
||
mov START_MEM,SS ; bottom of last segment ..
|
||
mov ax,sp
|
||
mov cl,4
|
||
shr ax,cl ; plus paragraphs of stack..
|
||
add ax,10 ; (plus fudge factor..)
|
||
add START_MEM,ax ; ..gives first free paragraph
|
||
mov ax,TOP_OF_MEMORY
|
||
sub ax,START_MEM
|
||
IF KEEP_COM
|
||
sub ax, TRANS_COM_SIZE
|
||
ENDIF
|
||
cmp ax, MAX_MEM_FOR_M2 ; more than we need?
|
||
jbe N2MUCH ; nope
|
||
mov ax, MAX_MEM_FOR_M2 ; yes, just take what is needed
|
||
N2MUCH: mov MEM_SIZE,ax ; compute free paragraphs
|
||
;
|
||
; Find the current disk, and fill in the Filespec of the program to run
|
||
;
|
||
mov ah, 25
|
||
int OS ; get current default disk
|
||
mov START_DISK, al ; save for Postmortem dump
|
||
mov RTS_DISK, al
|
||
; => RESTRICTION: The user has to log in the disk on which reside
|
||
; both, the Run-Time-Support and the RESIDENT.CMD
|
||
mov di,offset FILE_SPEC
|
||
FN_COPY2:
|
||
mov si,DEFAULT_DMA
|
||
cld
|
||
mov cx,0
|
||
mov cl,byte ptr[si]
|
||
inc si
|
||
jcxz FN_COPY5 ; no command tail, use default name
|
||
FN_COPY2a:
|
||
lodsb ; look for first non-blank
|
||
cmp al,' '
|
||
jne FN_COPY6a ; that must be file name
|
||
loop FN_COPY2a
|
||
jmp FN_COPY5 ; all blanks, use default name
|
||
|
||
FN_COPY6: ; copy in file name!
|
||
lodsb
|
||
cmp al,' '
|
||
je FN_COPY6b ; until blank
|
||
FN_COPY6a:
|
||
stosb
|
||
loop FN_COPY6 ; or end of command line
|
||
inc si ; pretend we saw a blank..
|
||
FN_COPY6b:
|
||
dec si ; back up over terminating blank
|
||
cmp byte ptr[si-1],":" ; was only the device there?
|
||
je FN_COPY5 ; yes, so set the default name.
|
||
FN_COPY9:
|
||
dec si
|
||
cmp byte ptr[si],"."
|
||
je EXT_END ; extension already here.
|
||
cmp si,DEFAULT_DMA ; at start of command tail?
|
||
ja FN_COPY9 ; no: keep looking for '.'
|
||
jmp FN_COPY3 ; yes: no extension, supply one.
|
||
|
||
FN_COPY5: ; use default name
|
||
mov si,offset DEFAULT_NAME
|
||
mov cx,6
|
||
FN_COPY4:
|
||
movsb
|
||
dec cx
|
||
jz FN_COPY3
|
||
cmp byte ptr[si]," "
|
||
jne FN_COPY4
|
||
FN_COPY3: ; end of all the 'write filename' loops
|
||
mov byte ptr[di],"."
|
||
inc di
|
||
mov si,offset DEFAULT_TYPE
|
||
mov cx,3
|
||
FEXT_COPY1:
|
||
cmp byte ptr[si]," "
|
||
je EXT_END
|
||
movsb
|
||
dec cx
|
||
jnz FEXT_COPY1
|
||
EXT_END:
|
||
mov byte ptr[di],0
|
||
CALL OPEN_FILE ; open program file
|
||
jnb FOUND
|
||
mov si,offset FILE_SPEC
|
||
FN_COPY11:
|
||
cmp byte ptr[si],"\"
|
||
je NOT_FOUND ; path speficied, so don't retry
|
||
cmp byte ptr[si],0
|
||
je LOOK_AGAIN ; no path, so look in default path
|
||
inc si
|
||
jmp FN_COPY11
|
||
LOOK_AGAIN:
|
||
mov cx,15
|
||
mov di,offset FILE_MSG1
|
||
mov si,offset FILE_SPEC
|
||
cld
|
||
rep movsb
|
||
mov cx,64-DEF_PATH_LENGTH
|
||
mov di,offset FILE_SPEC+63
|
||
mov si,offset FILE_SPEC+63-DEF_PATH_LENGTH
|
||
std ; move filename down so path can
|
||
rep movsb ; be inserted.
|
||
mov di,offset FILE_SPEC+2
|
||
cmp byte ptr[di]-1,":"
|
||
je INS_PATH
|
||
mov di,offset FILE_SPEC
|
||
INS_PATH:
|
||
mov si,offset DEFAULT_PATH
|
||
cld
|
||
mov cx,DEF_PATH_LENGTH
|
||
rep movsb ; insert path
|
||
call OPEN_FILE ; check if file is there...
|
||
jnb FOUND
|
||
jmp N_FOUND1 ; nope. issue special message.
|
||
NOT_FOUND:
|
||
MOV DX, OFFSET NO_FILE ; nope
|
||
CALL WRITE_MSG
|
||
CALL WRITE_FILE_NAME
|
||
jmp DOS
|
||
N_FOUND1:
|
||
mov dx,offset FILE_MSG1
|
||
call WRITE_MSG
|
||
jmp DOS
|
||
FOUND:
|
||
mov FILE_HANDLE,ax
|
||
mov bx,FILE_HANDLE
|
||
mov ax,RTS_PROCESS.PD_PROG_ID ; AX = current prog id
|
||
mov dx,RTS_PROCESS.PD_MOD_TABLE
|
||
mov cx,RTS_PROCESS.PD_MOD_TABLE+2 ; CX:DX = old module table
|
||
call LoadProg ; load Resident
|
||
mov RTS_PROCESS.PD_MOD_TABLE,dx
|
||
mov RTS_PROCESS.PD_MOD_TABLE+2,cx ; CX:DX = new module table
|
||
mov word ptr START_ADDR,di
|
||
mov word ptr START_ADDR+2,ES ; ES:DI = start address
|
||
push bx
|
||
call CLOSE_FILE
|
||
pop bx
|
||
test bx,bx ; load ok?
|
||
jz LOADED ; yes
|
||
dec bx
|
||
shl bx,1
|
||
mov dx,LdErr[bx]
|
||
CALL WRITE_MSG
|
||
CALL WRITE_FILE_NAME
|
||
jmp DOS
|
||
|
||
data segment
|
||
;; - load error table and messages -
|
||
badstr db '** Bad Structure - $'
|
||
badver db '** Bad Version or Target system - $'
|
||
badeof db '** Unexpected EOF - $'
|
||
badmem db '** Insufficient Memory - $'
|
||
badchk db '** Bad Checksum - $'
|
||
baderr db '** LOAD error table fu - $'
|
||
even
|
||
LdErr dw badstr,badver,badeof,badmem,badchk,baderr
|
||
data ends
|
||
|
||
|
||
; Alloc_Mem - called by LoadProg to allocate memory for the 'IPL'
|
||
;
|
||
; in: AX memory request size, in paragraphs
|
||
; out: AX first paragraph of allocated chunk
|
||
; BX =0 if ok, <>0 if memory not available
|
||
;
|
||
public Alloc_Mem
|
||
Alloc_Mem:
|
||
mov bx,1
|
||
cmp ax, MEM_SIZE ; can request be satisfied?
|
||
ja AllFU ; no
|
||
sub MEM_SIZE,ax ; yes
|
||
add ax, START_MEM ; compute next free paragraph..
|
||
xchg ax, START_MEM ; update start_mem, return old value
|
||
xor bx,bx
|
||
AllFU: ret
|
||
|
||
LOADED:
|
||
MEM_OK:
|
||
; switch to real run-time stack, at top of workspace:
|
||
MOV AX, MEM_SIZE
|
||
MOV BX, START_MEM
|
||
CALL COMP_STACK ; BX becomes SS, AX becomes SP
|
||
MOV MAIN_SS, BX
|
||
MOV MAIN_SP, AX
|
||
MOV SS, BX ; No need to disable Interrupts,
|
||
MOV SP, AX ; the processor does it here
|
||
|
||
|
||
;******************************************************
|
||
; Fill in the Default Process Descriptor:
|
||
;******************************************************
|
||
|
||
; First we put the Return Address in RTS on the
|
||
; Stack. It will be used in case of an error
|
||
; in the Main program (RESIDENT)
|
||
PUSHF
|
||
PUSH CS
|
||
MOV AX, OFFSET AFTER_RESIDENT
|
||
PUSH AX
|
||
PUSH DS
|
||
PUSH BP
|
||
|
||
|
||
; Now put all the significant registers at
|
||
; their places in P.D:
|
||
PUSHF
|
||
POP RTS_PROCESS.PD_FLAGS
|
||
MOV RTS_PROCESS.PD_SP, SP
|
||
MOV RTS_PROCESS.PD_SS, SS
|
||
MOV RTS_PROCESS.PD_DS, DS
|
||
|
||
; and the initial value for the stack test:
|
||
MOV AX, SP
|
||
SUB AX, SP_RESERVE+4
|
||
; 4 stands for the CALLF to RESIDENT
|
||
MOV RTS_PROCESS.PD_SP_LIM, AX
|
||
; Stack Limit is actual value of SP
|
||
; minus some reserve
|
||
|
||
; and the initial values for the heap managment:
|
||
MOV AX, START_MEM ; Paragraph addr
|
||
MOV RTS_PROCESS.PD_HEAP_BASE + 2, AX ; first para of heap
|
||
MOV RTS_PROCESS.PD_HEAP_TOP + 2, AX ; top para. of heap
|
||
; Only the minimum is done here, to be as
|
||
; independant from the implementation of the
|
||
; heap manager as possible. See also NEW_PROCESS
|
||
|
||
; Set all the values needed for TRANSFER
|
||
; and error handling:
|
||
MOV RTS_PROCESS.PD_RET_SP, SP
|
||
MOV RTS_PROCESS.PD_PROG_END, SP
|
||
MOV RTS_PROCESS.PD_PROG_END+2, SS
|
||
|
||
|
||
|
||
;******************************************************
|
||
; Create the Main Process:
|
||
;******************************************************
|
||
|
||
; The Default Process Descriptor becomes the
|
||
; current one. This is not a TRANSFER, the
|
||
; very first process has to be created
|
||
; simply by copying its descriptor into the
|
||
; current-one:
|
||
push DS
|
||
pop ES
|
||
mov si, offset RTS_PROCESS
|
||
mov di, offset CUR_PROCESS
|
||
mov cx, size CUR_PROCESS
|
||
rep movsb
|
||
|
||
|
||
;******************************************************
|
||
; Prepare the interrupt system:
|
||
;******************************************************
|
||
|
||
CALL GET_INTERRUPT_MASK
|
||
MOV CUR_PROCESS.PD_PRIO_MASK, AX
|
||
|
||
; Compute physical address of RTS vector:
|
||
mov bx,RTS_INT*4
|
||
MOV AX, 0
|
||
MOV ES, AX
|
||
|
||
; Set entry point for RTS-interrupt-vector:
|
||
MOV AX, ES: [BX] ; Save the old value
|
||
MOV OLD_RTS_VECTOR, AX
|
||
MOV AX, ES: 2[BX]
|
||
MOV OLD_RTS_VECTOR + 2, AX
|
||
MOV ES:word ptr [BX], offset RTS_BRANCH
|
||
MOV ES:word ptr 2[BX], CS ; Set the new-one
|
||
|
||
; Interrupt vector 0, used for 'divide by zero':
|
||
mov bx,0*4
|
||
mov ax, ES:[bx] ; Save the old value
|
||
mov OLD_DIV0_VECTOR, ax
|
||
mov ax, ES:2[bx]
|
||
mov OLD_DIV0_VECTOR + 2, ax
|
||
mov ES:word ptr 2[bx], CS ; Set the new-one
|
||
mov ES:word ptr[bx], offset DIV_BY_ZERO
|
||
|
||
; Interrupt vector 4 (used for INTO) has also
|
||
; to point to the RTS:
|
||
mov bx,4*4
|
||
MOV AX, ES:[bx] ; Save the old value
|
||
MOV OLD_INTO_VECTOR, AX
|
||
MOV AX, ES:2[bx]
|
||
MOV OLD_INTO_VECTOR + 2, AX
|
||
MOV ES:word ptr [bx], offset RTS_BRANCH
|
||
MOV ES:word ptr 2[bx], CS ; Set the new-one
|
||
; Note, that there is no special entry for the
|
||
; interrupt on overflow. The calling program
|
||
; has to set the function code in AX as for the
|
||
; other RTS calls. Needed, because an overflow
|
||
; may occur in several error conditions
|
||
; (INTEGER, CARDINAL, SUBRANGE...)
|
||
|
||
; Interrupt vector 23H (used for BREAK) has also
|
||
; to point to the RTS:
|
||
mov bx,4*23H
|
||
MOV AX, ES:[bx] ; Save the old value
|
||
MOV OLD_BREAK_VECTOR, AX
|
||
MOV AX, ES:2[bx]
|
||
MOV OLD_BREAK_VECTOR + 2, AX
|
||
MOV ES:word ptr [bx], offset STOPPED
|
||
MOV ES:word ptr 2[bx], CS ; Set the new-one
|
||
|
||
|
||
|
||
;******************************************************
|
||
; Call the RESIDENT part:
|
||
;******************************************************
|
||
|
||
; For debugger, to detect first
|
||
; procedure in calling sequence:
|
||
MOV BP, 0
|
||
|
||
CALL_RESIDENT:
|
||
CALL START_ADDR ; call loaded program
|
||
|
||
; We are back from the MODULA program.
|
||
AFTER_RESIDENT:
|
||
MOV DS, RTS_DS ; restore data segment
|
||
MOV AX, CUR_PROCESS.PD_STATUS
|
||
test ax,ax
|
||
jz RTS_END ; 0 = No error
|
||
MOV DX, OFFSET SOME_ERROR
|
||
CALL WRITE_MSG
|
||
MOV BX, CUR_PROCESS.PD_STATUS
|
||
CALL WRITE_STATUS
|
||
CALL WRITE_LN
|
||
; It is safer to return to DOS and possibly reload the
|
||
; RTS rather then restarting its execution, since the
|
||
; code might have been overwritten.
|
||
|
||
RTS_END:
|
||
; Restore the modified interrupt vectors
|
||
MOV AX, 0
|
||
MOV ES, AX
|
||
MOV BX, RTS_INT*4
|
||
MOV AX, OLD_RTS_VECTOR ; The RTS entry
|
||
MOV ES: [BX], AX
|
||
MOV AX, OLD_RTS_VECTOR + 2
|
||
MOV ES: [BX]+2, AX
|
||
MOV AX, OLD_DIV0_VECTOR ; The entry for DIV0
|
||
MOV ES:word ptr 0, AX
|
||
MOV AX, OLD_DIV0_VECTOR + 2
|
||
MOV ES:word ptr 2, AX
|
||
MOV AX, OLD_INTO_VECTOR ; The entry for INTO
|
||
MOV ES:word ptr 16, AX
|
||
MOV AX, OLD_INTO_VECTOR + 2
|
||
MOV ES:word ptr 18, AX
|
||
MOV AX, OLD_BREAK_VECTOR ; The entry for BREAK
|
||
MOV ES:word ptr 140, AX
|
||
MOV AX, OLD_BREAK_VECTOR + 2
|
||
MOV ES:word ptr 142, AX
|
||
; Restore the old interrupt vectors for every IO-Process,
|
||
; waiting on an interrupt:
|
||
mov CUR_PROCESS.PD_PROG_ID, 0
|
||
; 0 as program id is a sort of a joker.
|
||
CALL REST_I_V
|
||
; select the same drive that was selected at beginning:
|
||
MOV DL, START_DISK
|
||
CALL SELECT_DISK
|
||
|
||
public SYS_RESET
|
||
SYS_RESET:
|
||
jmp DOS ; Back to DOS
|
||
|
||
|
||
;**********************************************************
|
||
public TERMINATE
|
||
;**********************************************************
|
||
|
||
; We arrive here, when a program is terminated or if any error had
|
||
; occured. In the former case, status is 'normal', in the latter case
|
||
; the error-code is set in the Status-field of Current Process Descriptor:
|
||
|
||
data segment
|
||
SAVE_SP dw ?
|
||
SAVE_SS dw ?
|
||
PMD_STACK dw 160 dup (?) ; should be enough
|
||
PMD_STACK_END label word
|
||
data ends
|
||
extrn P_M_DUMP:near
|
||
|
||
TERMINATE:
|
||
;=========
|
||
MOV DS, RTS_DS
|
||
assume DS:data
|
||
; We produce a memory dump, if the status is not NORMAL or WARNED:
|
||
MOV AX, CUR_PROCESS.PD_STATUS
|
||
CMP AX, 2
|
||
JB TERMINATION
|
||
|
||
; lines added so bad function call will not cause parity check
|
||
cmp ax,ILL_FCT_CODE
|
||
je TERMINATION
|
||
|
||
CMP AX, HIGHEST_ERR_CODE ; Test if err-code legal
|
||
JBE TERM_DUMP
|
||
MOV CUR_PROCESS.PD_STATUS, ILL_FCT_CODE
|
||
|
||
; line added to avoid parity check
|
||
jmp TERMINATION
|
||
TERM_DUMP:
|
||
; First, we're going to set (SS,SP) to the
|
||
; auxiliary stack:
|
||
MOV SAVE_SS, SS
|
||
MOV SAVE_SP, SP
|
||
MOV AX, DS
|
||
MOV SS, AX
|
||
MOV SP, OFFSET PMD_STACK_END
|
||
|
||
CALL P_M_DUMP
|
||
|
||
; Restore stack of user process:
|
||
MOV SS, SAVE_SS
|
||
MOV SP, SAVE_SP
|
||
|
||
TERMINATION:
|
||
; Free the resources, managed by RTS:
|
||
CALL REST_I_V ; only Interrupt Vectors
|
||
MOV DS, CS:RTS_DS
|
||
; Prepare return:
|
||
MOV SS, CUR_PROCESS.PD_PROG_END+2
|
||
MOV SP, CUR_PROCESS.PD_PROG_END
|
||
POP BP ; BP and DS of Father Program
|
||
POP DS
|
||
IRET
|
||
|
||
|
||
;******************************************************
|
||
; Some Utilities:
|
||
;******************************************************
|
||
|
||
|
||
public COMP_STACK
|
||
COMP_STACK:
|
||
; Upon Entry:
|
||
; AX holds size of free memory (in paragraphs)
|
||
; BX holds (paragraph) start address of free memory
|
||
; Upon Exit:
|
||
; AX holds SP and BX holds SS
|
||
; Policy:
|
||
; Set STACK to the end of memory. Check if
|
||
; there is room for a minimal stack.
|
||
CMP AX, 1000H
|
||
JBE SMALL_MEM
|
||
; There is more than 64K of free memory:
|
||
SUB AX, 1000H
|
||
ADD BX, AX
|
||
; Set SS to end of memory - 64K
|
||
MOV AX, 0
|
||
; and SP to 0
|
||
RET
|
||
SMALL_MEM:
|
||
; Less than 64K of free memory
|
||
; SS is start of free memory
|
||
MOV CL, 4
|
||
SHL AX, CL
|
||
; SP is length * 16
|
||
CMP AX, SP_INI_SIZE + SP_RESERVE + 4
|
||
; 4 is for the call of RESIDENT
|
||
JAE LARGE_ENOUGH
|
||
JMP STACK_OVF
|
||
; Not enough for initial stack and
|
||
; for some reserve!
|
||
LARGE_ENOUGH:
|
||
RET
|
||
|
||
|
||
|
||
|
||
public WRITE
|
||
|
||
WRITE:
|
||
; The character to be printed is in DL
|
||
MOV AH, 2 ; Console Output
|
||
INT OS
|
||
RET
|
||
|
||
public WRITE_MSG
|
||
|
||
WRITE_MSG:
|
||
; The address of the message is in DX
|
||
MOV AH, 9 ; Print String
|
||
INT OS
|
||
RET
|
||
|
||
public WRITE_LN
|
||
|
||
WRITE_LN:
|
||
MOV DL, 0DH ; Print CR
|
||
CALL WRITE
|
||
MOV DL, 0AH ; Print LF
|
||
CALL WRITE
|
||
RET
|
||
|
||
public WRITE_FILE_NAME
|
||
|
||
WRITE_FILE_NAME:
|
||
mov di,offset FILE_SPEC
|
||
WFN1:
|
||
cmp byte ptr[di],0
|
||
je WFN3
|
||
inc di
|
||
jmp WFN1
|
||
WFN3:
|
||
mov byte ptr[di],'$'
|
||
mov dx,offset FILE_SPEC
|
||
call WRITE_MSG
|
||
ret
|
||
|
||
|
||
public WRITE_STATUS
|
||
|
||
WRITE_STATUS:
|
||
; prints on the screen the meaning of a
|
||
; program status (passed in BL):
|
||
push bx
|
||
MOV DL, ' '
|
||
CALL WRITE
|
||
pop bx
|
||
xor bh,bh
|
||
ADD BX, BX
|
||
MOV DX, STATUS_MSG [BX]
|
||
CALL WRITE_MSG
|
||
RET
|
||
|
||
|
||
public SET_DEFAULT_DMA
|
||
|
||
SET_DEFAULT_DMA:
|
||
mov dx, DEFAULT_DMA
|
||
mov ah, 01Ah
|
||
int OS
|
||
ret
|
||
|
||
public SELECT_DISK
|
||
|
||
SELECT_DISK:
|
||
; the drive to be selected is passed in DL
|
||
mov ah, 14
|
||
int OS
|
||
ret
|
||
|
||
public OPEN_FILE
|
||
|
||
OPEN_FILE:
|
||
; open file in FILE_SPEC: returns carry flag set if not found
|
||
mov ax,3D02H ; open for read/write
|
||
mov dx,offset FILE_SPEC
|
||
int OS
|
||
ret
|
||
|
||
public CLOSE_FILE
|
||
|
||
CLOSE_FILE:
|
||
; closes the file given in the FILE_HANDLE
|
||
mov ah,3EH
|
||
mov bx,FILE_HANDLE
|
||
int OS
|
||
ret
|
||
|
||
public DELETE_FILE
|
||
|
||
DELETE_FILE:
|
||
; deletes the file given in the FILE_SPEC
|
||
mov ah,41H
|
||
mov dx,offset FILE_SPEC
|
||
int OS
|
||
ret
|
||
|
||
public SEQ_WRITE
|
||
|
||
SEQ_WRITE:
|
||
; writes the next byte in the file given
|
||
; in the FILE_HANDLE.
|
||
push ds
|
||
mov ah,2FH ; get current dma (buffer address)
|
||
int os
|
||
push es
|
||
push bx
|
||
mov bx,FILE_HANDLE
|
||
pop dx
|
||
mov cx,80H
|
||
mov ah,40H
|
||
pop ds
|
||
int OS
|
||
pop ds
|
||
ret
|
||
|
||
public MAKE_FILE
|
||
|
||
MAKE_FILE:
|
||
; creates the file given in the FILE_SPEC
|
||
mov ah, 3CH
|
||
mov cx,0 ; attribute of zero
|
||
mov dx,offset FILE_SPEC
|
||
int OS
|
||
ret
|
||
|
||
public GET_CURR_DISK
|
||
|
||
GET_CURR_DISK:
|
||
; gets the currently logged in disk and stores
|
||
; the value in the variable 'SAVED_DISK'
|
||
mov ah, 25
|
||
int OS
|
||
mov SAVED_DISK, al
|
||
ret
|
||
|
||
public NORM_ADDR
|
||
|
||
NORM_ADDR:
|
||
; To normalize a address with segment and offset,
|
||
; i.e the segment value is as large as possible
|
||
; and the offset is smaller than 16.
|
||
; Upon entry:
|
||
; BX holds the old segment and AX the old offset.
|
||
; Upon exit:
|
||
; BX holds the normalized segment and AX the offset.
|
||
; If an overflow occurs, the 'CF' flag is set.
|
||
MOV DX, AX
|
||
AND AX, 0FH
|
||
MOV CL, 4
|
||
SHR DX, CL
|
||
ADD BX, DX
|
||
RET
|
||
|
||
main endp
|
||
code ends
|
||
end RTS_START
|
||
|