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
|
|||
|
|