/* --- Copyright University of Sussex 1994. All rights reserved. ----------
 * File:		S.pcwnt/src/aprocess.s
 * Purpose:		Process support for 80x86 (Microsoft assembler)
 * Author:		Robert John Duncan, Apr 15 1994
 * Related Files:	S.pcunix/src/aprocess.s
 */

/*************************************************************************
		THIS FILE WAS GENERATED AUTOMATICALLY FROM
		/rsuna/pop/master/S.pcunix/src/aprocess.s
		     ON Fri Apr 15 10:37:42 BST 1994
	  AND SUBSEQUENTLY EDITED ON Fri Apr 15 13:10:24 BST 1994
*************************************************************************/

#_<

#_INCLUDE 'declare.ph'
#_INCLUDE 'process.ph'

lconstant macro (

	USP			= "ebx",
	PB			= "ebp",
	CHAIN_REG		= "edx",

	_ID_VALOF		= @@ID_VALOF,
	_PD_EXECUTE		= @@PD_EXECUTE,
	_PD_EXIT		= @@PD_EXIT,
	_PD_FLAGS		= @@PD_FLAGS,
	_PD_FRAME_LEN		= @@PD_FRAME_LEN,
	_PD_NLOCALS		= @@PD_NLOCALS,
	_PD_NUM_STK_VARS	= @@PD_NUM_STK_VARS,
	_PD_TABLE		= @@PD_TABLE,
	_PS_CALLSTACK_LIM	= @@PS_CALLSTACK_LIM,
	_PS_CALLSTACK_PARTIAL	= @@PS_CALLSTACK_PARTIAL,
	_PS_FLAGS		= @@PS_FLAGS,
	_PS_PARTIAL_RETURN	= @@PS_PARTIAL_RETURN,
	_PS_STATE		= @@PS_STATE,
);

>_#

	.erre	@Version ge 611
	option	casemap:none
	.386
	.model	flat


/************************* wrapping structures ************************/

	.code
	dword	L$text_size, C_LAB(Sys$-objmod_pad_key)
L$text_start:
	.data
	assume	cs:nothing
	dword	L$data_size, C_LAB(Sys$-objmod_pad_key)
L$data_start:

/**********************************************************************/


;;; === SWAPPING THE CALLSTACK ========================================

	.data
	assume	cs:nothing

PROCESS:		;;; Process record
	dword	0
LIMIT:			;;; Limit of the callstack save area
	dword	0
NEXT_FRAME:		;;; Pointer to the next frame in the saved callstack
	dword	0

	.code

BRANCH_std	equ	7	;;; Size of a standard branch instruction
				;;; (as placed by I_BRANCH_std)

;;; _SWAP_OUT_CALLSTACK:
;;;	Swap out the callstack for a process
;;;	(i.e. copy the callstack into the process record)

;;; Call:
;;;	_swap_out_callstack(PROCESS)

DEF_C_LAB(_swap_out_callstack)

	mov	eax, dword ptr [USP]
	add	USP, 4

	mov	dword ptr PROCESS, eax	;;; Save the process record
	mov	edi, dword ptr [eax+_PS_CALLSTACK_LIM]	;;; Start of saved callstack
	mov	eax, dword ptr [eax+_PS_STATE]
	mov	dword ptr LIMIT, eax	;;; End of saved callstack
	jmp	sotest

soloop:

	;;; Copy out the next stack frame:
	;;; save the return address in ECX, then set the procedure base
	;;; register from the owner address on the stack top

	pop	ecx
	mov	PB, dword ptr [esp]

	;;; Make the return address relative to the procedure base

	sub	ecx, PB

	;;; Test if the procedure has dlocal expression code to run

	test	byte ptr [PB+_PD_FLAGS], _:M_PD_PROC_DLEXPR_CODE
	jnz	sobrk

socont:

	;;; Continue here after running the dlocal expression code:

	;;; EDI points to where the stack frame is to be saved in the process
	;;; record; we compute the position of the next frame by subtracting
	;;; the length of the current stack frame

	movzx	eax, byte ptr [PB+_PD_FRAME_LEN]
	sal	eax, 2
	sub	edi, eax
	mov	dword ptr NEXT_FRAME, edi

	;;; Save relative return address and owner address in the process
	;;; record

	mov	dword ptr [edi], ecx
	mov	dword ptr [edi+4], PB
	add	edi, 8

	;;; Copy any on-stack lvars into the process record

	cld
	movzx	ecx, byte ptr [PB+_PD_NUM_STK_VARS]
	lea	esi, dword ptr [esp+4]
	rep	movsd
	mov	esp, esi

	;;; Test if there are any dynamic locals

	movzx	ecx, byte ptr [PB+_PD_NLOCALS]
	cmp	ecx, 0
	je	L$2$1

	;;; If so, copy the current values of the locals into the
	;;; process record, and restore their previous values from the
	;;; stack.
	;;; ESI is initialised to point to the last entry in the procedure's
	;;; identifier table

	lea	esi, dword ptr [PB+ecx*4+_PD_TABLE-4]

L$1$1:	;;; Loop:
	mov	edx, dword ptr [esi]	;;; ident to EDX
	mov	eax, dword ptr [edx+_ID_VALOF]	;;; idval to EAX
	stosd	;;; save idval in process record
	pop	dword ptr [edx+_ID_VALOF]	;;; restore previous idval from stack
	sub	esi, 4	;;; decrement ESI
	loop	L$1$1

L$2$1:	;;; Stack frame saved -- restore EDI to point to where the next
	;;; should go

	mov	edi, dword ptr NEXT_FRAME

sotest:

	;;; Test if there are any more stack frames to swap out

	cmp	dword ptr LIMIT, edi
	jb	soloop

sodone:

	;;; Finished swapping out -- chain procedure on stack

	mov	eax, dword ptr PROCESS
	mov	dword ptr [eax+_PS_CALLSTACK_PARTIAL], 0
	mov	word ptr [eax+_PS_FLAGS], 0	;;; 0 flags = suspended
	mov	eax, dword ptr [USP]
	add	USP, 4
	jmp	dword ptr [eax+_PD_EXECUTE]	;;; chain procedure

sobrk:

	;;; Come here to run dlocal expression code, by jumping into the
	;;; appropriate part of the exit code of the procedure.
	;;; The process is passed in CHAIN_REG

	mov	CHAIN_REG, dword ptr PROCESS

	;;; Save relative return address and callstack save pointer in the
	;;; process record

	mov	dword ptr [CHAIN_REG+_PS_PARTIAL_RETURN], ecx
	mov	dword ptr [CHAIN_REG+_PS_CALLSTACK_PARTIAL], edi

	;;; Go to the procedure's suspend code

	mov	eax, dword ptr [PB+_PD_EXIT]
	sub	eax, BRANCH_std*2
	jmp	eax

	align	4

DEF_C_LAB(_swap_out_continue)

	;;; The suspend code returns here to resume the swapout loop
	;;; The process will still be in CHAIN_REG

	mov	dword ptr PROCESS, CHAIN_REG

	;;; Restore callstack save pointer, relative return address and
	;;; save limit from the process record

	mov	edi, dword ptr [CHAIN_REG+_PS_CALLSTACK_PARTIAL]
	mov	ecx, dword ptr [CHAIN_REG+_PS_PARTIAL_RETURN]
	mov	eax, dword ptr [CHAIN_REG+_PS_STATE]
	mov	dword ptr LIMIT, eax

	;;; Restore procedure base from the stack top

	mov	PB, dword ptr [esp]

	;;; Continue the swapout loop

	jmp	socont

	align	4


;;; _SWAP_IN_CALLSTACK:
;;;	Swap in the callstack for a process
;;;	(i.e. copy stack frames from the process record onto the stack)

;;; Call:
;;;	_swap_in_callstack(PROCESS)

DEF_C_LAB(_swap_in_callstack)

	mov	eax, dword ptr [USP]
	add	USP, 4

	mov	dword ptr PROCESS, eax	;;; Save the process record

	;;; Get a pointer to the start of the saved callstack in ESI
	;;; and the limit in LIMIT

	mov	esi, dword ptr [eax+_PS_STATE]
	mov	eax, dword ptr [eax+_PS_CALLSTACK_LIM]
	mov	dword ptr LIMIT, eax
	jmp	sitest

siloop:

	;;; ESI points to the saved stack frame in the process record.
	;;; Restore the procedure base register from the saved owner address.

	mov	PB, dword ptr [esi+4]

	;;; Compute the position of the next frame in the record by adding
	;;; the length of the current frame

	movzx	eax, byte ptr [PB+_PD_FRAME_LEN]
	lea	esi, dword ptr [esi+eax*4]
	mov	dword ptr NEXT_FRAME, esi
	sub	esi, 4

	;;; Check for any dynamic locals

	std
	movzx	ecx, byte ptr [PB+_PD_NLOCALS]
	cmp	ecx, 0
	je	L$2$2

	;;; If so, push the current values of the locals on the stack
	;;; and get their new values from the process record.
	;;; EDI is initialised to point to the first entry in the procedure's
	;;; identifier table

	lea	edi, dword ptr [PB+_PD_TABLE]

L$1$2:	;;; Loop:
	mov	edx, dword ptr [edi]	;;; ident to EDX
	push	dword ptr [edx+_ID_VALOF]	;;; push the idval
	lodsd	;;; new idval from process record to EAX
	mov	dword ptr [edx+_ID_VALOF], eax	;;; set new idval
	add	edi, 4	;;; increment EDI
	loop	L$1$2

L$2$2:	;;; Copy any on-stack lvars from the saved frame,
	;;; adjusting ESP first in case of interrupts

	movzx	ecx, byte ptr [PB+_PD_NUM_STK_VARS]
	lea	edi, dword ptr [esp-4]

	lea	eax, dword ptr [ecx*4]
	sub	esp, eax

	rep	movsd

	;;; Push the owner address from the procedure base register
	;;; and set ECX to be the saved relative return address

	push	PB
	mov	ecx, dword ptr [esi-4]

	;;; Set ESI to point to the next frame

	mov	esi, dword ptr NEXT_FRAME

	;;; Test for dlocal expression code to run

	test	byte ptr [PB+_PD_FLAGS], _:M_PD_PROC_DLEXPR_CODE
	jnz	sibrk

sicont:

	;;; Continue here after running dlocal expression code:

	;;; Make the relative return address in ECX absolute and push it

	add	ecx, PB
	push	ecx

sitest:

	;;; Test if there are more frames to swap in

	cmp	dword ptr LIMIT, esi
	ja	siloop

sidone:

	;;; Finished swapping in - chain procedure from stack

	mov	eax, dword ptr PROCESS
	mov	dword ptr [eax+_PS_CALLSTACK_PARTIAL], 0
	mov	eax, dword ptr [USP]
	add	USP, 4
	jmp	dword ptr [eax+_PD_EXECUTE]	;;; chain procedure

sibrk:

	;;; Come here to run dlocal expression code, by jumping into the
	;;; appropriate part of the exit code of the procedure.
	;;; The process is passed in CHAIN_REG

	mov	CHAIN_REG, dword ptr PROCESS

	;;; Save relative return address and callstack save pointer in the
	;;; process record

	mov	dword ptr [CHAIN_REG+_PS_PARTIAL_RETURN], ecx
	mov	dword ptr [CHAIN_REG+_PS_CALLSTACK_PARTIAL], esi

	;;; Go to the procedure's resume code

	mov	eax, dword ptr [PB+_PD_EXIT]
	sub	eax, BRANCH_std
	jmp	eax

	align	4

DEF_C_LAB(_swap_in_continue)

	;;; The resume code returns here to continue the swapin loop
	;;; The process will still be in CHAIN_REG

	mov	dword ptr PROCESS, CHAIN_REG

	;;; Restore callstack save pointer, relative return address and
	;;; save limit from the process record

	mov	esi, dword ptr [CHAIN_REG+_PS_CALLSTACK_PARTIAL]
	mov	ecx, dword ptr [CHAIN_REG+_PS_PARTIAL_RETURN]
	mov	eax, dword ptr [CHAIN_REG+_PS_CALLSTACK_LIM]
	mov	dword ptr LIMIT, eax

	;;; Restore the procedure base from the stack top

	mov	PB, dword ptr [esp]

	;;; Continue the swapin loop

	jmp	sicont

	align	4


;;; === SWAPPING THE USERSTACK ========================================

;;; _USSAVE:
;;;	Save and erase a number of bytes from the end of the userstack

;;; Call:
;;;	_ussave(_BYTE_LENGTH, _DST_ADDR);

DEF_C_LAB(_ussave)

	mov	edi, dword ptr [USP]
	mov	ecx, dword ptr [USP+4]
	add	USP, 8
	std

	;;; Make ESI point to the first word on the stack,
	;;; and EDI point to the end of the save area

	mov	esi, dword ptr I_LAB(_userhi)
	sub	esi, 4
	mov	edx, esi	;;; Save for later
	lea	edi, dword ptr [edi+ecx-4]

	;;; Do the save

	sar	ecx, 2
	rep	movsd

	;;; Make EDI point to the stack end

	mov	edi, edx

	;;; Compute the amount of stack left and shift it up

	lea	ecx, dword ptr [esi+4]
	sub	ecx, USP
	sar	ecx, 2
	rep	movsd

	;;; Set the stack pointer and return

	lea	USP, dword ptr [edi+4]
	ret

	align	4

;;; _USRESTORE:
;;;	Restore a number of bytes at the end of the user stack

;;; Call:
;;;	_usrestore(_BYTE_LENGTH, _SRC_ADDR)

DEF_C_LAB(_usrestore)

	mov	eax, dword ptr [USP]
	mov	edx, dword ptr [USP+4]
	add	USP, 8
	cld

	;;; Shift existing stack down, leaving room for restored stuff

	mov	ecx, dword ptr I_LAB(_userhi)
	sub	ecx, USP
	sar	ecx, 2	;;; Length of existing stack in words
	mov	esi, USP	;;; Source address is current USP
	sub	USP, edx	;;; New USP ...
	mov	edi, USP	;;; ... becomes destination address
	rep	movsd

	;;; Now copy the new stuff in

	mov	esi, eax	;;; Source of new stuff
	mov	edi, dword ptr I_LAB(_userhi)
	sub	edi, edx	;;; Destination
	mov	ecx, edx
	sar	ecx, 2	;;; Length in words
	rep	movsd

	ret

	align	4

;;; _USERASUND:
;;;	Erase a number of bytes from the end of the user stack

;;; Call:
;;;	_userasund(_BYTE_LENGTH)

DEF_C_LAB(_userasund)

	mov	eax, dword ptr [USP]	;;; Load length to erase to EAX
	add	USP, 4
	mov	ecx, dword ptr I_LAB(_userhi)
	lea	edi, dword ptr [ecx-4]	;;; Destination is first word on the stack
	add	USP, eax	;;; New stack pointer
	sub	ecx, USP	;;; New stack length (= length of move)
	jz	L$1$3	;;; If zero, nothing to do
	mov	esi, edi	;;; Source is destination ...
	sub	esi, eax	;;; ... offset by the amount being erased
	sar	ecx, 2	;;; Length of move in words
	std
	rep	movsd
L$1$3:	ret

	align	4


/***************** end labels for wrapping structures *****************/

	.code
L$text_end:
	L$text_size	equ	L$text_end-L$text_start
	.data
	assume	cs:nothing
L$data_end:
	L$data_size	equ	L$data_end-L$data_start

/**********************************************************************/

@CurSeg	ends
	extern	C_LAB(Sys$-objmod_pad_key):near
	extern	I_LAB(_userhi):near
	end
