742 lines
27 KiB
NASM
742 lines
27 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.
|
||
;
|
||
; LOGITECH SA. CH-1143 Apples, Switzerland
|
||
;
|
||
; Module: RTS.ASM
|
||
; Mainline of Modula-2/86(tm) Run Time System
|
||
;
|
||
; Version: 8086, RAM-based, MS-DOS 2.0
|
||
;
|
||
; Release: 1.1 - Dec 84
|
||
;
|
||
;*****************************************************************************
|
||
;
|
||
; process descriptors are always allocated at a paragraph address
|
||
;
|
||
;*****************************************************************************
|
||
|
||
CGROUP group code
|
||
DGROUP group data
|
||
SGROUP group stack
|
||
|
||
assume CS: CGROUP
|
||
assume DS: DGROUP
|
||
assume ES: NOTHING
|
||
assume SS: SGROUP
|
||
|
||
include RTS.INC
|
||
|
||
|
||
;*****************************************************************************
|
||
|
||
; EXPORT QUALIFIED
|
||
; (* data: variables *)
|
||
public START_MEM, MEM_SIZE
|
||
public m2_start_mark
|
||
public m2_end_mark
|
||
public RTS_PROCESS
|
||
public cur_proc_addr
|
||
public cur_process
|
||
public BASE_PAGE_PTR;
|
||
public base_page
|
||
public SAVED_DISK, RTS_DISK
|
||
public device_mask
|
||
public term_proc_addr
|
||
public old_NMI_vector
|
||
|
||
; (* code: procedures, labels *)
|
||
public DUMMY_ISR
|
||
public AFTER_RESIDENT
|
||
public RTS_DS
|
||
public SYS_RESET
|
||
public TERMINATE
|
||
public COMP_STACK
|
||
public WRITE
|
||
public WRITE_MSG
|
||
public WRITE_LN
|
||
public WRITE_STATUS
|
||
public NORM_ADDR
|
||
public GET_CURR_DISK
|
||
public SELECT_DISK
|
||
;
|
||
;***********************************************************************
|
||
code segment public 'code'
|
||
|
||
; FROM LOADER IMPORT
|
||
extrn LoadProg:NEAR ; resident loader
|
||
|
||
; FROM SERVICES IMPORT
|
||
extrn RTS_BRANCH:NEAR ; interrupt dispatcher
|
||
extrn STACK_OVF:NEAR ; stack overflow
|
||
extrn DIV_BY_ZERO:NEAR ; divide by zero handler
|
||
; FROM TRANSFER IMPORT
|
||
extrn REST_I_V:NEAR ; restore interrupt vectors
|
||
extrn STOPPED_1B:NEAR ; break handler interrupt 1bH
|
||
extrn STOPPED_23:NEAR ; break handler interrupt 23H
|
||
extrn GET_INTERRUPT_MASK:NEAR ; reads the current interrupt mask
|
||
extrn SET_INTERRUPT_MASK:NEAR ; restores the interrupt mask
|
||
extrn NMI_server:near ; interrupts from 8087
|
||
; FROM DBUG IMPORT
|
||
extrn DEBUGGER : near
|
||
|
||
code ends
|
||
;*****************************************************************************
|
||
|
||
|
||
|
||
;*****************************************************************************
|
||
data segment public 'data'
|
||
|
||
; Workspace of the MAIN process, starting with RTS:
|
||
|
||
org 0
|
||
|
||
cur_process label ProcessDescriptor
|
||
|
||
BASE_PAGE db 100H dup (?) ; required for the Main-Module
|
||
; BASE_PAGE has to be at offset 0 !!!!!!!
|
||
|
||
; RTS_PROCESS has to be on paragraph boundary. keep it here, and never
|
||
; insert any definitions between BASE_PAGE and RTS_PROCESS
|
||
RTS_PROCESS ProcessDescriptor <>
|
||
|
||
cur_proc_addr dd ? ; pointer to current process
|
||
|
||
TOP_OF_MEMORY equ word ptr BASE_PAGE+2
|
||
; last free paragraph, +1. Set up by DOS loader
|
||
TRANS_COM_SIZE equ 440H ; transient part of COMMAND.COM (in parag)
|
||
|
||
m2_start_mark dw ? ; this two variables show the region of
|
||
m2_end_mark dw ? ; possibly loaded m2 programs
|
||
|
||
START_MEM dw ? ; points behind the loaded modula program
|
||
MEM_SIZE dw ? ; number of free paragraphs at START_MEM
|
||
DOS dd ? ; jump to DOS
|
||
START_ADDR dd ? ; start address of Modula program (.LOD file)
|
||
term_proc_addr dd term_procedure ; termination procedure
|
||
|
||
; - saved interrupt vectors -
|
||
old_NMI_vector dd ?
|
||
OLD_RTS_VECTOR dd ?
|
||
OLD_DIV0_VECTOR dd ?
|
||
OLD_INTO_VECTOR dd ?
|
||
OLD_BREAK_VECTOR_23 dd ?
|
||
OLD_BREAK_VECTOR_1B dd ?
|
||
old_interrupt_controller_mask dw ?
|
||
device_mask dw ? ; initial value is the old interrupt mask
|
||
; of the interrupt controller
|
||
; a device may be enabled/disabled, by setting
|
||
; the corresponding bit to 0/1
|
||
; two functions to do operations on this mask
|
||
; are provided:
|
||
; get_device_status, set_device_status
|
||
|
||
|
||
|
||
set_CW8087 dw 360H ; init 8087 with interrupts
|
||
get_CW8087 dw 0
|
||
|
||
BASE_PAGE_PTR dd BASE_PAGE ; ptr to program segment prefix
|
||
MAIN_SP dw ?
|
||
MAIN_SS dw ?
|
||
|
||
START_DISK db ?
|
||
RTS_DISK db ?
|
||
SAVED_DISK DB ?
|
||
|
||
; string constants and jump or index tables
|
||
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 overflow$'
|
||
real_udf_msg db 'real underflow$'
|
||
bad_op_msg db 'bad operand$'
|
||
CARD_OVF_MSG DB 'cardinal overflow$'
|
||
INTEGER_OVF_MSG DB 'integer overflow$'
|
||
RANGE_ERR_MSG DB 'range error$'
|
||
ZERO_DIV_MSG DB 'division 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
|
||
dw real_udf_msg,bad_op_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 'stack'
|
||
|
||
; the stack will be used to load and start the modula-2 program
|
||
|
||
dw 400h dup (?) ; MS-DOS loader will set up stack
|
||
|
||
stack ends
|
||
;*****************************************************************************
|
||
|
||
|
||
;*****************************************************************************
|
||
code segment public 'code'
|
||
|
||
; 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.
|
||
|
||
; After loading of the RTS, memory has the following structure:
|
||
; 0000H .. 03FFH : interrupt vectors
|
||
; 0400H .. DOS_END : resident portion of DOS
|
||
; DOS_END .. DOS_END + 0FFH : Program Segment Prefix (PSP), set up by DOS
|
||
; DOS_END + 100H .. xx : Code of RTS
|
||
; xx .. yy : Data of RTS ( xx = RTS_DS)
|
||
; yy .. zz : Stack of RTS
|
||
; zz .. end_of_memory - 17K : free memory ( zz = START_MEM)
|
||
; last 17K of RAM : DOS command interpreter (COMMAND.COM)
|
||
|
||
|
||
RTS_DS DW ? ; We need a way to set the DS later on
|
||
|
||
RTS_START: ; start address of the RTS
|
||
|
||
|
||
push DS ; base of PSP
|
||
; read and store data-segment of RTS:
|
||
mov ax,DGROUP
|
||
mov ES,ax ; point to data segment
|
||
mov RTS_DS,ax ; (make it easy to access later, in ISR's)
|
||
; copy the PSP into the privat variable BASE_PAGE:
|
||
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
|
||
; store return point:
|
||
pop word ptr DOS+2 ; set up exit vector, which
|
||
mov word ptr DOS,0 ; goes to PSP:0
|
||
; Find the current disk:
|
||
CALL GET_CURR_DISK
|
||
mov START_DISK, al ; save for Postmortem dump
|
||
mov RTS_DISK, al
|
||
|
||
STI ; Allow interruptions
|
||
|
||
;-------------------------------------------------------
|
||
; Initial Memory Allocation
|
||
;-------------------------------------------------------
|
||
; find the beginning of not used memory (right after the RTS):
|
||
mov m2_start_mark,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 m2_start_mark,ax ; ..gives first free paragraph
|
||
; after the RTS
|
||
mov ax, m2_start_mark
|
||
mov start_mem, ax ; initialize start_mem
|
||
; will be updated by loader
|
||
; now find out, how much memory is available for the Modula program:
|
||
mov ax,TOP_OF_MEMORY
|
||
sub ax,m2_start_mark
|
||
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
|
||
|
||
; initialize cur_proc_addr with normalized address of RTS_PROCESS
|
||
; where the offset is calculated to 0 (if not ERROR !!!!!!!)
|
||
mov bx,ds
|
||
mov ax,offset rts_process
|
||
call norm_addr
|
||
mov word ptr cur_proc_addr,ax
|
||
mov word ptr cur_proc_addr + 2,bx
|
||
|
||
; Load the Modula-2 program to run. It will be loaded at START_MEM and
|
||
; START_MEM will be updated to point behind that loaded program:
|
||
;
|
||
mov ax,RTS_PROCESS.PD_PROG_ID ; AX = current prog id
|
||
mov dx,word ptr RTS_PROCESS.PD_MOD_TABLE
|
||
mov cx,word ptr RTS_PROCESS.PD_MOD_TABLE+2 ; CX:DX = old mod tab
|
||
call LoadProg ; load it and return error
|
||
; code in BX (0=ok).
|
||
mov word ptr RTS_PROCESS.PD_MOD_TABLE,dx
|
||
mov word ptr RTS_PROCESS.PD_MOD_TABLE+2,cx ; CX:DX = new mod tab
|
||
mov word ptr START_ADDR,di
|
||
mov word ptr START_ADDR+2,ES ; ES:DI = start address
|
||
test bx,bx ; load ok?
|
||
jz LOADED ; yes
|
||
jmp SYS_RESET ; no
|
||
|
||
LOADED:
|
||
; At this point START_MEM is the first free paragraph after code and data
|
||
; of the Modula program. This space will be used for heap (starting at
|
||
; the lowest address) and for stack (starting at the high end of memory).
|
||
|
||
; Switch to real run-time stack. The stack of the main process is set
|
||
; to the end of the free memory:
|
||
MOV AX, MEM_SIZE
|
||
MOV BX, START_MEM
|
||
mov m2_end_mark, bx ; upper limit for a m2 codesegment
|
||
add m2_end_mark, ax
|
||
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.
|
||
PUSHF
|
||
PUSH CS
|
||
MOV AX, OFFSET AFTER_RESIDENT
|
||
PUSH AX
|
||
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 values for the heap managment:
|
||
MOV AX, START_MEM ; Paragraph addr
|
||
MOV word ptr RTS_PROCESS.PD_HEAP_BASE + 2,AX ; first para of heap
|
||
MOV word ptr 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_dbug_status,0 ; for debugger
|
||
MOV word ptr RTS_PROCESS.PD_PROG_END, SP
|
||
MOV word ptr RTS_PROCESS.PD_PROG_END+2, SS
|
||
|
||
|
||
;-------------------------------------------------------
|
||
; Prepare the interrupt system:
|
||
;-------------------------------------------------------
|
||
|
||
CALL GET_INTERRUPT_MASK
|
||
MOV rts_process.PD_PRIO_MASK, 0 ; no priority
|
||
MOV old_interrupt_controller_mask,AX ; save it
|
||
mov device_mask, AX ; initial value
|
||
|
||
; 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 word ptr OLD_RTS_VECTOR, AX
|
||
MOV AX, ES: 2[BX]
|
||
MOV word ptr 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 word ptr OLD_DIV0_VECTOR, ax
|
||
mov ax,ES:2[bx]
|
||
mov word ptr 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 word ptr OLD_INTO_VECTOR, AX
|
||
MOV AX, ES:2[bx]
|
||
MOV word ptr 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 2, used for '8087 interrupts':
|
||
esc 1cH,BX ; FNINIT: clear and init 8087
|
||
esc 0DH,set_CW8087 ; FLDCW: set ctrl word interrupt enabled
|
||
esc 0FH,get_CW8087 ; FNSTCW: read it again
|
||
; save old value in any case, it is always restored
|
||
mov bx,2*4
|
||
mov ax,ES:[bx] ; Save the old value
|
||
mov word ptr old_NMI_vector, ax
|
||
mov ax,ES:2[bx]
|
||
mov word ptr old_NMI_vector + 2,ax
|
||
mov ax,set_CW8087 ; simulate WAIT (dont execute it!!)
|
||
mov ax,set_CW8087
|
||
mov ax,set_CW8087
|
||
mov ax,set_CW8087
|
||
cmp ax,get_CW8087 ; if equal, the 8087 is assumed present
|
||
jne No8087
|
||
; install interrupt service routine only if 8087 is present
|
||
mov ES:word ptr 2[bx], CS ; Set the new-one
|
||
mov ES:word ptr[bx], offset NMI_server
|
||
No8087:
|
||
|
||
; Interrupt vector 1BH (used for BREAK) has also
|
||
; to point to the RTS:
|
||
mov bx,4*1BH
|
||
MOV AX, ES:[bx] ; Save the old value
|
||
MOV word ptr OLD_BREAK_VECTOR_1B, AX
|
||
MOV AX, ES:2[bx]
|
||
MOV word ptr OLD_BREAK_VECTOR_1B + 2, AX
|
||
MOV ES:word ptr [bx], offset STOPPED_1b
|
||
MOV ES:word ptr 2[bx], CS ; Set the new-one
|
||
|
||
; Interrupt vector 23H (used for ^C) has also
|
||
; to point to the RTS:
|
||
mov bx,4*23H
|
||
MOV AX, ES:[bx] ; Save the old value
|
||
MOV word ptr OLD_BREAK_VECTOR_23, AX
|
||
MOV AX, ES:2[bx]
|
||
MOV word ptr OLD_BREAK_VECTOR_23 + 2, AX
|
||
MOV ES:word ptr [bx], offset stopped_23
|
||
MOV ES:word ptr 2[bx], CS ; Set the new-one
|
||
|
||
|
||
|
||
;-------------------------------------------------------
|
||
; Call the MODULA program:
|
||
;-------------------------------------------------------
|
||
|
||
; 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:
|
||
; if program terminated normally, we came here not through
|
||
; an interrupt, but by a RET instruction (therefore interrupts
|
||
; are enabled:
|
||
CLI
|
||
MOV DS, RTS_DS ; restore data segment
|
||
|
||
; Restore the old interrupt vectors for every IO-Process,
|
||
; waiting on an interrupt:
|
||
mov ax,word ptr cur_proc_addr + 2
|
||
mov es,ax
|
||
mov ES:CUR_PROCESS.PD_PROG_ID, 0
|
||
; 0 as program id is a sort of a joker.
|
||
|
||
; Restore the old Interrupt Mask:
|
||
mov ES:CUR_PROCESS.PD_PRIO_MASK, 0 ; no priority
|
||
MOV AX, old_interrupt_controller_mask
|
||
CALL SET_INTERRUPT_MASK
|
||
|
||
CALL REST_I_V
|
||
|
||
; set break vector to a dummy interrupt service routine,
|
||
; because the following write functions will enable
|
||
; interrupts; we don't want to be interrupted by a ^break
|
||
mov ax, 0
|
||
mov es, ax
|
||
mov bx,4*1BH
|
||
MOV ES:word ptr [bx], offset DUMMY_ISR
|
||
MOV ES:word ptr 2[bx], CS ; Set the new-one
|
||
|
||
mov ax,word ptr cur_proc_addr + 2
|
||
mov es,ax
|
||
MOV AX,ES: CUR_PROCESS.PD_STATUS
|
||
test ax,ax
|
||
jz RTS_END ; 0 = No error
|
||
CALL WRITE_LN
|
||
MOV DX,OFFSET SOME_ERROR
|
||
CALL WRITE_MSG
|
||
MOV BX,ES: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.
|
||
|
||
;-------------------------------------------------------
|
||
; Back from the MODULA program:
|
||
;-------------------------------------------------------
|
||
|
||
RTS_END:
|
||
|
||
CallTermProc:
|
||
;============
|
||
; this 'procedure' is the equivalent to 'CallTermProc' in module
|
||
; 'System'. It has to be repeated here, because the termination
|
||
; of the base layer does not pass through module 'Program', and
|
||
; therefore 'System.CallTermProc' is not called
|
||
|
||
mov ds,rts_ds ; set data segment
|
||
mov ax,word ptr term_proc_addr + 2 ; segement addr
|
||
mov bx,cs
|
||
cmp ax,bx
|
||
je CallTermProc_end ; OwnTermProc
|
||
call term_proc_addr ; invoke termination procedure
|
||
jmp CallTermProc
|
||
CallTermProc_end:
|
||
|
||
; Restore the modified interrupt vectors
|
||
; mov ds,rts_ds
|
||
MOV AX, 0
|
||
MOV ES, AX
|
||
MOV BX, RTS_INT*4
|
||
MOV AX,word ptr OLD_RTS_VECTOR ; The RTS entry
|
||
MOV ES:word ptr [BX],AX
|
||
MOV AX,word ptr OLD_RTS_VECTOR + 2
|
||
MOV ES:word ptr [BX]+2,AX
|
||
MOV AX,word ptr OLD_DIV0_VECTOR ; The entry for DIV0
|
||
MOV ES:word ptr 0, AX
|
||
MOV AX,word ptr OLD_DIV0_VECTOR + 2
|
||
MOV ES:word ptr 2, AX
|
||
MOV AX,word ptr OLD_INTO_VECTOR ; The entry for INTO
|
||
MOV ES:word ptr 16, AX
|
||
MOV AX,word ptr OLD_INTO_VECTOR + 2
|
||
MOV ES:word ptr 18, AX
|
||
MOV AX,word ptr old_NMI_vector ; The entry for NMI
|
||
MOV ES:word ptr 4*2, AX
|
||
MOV AX,word ptr old_NMI_vector + 2
|
||
MOV ES:word ptr 4*2 + 2, AX
|
||
|
||
MOV AX,word ptr OLD_BREAK_VECTOR_1B ; The entry for BREAK 1BH
|
||
MOV ES:word ptr 4*1bH, AX
|
||
MOV AX,word ptr OLD_BREAK_VECTOR_1B + 2
|
||
MOV ES:word ptr 4*1bH + 2, AX
|
||
|
||
MOV AX,word ptr OLD_BREAK_VECTOR_23 ; The entry for BREAK 23H
|
||
MOV ES:word ptr 4*23H, AX
|
||
MOV AX,word ptr OLD_BREAK_VECTOR_23 + 2
|
||
MOV ES:word ptr 4*23H + 2, AX
|
||
|
||
; select the same drive that was selected at beginning:
|
||
MOV DL, START_DISK
|
||
CALL SELECT_DISK
|
||
|
||
SYS_RESET:
|
||
jmp DOS ; Back to DOS
|
||
|
||
|
||
;-------------------------------------------------------
|
||
; Termination procedure, called after end of main
|
||
; program. If System announced one, then this is
|
||
; called, else an empty procedure is called
|
||
;-------------------------------------------------------
|
||
Term_Procedure proc far ; MUST be FAR !!!!!
|
||
ret
|
||
Term_Procedure endp
|
||
|
||
|
||
; We arrive here, when a program is terminated (exept level 0) 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.
|
||
|
||
|
||
|
||
TERMINATE:
|
||
;=========
|
||
MOV DS, RTS_DS
|
||
|
||
; Free the resources, managed by RTS:
|
||
CALL REST_I_V ; only Interrupt Vectors
|
||
|
||
; Call the debugger in any case. It will take an
|
||
; action according to status and presence or absence
|
||
; of a run-time debugger:
|
||
CALL DEBUGGER
|
||
|
||
TERMINATION:
|
||
MOV DS, CS:RTS_DS
|
||
mov ax,word ptr cur_proc_addr + 2
|
||
mov ds,ax
|
||
; Prepare return:
|
||
MOV SS,word ptr CUR_PROCESS.PD_PROG_END+2
|
||
MOV SP,word ptr CUR_PROCESS.PD_PROG_END
|
||
POP BP ; BP of Father Program
|
||
|
||
DUMMY_ISR: ; we use the following IRET also for
|
||
; a dummy interrupt service routine
|
||
|
||
IRET ; from here we go either to Get_Old_Program
|
||
; or to After_Resident
|
||
|
||
|
||
;------------------------------------------------------
|
||
; 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 the Modula program
|
||
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 CS: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_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 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
|
||
|
||
|
||
; 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 SELECT_DISK
|
||
|
||
SELECT_DISK:
|
||
; the drive to be selected is passed in DL
|
||
mov ah, 14
|
||
int OS
|
||
ret
|
||
|
||
|
||
code ends
|
||
|
||
;*****************************************************************************
|
||
|
||
|
||
|
||
|
||
end RTS_START ; defines startaddress of the RTS
|
||
|