process.asm

来自「汇编编程艺术」· 汇编 代码 · 共 1,414 行 · 第 1/3 页

ASM
1,414
字号
		include	process.a

StdGrp		group	stdlib,stddata
stddata		segment	para public 'sldata'

wp		equ	<word ptr>

DefaultPCB	pcb	<>
DefaultCortn	pcb	<>

ProcessID	dw	0
ReadyQ		dd	DefaultPCB
LastRdyQ	dd	DefaultPCB

CurCoroutine	dd	DefaultCortn	;Points at the currently executing
					; coroutine.

TimerIntVect	dd	?

SaveSP		dw	?		;Temp holding location for fork.
SaveSS		dw	?		;Temp holding location for fork.

stddata		ends



stdlib		segment	para public 'slcode'
		assume	cs:stdgrp

; Special case to handle MASM 6.0 vs. all other assemblers:
; If not MASM 5.1 or MASM 6.0, set the version to 5.00:

		ifndef	@version
@version	equ	500
		endif
;
;
;
;============================================================================
; Process package.
; These routines handle multitasking/multiprogramming in the standard
; library.
;============================================================================
;
;
; sl_prcsinit-	Initializes the process manager.  By default, this guy
;		assumes the use of the 1/18 second timer.  At some future
;		date I may add support for the AT msec timer.
;
;		Warning: This code patches into several interrupts.  If
;		you call this routine in your program, you must call the
;		sl_prcsquit routine before your program terminates.  Other-
;		wise the system will crash shortly thereafter.

		public	sl_prcsinit
;
sl_prcsinit	proc	far
		assume	ds:stdgrp
		push	ds
		push	es
		push	ax
		push	bx
		push	cx
		push	dx

		mov	ax, StdGrp
		mov	ds, ax

; Okay, set up this code as the first (and only) process currently in the
; ready queue:

		mov	ax, offset StdGrp:DefaultPCB
		mov	wp StdGrp:ReadyQ, ax
		mov	wp StdGrp:LastRdyQ, ax
		mov	ax, ds
		mov	wp StdGrp:ReadyQ+2, ax
		mov	wp StdGrp:LastRdyQ+2, ax

		xor	ax, ax
		mov	ProcessID, ax			;Start process IDs at 0

		mov	wp StdGrp:DefaultPCB.NextProc, ax
		mov	wp StdGrp:DefaultPCB.NextProc[2], ax
		mov	wp StdGrp:DefaultPCB.CPUTime+2, ax
		mov	wp StdGrp:DefaultPCB.CPUTime+2, 1

		mov	ah, 2ah				;Get the date.
		int	21h
		mov	wp StdGrp:DefaultPCB.StartingDate, cx
		mov	wp StdGrp:DefaultPCB.StartingDate+2, dx

		mov	ah, 2ch				;Get the time.
		int	21h
		mov	wp StdGrp:DefaultPCB.StartingTime, cx
		mov	wp StdGrp:DefaultPCB.StartingTime+2, dx


		mov	ax, 3508h		;Timer interrupt vector.
		int	21h
		mov	wp StdGrp:TimerIntVect, bx
		mov	wp StdGrp:TimerIntVect+2, es

		mov	ax, 2508h		;Patch the dispatcher into the
		mov	dx, seg StdGrp:TimerISR	; timer interrupt.
		mov	ds, dx
		mov	dx, offset StdGrp:TimerISR
		int	21h


		pop	dx
		pop	cx
		pop	bx
		pop	ax
		pop	es
		pop	ds
		ret
sl_prcsinit	endp
		assume	ds:nothing


; sl_prcsquit-	This code restores the interrupt vectors patched by the
;		sl_prcsinit routine.  This routine *must* be called before
;		you exit your program or the system will crash shortly
;		thereafter.

		public	sl_prcsquit
sl_prcsquit	proc	far
		assume	ds:StdGrp

		push	ds
		push	es
		push	ax
		mov	ax, StdGrp
		mov	ds, ax

		mov	ax, 0
		mov	es, ax

; Cannot call DOS to restore this vector because this call might
; occur in a critical error or break handler routine.

		pushf
		cli
		mov	ax, word ptr StdGrp:TimerIntVect
		mov	es:[8*4], ax
		mov	ax, word ptr StdGrp:TimerIntVect+2
		mov	es:[8*4 + 2], ax
		popf

		pop	ax
		pop	es
		pop	ds
		ret
sl_prcsquit	endp
		assume	ds:nothing



; sl_fork-	Starts a new process.  On entry, ES:DI points at a PCB.
;		This routine initializes that process and adds it to the
;		ready queue.
;
;		WARNING: This routine assumes that the only information to
;		copy off the stack is a far return address (to FORK). When
;		fork returns there will be nothing sitting on the stack of
;		the new process.  Therefore, you should not call fork from
;		inside a procedure if you expect the child process to return
;		to the called procedure.
;
;		This code assumes that you've initialized the ssSave and
;		spSave fields of the new PCB with the address of a stack
;		for that new process.
;
;		This guy returns with AX=0 and BX=<ChildProcessID> to the
;		parent process.  It returns AX=<ChildProcessID> and BX=0
;		to the child process.

		public	sl_fork
sl_fork		proc	far
		assume	ds:stdgrp

		push	bp
		mov	bp, sp
		pushf
		push	ds
		push	cx
		push	dx

		mov	ax, stdgrp
		mov	ds, ax

		if	@version ge 600

; Initialize various fields in the new PCB:
; Start with the register.  Remember, AX contains the process ID for the
; child process, BX contains zero for the child process.  AX contains zero
; for the parent process, and BX contains the child process ID for the
; parent process.
;
; SS:SP should already be set up on entry (by the caller).

		inc	StdGrp:ProcessID	;Grab a new process ID.
		mov	ax, StdGrp:ProcessID
		mov	es:[di].pcb.regax, ax
		mov	es:[di].pcb.PrcsID, ax
		mov	wp es:[di].pcb.regbx, 0
		mov	es:[di].pcb.regcx, cx
		mov	es:[di].pcb.regdx, dx
		mov	ax, 0[bp]		;Get bp value off stack.
		mov	es:[di].pcb.regbp, ax
		mov	es:[di].pcb.regsi, si
		mov	es:[di].pcb.regdi, di
		mov	ax, [bp-4]		;Get ds value off stack.
		mov	es:[di].pcb.regds, ax
		mov	es:[di].pcb.reges, es
		sti				;Must have interrupts on!
		pushf
		cli				;But the rest is a critical
		pop	ax			; section.
		mov	es:[di].pcb.regflags, ax

; The return address should be the return address for fork:

		mov	ax, 2[bp]		;Get return offset
		mov	es:[di].pcb.regip, ax
		mov	ax, 4[bp]		;Get return segment
		mov	es:[di].pcb.regcs, ax


; Set up accounting information (CPU time):

		mov	wp es:[di].pcb.CPUTime, 0
		mov	wp es:[di+2].pcb.CPUTime, 0

		mov	ah, 2ah				;Get the date.
		int	21h
		mov	wp es:[di].pcb.StartingDate, cx
		mov	wp es:[di].pcb.StartingDate+2, dx

		mov	ah, 2ch				;Get the time.
		int	21h
		mov	wp es:[di].pcb.StartingTime, cx
		mov	wp es:[di].pcb.StartingTime+2, dx


; Okay, now move the new PCB onto the ready queue (interrupts must be off
; while we're doing this!).  Place this guy in the ready queue after the
; current process so it gets a time slice real soon.

		cli
		push	es
		push	di
		les	di, StdGrp:ReadyQ
		mov	cx, wp es:[di].pcb.NextProc
		mov	dx, wp es:[di+2].pcb.NextProc
		pop	ax
		mov	wp es:[di].pcb.NextProc, ax
		pop	ax
		mov	wp es:[di+2].pcb.NextProc, ax
		les	di, es:[di].pcb.NextProc	;Pt ES:DI @ new prcs.
		mov	wp es:[di].pcb.NextProc, cx	;Link in prev 2nd
		mov	wp es:[di+2].pcb.NextProc, dx	; process.

; If there was only one process on the ready queue prior to adding this
; process, point the LastRdyQ pointer at the new process.

		mov	ax, wp StdGrp:ReadyQ
		cmp	ax, wp StdGrp:LastRdyQ
		jne	RdyNELast
		mov	ax, wp StdGrp:ReadyQ+2
		cmp	ax, wp StdGrp:LastRdyQ+2
		jne	RdyNELast
		mov	wp StdGrp:LastRdyQ, di
		mov	wp StdGrp:LastRdyQ+2, es

; Okay, return back to the calling code with AX=0 to denote that this is
; the parent routine returning.  It also returns the child process ID in
; the BX register.

RdyNELast:	xor	ax, ax
		mov	bx, StdGrp:ProcessID

		else				;TASM or MASM pre-6.0

		inc	StdGrp:ProcessID
		mov	ax, StdGrp:ProcessID
		mov	es:[di].regax, ax
		mov	es:[di].PrcsID, ax
		mov	wp es:[di].regbx, 0
		mov	es:[di].regcx, cx
		mov	es:[di].regdx, dx
		mov	ax, 0[bp]
		mov	es:[di].regbp, ax
		mov	es:[di].regsi, si
		mov	es:[di].regdi, di
		mov	ax, [bp-4]
		mov	es:[di].regds, ax
		mov	es:[di].reges, es
		sti
		pushf
		cli
		pop	ax
		mov	es:[di].regflags, ax
		mov	ax, 2[bp]
		mov	es:[di].regip, ax
		mov	ax, 4[bp]
		mov	es:[di].regcs, ax
		mov	wp es:[di].CPUTime, 0
		mov	wp es:[di+2].CPUTime, 0
		mov	ah, 2ah
		int	21h
		mov	wp es:[di].StartingDate, cx
		mov	wp es:[di].StartingDate+2, dx
		mov	ah, 2ch
		int	21h
		mov	wp es:[di].StartingTime, cx
		mov	wp es:[di].StartingTime+2, dx
		cli
		push	es
		push	di
		les	di, StdGrp:ReadyQ
		mov	cx, wp es:[di].NextProc
		mov	dx, wp es:[di+2].NextProc
		pop	ax
		mov	wp es:[di].NextProc, ax
		pop	ax
		mov	wp es:[di+2].NextProc, ax
		les	di, es:[di].NextProc
		mov	wp es:[di].NextProc, cx
		mov	wp es:[di+2].NextProc, dx
		mov	ax, wp StdGrp:ReadyQ
		cmp	ax, wp StdGrp:LastRdyQ
		jne	RdyNELast
		mov	ax, wp StdGrp:ReadyQ+2
		cmp	ax, wp StdGrp:LastRdyQ+2
		jne	RdyNELast
		mov	wp StdGrp:LastRdyQ, di
		mov	wp StdGrp:LastRdyQ+2, es
RdyNELast:	xor	ax, ax
		mov	bx, StdGrp:ProcessID

		endif



		pop	dx
		pop	cx
		pop	ds
		popf
		pop	bp
		ret
sl_fork		endp
		assume	ds:nothing



; sl_Die-	Terminate the current process.  If this is not the only
;		process in the ready queue, then this code removes the current
;		process from the ready queue and transfers control to the
;		next process in the Ready Queue.  Since the current process
;		is not the the ready queue, this action effectively kills
;		the current process.
;
;		This routine will *not* delete the current process from the
;		ready queue if it is the *only* process in the ready queue.
;		In such an event, this code returns to the caller with the
;		carry flag set (it returns this way because sl_Kill can call
;		this routine and sl_Kill requires the carry set if an error
;		occurs).

		public	sl_Die
sl_Die		proc	far
		assume	ds:StdGrp

		pushf			;Push registers onto the stack just
		push	ds		; in case there is an error return.
		push	di

		mov	di, StdGrp
		mov	ds, di
		cli				;Critical region ahead!

		if	@version ge 600

		les	di, StdGrp:ReadyQ
		cmp	wp es:[di].pcb.NextProc+2, 0
		jne	GoodDIE

; YIKES! The caller is trying to delete the only process in the ReadyQ.
; We can't let that happen, so return an error down here.

		pop	di
		pop	ds
		stc
		ret

; Okay, this DIE operation can proceed.  Handle that down here.

GoodDIE:	mov	ax, wp es:[di].pcb.NextProc
		mov	wp StdGrp:ReadyQ, ax
		mov	ax, wp es:[di].pcb.NextProc+2
		mov	wp StdGrp:ReadyQ+2, ax

; The following code, which passes control to the new "current process"
; must look exactly like the code at the tail end of the dispatcher!
; In particular, the values pushed at the beginning of this routine are
; history.  They are on a different stack, which will not be accessed again,
; so we do not need to worry about them.

		les	di, StdGrp:ReadyQ
		inc	wp es:[di].pcb.CPUTime
		jne	NoHOIncCPU
		inc	wp es:[di+2].pcb.CPUTime

NoHOIncCPU:	mov	ss, es:[di].pcb.regss
		mov	sp, es:[di].pcb.regsp
		push	es:[di].pcb.regflags
		push	es:[di].pcb.regcs
		push	es:[di].pcb.regip
		mov	ax, es:[di].pcb.regax
		mov	bx, es:[di].pcb.regbx
		mov	cx, es:[di].pcb.regcx
		mov	dx, es:[di].pcb.regdx
		mov	bp, es:[di].pcb.regbp
		mov	si, es:[di].pcb.regsi
		mov	ds, es:[di].pcb.regds
		push	es:[di].pcb.regdi
		mov	es, es:[di].pcb.reges
		pop	di
		iret


		else

		les	di, StdGrp:ReadyQ
		cmp	wp es:[di].NextProc+2, 0
		jne	GoodDIE
		pop	di
		pop	ds
		stc
		ret

GoodDIE:	mov	ax, wp es:[di].NextProc
		mov	wp StdGrp:ReadyQ, ax
		mov	ax, wp es:[di].NextProc+2
		mov	wp StdGrp:ReadyQ+2, ax
		les	di, StdGrp:ReadyQ
		inc	wp es:[di].CPUTime
		jne	NoHOIncCPU
		inc	wp es:[di+2].CPUTime
NoHOIncCPU:	mov	ss, es:[di].regss
		mov	sp, es:[di].regsp
		push	es:[di].regflags
		push	es:[di].regcs
		push	es:[di].regip
		mov	ax, es:[di].regax
		mov	bx, es:[di].regbx
		mov	cx, es:[di].regcx
		mov	dx, es:[di].regdx
		mov	bp, es:[di].regbp
		mov	si, es:[di].regsi
		mov	ds, es:[di].regds
		push	es:[di].regdi
		mov	es, es:[di].reges
		pop	di
		iret

		endif

sl_DIE		endp


⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?