summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/alpha
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/rtl/alpha
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/rtl/alpha')
-rw-r--r--private/ntos/rtl/alpha/capture.s310
-rw-r--r--private/ntos/rtl/alpha/chandler.c297
-rw-r--r--private/ntos/rtl/alpha/chkstk.s131
-rw-r--r--private/ntos/rtl/alpha/context.c330
-rw-r--r--private/ntos/rtl/alpha/debugstb.s269
-rw-r--r--private/ntos/rtl/alpha/exdsptch.c2335
-rw-r--r--private/ntos/rtl/alpha/getcalr.c189
-rw-r--r--private/ntos/rtl/alpha/ghandler.c436
-rw-r--r--private/ntos/rtl/alpha/largeint.s999
-rw-r--r--private/ntos/rtl/alpha/localrtl.c160
-rw-r--r--private/ntos/rtl/alpha/localrtl.h82
-rw-r--r--private/ntos/rtl/alpha/longjmp.s200
-rw-r--r--private/ntos/rtl/alpha/lzntaxp.s485
-rw-r--r--private/ntos/rtl/alpha/mvmem.s1920
-rw-r--r--private/ntos/rtl/alpha/ntcurteb.s57
-rw-r--r--private/ntos/rtl/alpha/ntrtlalp.h123
-rw-r--r--private/ntos/rtl/alpha/setjmp.s148
-rw-r--r--private/ntos/rtl/alpha/tcmpmem.c105
-rw-r--r--private/ntos/rtl/alpha/tfilmem.c79
-rw-r--r--private/ntos/rtl/alpha/tmovmem.c129
-rw-r--r--private/ntos/rtl/alpha/trampoln.s524
-rw-r--r--private/ntos/rtl/alpha/tzermem.c79
-rw-r--r--private/ntos/rtl/alpha/unwindr.c871
-rw-r--r--private/ntos/rtl/alpha/xcptmisc.s501
24 files changed, 10759 insertions, 0 deletions
diff --git a/private/ntos/rtl/alpha/capture.s b/private/ntos/rtl/alpha/capture.s
new file mode 100644
index 000000000..027ced3f5
--- /dev/null
+++ b/private/ntos/rtl/alpha/capture.s
@@ -0,0 +1,310 @@
+// TITLE("Capture and Restore Context")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// capture.s
+//
+// Abstract:
+//
+// This module implements the code necessary to capture and restore
+// the context of the caller.
+//
+// Author:
+//
+// David N. Cutler (davec) 14-Sep-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 13-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Capture Context")
+//++
+//
+// VOID
+// RtlCaptureContext (
+// OUT PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This function captures the context of the caller in the specified
+// context record.
+//
+// The context record is assumed to be quadword aligned (since all the
+// members of the record are themselves quadwords).
+//
+// N.B. The stored value of registers a0 and ra will be a side effect of
+// having made this call. All other registers will be stored as they
+// were when the call to this function was made.
+//
+// Arguments:
+//
+// ContextRecord (a0) - Supplies the address of a context record.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlCaptureContext)
+
+//
+// Save all the integer registers.
+//
+
+ .set noreorder
+ .set noat
+ stq v0, CxIntV0(a0) // 0: store integer register v0
+ stq t0, CxIntT0(a0) // 1: store integer registers t0 - t7
+ stq t1, CxIntT1(a0) // 2:
+ stq t2, CxIntT2(a0) // 3:
+ stq t3, CxIntT3(a0) // 4:
+ stq t4, CxIntT4(a0) // 5:
+ stq t5, CxIntT5(a0) // 6:
+ stq t6, CxIntT6(a0) // 7:
+ stq t7, CxIntT7(a0) // 8:
+
+ stq s0, CxIntS0(a0) // 9: store integer registers s0 - s5
+ stq s1, CxIntS1(a0) // 10:
+ stq s2, CxIntS2(a0) // 11:
+ stq s3, CxIntS3(a0) // 12:
+ stq s4, CxIntS4(a0) // 13:
+ stq s5, CxIntS5(a0) // 14:
+ stq fp, CxIntFp(a0) // 15: store integer register fp/s6
+
+ stq a0, CxIntA0(a0) // 16: store integer registers a0 - a5
+ stq a1, CxIntA1(a0) // 17:
+ stq a2, CxIntA2(a0) // 18:
+ stq a3, CxIntA3(a0) // 19:
+ stq a4, CxIntA4(a0) // 20:
+ stq a5, CxIntA5(a0) // 21:
+
+ stq t8, CxIntT8(a0) // 22: store integer registers t8 - t11
+ stq t9, CxIntT9(a0) // 23:
+ stq t10, CxIntT10(a0) // 24:
+ stq t11, CxIntT11(a0) // 25:
+
+ stq ra, CxIntRa(a0) // 26: store integer register ra
+ stq t12, CxIntT12(a0) // 27: store integer register t12
+ stq AT, CxIntAt(a0) // 28: store integer register at
+ stq gp, CxIntGp(a0) // 29: store integer register gp
+ stq sp, CxIntSp(a0) // 30: store integer register sp
+ stq zero, CxIntZero(a0) // 31: store integer register zero
+
+//
+// Save all the floating registers, and the floating control register.
+//
+
+ stt f0, CxFltF0(a0) // store floating registers f0 - f31
+ stt f1, CxFltF1(a0) //
+ stt f2, CxFltF2(a0) //
+ stt f3, CxFltF3(a0) //
+ stt f4, CxFltF4(a0) //
+ stt f5, CxFltF5(a0) //
+ stt f6, CxFltF6(a0) //
+ stt f7, CxFltF7(a0) //
+ stt f8, CxFltF8(a0) //
+ stt f9, CxFltF9(a0) //
+ stt f10, CxFltF10(a0) //
+ stt f11, CxFltF11(a0) //
+ stt f12, CxFltF12(a0) //
+ stt f13, CxFltF13(a0) //
+ stt f14, CxFltF14(a0) //
+ stt f15, CxFltF15(a0) //
+ stt f16, CxFltF16(a0) //
+ stt f17, CxFltF17(a0) //
+ stt f18, CxFltF18(a0) //
+ stt f19, CxFltF19(a0) //
+ stt f20, CxFltF20(a0) //
+ stt f21, CxFltF21(a0) //
+ stt f22, CxFltF22(a0) //
+ stt f23, CxFltF23(a0) //
+ stt f24, CxFltF24(a0) //
+ stt f25, CxFltF25(a0) //
+ stt f26, CxFltF26(a0) //
+ stt f27, CxFltF27(a0) //
+ stt f28, CxFltF28(a0) //
+ stt f29, CxFltF29(a0) //
+ stt f30, CxFltF30(a0) //
+ stt f31, CxFltF31(a0) //
+
+//
+// Save control information and set context flags.
+//
+
+ mf_fpcr f0, f0, f0 // get floating point control register
+ stt f0, CxFpcr(a0) // store it
+ ldt f0, CxFltF0(a0) // restore original f0 value
+ stq ra, CxFir(a0) // set continuation address
+ .set at
+ .set reorder
+
+//
+// If called from user mode set a known fixed PSR value for user mode. If
+// called from kernel mode we are able to execute the privileged call pal
+// to get the actual PSR.
+//
+
+ ldil v0, PSR_USER_MODE // set constant user processor status
+ bgt sp, 10f // if sp > 0, call came from user mode
+
+ GET_CURRENT_PROCESSOR_STATUS_REGISTER // get contents of PSR in v0
+
+10: stl v0, CxPsr(a0) // store processor status
+ ldil v0, CONTEXT_FULL // set context control flags
+ stl v0, CxContextFlags(a0) // store it
+ ret zero, (ra) // return
+
+ .end RtlCaptureContext
+
+ SBTTL("Restore Context")
+//++
+//
+// VOID
+// RtlpRestoreContext (
+// IN PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This function restores the context of the caller to the specified
+// context.
+//
+// The context record is assumed to be quadword aligned (since all the
+// members of the record are themselves quadwords).
+//
+// N.B. This is a special routine that is used by RtlUnwind to restore
+// context in the current mode. PSR, t0, and t1 are not restored.
+//
+// Arguments:
+//
+// ContextRecord (a0) - Supplies the address of a context record.
+//
+// Return Value:
+//
+// None.
+//
+// N.B. There is no return from this routine.
+//
+//--
+
+ LEAF_ENTRY(RtlpRestoreContext)
+
+//
+// If the call is from user mode, then use the continue system service to
+// continue execution. Otherwise, restore the context directly since the
+// current mode is kernel and threads can't be arbitrarily interrupted.
+//
+
+ blt ra, 10f // if ra < 0, kernel mode restore
+ ldil a1, FALSE // set test alert argument false
+ bsr ra, ZwContinue // continue execution
+
+//
+// Save the address of the context record and continuation address in
+// registers t0 and t1. These registers are not restored.
+//
+
+10: mov a0, t0 // save context record address
+ ldq t1, CxFir(t0) // get continuation address
+
+//
+// Restore floating control and floating registers f0 - f30.
+//
+
+ .set noreorder
+ .set noat
+ ldt f0, CxFpcr(t0) // get floating point control register
+ mt_fpcr f0, f0, f0 // load register
+
+ ldt f0, CxFltF0(t0) // restore floating registers f0 - f30
+ ldt f1, CxFltF1(t0) //
+ ldt f2, CxFltF2(t0) //
+ ldt f3, CxFltF3(t0) //
+ ldt f4, CxFltF4(t0) //
+ ldt f5, CxFltF5(t0) //
+ ldt f6, CxFltF6(t0) //
+ ldt f7, CxFltF7(t0) //
+ ldt f8, CxFltF8(t0) //
+ ldt f9, CxFltF9(t0) //
+ ldt f10, CxFltF10(t0) //
+ ldt f11, CxFltF11(t0) //
+ ldt f12, CxFltF12(t0) //
+ ldt f13, CxFltF13(t0) //
+ ldt f14, CxFltF14(t0) //
+ ldt f15, CxFltF15(t0) //
+ ldt f16, CxFltF16(t0) //
+ ldt f17, CxFltF17(t0) //
+ ldt f18, CxFltF18(t0) //
+ ldt f19, CxFltF19(t0) //
+ ldt f20, CxFltF20(t0) //
+ ldt f21, CxFltF21(t0) //
+ ldt f22, CxFltF22(t0) //
+ ldt f23, CxFltF23(t0) //
+ ldt f24, CxFltF24(t0) //
+ ldt f25, CxFltF25(t0) //
+ ldt f26, CxFltF26(t0) //
+ ldt f27, CxFltF27(t0) //
+ ldt f28, CxFltF28(t0) //
+ ldt f29, CxFltF29(t0) //
+ ldt f30, CxFltF30(t0) //
+
+//
+// Restore integer registers and continue execution.
+// N.B. Integer registers t0 and t1 cannot be restored by this function.
+//
+
+ ldq v0, CxIntV0(t0) // restore integer register v0
+ ldq t2, CxIntT2(t0) // restore integer registers t2 - t7
+ ldq t3, CxIntT3(t0) //
+ ldq t4, CxIntT4(t0) //
+ ldq t5, CxIntT5(t0) //
+ ldq t6, CxIntT6(t0) //
+ ldq t7, CxIntT7(t0) //
+
+ ldq s0, CxIntS0(t0) // restore integer registers s0 - s5
+ ldq s1, CxIntS1(t0) //
+ ldq s2, CxIntS2(t0) //
+ ldq s3, CxIntS3(t0) //
+ ldq s4, CxIntS4(t0) //
+ ldq s5, CxIntS5(t0) //
+ ldq fp, CxIntFp(t0) // restore integer register fp/s6
+
+ ldq a0, CxIntA0(t0) // restore integer registers a0 - a5
+ ldq a1, CxIntA1(t0) //
+ ldq a2, CxIntA2(t0) //
+ ldq a3, CxIntA3(t0) //
+ ldq a4, CxIntA4(t0) //
+ ldq a5, CxIntA5(t0) //
+
+ ldq t8, CxIntT8(t0) // restore integer registers t8 - t11
+ ldq t9, CxIntT9(t0) //
+ ldq t10, CxIntT10(t0) //
+ ldq t11, CxIntT11(t0) //
+
+ ldq ra, CxIntRa(t0) // restore integer register ra
+ ldq t12, CxIntT12(t0) // restore integer register t12
+ ldq AT, CxIntAt(t0) // restore integer register at
+ ldq gp, CxIntGp(t0) // restore integer register gp
+ ldq sp, CxIntSp(t0) // restore integer register sp
+
+ jmp zero, (t1) // continue execution
+ .set at
+ .set reorder
+
+ .end RtlpRestoreContext
diff --git a/private/ntos/rtl/alpha/chandler.c b/private/ntos/rtl/alpha/chandler.c
new file mode 100644
index 000000000..07b6e3c6b
--- /dev/null
+++ b/private/ntos/rtl/alpha/chandler.c
@@ -0,0 +1,297 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ chandler.c
+
+Abstract:
+
+ This module implements the C specific exception handler that provides
+ structured condition handling for the C language.
+
+Author:
+
+ David N. Cutler (davec) 11-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Thomas Van Baak (tvb) 29-Apr-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+#include "nt.h"
+
+//
+// Define procedure prototypes for exception filter and termination handler
+// execution routines defined in jmpuwind.s.
+//
+
+LONG
+__C_ExecuteExceptionFilter (
+ PEXCEPTION_POINTERS ExceptionPointers,
+ EXCEPTION_FILTER ExceptionFilter,
+ ULONG EstablisherFrame
+ );
+
+VOID
+__C_ExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+EXCEPTION_DISPOSITION
+__C_specific_handler (
+ IN struct _EXCEPTION_RECORD *ExceptionRecord,
+ IN void *EstablisherFrame,
+ IN OUT struct _CONTEXT *ContextRecord,
+ IN OUT struct _DISPATCHER_CONTEXT *DispatcherContext
+ )
+
+/*++
+
+Routine Description:
+
+ This function scans the scope tables associated with the specified
+ procedure and calls exception and termination handlers as necessary.
+
+ This language specific exception handler function is called on a
+ per-frame basis and in two different cases:
+
+ First, the IS_DISPATCHING case, it is called by the exception
+ dispatcher, RtlDispatchException, via the short assembler routine,
+ __C_ExecuteHandlerForException, when trying to locate exception
+ filters within the given frame.
+
+ Second, the IS_UNWINDING case, it is called by the frame unwinder,
+ RtlUnwind, via the short assembler routine, __C_ExecuteHandlerForUnwind,
+ when unwinding the stack and trying to locate termination handlers
+ within the given frame.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ EstablisherFrame - Supplies a pointer to frame of the establisher function.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+ DispatcherContext - Supplies a pointer to the exception dispatcher or
+ unwind dispatcher context.
+
+Return Value:
+
+ If the exception is handled by one of the exception filter routines, then
+ there is no return from this routine and RtlUnwind is called. Otherwise,
+ an exception disposition value of continue execution or continue search is
+ returned.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ EXCEPTION_FILTER ExceptionFilter;
+ EXCEPTION_POINTERS ExceptionPointers;
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG Index;
+ PSCOPE_TABLE ScopeTable;
+ ULONG TargetPc;
+ TERMINATION_HANDLER TerminationHandler;
+ LONG Value;
+
+ //
+ // Get the address of where control left the establisher, the address of
+ // the function table entry that describes the function, and the address of
+ // the scope table.
+ //
+
+ ControlPc = DispatcherContext->ControlPc;
+ FunctionEntry = DispatcherContext->FunctionEntry;
+ ScopeTable = (PSCOPE_TABLE)(FunctionEntry->HandlerData);
+
+ //
+ // The scope table HandlerAddress is either the address of an exception
+ // filter or a termination handler. The C compiler wraps the code in the
+ // exception filter expression or the termination handler clause within
+ // an internal C function. The value of the scope table JumpTarget field
+ // is used to distinguish an exception filter function (JumpTarget non zero)
+ // from a termination handler function (JumpTarget is zero).
+ //
+
+ //
+ // If an unwind is not in progress, then scan the scope table and call
+ // the appropriate exception filter routines. Otherwise, scan the scope
+ // table and call the appropriate termination handlers using the target
+ // PC obtained from the context record.
+ //
+
+ if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // Set up the ExceptionPointers structure that is passed as the argument
+ // to the exception filter. It is used by the compiler to implement the
+ // intrinsic functions exception_code() and exception_info().
+ //
+
+ ExceptionPointers.ExceptionRecord = ExceptionRecord;
+ ExceptionPointers.ContextRecord = ContextRecord;
+
+ //
+ // Scan the scope table and call the appropriate exception filter
+ // routines. The scope table entries are known to be sorted by
+ // increasing EndAddress order. Thus a linear scan will naturally
+ // hit inner scope exception clauses before outer scope clauses.
+ //
+
+ for (Index = 0; Index < ScopeTable->Count; Index += 1) {
+ if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
+ (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress) &&
+ (ScopeTable->ScopeRecord[Index].JumpTarget != 0)) {
+
+ //
+ // Call the exception filter routine.
+ //
+
+ ExceptionFilter =
+ (EXCEPTION_FILTER)ScopeTable->ScopeRecord[Index].HandlerAddress;
+ Value = __C_ExecuteExceptionFilter(&ExceptionPointers,
+ ExceptionFilter,
+ (ULONG)EstablisherFrame);
+
+ //
+ // If the return value is less than zero, then dismiss the
+ // exception. Otherwise, if the value is greater than zero,
+ // then unwind to the target exception handler corresponding
+ // to the exception filter. Otherwise, continue the search for
+ // an exception filter.
+ //
+
+ //
+ // Exception filters will usually return one of the following
+ // defines, although the decision below is made only by sign:
+ //
+ // #define EXCEPTION_EXECUTE_HANDLER 1
+ // #define EXCEPTION_CONTINUE_SEARCH 0
+ // #define EXCEPTION_CONTINUE_EXECUTION -1
+ //
+
+ if (Value < 0) {
+ return ExceptionContinueExecution;
+
+ } else if (Value > 0) {
+
+ //
+ // Set the return value for the unwind to the exception
+ // code so the exception handler clause can retrieve it
+ // from v0. This is how GetExceptionCode() is implemented
+ // in exception handler clauses.
+ //
+
+ RtlUnwind2(EstablisherFrame,
+ (PVOID)ScopeTable->ScopeRecord[Index].JumpTarget,
+ ExceptionRecord,
+ (PVOID)ExceptionRecord->ExceptionCode,
+ ContextRecord);
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // Scan the scope table and call the appropriate termination handler
+ // routines.
+ //
+
+ TargetPc = (ULONG)ContextRecord->Fir;
+ for (Index = 0; Index < ScopeTable->Count; Index += 1) {
+ if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
+ (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress)) {
+
+ //
+ // If the target PC is within the same scope the control PC
+ // is within, then this is an uplevel goto out of an inner try
+ // scope or a long jump back into a try scope. Terminate the
+ // scan for termination handlers - because any other handlers
+ // will be outside the scope of both the goto and its label.
+ //
+ // N.B. The target PC can be just beyond the end of the scope,
+ // in which case it is a leave from the scope.
+ //
+
+ if ((TargetPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
+ (TargetPc <= ScopeTable->ScopeRecord[Index].EndAddress)) {
+ break;
+
+ } else {
+
+ //
+ // If the scope table entry describes an exception filter
+ // and the associated exception handler is the target of
+ // the unwind, then terminate the scan for termination
+ // handlers. Otherwise, if the scope table entry describes
+ // a termination handler, then record the address of the
+ // end of the scope as the new control PC address and call
+ // the termination handler.
+ //
+ // Recording a new control PC is necessary to ensure that
+ // termination handlers are called only once even when a
+ // collided unwind occurs.
+ //
+
+ if (ScopeTable->ScopeRecord[Index].JumpTarget != 0) {
+
+ //
+ // try/except - exception filter (JumpTarget != 0).
+ // After the exception filter is called, the exception
+ // handler clause is executed by the call to unwind
+ // above. Having reached this point in the scan of the
+ // scope tables, any other termination handlers will
+ // be outside the scope of the try/except.
+ //
+
+ if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) {
+ break;
+ }
+
+ } else {
+
+ //
+ // try/finally - termination handler (JumpTarget == 0).
+ //
+
+ //
+ // Unless the termination handler results in a long
+ // jump, execution will resume at the instruction after
+ // the exception handler clause.
+ //
+ // ## tvb - I'm still suspicious of the +4 below.
+
+ DispatcherContext->ControlPc =
+ ScopeTable->ScopeRecord[Index].EndAddress + 4;
+ TerminationHandler =
+ (TERMINATION_HANDLER)ScopeTable->ScopeRecord[Index].HandlerAddress;
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ (ULONG)EstablisherFrame);
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Continue search for exception filters or termination handlers.
+ //
+
+ return ExceptionContinueSearch;
+}
diff --git a/private/ntos/rtl/alpha/chkstk.s b/private/ntos/rtl/alpha/chkstk.s
new file mode 100644
index 000000000..749af86a7
--- /dev/null
+++ b/private/ntos/rtl/alpha/chkstk.s
@@ -0,0 +1,131 @@
+// TITLE("Runtime Stack Checking")
+//++
+//
+// Copyright (c) 1991 Microsoft Corporation
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// chkstk.s
+//
+// Abstract:
+//
+// This module implements runtime stack checking.
+//
+// Author:
+//
+// David N. Cutler (davec) 14-Mar-1991
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 7-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Check Stack")
+//++
+//
+// ULONG
+// _RtlCheckStack (
+// IN ULONG Allocation
+// )
+//
+// Routine Description:
+//
+// This function provides runtime stack checking for local allocations
+// that are more than a page and for storage dynamically allocated with
+// the alloca function. Stack checking consists of probing downward in
+// the stack a page at a time. If the current stack commitment is exceeded,
+// then the system will automatically attempts to expand the stack. If the
+// attempt succeeds, then another page is committed. Otherwise, a stack
+// overflow exception is raised. It is the responsibility of the caller to
+// handle this exception.
+//
+// N.B. This routine is called using a non-standard calling sequence since
+// it is typically called from within the prologue. The allocation size
+// argument is in register t12 and it must be preserved. Register t11
+// may contain the callers saved ra and it must be preserved. The choice
+// of these registers is hard-coded into the acc C compiler. Register v0
+// may contain a static link pointer (exception handlers) and so it must
+// be preserved. Since this function is called from within the prolog,
+// the a' registers must be preserved, as well as all the s' registers.
+// Registers t8, t9, and t10 are used by this function and are not
+// preserved.
+//
+// The typical calling sequence from the prologue is:
+//
+// mov ra, t11 // save return address
+// ldil t12, SIZE // set requested stack frame size
+// bsr ra, _RtlCheckStack // check stack page allocation
+// subq sp, t12, sp // allocate stack frame
+// mov t11, ra // restore return address
+//
+// Arguments:
+//
+// Allocation (t12) - Supplies the size of the allocation on the stack.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(_RtlCheckStack)
+
+ subq sp, t12, t8 // compute requested new stack address
+ mov v0, t10 // save v0 since the PALcode uses it
+#if defined(NTOS_KERNEL_RUNTIME)
+
+//
+// Running on kernel stack - get stack limit from thread object
+//
+
+ GET_CURRENT_THREAD // current thread in v0
+ ldl t9, ThStackLimit(v0)
+
+#else
+//
+// Running on user stack - get stack limit from thread environment block.
+//
+
+10: GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) put TEB address in v0
+
+ ldl t9, TeStackLimit(v0) // get low limit of user stack address
+#endif
+
+//
+// The requested bottom of the stack is in t8.
+// The current low limit of the stack is in t9.
+//
+// If the new stack address is greater than the current stack limit, then the
+// pages have already been allocated, and nothing further needs to be done.
+//
+
+ mov t10, v0 // restore v0, no further PAL calls
+ cmpult t8, t9, t10 // t8<t9? new stack base within limit?
+ beq t10, 40f // if eq [false], then t8>=t9, so yes
+
+ ldil t10, ~(PAGE_SIZE - 1) // round down new stack address
+ and t8, t10, t8 // to next page boundary
+
+//
+// Go down one page, touch one quadword in it, and repeat until we reach the
+// new stack limit.
+//
+
+30: lda t9, -PAGE_SIZE(t9) // compute next address to check
+ stq zero, 0(t9) // probe stack address with a write
+ cmpeq t8, t9, t10 // t8=t9? at the low limit yet?
+ beq t10, 30b // if eq [false], more pages to probe
+
+40: ret zero, (ra) // return
+
+ .end _RtlCheckStack
diff --git a/private/ntos/rtl/alpha/context.c b/private/ntos/rtl/alpha/context.c
new file mode 100644
index 000000000..dfcf0aa1e
--- /dev/null
+++ b/private/ntos/rtl/alpha/context.c
@@ -0,0 +1,330 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ context.c
+
+Abstract:
+
+ This module implements user-mode callable context manipulation routines.
+
+Author:
+
+ Mark Lucovsky (markl) 20-Jun-1989
+
+Revision History:
+
+ David N. Cutler (davec) 18-Apr-1990
+
+ Revise for MIPS environment.
+
+ Thomas Van Baak (tvb) 11-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <alphaops.h>
+
+VOID
+RtlInitializeContext(
+ IN HANDLE Process,
+ OUT PCONTEXT Context,
+ IN PVOID Parameter OPTIONAL,
+ IN PVOID InitialPc OPTIONAL,
+ IN PVOID InitialSp OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a context structure so that it can be used in
+ a subsequent call to NtCreateThread.
+
+Arguments:
+
+ Process - Supplies an open handle to the target process. This argument
+ is ignored by this function.
+
+ Context - Supplies a pointer to a context record that is to be initialized.
+
+ Parameter - Supplies an initial value for register A0.
+
+ InitialPc - Supplies an initial program counter value.
+
+ InitialSp - Supplies an initial stack pointer value.
+
+Return Value:
+
+ Raises STATUS_BAD_INITIAL_STACK if the value of InitialSp is not properly
+ aligned.
+
+ Raises STATUS_BAD_INITIAL_PC if the value of InitialPc is not properly
+ aligned.
+
+--*/
+
+{
+
+ //
+ // Check for proper initial stack and PC alignment.
+ //
+
+ if (((ULONG)InitialSp & 0xF) != 0) {
+ RtlRaiseStatus(STATUS_BAD_INITIAL_STACK);
+ }
+ if (((ULONG)InitialPc & 0x3) != 0) {
+ RtlRaiseStatus(STATUS_BAD_INITIAL_PC);
+ }
+
+ //
+ // Initialize the integer registers to contain their register number
+ // (they must be initialized and using register numbers instead of 0
+ // can be useful for debugging). Integer registers a0, ra, gp, and sp
+ // are set later.
+ //
+
+ Context->IntV0 = 0;
+ Context->IntT0 = 1;
+ Context->IntT1 = 2;
+ Context->IntT2 = 3;
+ Context->IntT3 = 4;
+ Context->IntT4 = 5;
+ Context->IntT5 = 6;
+ Context->IntT6 = 7;
+ Context->IntT7 = 8;
+ Context->IntS0 = 9;
+ Context->IntS1 = 10;
+ Context->IntS2 = 11;
+ Context->IntS3 = 12;
+ Context->IntS4 = 13;
+ Context->IntS5 = 14;
+ Context->IntFp = 15;
+ Context->IntA1 = 17;
+ Context->IntA2 = 18;
+ Context->IntA3 = 19;
+ Context->IntA4 = 20;
+ Context->IntA5 = 21;
+ Context->IntT8 = 22;
+ Context->IntT9 = 23;
+ Context->IntT10 = 24;
+ Context->IntT11 = 25;
+ Context->IntT12 = 27;
+ Context->IntAt = 28;
+
+ //
+ // Initialize the floating point registers to contain the integer value
+ // of their register number (they must be initialized and using register
+ // numbers instead of 0 can be useful for debugging).
+ //
+
+ Context->FltF0 = 0;
+ Context->FltF1 = 1;
+ Context->FltF2 = 2;
+ Context->FltF3 = 3;
+ Context->FltF4 = 4;
+ Context->FltF5 = 5;
+ Context->FltF6 = 6;
+ Context->FltF7 = 7;
+ Context->FltF8 = 8;
+ Context->FltF9 = 9;
+ Context->FltF10 = 10;
+ Context->FltF11 = 11;
+ Context->FltF12 = 12;
+ Context->FltF13 = 13;
+ Context->FltF14 = 14;
+ Context->FltF15 = 15;
+ Context->FltF16 = 16;
+ Context->FltF17 = 17;
+ Context->FltF18 = 18;
+ Context->FltF19 = 19;
+ Context->FltF20 = 20;
+ Context->FltF21 = 21;
+ Context->FltF22 = 22;
+ Context->FltF23 = 23;
+ Context->FltF24 = 24;
+ Context->FltF25 = 25;
+ Context->FltF26 = 26;
+ Context->FltF27 = 27;
+ Context->FltF28 = 28;
+ Context->FltF29 = 29;
+ Context->FltF30 = 30;
+ Context->FltF31 = 0;
+
+ //
+ // Initialize the control registers.
+ //
+ // Gp: will be set in LdrpInitialize at thread startup.
+ // Ra: some debuggers compare for 1 as a top-of-stack indication.
+ //
+ // N.B. ULONG becomes canonical longword with (ULONGLONG)(LONG) cast.
+ //
+
+ Context->IntGp = 0;
+ Context->IntSp = (ULONGLONG)(LONG)InitialSp;
+ Context->IntRa = 1;
+ Context->Fir = (ULONGLONG)(LONG)InitialPc;
+
+ //
+ // Set default Alpha floating point control register values.
+ //
+
+ Context->Fpcr = (ULONGLONG)0;
+ ((PFPCR)(&Context->Fpcr))->DynamicRoundingMode = ROUND_TO_NEAREST;
+ Context->SoftFpcr = (ULONGLONG)0;
+
+ Context->Psr = 0;
+ Context->ContextFlags = CONTEXT_FULL;
+
+ //
+ // Set the initial context of the thread in a machine specific way.
+ //
+
+ Context->IntA0 = (ULONGLONG)(LONG)Parameter;
+}
+
+NTSTATUS
+RtlRemoteCall(
+ HANDLE Process,
+ HANDLE Thread,
+ PVOID CallSite,
+ ULONG ArgumentCount,
+ PULONG Arguments,
+ BOOLEAN PassContext,
+ BOOLEAN AlreadySuspended
+ )
+
+/*++
+
+Routine Description:
+
+ This function calls a procedure in another thread/process by using
+ NtGetContext and NtSetContext. Parameters are passed to the target
+ procedure via the nonvolatile registers (s0 - s5).
+
+Arguments:
+
+ Process - Supplies an open handle to the target process.
+
+ Thread - Supplies an open handle to the target thread within the target
+ process.
+
+ CallSite - Supplies the address of the procedure to call in the target
+ process.
+
+ ArgumentCount - Supplies the number of 32 bit parameters to pass to the
+ target procedure.
+
+ Arguments - Supplies a pointer to the array of 32 bit parameters to pass.
+
+ PassContext - Supplies a boolean value that determines whether a parameter
+ is to be passed that points to a context record. This parameter is
+ ignored on MIPS and Alpha hosts.
+
+ AlreadySuspended - Supplies a boolean value that determines whether the
+ target thread is already in a suspended or waiting state.
+
+Return Value:
+
+ Status - Status value.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ CONTEXT Context;
+ ULONG Index;
+ ULONGLONG NewSp;
+
+ if ((ArgumentCount > 6) ||
+ (PassContext && (ArgumentCount > 5))) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // If necessary, suspend the target thread before getting the thread's
+ // current state.
+ //
+
+ if (AlreadySuspended == FALSE) {
+ Status = NtSuspendThread(Thread, NULL);
+ if (NT_SUCCESS(Status) == FALSE) {
+ return(Status);
+ }
+ }
+
+ //
+ // Get the current state of the target thread.
+ //
+
+ Context.ContextFlags = CONTEXT_FULL;
+ Status = NtGetContextThread(Thread, &Context);
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+ }
+
+ if (AlreadySuspended) {
+ Context.IntV0 = STATUS_ALERTED;
+ }
+
+ //
+ // Pass the parameters to the other thread via the non-volatile registers
+ // s0 - s5. The context record is passed on the stack of the target thread.
+ //
+
+ NewSp = Context.IntSp - sizeof(CONTEXT);
+ Status = NtWriteVirtualMemory(Process, (PVOID)NewSp, &Context,
+ sizeof(CONTEXT), NULL);
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+ }
+
+ //
+ // N.B. Each ULONG argument is converted to canonical form with the
+ // (ULONGLONG)(LONG) cast as required by the calling standard.
+ //
+ // N.B. Given the PULONG Arguments prototype, this function cannot pass
+ // quadword arguments (including structures such as LARGE_INTEGER).
+ //
+
+ Context.IntSp = NewSp;
+
+ if (PassContext) {
+ Context.IntS0 = NewSp;
+ for (Index = 0; Index < ArgumentCount; Index += 1) {
+ (&Context.IntS1)[Index] = (ULONGLONG)(LONG)Arguments[Index];
+ }
+
+ } else {
+ for (Index = 0; Index < ArgumentCount; Index += 1) {
+ (&Context.IntS0)[Index] = (ULONGLONG)(LONG)Arguments[Index];
+ }
+ }
+
+ //
+ // Set the address of the target code into FIR and set the thread context
+ // to cause the target procedure to be executed.
+ //
+ // N.B. The PVOID CallSite is stored as a canonical longword in order
+ // for Fir to be a valid 64-bit address.
+ //
+
+ Context.Fir = (ULONGLONG)(LONG)CallSite;
+ Status = NtSetContextThread(Thread, &Context);
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+}
diff --git a/private/ntos/rtl/alpha/debugstb.s b/private/ntos/rtl/alpha/debugstb.s
new file mode 100644
index 000000000..5847eb47f
--- /dev/null
+++ b/private/ntos/rtl/alpha/debugstb.s
@@ -0,0 +1,269 @@
+// TITLE("Debug Support Functions")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+//
+// Module Name:
+//
+// debug.s
+//
+// Abstract:
+//
+// This module implements functions to support debugging NT. Each
+// function executes a specific call pal which is interpreted by the
+// kernel debugger.
+//
+// Author:
+//
+// Steven R. Wood (stevewo) 3-Aug-1989
+// Joe Notarangelo 24-Jun-1992
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+//++
+//
+// VOID
+// DbgBreakPoint()
+//
+// Routine Description:
+//
+// This function executes a breakpoint instruction. Useful for entering
+// the debugger under program control. This breakpoint will always go to
+// the kernel debugger if one is installed, otherwise it will go to the
+// debug subsystem.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(DbgBreakPoint)
+
+ BREAK_DEBUG_STOP // debug stop breakpoint
+ ret zero, (ra) // return
+
+ .end DbgBreakPoint
+
+//++
+//
+// VOID
+// DbgBreakPointWithStatus(
+// IN ULONG Status
+// )
+//
+// Routine Description:
+//
+// This function executes a breakpoint instruction. Useful for entering
+// the debugger under program control. This breakpoint will always go to
+// the kernel debugger if one is installed, otherwise it will go to the
+// debug subsystem. This function is identical to DbgBreakPoint, except
+// that it takes an argument which the debugger can see.
+//
+// Arguments:
+//
+// A status code.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(DbgBreakPointWithStatus)
+
+
+ ALTERNATE_ENTRY(RtlpBreakWithStatusInstruction)
+ BREAK_DEBUG_STOP // debug stop breakpoint
+ ret zero, (ra) // return
+
+ .end DbgBreakPointWithStatus
+
+//++
+//
+// VOID
+// DbgUserBreakPoint()
+//
+// Routine Description:
+//
+// This function executes a breakpoint instruction. Useful for entering
+// the debug subsystem under program control. The kernel debug will ignore
+// this breakpoint since it will not find the instruction address in its
+// breakpoint table.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// None.
+//
+//--
+ LEAF_ENTRY(DbgUserBreakPoint)
+
+ BREAK // issue user breakpoint
+ ret zero, (ra) // return
+
+ .end DbgUserBreakPoint
+
+//++
+//
+// NTSTATUS
+// DebugPrint(
+// IN PSTRING Output
+// )
+//
+// Routine Description:
+//
+// This function executes a debug print breakpoint.
+//
+// Arguments:
+//
+// Output (a0) - Supplies a pointer to the output string descriptor.
+//
+// Return Value:
+//
+// Status code. STATUS_SUCCESS if debug print happened.
+// STATUS_BREAKPOINT if user typed a Control-C during print.
+// STATUS_DEVICE_NOT_CONNECTED if kernel debugger not present.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugPrint)
+
+ ldwu a1, StrLength(a0) // set length of output string
+ ldl a0, StrBuffer(a0) // set address of output string
+ BREAK_DEBUG_PRINT // execute a debug print breakpoint
+ ret zero, (ra) // return
+ .end DebugPrint
+
+#endif
+
+//++
+//
+// ULONG
+// DebugPrompt(
+// IN PSTRING Output,
+// IN PSTRING Input
+// )
+//
+// Routine Description:
+//
+// This function executes a debug prompt breakpoint.
+//
+// Arguments:
+//
+// Output (a0) - Supplies a pointer to the output string descriptor.
+//
+// Input (a1) - Supplies a pointer to the input string descriptor.
+//
+// Return Value:
+//
+// The length of the input string is returned as the function value.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugPrompt)
+
+ ldwu a3,StrMaximumLength(a1) // set maximum length of input string
+ ldl a2,StrBuffer(a1) // set address of input string
+ ldwu a1,StrLength(a0) // set length of output string
+ ldl a0,StrBuffer(a0) // set address of output string
+ BREAK_DEBUG_PROMPT
+ ret zero, (ra)
+
+ .end DebugPrompt
+
+#endif
+
+
+//++
+//
+// VOID
+// DebugLoadImageSymbols(
+// IN PSTRING ImagePathName,
+// IN PKD_SYMBOLS_INFO SymbolInfo
+// )
+//
+// Routine Description:
+//
+// This function calls the kernel debugger to load the symbol
+// table for the specified image.
+//
+// Arguments:
+//
+// ImagePathName - specifies the fully qualified path name of the image
+// file that has been loaded into an NT address space.
+//
+// SymbolInfo - information captured from header of image file.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugLoadImageSymbols)
+
+ BREAK_DEBUG_LOAD_SYMBOLS
+ ret zero, (ra)
+
+ .end DebugLoadImageSymbols
+
+#endif
+
+//++
+//
+// VOID
+// DebugUnLoadImageSymbols(
+// IN PSTRING ImagePathName,
+// IN PKD_SYMBOLS_INFO SymbolInfo
+// )
+//
+// Routine Description:
+//
+// This function calls the kernel debugger to unload the symbol
+// table for the specified image.
+//
+// Arguments:
+//
+// ImagePathName - specifies the fully qualified path name of the image
+// file that has been unloaded from an NT address space.
+//
+// SymbolInfo - information captured from header of image file.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugUnLoadImageSymbols)
+
+ BREAK_DEBUG_UNLOAD_SYMBOLS
+ ret zero, (ra)
+
+ .end DebugUnLoadImageSymbols
+
+#endif
diff --git a/private/ntos/rtl/alpha/exdsptch.c b/private/ntos/rtl/alpha/exdsptch.c
new file mode 100644
index 000000000..298073e6a
--- /dev/null
+++ b/private/ntos/rtl/alpha/exdsptch.c
@@ -0,0 +1,2335 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ exdsptch.c
+
+Abstract:
+
+ This module implements the dispatching of exceptions and the unwinding of
+ procedure call frames.
+
+Author:
+
+ David N. Cutler (davec) 11-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Thomas Van Baak (tvb) 13-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Define local macros.
+//
+// Raise noncontinuable exception with associated exception record.
+//
+
+#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \
+ EXCEPTION_RECORD ExceptionRecordn; \
+ \
+ ExceptionRecordn.ExceptionCode = Status; \
+ ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \
+ ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \
+ ExceptionRecordn.NumberParameters = 0; \
+ RtlRaiseException(&ExceptionRecordn); \
+ }
+
+//
+// The low 2 bits of ExceptionHandler are flags bits and not part of the
+// exception handler address.
+//
+
+#define IS_HANDLER_DEFINED(FunctionEntry) \
+ (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0)
+
+
+#if DBG
+
+//
+// Maintain a short history of PC's for malformed function table errors.
+//
+
+#define PC_HISTORY_DEPTH 4
+
+//
+// Definition of global flag to debug/validate exception handling.
+// See ntrtlalp.h for the bit definitions in this flag word.
+//
+
+ULONG RtlDebugFlags = 0;
+
+#endif
+
+#define Virtual VirtualFramePointer
+#define Real RealFramePointer
+
+//
+// Define private function prototypes.
+//
+
+VOID
+RtlpRaiseException (
+ IN PEXCEPTION_RECORD ExceptionRecord
+ );
+
+VOID
+RtlpRaiseStatus (
+ IN NTSTATUS Status
+ );
+
+ULONG
+RtlpVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame
+ );
+
+BOOLEAN
+RtlDispatchException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to dispatch an exception to a frame based
+ handler by searching backwards through the stack based call frames.
+ The search begins with the frame specified in the context record and
+ continues backward until either a handler is found that handles the
+ exception, the stack is found to be invalid (i.e., out of limits or
+ unaligned), or the end of the call hierarchy is reached.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called. If the
+ handler does not handle the exception, then the prologue of the routine
+ is executed backwards to "unwind" the effect of the prologue and then
+ the next frame is examined.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+Return Value:
+
+ If the exception is handled by one of the frame based handlers, then
+ a value of TRUE is returned. Otherwise a value of FALSE is returned.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LowLimit;
+ ULONG NestedFrame;
+ ULONG NextPc;
+
+ //
+ // Get current stack limits, copy the context record, get the initial
+ // PC value, capture the exception flags, and set the nested exception
+ // frame pointer.
+ //
+ // The initial PC value is obtained from ExceptionAddress rather than
+ // from ContextRecord.Fir since some Alpha exceptions are asynchronous.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT));
+ ControlPc = (ULONG)ExceptionRecord->ExceptionAddress;
+#if DBG
+ if ((ULONG)ExceptionRecord->ExceptionAddress != (ULONG)ContextRecord->Fir) {
+ DbgPrint("RtlDispatchException: ExceptionAddress = %lx, Fir = %lx\n",
+ (ULONG)ExceptionRecord->ExceptionAddress, (ULONG)ContextRecord->Fir);
+ }
+#endif
+ ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE;
+ NestedFrame = 0;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
+ DbgPrint("\nRtlDispatchException(ExceptionRecord = %lx, ContextRecord = %lx)\n",
+ ExceptionRecord, ContextRecord);
+ DbgPrint("RtlDispatchException: ControlPc = %lx, ExceptionRecord->ExceptionCode = %lx\n",
+ ControlPc, ExceptionRecord->ExceptionCode);
+ }
+#endif
+
+ //
+ // Start with the frame specified by the context record and search
+ // backwards through the call frame hierarchy attempting to find an
+ // exception handler that will handle the exception.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, ContextRecord1.IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then virtually
+ // unwind to the caller of the current routine to obtain the virtual
+ // frame pointer of the establisher and check if there is an exception
+ // handler for the frame.
+ //
+
+ if (FunctionEntry != NULL) {
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // If the virtual frame pointer is not within the specified stack
+ // limits or the virtual frame pointer is unaligned, then set the
+ // stack invalid flag in the exception record and return exception
+ // not handled. Otherwise, check if the current routine has an
+ // exception handler.
+ //
+
+ if ((EstablisherFrame.Virtual < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - stack invalid (exception).\n");
+ DbgPrint(" EstablisherFrame.Virtual = %08lx, EstablisherFrame.Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n",
+ NextPc, ControlPc);
+ DbgPrint(" Now setting EXCEPTION_STACK_INVALID flag.\n");
+#endif
+ ExceptionFlags |= EXCEPTION_STACK_INVALID;
+ break;
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+
+ ULONG Index;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+ }
+#endif
+
+ //
+ // The frame has an exception handler. The handler must be
+ // executed by calling another routine that is written in
+ // assembler. This is required because up level addressing
+ // of the handler information is required when a nested
+ // exception is encountered.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = ContextRecord;
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ Index = RtlpLogExceptionHandler(
+ ExceptionRecord,
+ ContextRecord,
+ ControlPc,
+ FunctionEntry,
+ sizeof(RUNTIME_FUNCTION));
+ }
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: calling RtlpExecuteHandlerForException, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForException(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ ContextRecord,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: RtlpExecuteHandlerForException returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ RtlpLogLastExceptionDisposition(Index, Disposition);
+ }
+
+ ExceptionFlags |=
+ (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE);
+
+ //
+ // If the current scan is within a nested context and the frame
+ // just examined is the end of the nested region, then clear
+ // the nested context frame and the nested exception flag in
+ // the exception flags.
+ //
+
+ if (NestedFrame == EstablisherFrame.Virtual) {
+ ExceptionFlags &= (~EXCEPTION_NESTED_CALL);
+ NestedFrame = 0;
+ }
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue execution.
+ //
+ // If the exception is not continuable, then raise the
+ // exception STATUS_NONCONTINUABLE_EXCEPTION. Otherwise
+ // return exception handled.
+ //
+
+ case ExceptionContinueExecution :
+ if ((ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0) {
+ RAISE_EXCEPTION(STATUS_NONCONTINUABLE_EXCEPTION, ExceptionRecord);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
+ DbgPrint("RtlDispatchException: returning TRUE\n");
+ }
+#endif
+ return TRUE;
+ }
+
+ //
+ // The disposition is to continue the search.
+ //
+ // Get next frame address and continue the search.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is nested exception.
+ //
+ // Set the nested context frame to the establisher frame
+ // address and set the nested exception flag in the
+ // exception flags.
+ //
+
+ case ExceptionNestedException :
+ ExceptionFlags |= EXCEPTION_NESTED_CALL;
+ if (DispatcherContext.EstablisherFrame > NestedFrame) {
+ NestedFrame = DispatcherContext.EstablisherFrame;
+ }
+
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ NextPc = (ULONG)ContextRecord1.IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (NextPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (exception).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+#endif
+ break;
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = NextPc;
+
+ } while ((ULONG)ContextRecord1.IntSp < HighLimit);
+
+ //
+ // Set final exception flags and return exception not handled.
+ //
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
+ DbgPrint("RtlDispatchException: returning FALSE\n");
+ }
+#endif
+ return FALSE;
+}
+
+PRUNTIME_FUNCTION
+RtlLookupFunctionEntry (
+ IN ULONG ControlPc
+ )
+
+/*++
+
+Routine Description:
+
+ This function searches the currently active function tables for an entry
+ that corresponds to the specified PC value.
+
+Arguments:
+
+ ControlPc - Supplies the address of an instruction within the specified
+ function.
+
+Return Value:
+
+ If there is no entry in the function table for the specified PC, then
+ NULL is returned. Otherwise, the address of the function table entry
+ that corresponds to the specified PC is returned.
+
+--*/
+
+{
+
+ PRUNTIME_FUNCTION FunctionEntry;
+ PRUNTIME_FUNCTION FunctionTable;
+ ULONG SizeOfExceptionTable;
+ LONG High;
+ PVOID ImageBase;
+ LONG Low;
+ LONG Middle;
+
+ //
+ // Search for the image that includes the specified PC value.
+ //
+
+ ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) {
+ DbgPrint("RtlLookupFunctionEntry(ControlPc = %lx) ImageBase = %lx\n",
+ ControlPc, ImageBase);
+ }
+#endif
+
+ //
+ // If an image is found that includes the specified PC, then locate the
+ // function table for the image.
+ //
+
+ if (ImageBase != NULL) {
+ FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
+ ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
+ &SizeOfExceptionTable);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint("RtlLookupFunctionEntry: FunctionTable = %lx, SizeOfExceptionTable = %lx\n",
+ FunctionTable, SizeOfExceptionTable);
+ }
+#endif
+
+ //
+ // If a function table is located, then search the function table
+ // for a function table entry for the specified PC.
+ //
+
+ if (FunctionTable != NULL) {
+
+ //
+ // Initialize search indicies.
+ //
+
+ Low = 0;
+ High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
+
+ //
+ // Perform binary search on the function table for a function table
+ // entry that subsumes the specified PC.
+ //
+
+ while (High >= Low) {
+
+ //
+ // Compute next probe index and test entry. If the specified PC
+ // is greater than of equal to the beginning address and less
+ // than the ending address of the function table entry, then
+ // return the address of the function table entry. Otherwise,
+ // continue the search.
+ //
+
+ Middle = (Low + High) >> 1;
+ FunctionEntry = &FunctionTable[Middle];
+ if (ControlPc < FunctionEntry->BeginAddress) {
+ High = Middle - 1;
+
+ } else if (ControlPc >= FunctionEntry->EndAddress) {
+ Low = Middle + 1;
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress);
+ DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress);
+ DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress);
+ DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler);
+ DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData);
+ }
+#endif
+ //
+ // The capability exists for more than one function entry
+ // to map to the same function. This permits a function to
+ // have (within reason) discontiguous code segment(s). If
+ // PrologEndAddress is out of range, it is re-interpreted
+ // as a pointer to the primary function table entry for
+ // that function.
+ //
+
+ if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
+ (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) {
+ FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint("RtlLookupFunctionEntry: ** indirect function entry **\n");
+ DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress);
+ DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress);
+ DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress);
+ DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler);
+ DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData);
+ }
+#endif
+ }
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = %lx\n",
+ FunctionEntry);
+ }
+#endif
+ return FunctionEntry;
+ }
+ }
+ }
+ }
+
+ //
+ // A function table entry for the specified PC was not found.
+ //
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) {
+ DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = NULL\n");
+ }
+#endif
+ return NULL;
+}
+
+VOID
+RtlRaiseException (
+ IN PEXCEPTION_RECORD ExceptionRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises a software exception by building a context record
+ and calling the raise exception system service.
+
+ N.B. This routine is a shell routine that simply calls another routine
+ to do the real work. The reason this is done is to avoid a problem
+ in try/finally scopes where the last statement in the scope is a
+ call to raise an exception.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) {
+ DbgPrint("RtlRaiseException(ExceptionRecord = %lx) Status = %lx\n",
+ ExceptionRecord, ExceptionRecord->ExceptionCode);
+ }
+#endif
+ RtlpRaiseException(ExceptionRecord);
+ return;
+}
+
+VOID
+RtlpRaiseException (
+ IN PEXCEPTION_RECORD ExceptionRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises a software exception by building a context record
+ and calling the raise exception system service.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ CONTEXT ContextRecord;
+ FRAME_POINTERS EstablisherFrame;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG NextPc;
+ NTSTATUS Status;
+
+ //
+ // Capture the current context, virtually unwind to the caller of this
+ // routine, set the fault instruction address to that of the caller, and
+ // call the raise exception system service.
+ //
+
+ RtlCaptureContext(&ContextRecord);
+ ControlPc = (ULONG)ContextRecord.IntRa - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4;
+ ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Fir;
+ Status = ZwRaiseException(ExceptionRecord, &ContextRecord, TRUE);
+
+ //
+ // There should never be a return from this system service unless
+ // there is a problem with the argument list itself. Raise another
+ // exception specifying the status value returned.
+ //
+
+ RtlRaiseStatus(Status);
+ return;
+}
+
+VOID
+RtlRaiseStatus (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises an exception with the specified status value. The
+ exception is marked as noncontinuable with no parameters.
+
+ N.B. This routine is a shell routine that simply calls another routine
+ to do the real work. The reason this is done is to avoid a problem
+ in try/finally scopes where the last statement in the scope is a
+ call to raise an exception.
+
+Arguments:
+
+ Status - Supplies the status value to be used as the exception code
+ for the exception that is to be raised.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) {
+ DbgPrint("RtlRaiseStatus(Status = %lx)\n", Status);
+ }
+#endif
+ RtlpRaiseStatus(Status);
+ return;
+}
+
+VOID
+RtlpRaiseStatus (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises an exception with the specified status value. The
+ exception is marked as noncontinuable with no parameters.
+
+Arguments:
+
+ Status - Supplies the status value to be used as the exception code
+ for the exception that is to be raised.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ CONTEXT ContextRecord;
+ FRAME_POINTERS EstablisherFrame;
+ EXCEPTION_RECORD ExceptionRecord;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG NextPc;
+
+ //
+ // Construct an exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = Status;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+
+ //
+ // Capture the current context, virtually unwind to the caller of this
+ // routine, set the fault instruction address to that of the caller, and
+ // call the raise exception system service.
+ //
+
+ RtlCaptureContext(&ContextRecord);
+ ControlPc = (ULONG)ContextRecord.IntRa - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4;
+ ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Fir;
+ Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, TRUE);
+
+ //
+ // There should never be a return from this system service unless
+ // there is a problem with the argument list itself. Raise another
+ // exception specifying the status value returned.
+ //
+
+ RtlRaiseStatus(Status);
+ return;
+}
+
+VOID
+RtlUnwind (
+ IN PVOID TargetFrame OPTIONAL,
+ IN PVOID TargetIp OPTIONAL,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. If the TargetFrame parameter is not specified, then the exit unwind
+ flag is also set in the exception flags of the exception record. A backward
+ scan through the procedure call frames is then performed to find the target
+ of the unwind operation.
+
+ As each frame is encounter, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ N.B. This routine is provided for backward compatibility with release 1.
+
+Arguments:
+
+ TargetFrame - Supplies an optional pointer to the call frame that is the
+ target of the unwind. If this parameter is not specified, then an exit
+ unwind is performed.
+
+ TargetIp - Supplies an optional instruction address that specifies the
+ continuation address of the unwind. This address is ignored if the
+ target frame parameter is not specified.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CONTEXT ContextRecord;
+
+
+ //
+ // Call real unwind routine specifying a context record as an
+ // extra argument.
+ //
+
+ RtlUnwind2(TargetFrame,
+ TargetIp,
+ ExceptionRecord,
+ ReturnValue,
+ &ContextRecord);
+
+ return;
+}
+
+VOID
+RtlUnwind2 (
+ IN PVOID TargetFrame OPTIONAL,
+ IN PVOID TargetIp OPTIONAL,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue,
+ IN PCONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. If the TargetFrame parameter is not specified, then the exit unwind
+ flag is also set in the exception flags of the exception record. A backward
+ scan through the procedure call frames is then performed to find the target
+ of the unwind operation.
+
+ As each frame is encounter, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ N.B. This routine is provided for backward compatibility with release 1.
+
+Arguments:
+
+ TargetFrame - Supplies an optional pointer to the call frame that is the
+ target of the unwind. If this parameter is not specified, then an exit
+ unwind is performed.
+
+ TargetIp - Supplies an optional instruction address that specifies the
+ continuation address of the unwind. This address is ignored if the
+ target frame parameter is not specified.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LowLimit;
+ ULONG NextPc;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("\nRtlUnwind(TargetFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n",
+ TargetFrame, TargetIp, ReturnValue);
+ }
+#endif
+ //
+ // Get current stack limits, capture the current context, virtually
+ // unwind to the caller of this routine, get the initial PC value, and
+ // set the unwind target address.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlCaptureContext(ContextRecord);
+ ControlPc = (ULONG)ContextRecord->IntRa - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = NextPc;
+ ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp;
+
+ //
+ // If an exception record is not specified, then build a local exception
+ // record for use in calling exception handlers during the unwind operation.
+ //
+
+ if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
+ ExceptionRecord = &ExceptionRecord1;
+ ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // If the target frame of the unwind is specified, then a normal unwind
+ // is being performed. Otherwise, an exit unwind is being performed.
+ //
+
+ ExceptionFlags = EXCEPTION_UNWINDING;
+ if (ARGUMENT_PRESENT(TargetFrame) == FALSE) {
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
+ }
+
+ //
+ // Scan backward through the call frame hierarchy and call exception
+ // handlers until the target frame of the unwind is reached.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwind: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, ContextRecord->IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then virtually
+ // unwind to the caller of the routine to obtain the virtual frame
+ // pointer of the establisher, but don't update the context record.
+ //
+
+ if (FunctionEntry != NULL) {
+ NextPc = RtlpVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ (PULONG)&EstablisherFrame);
+
+ //
+ // If the virtual frame pointer is not within the specified stack
+ // limits, the virtual frame pointer is unaligned, or the target
+ // frame is below the virtual frame and an exit unwind is not being
+ // performed, then raise the exception STATUS_BAD_STACK. Otherwise,
+ // check to determine if the current routine has an exception
+ // handler.
+ //
+
+ if ((EstablisherFrame.Virtual < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual)) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
+ DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" TargetFrame = %08lx\n", TargetFrame);
+ if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual)) {
+ DbgPrint(" TargetFrame is below EstablisherFrame!\n");
+ }
+ DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
+ (ULONG)ContextRecord->IntSp);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n",
+ NextPc, ControlPc);
+ DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
+#endif
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlUnwind: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+}
+#endif
+
+ //
+ // The frame has an exception handler.
+ //
+ // The control PC, establisher frame pointer, the address
+ // of the function table entry, and the address of the
+ // context record are all stored in the dispatcher context.
+ // This information is used by the unwind linkage routine
+ // and can be used by the exception handler itself.
+ //
+ // A linkage routine written in assembler is used to actually
+ // call the actual exception handler. This is required by the
+ // exception handler that is associated with the linkage
+ // routine so it can have access to two sets of dispatcher
+ // context when it is called.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = ContextRecord;
+
+ //
+ // Call the exception handler.
+ //
+
+ do {
+
+ //
+ // If the establisher frame is the target of the unwind
+ // operation, then set the target unwind flag.
+ //
+
+ if ((ULONG)TargetFrame == EstablisherFrame.Virtual) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwind: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ ContextRecord,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwind: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ //
+ // Clear target unwind and collided unwind flags.
+ //
+
+ ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
+ EXCEPTION_TARGET_UNWIND);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search.
+ //
+ // If the target frame has not been reached, then
+ // virtually unwind to the caller of the current
+ // routine, update the context record, and continue
+ // the search for a handler.
+ //
+
+ case ExceptionContinueSearch :
+ if (EstablisherFrame.Virtual != (ULONG)TargetFrame) {
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ }
+
+ break;
+
+ //
+ // The disposition is collided unwind.
+ //
+ // Set the target of the current unwind to the context
+ // record of the previous unwind, and reexecute the
+ // exception handler from the collided frame with the
+ // collided unwind flag set in the exception record.
+ //
+
+ case ExceptionCollidedUnwind :
+ ControlPc = DispatcherContext.ControlPc;
+ FunctionEntry = DispatcherContext.FunctionEntry;
+ ContextRecord = DispatcherContext.ContextRecord;
+ ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp;
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ EstablisherFrame.Virtual = DispatcherContext.EstablisherFrame;
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+
+ } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
+ } else {
+
+ //
+ // Virtually unwind to the caller of the current routine and
+ // update the context record.
+ //
+
+ if (EstablisherFrame.Virtual != (ULONG)TargetFrame) {
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ }
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (NextPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (unwind).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+ DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
+#endif
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = NextPc;
+
+ } while ((EstablisherFrame.Virtual < HighLimit) &&
+ (EstablisherFrame.Virtual != (ULONG)TargetFrame));
+
+ //
+ // If the establisher stack pointer is equal to the target frame
+ // pointer, then continue execution. Otherwise, an exit unwind was
+ // performed or the target of the unwind did not exist and the
+ // debugger and subsystem are given a second chance to handle the
+ // unwind.
+ //
+
+ if (EstablisherFrame.Virtual == (ULONG)TargetFrame) {
+ ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwind: finished unwinding, and calling RtlpRestoreContext(%lx)\n",ContextRecord);
+ }
+#endif
+ RtlpRestoreContext(ContextRecord);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwind: finished unwinding, but calling ZwRaiseException\n");
+ }
+#endif
+ ZwRaiseException(ExceptionRecord, ContextRecord, FALSE);
+ }
+
+}
+
+#if DBG
+//
+// Define an array of symbolic names for the integer registers.
+//
+
+PCHAR RtlpIntegerRegisterNames[32] = {
+ "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", // 0 - 7
+ "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", // 8 - 15
+ "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", // 16 - 23
+ "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", // 24 - 31
+};
+
+//
+// This function disassembles the instruction at the given address. It is
+// only used for debugging and recognizes only those few instructions that
+// are relevant during reverse execution of the prologue by virtual unwind.
+//
+
+VOID
+_RtlpDebugDisassemble (
+ IN ULONG ControlPc,
+ IN PCONTEXT ContextRecord
+ )
+{
+ UCHAR Comments[50];
+ PULONGLONG FloatingRegister;
+ ULONG Function;
+ ULONG Hint;
+ ULONG Literal8;
+ ALPHA_INSTRUCTION Instruction;
+ PULONGLONG IntegerRegister;
+ LONG Offset16;
+ UCHAR Operands[20];
+ ULONG Opcode;
+ PCHAR OpName;
+ ULONG Ra;
+ ULONG Rb;
+ ULONG Rc;
+ PCHAR RaName;
+ PCHAR RbName;
+ PCHAR RcName;
+
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND_DETAIL) {
+ Instruction.Long = *((PULONG)ControlPc);
+ Hint = Instruction.Jump.Hint;
+ Literal8 = Instruction.OpLit.Literal;
+ Offset16 = Instruction.Memory.MemDisp;
+ Opcode = Instruction.Memory.Opcode;
+ Ra = Instruction.OpReg.Ra;
+ RaName = RtlpIntegerRegisterNames[Ra];
+ Rb = Instruction.OpReg.Rb;
+ RbName = RtlpIntegerRegisterNames[Rb];
+ Rc = Instruction.OpReg.Rc;
+ RcName = RtlpIntegerRegisterNames[Rc];
+
+ IntegerRegister = &ContextRecord->IntV0;
+ FloatingRegister = &ContextRecord->FltF0;
+
+ OpName = NULL;
+ switch (Opcode) {
+ case JMP_OP :
+ if (Instruction.Jump.Function == RET_FUNC) {
+ OpName = "ret";
+ sprintf(Operands, "%s, (%s), %04lx", RaName, RbName, Hint);
+ sprintf(Comments, "%s = %Lx", RbName, IntegerRegister[Rb]);
+ }
+ break;
+
+ case LDAH_OP :
+ case LDA_OP :
+ case STQ_OP :
+ if (Opcode == LDA_OP) {
+ OpName = "lda";
+
+ } else if (Opcode == LDAH_OP) {
+ OpName = "ldah";
+
+ } else if (Opcode == STQ_OP) {
+ OpName = "stq";
+ }
+ sprintf(Operands, "%s, $%d(%s)", RaName, Offset16, RbName);
+ sprintf(Comments, "%s = %Lx", RaName, IntegerRegister[Ra]);
+ break;
+
+ case ARITH_OP :
+ case BIT_OP :
+ Function = Instruction.OpReg.Function;
+ if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC)) {
+ OpName = "addq";
+
+ } else if ((Opcode == ARITH_OP) && (Function == SUBQ_FUNC)) {
+ OpName = "subq";
+
+ } else if ((Opcode == BIT_OP) && (Function == BIS_FUNC)) {
+ OpName = "bis";
+
+ } else {
+ break;
+ }
+ if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
+ sprintf(Operands, "%s, %s, %s", RaName, RbName, RcName);
+
+ } else {
+ sprintf(Operands, "%s, $%d, %s", RaName, Literal8, RcName);
+ }
+ sprintf(Comments, "%s = %Lx", RcName, IntegerRegister[Rc]);
+ break;
+
+ case FPOP_OP :
+ if (Instruction.FpOp.Function == CPYS_FUNC) {
+ OpName = "cpys";
+ sprintf(Operands, "f%d, f%d, f%d", Ra, Rb, Rc);
+ sprintf(Comments, "f%d = %Lx", Rc, FloatingRegister[Rc]);
+ }
+ break;
+
+ case STT_OP :
+ OpName = "stt";
+ sprintf(Operands, "f%d, $%d(%s)", Ra, Offset16, RbName);
+ sprintf(Comments, "f%d = %Lx", Ra, FloatingRegister[Ra]);
+ break;
+ }
+ if (OpName == NULL) {
+ OpName = "???";
+ sprintf(Operands, "...");
+ sprintf(Comments, "Unknown to virtual unwind.");
+ }
+ DbgPrint(" %08lx: %08lx %-5s %-16s // %s\n",
+ ControlPc, Instruction.Long, OpName, Operands, Comments);
+ }
+ return;
+}
+
+#define _RtlpFoundTrapFrame(NextPc) \
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
+ DbgPrint(" *** Looks like a trap frame (fake prologue), Fir = %lx\n", \
+ NextPc); \
+ }
+
+#define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame) \
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
+ DbgPrint("RtlVirtualUnwind: EstablisherFrame Virtual = %08lx, Real = %08lx\n", \
+ (EstablisherFrame)->Virtual, (EstablisherFrame)->Real); \
+ DbgPrint("RtlVirtualUnwind: returning NextPc = %lx, sp = %lx\n\n", \
+ NextPc, ContextRecord->IntSp); \
+ }
+
+#else
+
+#define _RtlpDebugDisassemble(ControlPc, ContextRecord)
+#define _RtlpFoundTrapFrame(NextPc)
+#define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame)
+
+#endif
+
+ULONG
+RtlVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PFRAME_POINTERS EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specified function by executing its
+ prologue code backwards. Given the current context and the instructions
+ that preserve registers in the prologue, it is possible to recreate the
+ nonvolatile context at the point the function was called.
+
+ If the function is a leaf function, then the address where control left
+ the previous frame is obtained from the context record. If the function
+ is a nested function, but not an exception or interrupt frame, then the
+ prologue code is executed backwards and the address where control left
+ the previous frame is obtained from the updated context record.
+
+ Otherwise, an exception or interrupt entry to the system is being unwound
+ and a specially coded prologue restores the return address twice. Once
+ from the fault instruction address and once from the saved return address
+ register. The first restore is returned as the function value and the
+ second restore is placed in the updated context record.
+
+ During the unwind, the virtual and real frame pointers for the function
+ are calculated and returned in the given frame pointers structure.
+
+ If a context pointers record is specified, then the address where each
+ register is restored from is recorded in the appropriate element of the
+ context pointers record.
+
+Arguments:
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function.
+
+ ContextRecord - Supplies the address of a context record.
+
+ InFunction - Supplies a pointer to a variable that receives whether the
+ control PC is within the current function.
+
+ EstablisherFrame - Supplies a pointer to a frame pointers structure
+ that will receive the values for the virtual frame pointer and the
+ real frame pointer. The value of the real frame pointer is reliable
+ only when InFunction is TRUE.
+
+ ContextPointers - Supplies an optional pointer to a context pointers
+ record.
+
+Return Value:
+
+ The address where control left the previous frame is returned as the
+ function value.
+
+Implementation Notes:
+
+ N.B. "where control left" is not the "return address" of the call in the
+ previous frame. For normal frames, NextPc points to the last instruction
+ that completed in the previous frame (the JSR/BSR). The difference between
+ NextPc and NextPc + 4 (return address) is important for correct behavior
+ in boundary cases of exception addresses and scope tables.
+
+ For exception and interrupt frames, NextPc is obtained from the trap frame
+ contination address (Fir). For faults and synchronous traps, NextPc is both
+ the last instruction to execute in the previous frame and the next
+ instruction to execute if the function were to return. For asynchronous
+ traps, NextPc is the continuation address. It is the responsibility of the
+ compiler to insert TRAPB instructions to insure asynchronous traps do not
+ occur outside the scope from the instruction(s) that caused them.
+
+ N.B. in this and other files where RtlVirtualUnwind is used, the variable
+ named NextPc is perhaps more accurately, LastPc - the last PC value in
+ the previous frame, or CallPc - the address of the call instruction, or
+ ControlPc - the address where control left the previous frame. Instead
+ think of NextPc as the next PC to use in another call to virtual unwind.
+
+ The Alpha version of virtual unwind is similar in design, but slightly
+ more complex than the Mips version. This is because Alpha compilers
+ are given more flexibility to optimize generated code and instruction
+ sequences, including within procedure prologues. In addition, because of
+ the current inability of the GEM compiler to materialize virtual frame
+ pointers, this function must manage both virtual and real frame pointers.
+
+--*/
+
+{
+
+ ULONG Address;
+ ULONG DecrementOffset;
+ ULONG DecrementRegister;
+ ALPHA_INSTRUCTION FollowingInstruction;
+ PULONGLONG FloatingRegister;
+ ULONG FrameSize;
+ ULONG Function;
+ ALPHA_INSTRUCTION Instruction;
+ PULONGLONG IntegerRegister;
+ ULONG Literal8;
+ ULONG NextPc;
+ LONG Offset16;
+ ULONG Opcode;
+ ULONG Ra;
+ ULONG Rb;
+ ULONG Rc;
+ BOOLEAN RestoredRa;
+ BOOLEAN RestoredSp;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) {
+ DbgPrint("\nRtlVirtualUnwind(ControlPc = %lx, FunctionEntry = %lx,) sp = %lx\n",
+ ControlPc, FunctionEntry, ContextRecord->IntSp);
+ }
+#endif
+#if DBG
+ if ((FunctionEntry == NULL) || (ControlPc & 0x3) ||
+ (FunctionEntry->BeginAddress >= FunctionEntry->EndAddress) ||
+#if 0
+ (ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= FunctionEntry->EndAddress) ||
+#endif
+ (FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
+ (FunctionEntry->PrologEndAddress >= FunctionEntry->EndAddress)) {
+ DbgPrint("\n****** Warning - invalid PC or function table entry (virtual unwind).\n");
+ return ControlPc;
+ }
+#endif
+ //
+ // Set the base address of the integer and floating register arrays within
+ // the context record. Each set of 32 registers is known to be contiguous.
+ //
+
+ IntegerRegister = &ContextRecord->IntV0;
+ FloatingRegister = &ContextRecord->FltF0;
+
+ //
+ // Handle the epilogue case where the next instruction is a return.
+ //
+ // Exception handlers cannot be called if the ControlPc is within the
+ // epilogue because exception handlers expect to operate with a current
+ // stack frame. The value of SP is not current within the epilogue.
+ //
+
+ Instruction.Long = *((PULONG)ControlPc);
+ if (IS_RETURN_0001_INSTRUCTION(Instruction.Long)) {
+ Rb = Instruction.Jump.Rb;
+ NextPc = (ULONG)IntegerRegister[Rb] - 4;
+
+ //
+ // The instruction at the point where control left the specified
+ // function is a return, so any saved registers have already been
+ // restored, and the stack pointer has already been adjusted. The
+ // stack does not need to be unwound in this case and the saved
+ // return address register is returned as the function value.
+ //
+ // In fact, reverse execution of the prologue is not possible in
+ // this case: the stack pointer has already been incremented and
+ // so, for this frame, neither a valid stack pointer nor frame
+ // pointer exists from which to begin reverse execution of the
+ // prologue. In addition, the integrity of any data on the stack
+ // below the stack pointer is never guaranteed (due to interrupts
+ // and exceptions).
+ //
+ // The epilogue instruction sequence is:
+ //
+ // ==> ret zero, (Ra), 1 // return
+ // or
+ //
+ // mov ra, Rx // save return address
+ // ...
+ // ==> ret zero, (Rx), 1 // return
+ //
+
+ EstablisherFrame->Real = 0;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ *InFunction = FALSE;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+ }
+
+ //
+ // Handle the epilogue case where the next two instructions are a stack
+ // frame deallocation and a return.
+ //
+
+ FollowingInstruction.Long = *((PULONG)(ControlPc + 4));
+ if (IS_RETURN_0001_INSTRUCTION(FollowingInstruction.Long)) {
+ Rb = FollowingInstruction.Jump.Rb;
+ NextPc = (ULONG)IntegerRegister[Rb] - 4;
+
+ //
+ // The second instruction following the point where control
+ // left the specified function is a return. If the instruction
+ // before the return is a stack increment instruction, then all
+ // saved registers have already been restored except for SP.
+ // The value of the stack pointer register cannot be recovered
+ // through reverse execution of the prologue because in order
+ // to begin reverse execution either the stack pointer or the
+ // frame pointer (if any) must still be valid.
+ //
+ // Instead, the effect that the stack increment instruction
+ // would have had on the context is manually applied to the
+ // current context. This is forward execution of the epilogue
+ // rather than reverse execution of the prologue.
+ //
+ // In an epilogue, as in a prologue, the stack pointer is always
+ // adjusted with a single instruction: either an immediate-value
+ // (lda) or a register-value (addq) add instruction.
+ //
+
+ Function = Instruction.OpReg.Function;
+ Offset16 = Instruction.Memory.MemDisp;
+ Opcode = Instruction.OpReg.Opcode;
+ Ra = Instruction.OpReg.Ra;
+ Rb = Instruction.OpReg.Rb;
+ Rc = Instruction.OpReg.Rc;
+
+ if ((Opcode == LDA_OP) && (Ra == SP_REG)) {
+
+ //
+ // Load Address instruction.
+ //
+ // Since the destination (Ra) register is SP, an immediate-
+ // value stack deallocation operation is being performed. The
+ // displacement value should be added to SP. The displacement
+ // value is assumed to be positive. The amount of stack
+ // deallocation possible using this instruction ranges from
+ // 16 to 32752 (32768 - 16) bytes. The base register (Rb) is
+ // usually SP, but may be another register.
+ //
+ // The epilogue instruction sequence is:
+ //
+ // ==> lda sp, +N(sp) // deallocate stack frame
+ // ret zero, (ra) // return
+ // or
+ //
+ // ==> lda sp, +N(Rx) // restore SP and deallocate frame
+ // ret zero, (ra) // return
+ //
+
+ ContextRecord->IntSp = Offset16 + IntegerRegister[Rb];
+ EstablisherFrame->Real = 0;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ *InFunction = FALSE;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ _RtlpDebugDisassemble(ControlPc + 4, ContextRecord);
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+
+ } else if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC) &&
+ (Rc == SP_REG) &&
+ (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
+
+ //
+ // Add Quadword instruction.
+ //
+ // Since both source operands are registers, and the
+ // destination register is SP, a register-value stack
+ // deallocation is being performed. The value of the two
+ // source registers should be added and this is the new
+ // value of SP. One of the source registers is usually SP,
+ // but may be another register.
+ //
+ // The epilogue instruction sequence is:
+ //
+ // ldiq Rx, N // set [large] frame size
+ // ...
+ // ==> addq sp, Rx, sp // deallocate stack frame
+ // ret zero, (ra) // return
+ // or
+ //
+ // ==> addq Rx, Ry, sp // restore SP and deallocate frame
+ // ret zero, (ra) // return
+ //
+
+ ContextRecord->IntSp = IntegerRegister[Ra] + IntegerRegister[Rb];
+ EstablisherFrame->Real = 0;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ *InFunction = FALSE;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ _RtlpDebugDisassemble(ControlPc + 4, ContextRecord);
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+ }
+ }
+
+ //
+ // By default set the frame pointers to the current value of SP.
+ //
+ // When a procedure is called, the value of SP before the stack
+ // allocation instruction is the virtual frame pointer. When reverse
+ // executing instructions in the prologue, the value of SP before the
+ // stack allocation instruction is encountered is the real frame
+ // pointer. This is the current value of SP unless the procedure uses
+ // a frame pointer (e.g., FP_REG).
+ //
+
+ EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+
+ //
+ // If the address where control left the specified function is beyond
+ // the end of the prologue, then the control PC is considered to be
+ // within the function and the control address is set to the end of
+ // the prologue. Otherwise, the control PC is not considered to be
+ // within the function (i.e., the prologue).
+ //
+ // N.B. PrologEndAddress is equal to BeginAddress for a leaf function.
+ //
+ // The low-order two bits of PrologEndAddress are reserved for the IEEE
+ // exception mode and so must be masked out.
+ //
+
+ if ((ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= FunctionEntry->PrologEndAddress)) {
+ *InFunction = TRUE;
+ ControlPc = (FunctionEntry->PrologEndAddress & (~0x3));
+
+ } else {
+ *InFunction = FALSE;
+ }
+
+ //
+ // Scan backward through the prologue to reload callee saved registers
+ // that were stored or copied and to increment the stack pointer if it
+ // was decremented.
+ //
+
+ DecrementRegister = ZERO_REG;
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+ RestoredRa = FALSE;
+ RestoredSp = FALSE;
+ while (ControlPc > FunctionEntry->BeginAddress) {
+
+ //
+ // Get instruction value, decode fields, case on opcode value, and
+ // reverse register store and stack decrement operations.
+ // N.B. The location of Opcode, Ra, Rb, and Rc is the same across
+ // all opcode formats. The same is not true for Function.
+ //
+
+ ControlPc -= 4;
+ Instruction.Long = *((PULONG)ControlPc);
+ Function = Instruction.OpReg.Function;
+ Literal8 = Instruction.OpLit.Literal;
+ Offset16 = Instruction.Memory.MemDisp;
+ Opcode = Instruction.OpReg.Opcode;
+ Ra = Instruction.OpReg.Ra;
+ Rb = Instruction.OpReg.Rb;
+ Rc = Instruction.OpReg.Rc;
+
+ //
+ // Compare against each instruction type that will affect the context
+ // and that is allowed in a prologue. Any other instructions found
+ // in the prologue will be ignored since they are assumed to have no
+ // effect on the context.
+ //
+
+ switch (Opcode) {
+
+ case STQ_OP :
+
+ //
+ // Store Quad instruction.
+ //
+ // If the base register is SP, then reload the source register
+ // value from the value stored on the stack.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> stq Rx, N(sp) // save integer register Rx
+ //
+
+ if ((Rb == SP_REG) && (Ra != ZERO_REG)) {
+
+ //
+ // Reload the register by retrieving the value previously
+ // stored on the stack.
+ //
+
+ Address = Offset16 + ContextRecord->IntSp;
+ IntegerRegister[Ra] = *((PULONGLONG)Address);
+
+ //
+ // If the destination register is RA and this is the first
+ // time that RA is being restored, then set the address of
+ // where control left the previous frame. Otherwise, if this
+ // is the second time RA is being restored, then the first
+ // one was an interrupt or exception address and the return
+ // PC should not have been biased by 4.
+ //
+
+ if (Ra == RA_REG) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ _RtlpFoundTrapFrame(NextPc);
+ }
+
+ //
+ // Otherwise, if the destination register is SP and this is
+ // the first time that SP is being restored, then set the
+ // establisher frame pointers.
+ //
+
+ } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination register contents
+ // are stored.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->IntegerContext[Ra] = (PULONGLONG)Address;
+ }
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ break;
+
+ case LDAH_OP :
+ Offset16 <<= 16;
+
+ case LDA_OP :
+
+ //
+ // Load Address High, Load Address instruction.
+ //
+ // There are several cases where the lda and/or ldah instructions
+ // are used: one to decrement the stack pointer directly, and the
+ // others to load immediate values into another register and that
+ // register is then used to decrement the stack pointer.
+ //
+ // In the examples below, as a single instructions or as a pair,
+ // a lda may be substituted for a ldah and visa-versa.
+ //
+
+ if (Ra == SP_REG) {
+ if (Rb == SP_REG) {
+
+ //
+ // If both the destination (Ra) and base (Rb) registers
+ // are SP, then a standard stack allocation was performed
+ // and the negated displacement value is the stack frame
+ // size. The amount of stack allocation possible using
+ // the lda instruction ranges from 16 to 32768 bytes and
+ // the amount of stack allocation possible using the ldah
+ // instruction ranges from 65536 to 2GB in multiples of
+ // 65536 bytes. It is rare for the ldah instruction to be
+ // used in this manner.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> lda sp, -N(sp) // allocate stack frame
+ //
+
+ FrameSize = -Offset16;
+ goto StackAllocation;
+
+ } else {
+
+ //
+ // The destination register is SP and the base register
+ // is not SP, so this instruction must be the second
+ // half of an instruction pair to allocate a large size
+ // (>32768 bytes) stack frame. Save the displacement value
+ // as the partial decrement value and postpone adjusting
+ // the value of SP until the first instruction of the pair
+ // is encountered.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ldah Rx, -N(sp) // prepare new SP (upper)
+ // ==> lda sp, sN(Rx) // allocate stack frame
+ //
+
+ DecrementRegister = Rb;
+ DecrementOffset = Offset16;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+
+ } else if (Ra == DecrementRegister) {
+ if (Rb == DecrementRegister) {
+
+ //
+ // Both the destination and base registers are the
+ // decrement register, so this instruction exists as the
+ // second half of a two instruction pair to load a
+ // 31-bit immediate value into the decrement register.
+ // Save the displacement value as the partial decrement
+ // value.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ldah Rx, +N(zero) // set frame size (upper)
+ // ==> lda Rx, sN(Rx) // set frame size (+lower)
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ DecrementOffset += Offset16;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+
+ } else if (Rb == ZERO_REG) {
+
+ //
+ // The destination register is the decrement register and
+ // the base register is zero, so this instruction exists
+ // to load an immediate value into the decrement register.
+ // The stack frame size is the new displacement value added
+ // to the previous displacement value, if any.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> lda Rx, +N(zero) // set frame size
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ // or
+ //
+ // ==> ldah Rx, +N(zero) // set frame size (upper)
+ // lda Rx, sN(Rx) // set frame size (+lower)
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ FrameSize = (Offset16 + DecrementOffset);
+ goto StackAllocation;
+
+ } else if (Rb == SP_REG) {
+
+ //
+ // The destination (Ra) register is SP and the base (Rb)
+ // register is the decrement register, so a two
+ // instruction, large size (>32768 bytes) stack frame
+ // allocation was performed. Add the new displacement
+ // value to the previous displacement value. The negated
+ // displacement value is the stack frame size.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> ldah Rx, -N(sp) // prepare new SP (upper)
+ // lda sp, sN(Rx) // allocate stack frame
+ //
+
+ FrameSize = -(Offset16 + (LONG)DecrementOffset);
+ goto StackAllocation;
+ }
+ }
+ break;
+
+ case ARITH_OP :
+
+ if ((Function == ADDQ_FUNC) &&
+ (Instruction.OpReg.RbvType != RBV_REGISTER_FORMAT)) {
+
+ //
+ // Add Quadword (immediate) instruction.
+ //
+ // If the first source register is zero, and the second
+ // operand is a literal, and the destination register is
+ // the decrement register, then the instruction exists
+ // to load an unsigned immediate value less than 256 into
+ // the decrement register. The immediate value is the stack
+ // frame size.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> addq zero, N, Rx // set frame size
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
+ FrameSize = Literal8;
+ goto StackAllocation;
+ }
+
+ } else if ((Function == SUBQ_FUNC) &&
+ (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
+
+ //
+ // Subtract Quadword (register) instruction.
+ //
+ // If both source operands are registers and the first
+ // source (minuend) register and the destination
+ // (difference) register are both SP, then a register value
+ // stack allocation was performed and the second source
+ // (subtrahend) register value will be added to SP when its
+ // value is known. Until that time save the register number of
+ // this decrement register.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ldiq Rx, N // set frame size
+ // ...
+ // ==> subq sp, Rx, sp // allocate stack frame
+ //
+
+ if ((Ra == SP_REG) && (Rc == SP_REG)) {
+ DecrementRegister = Rb;
+ DecrementOffset = 0;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ }
+ break;
+
+ case BIT_OP :
+
+ //
+ // If the second operand is a register the bit set instruction
+ // may be a register move instruction, otherwise if the second
+ // operand is a literal, the bit set instruction may be a load
+ // immediate value instruction.
+ //
+
+ if ((Function == BIS_FUNC) && (Rc != ZERO_REG)) {
+ if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
+
+ //
+ // Bit Set (register move) instruction.
+ //
+ // If both source registers are the same register, or
+ // one of the source registers is zero, then this is a
+ // register move operation. Restore the value of the
+ // source register by copying the current destination
+ // register value back to the source register.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> bis Rx, Rx, Ry // copy register Rx
+ // or
+ //
+ // ==> bis Rx, zero, Ry // copy register Rx
+ // or
+ //
+ // ==> bis zero, Rx, Ry // copy register Rx
+ //
+
+ if (Ra == ZERO_REG) {
+
+ //
+ // Map the third case above to the first case.
+ //
+
+ Ra = Rb;
+
+ } else if (Rb == ZERO_REG) {
+
+ //
+ // Map the second case above to the first case.
+ //
+
+ Rb = Ra;
+ }
+
+ if ((Ra == Rb) && (Ra != ZERO_REG)) {
+ IntegerRegister[Ra] = IntegerRegister[Rc];
+
+ //
+ // If the destination register is RA and this is the
+ // first time that RA is being restored, then set the
+ // address of where control left the previous frame.
+ // Otherwise, if this is the second time RA is being
+ // restored, then the first one was an interrupt or
+ // exception address and the return PC should not
+ // have been biased by 4.
+ //
+
+ if (Ra == RA_REG) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ _RtlpFoundTrapFrame(NextPc);
+ }
+
+ //
+ // If the source register is SP and this is the first
+ // time SP is set, then this is a frame pointer set
+ // instruction. Reset the frame pointers to this new
+ // value of SP.
+ //
+
+ } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+
+ } else {
+
+ //
+ // Bit Set (load immediate) instruction.
+ //
+ // If the first source register is zero, and the second
+ // operand is a literal, and the destination register is
+ // the decrement register, then this instruction exists
+ // to load an unsigned immediate value less than 256 into
+ // the decrement register. The decrement register value is
+ // the stack frame size.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> bis zero, N, Rx // set frame size
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
+ FrameSize = Literal8;
+StackAllocation:
+ //
+ // Add the frame size to SP to reverse the stack frame
+ // allocation, leave the real frame pointer as is, set
+ // the virtual frame pointer with the updated SP value,
+ // and clear the decrement register.
+ //
+
+ ContextRecord->IntSp += FrameSize;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ DecrementRegister = ZERO_REG;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ }
+ }
+ break;
+
+ case STT_OP :
+
+ //
+ // Store T-Floating (quadword integer) instruction.
+ //
+ // If the base register is SP, then reload the source register
+ // value from the value stored on the stack.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> stt Fx, N(sp) // save floating register Fx
+ //
+
+ if ((Rb == SP_REG) && (Ra != FZERO_REG)) {
+
+ //
+ // Reload the register by retrieving the value previously
+ // stored on the stack.
+ //
+
+ Address = Offset16 + ContextRecord->IntSp;
+ FloatingRegister[Ra] = *((PULONGLONG)Address);
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination register contents are
+ // stored.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->FloatingContext[Ra] = (PULONGLONG)Address;
+ }
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ break;
+
+ case FPOP_OP :
+
+ //
+ // N.B. The floating operate function field is not the same as
+ // the integer operate nor the jump function fields.
+ //
+
+ if (Instruction.FpOp.Function == CPYS_FUNC) {
+
+ //
+ // Copy Sign (floating-point move) instruction.
+ //
+ // If both source registers are the same register, then this is
+ // a floating-point register move operation. Restore the value
+ // of the source register by copying the current destination
+ // register value to the source register.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> cpys Fx, Fx, Fy // copy floating register Fx
+ //
+
+ if ((Ra == Rb) && (Ra != FZERO_REG)) {
+ FloatingRegister[Ra] = FloatingRegister[Rc];
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ }
+
+ default :
+ break;
+ }
+ }
+
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+}
+
+ULONG
+RtlpVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specfified function by executing its
+ prologue code backwards.
+
+ If the function is a leaf function, then the address where control left
+ the previous frame is obtained from the context record. If the function
+ is a nested function, but not an exception or interrupt frame, then the
+ prologue code is executed backwards and the address where control left
+ the previous frame is obtained from the updated context record.
+
+ Otherwise, an exception or interrupt entry to the system is being unwound
+ and a specially coded prologue restores the return address twice. Once
+ from the fault instruction address and once from the saved return address
+ register. The first restore is returned as the function value and the
+ second restore is place in the updated context record.
+
+ If a context pointers record is specified, then the address where each
+ nonvolatile registers is restored from is recorded in the appropriate
+ element of the context pointers record.
+
+ N.B. This function copies the specified context record and only computes
+ the establisher frame and whether control is actually in a function.
+
+Arguments:
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function.
+
+ ContextRecord - Supplies the address of a context record.
+
+ InFunction - Supplies a pointer to a variable that receives whether the
+ control PC is within the current function.
+
+ EstablisherFrame - Supplies a pointer to a variable that receives the
+ the establisher frame pointer value.
+
+ ContextPointers - Supplies an optional pointer to a context pointers
+ record.
+
+Return Value:
+
+ The address where control left the previous frame is returned as the
+ function value.
+
+--*/
+
+{
+
+ CONTEXT LocalContext;
+
+ //
+ // Copy the context record so updates will not be reflected in the
+ // original copy and then virtually unwind to the caller of the
+ // specified control point.
+ //
+
+ RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT));
+ return RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &LocalContext,
+ InFunction,
+ (PFRAME_POINTERS)EstablisherFrame,
+ NULL);
+}
diff --git a/private/ntos/rtl/alpha/getcalr.c b/private/ntos/rtl/alpha/getcalr.c
new file mode 100644
index 000000000..01bbe89cb
--- /dev/null
+++ b/private/ntos/rtl/alpha/getcalr.c
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ getcalr.c
+
+Abstract:
+
+ This module implements the routine RtlGetCallerAddress. It will
+ return the address of the caller, and the callers caller to the
+ specified procedure.
+
+Author:
+
+ Larry Osterman (larryo) 18-Mar-1991 (with help from DaveC)
+
+Revision History:
+
+ 18-Mar-1991 larryo
+
+ Created
+
+ Thomas Van Baak (tvb) 5-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+#include "ntrtlp.h"
+
+//
+// Undefine get callers address since it is defined as a macro.
+//
+
+#undef RtlGetCallersAddress
+
+VOID
+RtlGetCallersAddress (
+ OUT PVOID *CallersPc,
+ OUT PVOID *CallersCallersPc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the address of the call to the routine that called
+ this routine, and the address of the call to the routine that called
+ the routine that called this routine. For example, if A called B called
+ C which called this routine, the return addresses in B and A would be
+ returned.
+
+Arguments:
+
+ CallersPc - Supplies a pointer to a variable that receives the address
+ of the caller of the caller of this routine (B).
+
+ CallersCallersPc - Supplies a pointer to a variable that receives the
+ address of the caller of the caller of the caller of this routine
+ (A).
+
+Return Value:
+
+ None.
+
+Note:
+
+ If either of the calling stack frames exceeds the limits of the stack,
+ they are set to NULL.
+
+--*/
+
+{
+#ifdef REALLY_GET_CALLERS_CALLER
+ CONTEXT ContextRecord;
+ FRAME_POINTERS EstablisherFrame;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG HighLimit, LowLimit;
+ ULONG NextPc;
+
+ //
+ // Assume the function table entries for the various routines cannot be
+ // found or there are not four procedure activation records on the stack.
+ //
+
+ *CallersPc = NULL;
+ *CallersCallersPc = NULL;
+
+ //
+ // Capture the current context.
+ //
+
+ RtlCaptureContext(&ContextRecord);
+ NextPc = (ULONG)ContextRecord.IntRa;
+
+ //
+ // Get the high and low limits of the current thread's stack.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+
+ //
+ // Attempt to unwind to the caller of this routine (C). The FunctionEntry
+ // returned here will be that of this routine since NextPc was the return
+ // address of our call to RtlCaptureContext. For the purposes of this
+ // function, the +4 and -4 adjustments to NextPc are unnecessary.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if (FunctionEntry != NULL) {
+
+ //
+ // A function entry was found for this routine. Virtually unwind
+ // to the caller of this routine (C).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // Attempt to unwind to the caller of the caller of this routine (B).
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.IntSp < HighLimit)) {
+
+ //
+ // A function table entry was found for the caller of the caller
+ // of this routine (B). Virtually unwind to the caller of the
+ // caller of this routine (B).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ *CallersPc = (PVOID)NextPc;
+
+ //
+ // Attempt to unwind to the caller of the caller of the caller
+ // of the caller of this routine (A).
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.IntSp < HighLimit)) {
+
+ //
+ // A function table entry was found for the caller of the
+ // caller of the caller of this routine (A). Virtually unwind
+ // to the caller of the caller of the caller of this routine
+ // (A).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ *CallersCallersPc = (PVOID)NextPc;
+ }
+ }
+ }
+#else
+ *CallersPc = NULL;
+ *CallersCallersPc = NULL;
+#endif
+ return;
+}
+
+USHORT
+RtlCaptureStackBackTrace(
+ IN ULONG FramesToSkip,
+ IN ULONG FramesToCapture,
+ OUT PVOID *BackTrace,
+ OUT PULONG BackTraceHash
+ )
+{
+ return 0;
+}
diff --git a/private/ntos/rtl/alpha/ghandler.c b/private/ntos/rtl/alpha/ghandler.c
new file mode 100644
index 000000000..c15c615b5
--- /dev/null
+++ b/private/ntos/rtl/alpha/ghandler.c
@@ -0,0 +1,436 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ ghandler.c
+
+Abstract:
+
+ This module implements the C specific exception handler that provides
+ structured exception handling for code generated by the GEM compiler.
+
+Author:
+
+ John Parks (parks) 12-Jan-1993
+ Thomas Van Baak (tvb) 28-Jan-1993
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+
+//
+// Define procedure prototypes for exception filter and termination handler
+// execution routines defined in jmpunwnd.s.
+//
+
+LONG
+__C_ExecuteExceptionFilter (
+ PEXCEPTION_POINTERS ExceptionPointers,
+ EXCEPTION_FILTER ExceptionFilter,
+ ULONG EstablisherFrame
+ );
+
+ULONG
+__C_ExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+//
+// Define local procedure prototypes.
+//
+
+EXCEPTION_DISPOSITION
+_OtsCSpecificHandler (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext
+ );
+
+ULONG
+_OtsLocalFinallyUnwind (
+ IN PSEH_CONTEXT SehContext,
+ IN PSEH_BLOCK TargetSeb,
+ IN PVOID RealFramePointer
+ );
+
+//
+// Define local macros.
+//
+
+#define IS_EXCEPT(Seb) ((Seb)->JumpTarget != 0)
+#define IS_FINALLY(Seb) ((Seb)->JumpTarget == 0)
+
+//
+// Initialize an exception record for the unwind with the SEB of the target
+// included as one information parameter. This is done so that the target
+// frame of the unwind may execute all the finally handlers necessary given
+// the SEB pointer at the unwind target.
+//
+
+#define MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, Seb) { \
+ ExceptionRecord->ExceptionCode = STATUS_UNWIND; \
+ ExceptionRecord->ExceptionFlags = EXCEPTION_UNWINDING; \
+ ExceptionRecord->ExceptionRecord = NULL; \
+ ExceptionRecord->ExceptionAddress = 0; \
+ ExceptionRecord->NumberParameters = 1; \
+ ExceptionRecord->ExceptionInformation[0] = (ULONG)(Seb); \
+ }
+
+EXCEPTION_DISPOSITION
+_OtsCSpecificHandler (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext
+ )
+
+/*++
+
+Routine Description:
+
+ This function walks up the list of SEB's associated with the specified
+ procedure and calls except filters and finally handlers as necessary.
+
+ It is called in two different contexts:
+ (i) by the exception dispatcher after an exception is raised
+ (ii) by the unwinder during an unwind operation
+
+ In the first case, is searches the SEB list for except filters to evaluate.
+ In the second case, it searches for finally handlers to execute.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ EstablisherFrame - Supplies a (virtual frame) pointer to the frame of the
+ establisher function.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+ DispatcherContext - Supplies a pointer to the exception dispatcher or
+ unwind dispatcher context.
+
+Return Value:
+
+ If the exception is handled by one of the exception filter routines, then
+ there is no return from this routine and RtlUnwind is called. Otherwise,
+ an exception disposition value of continue execution or continue search is
+ returned.
+
+Notes:
+ In context (i) there are 3 possibilities:
+
+ (a) If an exception filter returns a value greater that 0 (meaning
+ that the associated handler should be invoked) there is no
+ return from this function. RtlUnwind is called to unwind the
+ stack to the exception handler corresponding to that filter.
+
+ (b) If an exception filter returns a value less than 0 (meaning
+ that the exception should be dismissed), this routine returns
+ value ExceptionContinueExecution.
+
+ (c) If every filter returns value 0 (meaning that the search for a
+ handler should continue elsewhere), this function returns
+ ExceptionContinueSearch.
+
+ In context (ii) there are 2 possibilities:
+
+ (d) If no branches are detected out of finally handlers, this
+ function returns ExceptionContinueSearch.
+
+ (e) If a branch is detected out of a finally handler, there is no
+ return from this routine. RtlUnwind is called to unwind to the
+ branch target (and cancel the current unwind).
+
+ There may be long jumps out of both except filters and finally handlers
+ in which case this routine will be peeled off the stack without returning.
+
+--*/
+
+{
+
+ ULONG ContinuationAddress;
+ EXCEPTION_FILTER ExceptionFilter;
+ PVOID ExceptionHandler;
+ EXCEPTION_POINTERS ExceptionPointers;
+ LONG FilterValue;
+ ULONG RealFramePointer;
+ PSEH_BLOCK Seb;
+ PSEH_CONTEXT SehContext;
+ PSEH_BLOCK TargetSeb;
+ TERMINATION_HANDLER TerminationHandler;
+
+ //
+ // Get the address of the SEH context which is at some negative offset
+ // from the virtual frame pointer. For GEM, the handler data field of
+ // the function entry contains that offset. The current SEB pointer and
+ // the RFP (static link) are obtained from the SEH context.
+ //
+
+ SehContext = (PSEH_CONTEXT)((ULONG)EstablisherFrame +
+ (LONG)DispatcherContext->FunctionEntry->HandlerData);
+ RealFramePointer = SehContext->RealFramePointer;
+
+ //
+ // If this is a dispatching context, walk up the list of SEBs evaluating
+ // except filters.
+ //
+
+ if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // Set up the ExceptionPointers structure that is used by except
+ // filters to obtain data for the GetExceptionInformation intrinsic
+ // function. Copy the current SEB pointer into a local variable
+ // because the real SEB pointer is only modified in unwind contexts.
+ //
+
+ ExceptionPointers.ExceptionRecord = ExceptionRecord;
+ ExceptionPointers.ContextRecord = ContextRecord;
+
+ for (Seb = SehContext->CurrentSeb; Seb != NULL; Seb = Seb->ParentSeb) {
+ if (IS_EXCEPT(Seb)) {
+
+ //
+ // This is an except filter. Get the addresses of the filter
+ // and exception handler from the SEB, then call the except
+ // filter.
+ //
+
+ ExceptionFilter = (EXCEPTION_FILTER)Seb->HandlerAddress;
+ ExceptionHandler = (PVOID)Seb->JumpTarget;
+
+ FilterValue = __C_ExecuteExceptionFilter(&ExceptionPointers,
+ ExceptionFilter,
+ RealFramePointer);
+
+ //
+ // If the except filter < 0, dismiss the exception. If > 0,
+ // store the exception code on the stack for the except
+ // handler, modify the given ExceptionRecord so that finally
+ // handlers will be called properly during the unwind, then
+ // unwind down to the except handler. If = 0, resume the
+ // search for except filters.
+ //
+
+ if (FilterValue < 0) {
+ return ExceptionContinueExecution;
+
+ } else if (FilterValue > 0) {
+ SehContext->ExceptionCode = ExceptionRecord->ExceptionCode;
+ MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
+ Seb->ParentSeb);
+ RtlUnwind2(EstablisherFrame,
+ ExceptionHandler,
+ ExceptionRecord,
+ 0,
+ ContextRecord);
+ }
+ }
+ }
+
+ } else if (!IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // This is an unwind but is not the target frame. Since the function
+ // is being terminated, finally handlers for all try bodies that are
+ // presently in scope must be executed. Walk up the SEB list all the
+ // way to the top executing finally handlers. This corresponds to
+ // exiting all try bodies that are presently in scope.
+ //
+
+ while (SehContext->CurrentSeb != NULL) {
+
+ //
+ // Get the address of the SEB and then update the SEH context.
+ //
+
+ Seb = SehContext->CurrentSeb;
+ SehContext->CurrentSeb = Seb->ParentSeb;
+
+ if (IS_FINALLY(Seb)) {
+
+ //
+ // This is a finally handler. Get the address of the handler
+ // from the SEB and call the finally handler.
+ //
+
+ TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress;
+ ContinuationAddress =
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ RealFramePointer);
+
+ //
+ // If the finally handler returns a non-zero result, there
+ // was a branch out of the handler (to that address) and this
+ // routine should unwind to that target.
+ //
+
+ if (ContinuationAddress != 0) {
+ MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
+ SehContext->CurrentSeb);
+ RtlUnwind(EstablisherFrame,
+ (PVOID)ContinuationAddress,
+ ExceptionRecord,
+ 0);
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // This is the target frame of an unwind. Since the target may be
+ // in a different try scope than the one defined by the current SEB
+ // pointer, finally handlers between the two scopes must execute.
+ // Walk up the SEB list from the current SEB to the target SEB and
+ // execute all finally handlers encountered.
+ //
+
+ TargetSeb = (PSEH_BLOCK)ExceptionRecord->ExceptionInformation[0];
+ ContinuationAddress = _OtsLocalFinallyUnwind(SehContext,
+ TargetSeb,
+ (PVOID)RealFramePointer);
+ if (ContinuationAddress != 0) {
+
+ //
+ // A non-zero result indicates there was a branch out of a
+ // finally handler that was being executed during the unwind.
+ // This routine should unwind to that address.
+ //
+
+ MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
+ SehContext->CurrentSeb);
+ RtlUnwind(EstablisherFrame,
+ (PVOID)ContinuationAddress,
+ ExceptionRecord,
+ 0);
+ }
+ }
+
+ //
+ // Continue search for exception or termination handlers.
+ //
+
+ return ExceptionContinueSearch;
+}
+
+ULONG
+_OtsLocalFinallyUnwind (
+ IN PSEH_CONTEXT SehContext,
+ IN PSEH_BLOCK TargetSeb,
+ IN PVOID RealFramePointer
+ )
+
+/*++
+
+Routine Description:
+
+ This function walks up the SEB tree of the current procedure from the
+ current SEB to the target SEB and executes all the finally handlers it
+ encounters.
+
+ Calls to this function are inserted into user code by the compiler when
+ there are branches out of guarded regions that may require finally
+ handlers to execute.
+
+ This function is also called from _OtsCSpecificHandler when the target
+ frame is reached during an unwind operation. There may be finally handlers
+ that should execute before resuming execution at the unwind target.
+
+Arguments:
+
+ SehContext - Supplies the address of the SEH context structure which is
+ located in the stack frame.
+
+ TargetSeb - Supplies the address of the SEB corresponding to the branch
+ target address.
+
+ RealFramePointer - Supplies the (real frame) pointer of the establisher
+ frame, which is the current stack frame. This is used to set up the
+ static link if a finally handler is invoked.
+
+Return Value:
+
+ If a branch out of a finally handler is detected, the function value is
+ the address of the branch target. Otherwise, the function value is zero.
+
+--*/
+
+{
+
+ ULONG ContinuationAddress;
+ BOOLEAN Nested;
+ PSEH_BLOCK Seb;
+ TERMINATION_HANDLER TerminationHandler;
+
+ //
+ // If the SEB pointers are the same, no finally handlers need to execute.
+ // The branch is to a target location in the same guarded scope.
+ //
+
+ if (SehContext->CurrentSeb == TargetSeb) {
+ return 0;
+ }
+
+ //
+ // If the current SEB scope is not nested within the target SEB scope, no
+ // finally handlers need to execute. Reset the current SEB pointer to the
+ // target SEB pointer and return.
+ //
+
+ Nested = FALSE;
+ Seb = SehContext->CurrentSeb;
+
+ while (Seb != NULL) {
+ Seb = Seb->ParentSeb;
+ if (Seb == TargetSeb) {
+ Nested = TRUE;
+ break;
+ }
+ }
+ if (Nested == FALSE) {
+ SehContext->CurrentSeb = TargetSeb;
+ return 0;
+ }
+
+ //
+ // Walk up the list of SEB blocks executing finally handlers. If a branch
+ // out of a finally is encountered along the way, return the target
+ // address, otherwise return 0.
+ //
+
+ while (SehContext->CurrentSeb != TargetSeb) {
+
+ //
+ // Get the address of the SEB and then update the SEH context.
+ //
+
+ Seb = SehContext->CurrentSeb;
+ SehContext->CurrentSeb = Seb->ParentSeb;
+
+ if (IS_FINALLY(Seb)) {
+ TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress;
+ ContinuationAddress =
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ (ULONG)RealFramePointer);
+ if (ContinuationAddress != 0) {
+ return ContinuationAddress;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/private/ntos/rtl/alpha/largeint.s b/private/ntos/rtl/alpha/largeint.s
new file mode 100644
index 000000000..e6fb89dbb
--- /dev/null
+++ b/private/ntos/rtl/alpha/largeint.s
@@ -0,0 +1,999 @@
+// TITLE("Large Integer Arithmetic")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// largeint.s
+//
+// Abstract:
+//
+// This module implements routines for performing extended integer
+// arithmetic.
+//
+// Author:
+//
+// David N. Cutler (davec) 18-Apr-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 9-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Alpha AXP Implementation Notes:
+//
+// The LargeInteger functions defined below implement a set of portable
+// 64-bit integer arithmetic operations for x86, Mips, and Alpha systems
+// using the LARGE_INTEGER data type. Code using LARGE_INTEGER variables
+// and calling these functions will be portable across all NT platforms.
+// This is the recommended approach to 64-bit arithmetic on NT.
+//
+// However, if performance is more important than portability, then for
+// Alpha systems, the native 64-bit integer data types may be used instead
+// of the LARGE_INTEGER type and the LargeInteger functions. The Alpha C
+// compilers support a __int64 data type (renamed LONGLONG in the system
+// header files). All C integer arithmetic operators may be used with
+// these quadword types. This eliminates the need for, and the overhead
+// of, any of the portable LargeInteger functions.
+//
+// In general, a LARGE_INTEGER cannot simply be converted to a LONGLONG
+// because of explicit references in application code to the 32-bit LowPart
+// and HighPart members of the LARGE_INTEGER structure.
+//
+// The performance difference between using the portable LARGE_INTEGER
+// types with LargeInteger functions and the Alpha LONGLONG types and
+// operations is often not significant enough to warrant modifying otherwise
+// portable code. In addition, future compiler optimization and inlining may
+// actually result in identical performance between portable LARGE_INTEGER
+// code and Alpha specific LONGLONG code. Therefore it is recommended to
+// keep NT source code portable.
+//
+// The Alpha source for the large integer functions below differs from the
+// Mips version since for Alpha a 64-bit argument or return value is passed
+// through a 64-bit integer register. In addition, most operations are
+// implemented with a single instruction. The division routines below,
+// however, like Mips, use a simple shift/subtract algorithm which is
+// considerably slower than the algorithm used by Alpha runtime division
+// routines.
+//
+
+ SBTTL("Convert Long to Large Integer")
+//++
+//
+// LARGE_INTEGER
+// RtlConvertLongToLargeInteger (
+// IN LONG SignedInteger
+// )
+//
+// Routine Description:
+//
+// This function converts a signed integer to a signed large integer
+// and returns the result.
+//
+// Arguments:
+//
+// SignedInteger (a0) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertLongToLargeInteger)
+
+ addl a0, 0, v0 // ensure canonical (signed long) form
+ ret zero, (ra) // return
+
+ .end RtlConvertLongToLargeInteger
+
+ SBTTL("Convert Ulong to Large Integer")
+//++
+//
+// LARGE_INTEGER
+// RtlConvertUlongToLargeInteger (
+// IN ULONG UnsignedInteger
+// )
+//
+// Routine Description:
+//
+// This function converts an unsigned integer to a signed large
+// integer and returns the result.
+//
+// Arguments:
+//
+// UnsignedInteger (a0) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertUlongToLargeInteger)
+
+ zap a0, 0xf0, v0 // convert canonical ULONG to quadword
+ ret zero, (ra) // return
+
+ .end RtlConvertUlongToLargeInteger
+
+ SBTTL("Enlarged Signed Integer Multiply")
+//++
+//
+// LARGE_INTEGER
+// RtlEnlargedIntegerMultiply (
+// IN LONG Multiplicand,
+// IN LONG Multiplier
+// )
+//
+// Routine Description:
+//
+// This function multiplies a signed integer by a signed integer and
+// returns a signed large integer result.
+//
+// N.B. An overflow is not possible.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedIntegerMultiply)
+
+ addl a0, 0, a0 // ensure canonical (signed long) form
+ addl a1, 0, a1 // ensure canonical (signed long) form
+ mulq a0, a1, v0 // multiply signed both quadwords
+ ret zero, (ra) // return
+
+ .end RtlEnlargedIntegerMultiply
+
+ SBTTL("Enlarged Unsigned Divide")
+//++
+//
+// ULONG
+// RtlEnlargedUnsignedDivide (
+// IN ULARGE_INTEGER Dividend,
+// IN ULONG Divisor,
+// IN OUT PULONG Remainder OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned long
+// and returns the resultant quotient and optionally the remainder.
+//
+// N.B. An overflow or divide by zero exception is possible.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the unsigned 64-bit dividend value.
+//
+// Divisor (a1) - Supplies the unsigned 32-bit divisor value.
+//
+// Remainder (a2) - Supplies an optional pointer to a variable that
+// receives the unsigned 32-bit remainder.
+//
+// Return Value:
+//
+// The unsigned long integer quotient is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedDivide)
+
+//
+// Check for division by zero.
+//
+
+ zap a1, 0xf0, a1 // convert ULONG divisor to quadword
+ beq a1, 30f // trap if divisor is zero
+
+//
+// Check for overflow. If the divisor is less than the upper half of the
+// dividend the quotient would be wider than 32 bits.
+//
+
+ srl a0, 32, t0 // get upper longword of dividend
+ cmpule a1, t0, t1 // is divisor <= upper dividend?
+ bne t1, 40f // if ne[true], then overflow trap
+
+//
+// Perform the shift/subtract loop 8 times and 4 bits per loop.
+//
+// t0 - Temp used for 0/1 results of compares.
+// t1 - High 64-bits of 128-bit (t1, a0) dividend.
+// t2 - Loop counter.
+//
+
+ ldiq t2, 32/4 // set iteration count
+
+ srl a0, 32, t1 // get top 32 bits of carry-out
+ sll a0, 32, a0 // preshift first 32 bits left
+
+10: cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ subq t2, 1, t2 // any more iterations?
+ bne t2, 10b //
+
+//
+// Finished with remainder value in t1 and quotient value in a0.
+//
+
+ addl a0, 0, v0 // set longword quotient return value
+ beq a2, 20f // skip optional remainder store
+ stl t1, 0(a2) // store longword remainder
+
+20: ret zero, (ra) // return
+
+//
+// Generate an exception for divide by zero and return a zero quotient if the
+// caller continues execution.
+//
+
+30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO
+
+ GENERATE_TRAP
+
+ ldil v0, 0 // return zero quotient
+ ret zero, (ra) // return
+
+//
+// Generate an exception for overflow.
+//
+
+40: ldiq a0, 0x8000000000000000 //
+ subqv zero, a0, v0 // negate in order to overflow
+ trapb // wait for trap to occur
+ ret zero, (ra) // return
+
+ .end RtlEnlargedUnsignedDivide
+
+ SBTTL("Enlarged Unsigned Integer Multiply")
+//++
+//
+// LARGE_INTEGER
+// RtlEnlargedUnsignedMultiply (
+// IN ULONG Multiplicand,
+// IN ULONG Multiplier
+// )
+//
+// Routine Description:
+//
+// This function multiplies an unsigned integer by an unsigned integer
+// and returns a signed large integer result.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedMultiply)
+
+ zap a0, 0xf0, a0 // convert canonical ULONG to quadword
+ zap a1, 0xf0, a1 // convert canonical ULONG to quadword
+ mulq a0, a1, v0 // multiply signed both quadwords
+ ret zero, (ra) // return
+
+ .end RtlEnlargedUnsignedMultiply
+
+ SBTTL("Extended Integer Multiply")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedIntegerMultiply (
+// IN LARGE_INTEGER Multiplicand,
+// IN LONG Multiplier
+// )
+//
+// Routine Description:
+//
+// This function multiplies a signed large integer by a signed integer and
+// returns the signed large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedIntegerMultiply)
+
+ addl a1, 0, a1 // ensure canonical (signed long) form
+ mulq a0, a1, v0 // multiply signed both quadwords
+ ret zero, (ra) // return
+
+ .end RtlExtendedIntegerMultiply
+
+ SBTTL("Extended Large Integer Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedLargeIntegerDivide (
+// IN LARGE_INTEGER Dividend,
+// IN ULONG Divisor,
+// IN OUT PULONG Remainder OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned long
+// and returns the quadword quotient and optionally the long remainder.
+//
+// N.B. A divide by zero exception is possible.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the dividend value.
+//
+// Divisor (a1) - Supplies the divisor value.
+//
+// Remainder (a2) - Supplies an optional pointer to a variable that
+// receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedLargeIntegerDivide)
+
+//
+// Check for division by zero.
+//
+
+ zap a1, 0xf0, a1 // convert canonical ULONG to quadword
+ beq a1, 30f // trap if divisor is zero
+
+//
+// Perform the shift/subtract loop 16 times and 4 bits per loop.
+//
+// t0 - Temp used for 0/1 results of compares.
+// t1 - High 64-bits of 128-bit (t1, a0) dividend.
+// t2 - Loop counter.
+//
+
+ ldiq t2, 64/4 // set iteration count
+
+ ldiq t1, 0 // zero-extend dividend to 128 bits
+
+10: cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ subq t2, 1, t2 // any more iterations?
+ bne t2, 10b //
+
+//
+// Finished with remainder value in t1 and quotient value in a0.
+//
+
+ mov a0, v0 // set quadword quotient return value
+ beq a2, 20f // skip optional remainder store
+ stl t1, 0(a2) // store longword remainder
+
+20: ret zero, (ra) // return
+
+//
+// Generate an exception for divide by zero.
+//
+
+30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO
+
+ GENERATE_TRAP
+
+ br zero, 30b // in case they continue
+
+ .end RtlExtendedLargeIntegerDivide
+
+ SBTTL("Extended Magic Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedMagicDivide (
+// IN LARGE_INTEGER Dividend,
+// IN LARGE_INTEGER MagicDivisor,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function divides a signed large integer by an unsigned large integer
+// and returns the signed large integer result. The division is performed
+// using reciprocal multiplication of a signed large integer value by an
+// unsigned large integer fraction which represents the most significant
+// 64-bits of the reciprocal divisor rounded up in its least significant bit
+// and normalized with respect to bit 63. A shift count is also provided
+// which is used to truncate the fractional bits from the result value.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the dividend value.
+//
+// MagicDivisor (a1) - Supplies the magic divisor value which
+// is a 64-bit multiplicative reciprocal.
+//
+// Shiftcount (a2) - Supplies the right shift adjustment value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedMagicDivide)
+
+//
+// Make the dividend positive for the reciprocal multiplication to work.
+//
+
+ negq a0, t0 // negate dividend
+ cmovgt a0, a0, t0 // get absolute value in t0
+
+//
+// Multiply both quadword arguments together and take only the upper 64 bits of
+// the resulting 128 bit product. This can be done using the umulh instruction.
+//
+// Division of a dividend by a constant divisor through reciprocal
+// multiplication works because a/b is equivalent to (a*x)/(b*x) for any
+// value of x. Now if b is a constant, some "magic" integer x can be chosen,
+// based on the value of b, so that (b*x) is very close to a large power of
+// two (e.g., 2^64). Then a/b = (a*x)/(b*x) = (a*x)/(2^64) = (a*x)>>64. This
+// effectively turns the problem of division by a constant into multiplication
+// by a constant which is a much faster operation.
+//
+
+ umulh t0, a1, t1 // multiply high both quadword arguments
+ sra t1, a2, t1 // shift result right by requested amount
+
+//
+// Make the result negative if the dividend was negative.
+//
+
+ negq t1, t0 // negate result
+ cmovgt a0, t1, t0 // restore sign of dividend
+
+ mov t0, v0 // set quadword result return value
+ ret zero, (ra) // return
+
+ .end RtlExtendedMagicDivide
+
+ SBTTL("Large Integer Add")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerAdd (
+// IN LARGE_INTEGER Addend1,
+// IN LARGE_INTEGER Addend2
+// )
+//
+// Routine Description:
+//
+// This function adds a signed large integer to a signed large integer and
+// returns the signed large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Addend1 (a0) - Supplies the first addend value.
+//
+// Addend2 (a1) - Supplies the second addend value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerAdd)
+
+ addq a0, a1, v0 // add both quadword arguments
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerAdd
+
+ SBTTL("Large Integer Arithmetic Shift Right")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerArithmeticShift (
+// IN LARGE_INTEGER LargeInteger,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function shifts a signed large integer right by an unsigned integer
+// modulo 64 and returns the shifted signed large integer result.
+//
+// Arguments:
+//
+// LargeInteger (a0) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a1) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerArithmeticShift)
+
+ sra a0, a1, v0 // shift the quadword right/arithmetic
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerArithmeticShift
+
+ SBTTL("Large Integer Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerDivide (
+// IN LARGE_INTEGER Dividend,
+// IN LARGE_INTEGER Divisor,
+// IN OUT PLARGE_INTEGER Remainder OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned large
+// integer and returns the quadword quotient and optionally the quadword
+// remainder.
+//
+// N.B. A divide by zero exception is possible.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the dividend value.
+//
+// Divisor (a1) - Supplies the divisor value.
+//
+// Remainder (a2) - Supplies an optional pointer to a variable that
+// receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerDivide)
+
+//
+// Check for division by zero.
+//
+
+ beq a1, 30f // trap if divisor is zero
+
+//
+// Perform the shift/subtract loop 16 times and 4 bits per loop.
+//
+// t0 - Temp used for 0/1 results of compares.
+// t1 - High 64-bits of 128-bit (t1, a0) dividend.
+// t2 - Loop counter.
+//
+
+ ldiq t2, 64/4 // set iteration count
+
+ ldiq t1, 0 // zero-extend dividend to 128 bits
+
+10: cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ subq t2, 1, t2 // any more iterations?
+ bne t2, 10b //
+
+//
+// Finished with remainder value in t1 and quotient value in a0.
+//
+
+ mov a0, v0 // set quadword quotient return value
+ beq a2, 20f // skip optional remainder store
+ stq t1, 0(a2) // store quadword remainder
+
+20: ret zero, (ra) // return
+
+//
+// Generate an exception for divide by zero.
+//
+
+30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO
+
+ GENERATE_TRAP
+
+ br zero, 30b // in case they continue
+
+ .end RtlLargeIntegerDivide
+
+ SBTTL("Large Integer Negate")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerNegate (
+// IN LARGE_INTEGER Subtrahend
+// )
+//
+// Routine Description:
+//
+// This function negates a signed large integer and returns the signed
+// large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Subtrahend (a0) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerNegate)
+
+ subq zero, a0, v0 // negate the quadword argument
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerNegate
+
+ SBTTL("Large Integer Shift Left")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerShiftLeft (
+// IN LARGE_INTEGER LargeInteger,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function shifts a signed large integer left by an unsigned integer
+// modulo 64 and returns the shifted signed large integer result.
+//
+// Arguments:
+//
+// LargeInteger (a0) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a1) - Supplies the left shift count.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftLeft)
+
+ sll a0, a1, v0 // shift the quadword argument left
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerShiftLeft
+
+ SBTTL("Large Integer Logical Shift Right")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerShiftRight (
+// IN LARGE_INTEGER LargeInteger,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function shifts an unsigned large integer right by an unsigned
+// integer modulo 64 and returns the shifted unsigned large integer result.
+//
+// Arguments:
+//
+// LargeInteger (a0) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a1) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftRight)
+
+ srl a0, a1, v0 // shift the quadword right/logical
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerShiftRight
+
+ SBTTL("Large Integer Subtract")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerSubtract (
+// IN LARGE_INTEGER Minuend,
+// IN LARGE_INTEGER Subtrahend
+// )
+//
+// Routine Description:
+//
+// This function subtracts a signed large integer from a signed large
+// integer and returns the signed large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Minuend (a0) - Supplies the minuend value.
+//
+// Subtrahend (a1) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerSubtract)
+
+ subq a0, a1, v0 // subtract the quadword arguments
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerSubtract
+
+ SBTTL("128-bit Signed Integer Multiplication")
+//++
+//
+// VOID
+// Rtlp128BitSignedMultiply (
+// IN LONGLONG Multiplicand,
+// IN LONGLONG Multiplier,
+// IN OUT PULONGLONG ProductLower,
+// IN OUT PLONGLONG ProductUpper
+// )
+//
+// Routine Description:
+//
+// This function multiplies a signed quadword (or signed large integer)
+// by a signed quadword (or signed large integer) and returns the full
+// 128-bit signed product indirectly through pointers to two quadwords.
+//
+// N.B. Signed multiplication is implemented with an unsigned multiply
+// followed by up to two subtractions. The subtractions are necessary for
+// the following reason. Within an N-bit register, a negative N-bit two's
+// compliment signed integer (-x), is equal to (2^N - x). So in this case
+// of 64x64 bit multiplication, the following holds:
+//
+// (-x) * (-y) =
+// (2^64 - x) * (2^64 - y) =
+// 2^128 - (2^64)*x - (2^64)*y + (x*y)
+//
+// The lower 64-bits of the 128-bit product is determined solely by the
+// (x*y) term. For a 128-bit result, the 2^128 term is irrelevant. And if
+// either the x and/or the y operand is negative, then either y and/or x
+// must be subtracted from the upper 64 bits of the 128-bit product.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// ProductLower (a2) - Supplies a pointer to an unsigned quadword variable
+// that receives the lower 64-bits of the product.
+//
+// ProductLower (a3) - Supplies a pointer to a signed quadword variable
+// that receives the upper 64-bits of the product.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(Rtlp128BitSignedMultiply)
+
+ mulq a0, a1, t0 // get lower 64 bits of product
+ stq t0, 0(a2) // store lower half of 128-bit product
+
+ umulh a0, a1, t1 // get upper 64 bits of product
+ subq t1, a0, t2 // subtract first operand from product
+ cmovge a1, t1, t2 // if second operand is negative
+ subq t2, a1, t3 // subtract second operand from product
+ cmovge a0, t2, t3 // if first operand is negative
+ stq t3, 0(a3) // store upper half of 128-bit product
+
+ ret zero, (ra) // return
+
+ .end Rtlp128BitSignedMultiply
+
+ SBTTL("128-bit Unsigned Integer Multiplication")
+//++
+//
+// VOID
+// Rtlp128BitUnsignedMultiply (
+// IN ULONGLONG Multiplicand,
+// IN ULONGLONG Multiplier,
+// IN OUT PULONGLONG ProductLower,
+// IN OUT PULONGLONG ProductUpper
+// )
+//
+// Routine Description:
+//
+// This function multiplies an unsigned quadword (or large integer) by an
+// unsigned quadword (or large integer) and returns the full 128-bit unsigned
+// product indirectly through pointers to two unsigned quadwords.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// ProductLower (a2) - Supplies a pointer to an unsigned quadword variable
+// that receives the lower 64-bits of the product.
+//
+// ProductLower (a3) - Supplies a pointer to an unsigned quadword variable
+// that receives the upper 64-bits of the product.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(Rtlp128BitUnsignedMultiply)
+
+ mulq a0, a1, t0 // get lower 64 bits of product
+ stq t0, 0(a2) // store lower half of 128-bit product
+
+ umulh a0, a1, t1 // get upper 64 bits of product
+ stq t1, 0(a3) // store upper half of 128-bit product
+
+ ret zero, (ra) // return
+
+ .end Rtlp128BitUnsignedMultiply
diff --git a/private/ntos/rtl/alpha/localrtl.c b/private/ntos/rtl/alpha/localrtl.c
new file mode 100644
index 000000000..e8e91b9ab
--- /dev/null
+++ b/private/ntos/rtl/alpha/localrtl.c
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ localrtl.c
+
+Abstract:
+
+ This module contains alternate implementations of each of the Rtl Memory
+ functions and other common functions required by the Rtl Memory test
+ programs.
+
+Author:
+
+ Thomas Van Baak (tvb) 11-Jan-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include "localrtl.h"
+
+//
+// Simple pattern generator.
+//
+
+#define PATTERN "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PATTERN_SIZE (sizeof(PATTERN) - 1)
+UCHAR Pattern[] = PATTERN;
+
+VOID
+FillPattern(PUCHAR To, ULONG Length)
+{
+ ULONG Index;
+ ULONG Rotor = 0;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ To[Index] = Pattern[Rotor];
+ Rotor += 1;
+ if (Rotor == PATTERN_SIZE) {
+ Rotor = 0;
+ }
+ }
+}
+
+//
+// The following functions are simple, non-optimized, (and thus maybe even
+// bug-proof) implementations of each of the Rtl Memory functions.
+//
+
+ULONG
+LocalCompareMemory (
+ PVOID Source1,
+ PVOID Source2,
+ ULONG Length
+ )
+{
+ ULONG Index;
+ PUCHAR Left = Source1;
+ ULONG Match;
+ PUCHAR Right = Source2;
+
+ Match = 0;
+ for (Index = 0; Index < Length; Index += 1) {
+ if (Left[Index] != Right[Index]) {
+ break;
+ }
+ Match += 1;
+ }
+ return Match;
+}
+
+ULONG
+LocalCompareMemoryUlong (
+ PVOID Source,
+ ULONG Length,
+ ULONG Pattern
+ )
+{
+ PULONG From = Source;
+ ULONG Index;
+ ULONG Match;
+
+ Match = 0;
+ for (Index = 0; Index < Length / sizeof(ULONG); Index += 1) {
+ if (From[Index] != Pattern) {
+ break;
+ }
+ Match += sizeof(ULONG);
+ }
+ return Match;
+}
+
+VOID
+LocalMoveMemory (
+ PVOID Destination,
+ PVOID Source,
+ ULONG Length
+ )
+{
+ PUCHAR From = Source;
+ ULONG Index;
+ PUCHAR To = Destination;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ if (To <= From) {
+ To[Index] = From[Index];
+
+ } else {
+ To[Length - 1 - Index] = From[Length - 1 - Index];
+ }
+ }
+}
+
+VOID
+LocalFillMemory (
+ PVOID Destination,
+ ULONG Length,
+ UCHAR Fill
+ )
+{
+ ULONG Index;
+ PUCHAR To = Destination;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ To[Index] = Fill;
+ }
+}
+
+VOID
+LocalFillMemoryUlong (
+ PVOID Destination,
+ ULONG Length,
+ ULONG Pattern
+ )
+{
+ ULONG Index;
+ PULONG To = Destination;
+
+ for (Index = 0; Index < Length / sizeof(ULONG); Index += 1) {
+ To[Index] = Pattern;
+ }
+}
+
+VOID
+LocalZeroMemory (
+ PVOID Destination,
+ ULONG Length
+ )
+{
+ ULONG Index;
+ PUCHAR To = Destination;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ To[Index] = 0;
+ }
+}
diff --git a/private/ntos/rtl/alpha/localrtl.h b/private/ntos/rtl/alpha/localrtl.h
new file mode 100644
index 000000000..5445f2b38
--- /dev/null
+++ b/private/ntos/rtl/alpha/localrtl.h
@@ -0,0 +1,82 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ localrtl.h
+
+Abstract:
+
+ This module contains the local Rtl header file.
+
+Author:
+
+ Thomas Van Baak (tvb) 11-Jan-1993
+
+Revision History:
+
+--*/
+
+//
+// Define function prototypes of local Rtl Memory functions.
+//
+
+ULONG
+LocalCompareMemory (
+ PVOID Source1,
+ PVOID Source2,
+ ULONG Length
+ );
+
+ULONG
+LocalCompareMemoryUlong (
+ PVOID Source,
+ ULONG Length,
+ ULONG Pattern
+ );
+
+VOID
+LocalMoveMemory (
+ PVOID Destination,
+ CONST VOID *Source,
+ ULONG Length
+ );
+
+VOID
+LocalFillMemory (
+ PVOID Destination,
+ ULONG Length,
+ UCHAR Fill
+ );
+
+VOID
+LocalFillMemoryUlong (
+ PVOID Destination,
+ ULONG Length,
+ ULONG Pattern
+ );
+
+VOID
+LocalZeroMemory (
+ PVOID Destination,
+ ULONG Length
+ );
+
+//
+// Define function prototypes of other common functions.
+//
+
+VOID
+FillPattern(
+ PUCHAR To,
+ ULONG Length
+ );
+
+//
+// Define maximum values for the string tests.
+//
+
+#define MAX_MARGIN 8
+#define MAX_OFFSET 32
+#define MAX_LENGTH 72
diff --git a/private/ntos/rtl/alpha/longjmp.s b/private/ntos/rtl/alpha/longjmp.s
new file mode 100644
index 000000000..1999d1573
--- /dev/null
+++ b/private/ntos/rtl/alpha/longjmp.s
@@ -0,0 +1,200 @@
+// TITLE("Long Jump")
+//++
+//
+// Copyright (c) 1993 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// longjmp.s
+//
+// Abstract:
+//
+// This module implements the Alpha specific routine to perform a long
+// jump operation. Three jump buffer types are supported: unsafe, safe
+// acc-style (virtual frame pointer, PC mapped SEH scope), and safe
+// GEM-style (real frame pointer, SEB-based SEH context).
+//
+// N.B. This function has been replaced by setjmp/setjmpex/longjmp in the
+// C runtime library. It remains here for backwards compatibility of
+// Beta 2 applications expecting setjmp and longjmp to be present in
+// ntdll, or for new acc or GEM compiled applications that link with
+// ntdll before libc.
+//
+// Author:
+//
+// David N. Cutler (davec) 2-Apr-1993
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 22-Apr-1993
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Define jump buffer types.
+//
+// _JMPBUF_TYPE_ZERO was used by the Beta 2 ntdll setjmp and can be handled
+// the same as _JMPBUF_TYPE_ACC.
+//
+// _JMPBUF_TYPE_FAST is for jump buffers containing the set of non-volatile
+// integer and floating registers. This form of setjmp/longjmp is not
+// compatible with SEH. It is an order of magnitude faster however.
+//
+// _JMPBUF_TYPE_ACC is for setjmp/longjmp compatible with SEH. The Alpha
+// acc compiler uses a virtual frame pointer, and SEH scope is inferred
+// from the PC through passive PC-mapping scope tables.
+//
+// _JMPBUF_TYPE_GEM is for setjmp/longjmp compatible with SEH. The Alpha
+// GEM C compiler uses a real frame pointer, and SEH scope is maintained
+// actively with an SEB pointer.
+//
+
+#define _JMPBUF_TYPE_ZERO 0
+#define _JMPBUF_TYPE_FAST 1
+#define _JMPBUF_TYPE_ACC 2
+#define _JMPBUF_TYPE_GEM 3
+
+ SBTTL("Long Jump")
+//++
+//
+// int
+// longjmp (
+// IN jmp_buf JumpBuffer,
+// IN int ReturnValue
+// )
+//
+// Routine Description:
+//
+// This function performs a long jump to the context specified by the
+// jump buffer.
+//
+// Arguments:
+//
+// JumpBuffer (a0) - Supplies the address of a jump buffer that contains
+// jump information.
+//
+// N.B. This is an array of double's to force quadword alignment.
+//
+// ReturnValue (a1) - Supplies the value that is to be returned to the
+// caller of set jump.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(longjmp)
+
+ ldil t0, 1 // force nonzero value, if
+ cmoveq a1, t0, a1 // given return value is zero
+
+ ldl t1, JbType(a0) // get setjmp context type flag
+ subq t1, 1, t2 // if eq 1, fast, unsafe longjmp
+ bne t2, 10f // otherwise, provide safe longjmp
+
+//
+// Type 0x1: Provide unsafe handling of longjmp.
+//
+
+ mov a1, v0 // set return value
+ .set noreorder
+ .set noat
+ ldt f2, JbFltF2(a0) // restore floating registers f2 - f9
+ ldt f3, JbFltF3(a0) //
+ ldt f4, JbFltF4(a0) //
+ ldt f5, JbFltF5(a0) //
+ ldt f6, JbFltF6(a0) //
+ ldt f7, JbFltF7(a0) //
+ ldt f8, JbFltF8(a0) //
+ ldt f9, JbFltF9(a0) //
+
+ ldq s0, JbIntS0(a0) // restore integer registers s0 - s6/fp
+ ldq s1, JbIntS1(a0) //
+ ldq s2, JbIntS2(a0) //
+ ldq s3, JbIntS3(a0) //
+ ldq s4, JbIntS4(a0) //
+ ldq s5, JbIntS5(a0) //
+ ldq fp, JbIntS6(a0) //
+ .set at
+ .set reorder
+
+ ldq a1, JbFir(a0) // get setjmp return address
+ ldq sp, JbIntSp(a0) // restore stack pointer
+ jmp zero, (a1) // jump back to setjmp site
+
+//
+// Type 0x0: Provide safe handling of longjmp (idw 404 style).
+// Type 0x2: Provide safe handling of longjmp (acc style).
+//
+
+10: bic t1, 0x2, t2 // if 0 or 2, safe acc longjmp
+ bne t2, longjmpRfp // if not, safe GEM longjmp
+
+ mov a1, a3 // set return value
+ mov zero, a2 // set exception record address
+ ldl a1, JbPc(a0) // set target instruction address
+ ldl a0, JbFp(a0) // set target virtual frame pointer
+ br zero, RtlUnwind // finish in common code
+
+ .end longjmp
+
+ SBTTL("Long Jump - GEM")
+
+ .struct 0
+LjRa: .space 8 // saved return address
+ .space 8 // padding for 16-byte stack alignment
+LjEr: .space ExceptionRecordLength // local exception record
+LongjmpFrameLength:
+
+//
+// Type 0x3: Provide safe handling of longjmp (GEM style).
+//
+
+ NESTED_ENTRY(longjmpRfp, LongjmpFrameLength, ra)
+
+ lda sp, -LongjmpFrameLength(sp) // allocate stack frame
+ stq ra, LjRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// The SEB is passed to the GEM exception handler through an exception
+// record. Set up the following local exception record:
+//
+// ExceptionRecord.ExceptionCode = STATUS_UNWIND;
+// ExceptionRecord.ExceptionFlags = EXCEPTION_UNWINDING;
+// ExceptionRecord.ExceptionRecord = NULL;
+// ExceptionRecord.ExceptionAddress = 0;
+// ExceptionRecord.NumberParameters = 1;
+// ExceptionRecord.ExceptionInformation[0] = Seb;
+//
+
+10: mov a1, a3 // set return value
+ lda a2, LjEr(sp) // set exception record address
+
+ ldil t0, STATUS_UNWIND // get status code
+ stl t0, ErExceptionCode(a2) // store in exception record
+ ldil t1, EXCEPTION_UNWINDING // get exception flags
+ stl t1, ErExceptionFlags(a2) // store in exception record
+ stl zero, ErExceptionRecord(a2) // clear indirect exception record
+ stl zero, ErExceptionAddress(a2) // clear exception address
+ ldil t2, 1 // get number of parameters
+ stl t2, ErNumberParameters(a2) // store in exception record
+ ldl t3, JbSeb(a0) // get SEB pointer
+ stl t3, ErExceptionInformation(a2) // store in exception record
+ ldl a1, JbPc(a0) // set target instruction address
+ ldl a0, JbFp(a0) // set target real frame pointer
+
+ bsr ra, RtlUnwindRfp // finish in common code
+
+ .end longjmpRfp
diff --git a/private/ntos/rtl/alpha/lzntaxp.s b/private/ntos/rtl/alpha/lzntaxp.s
new file mode 100644
index 000000000..2eea2462d
--- /dev/null
+++ b/private/ntos/rtl/alpha/lzntaxp.s
@@ -0,0 +1,485 @@
+// TITLE("Decompression Engine")
+//++
+//
+// Copyright (c) 1994 Microsoft Corporation
+//
+// Module Name:
+//
+// lzntaxp.s
+//
+// Abstract:
+//
+// This module implements the lznt1 decompression engine needed
+// to support file system decompression.
+//
+// Author:
+//
+// John Vert (jvert) 19-Jul-1994
+//
+// Environment:
+//
+// Any.
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+
+ SBTTL("Decompress a buffer")
+//++
+// NTSTATUS
+// LZNT1DecompressChunk (
+// OUT PUCHAR UncompressedBuffer,
+// IN PUCHAR EndOfUncompressedBufferPlus1,
+// IN PUCHAR CompressedBuffer,
+// IN PUCHAR EndOfCompressedBufferPlus1,
+// OUT PULONG FinalUncompressedChunkSize
+// )
+//
+// Routine Description:
+//
+// This function decodes a stream of compression tokens and places the
+// resultant output into the destination buffer. The format of the input
+// is described ..\lznt1.c. As the input is decoded, checks are made to
+// ensure that no data is read past the end of the compressed input buffer
+// and that no data is stored past the end of the output buffer. Violations
+// indicate corrupt input and are indicated by a status return.
+//
+//
+// Arguments:
+//
+// UncompressedBuffer (a0) - pointer to destination of uncompression.
+//
+// EndOfUncompressedBufferPlus1 (a1) - pointer just beyond the
+// output buffer. This is used for consistency checking of the stored
+// compressed data.
+//
+// CompressedBuffer (a2) - pointer to compressed source. This begins
+// with a header word followed by a tag byte describing which of the
+// following tokens are literals and which are copy groups.
+//
+// EndOfCompressedBufferPlus1 (a3) - pointer just beyond end of input
+// buffer. This is used to terminate the decompression.
+//
+// FinalUncompressedChunkSize (a4) - pointer to a returned decompressed
+// size. This has meaningful data ONLY when LZNT1DecompressChunk returns
+// STATUS_SUCCESS
+//
+// Return Value:
+//
+// STATUS_SUCCESS is returned only if the decompression consumes thee entire
+// input buffer and does not exceed the output buffer.
+// STATUS_BAD_COMPRESSION_BUFFER is returned when the output buffer would be
+// overflowed.
+//--
+//
+//
+// Register usage:
+// a0 - current destination pointer
+// a1 - end of output buffer
+// a2 - current source pointer
+// a3 - end of compressed buffer
+// a4 - pointer to decompressed size
+// a5 - current decompressed size
+// v0 - boundary for next format transition
+// t0 - count of consecutive copy tokens
+// t1 - current flag byte
+// t2 - bits of t1 processed
+// t3 - temp
+// t4 - temp
+// t5 - bytes following flag byte
+// t6 - temp
+// t7 - temp
+// t8 - temp
+// t9 - temp
+// t10 - temp
+// t11 - current length mask
+// t12 - current displacement shift
+//
+ LEAF_ENTRY(LZNT1DecompressChunk)
+
+ bis zero, zero, a5 // initialize decompressed size
+ ldil t12, 12 // get initial displacement shift
+ lda t11, -1(zero)
+ sll t11, 12, t11 // get initial length mask
+
+ addq a0, 16, v0 // get displacement boundary
+ subq a3, 1, a3 // adjust input buffer end
+10:
+ addq a0, 8, t3 // check for at least 8 bytes available output
+ addq a2, 17, t4 // check for at least 17 bytes available input
+ cmpule t3, a1, t2 // check for output buffer exceeded
+ cmpule t4, a3, t3 // check for input buffer exceeded
+ ldq_u t0, 0(a2) // load flag byte and any subsequent bytes
+ extbl t0, a2, t1 // extract flag byte
+ addq a2, 1, a2
+ beq t3, CopyTailFlag // input buffer exceeded
+ ldq_u t6, 7(a2) // load subsequent bytes
+ and a2, 7, t10 // check for qword alignment
+ extql t0, a2, t3 // extract low part of next 8 bytes
+ extqh t6, a2, t4 // extract high part
+ bis t3, t4, t5 // merge
+ cmoveq t10, t6, t5 // qword aligned, undo merge
+ beq t2, CopyTailFlag // output buffer exceeded
+ bne t1, 20f // !=0 deal with copy tokens
+
+//
+// This is the special case where the next 8 bytes are literal tokens.
+//
+ addq a5, 8, a5 // increment bytes copied
+ addq a0, 8, a0 // increment destination pointer
+ lda a2, 8(a2) // compute pointer to next tag byte
+ and a0, 7, t4 // check for qword-aligned destination
+ bne t4, 15f
+
+//
+// Destination is quadword aligned, do direct store
+//
+ stq t5, -8(a0)
+ br zero, 10b // do next tag byte
+15:
+//
+// Destination is not quadword aligned, merge eight bytes into buffer.
+//
+ ldq_u t4, -8(a0) // get low destination
+ mskql t4, a0, t0 // clear position in destination
+ insql t5, a0, t2 // get low part in position
+ bis t0, t2, t4 // merge in new bytes
+ stq_u t4, -8(a0) // store low part
+ ldq_u t4, -1(a0) // get high destination
+ mskqh t4, a0, t0 // clear position in destination
+ insqh t5, a0, t2 // get high part in position
+ bis t0, t2, t4 // merge in new bytes
+ stq_u t4, -1(a0) // store high part
+ br zero, 10b // do next tag byte
+
+20:
+//
+// Tag indicates both literal bytes and copy tokens. The approach
+// we use here is to loop through the bits counting the consecutive
+// literal bytes until we find a copy token.
+//
+ bis zero, zero, t0 // set bit count to zero
+ ldil t2, 8 // set count of bits to process
+25:
+ blbs t1, CopyToken // go copy the token.
+
+//
+// Count the consecutive clear bits.
+//
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+30:
+ bis zero, 1, t9 // compute byte mask
+ sll t9, t0, t3
+ subq t3, 1, t9
+ zapnot t5, t9, t4 // get masked bytes to store
+ and a0, 7, t3 // get byte position of dest
+ addq t3, t0, t10 // compute ending offset
+ sll t9, t3, t9 // shift byte mask into position
+ ldq_u t7, 0(a0) // get low part of dest.
+ insql t4, t3, t6 // insert source bytes into position
+ zap t7, t9, t8 // clear dest
+ bis t8, t6, t7 // merge bytes to store
+ stq_u t7, 0(a0) // store merged result
+
+//
+// Check to see whether the bytes to store extend into the next
+// quadword of the destination.
+//
+ cmpult t10, 8, t9
+ bne t9, 40f // ending offset < 8, next quadword unaffected
+ insqh t4, t3, t6 // shift source bytes into position
+ stq_u t6, 8(a0) // store merged results
+
+40:
+ addq a0, t0, a0 // adjust destination pointer
+ addq a2, t0, a2 // adjust source pointer
+ addq a5, t0, a5 // adjust bytes copied
+ subq t2, t0, t2 // adjust flag bits left
+
+CopyToken:
+//
+// Get the token word
+//
+ ldq_u t10, 0(a2)
+ ldq_u t6, 1(a2)
+ extwl t10, a2, t7
+ extwh t6, a2, t8
+ bis t7, t8, t9
+
+//
+// Check the displacement and length.
+//
+50:
+ cmpult v0, a0, t10
+ bne t10, UpdateFormat // if nez, max displacement < output
+ srl t9, t12, t7 // compute offset
+ andnot t9, t11, t8 // compute length
+ addq t8, 3, t8
+ addq t7, 1, t7
+
+//
+// Check displacement against number of bytes copied
+//
+ cmpule t7, a5, t10
+ beq t10, ErrorExit // if eqz, bytes copied <= displacement
+//
+// Account for end of output buffer and compute ending
+// address of copy.
+//
+ addq a0, t8, t9
+ cmpule t9, a1, t10
+ cmoveq t10, a1, t9 // if ending address > buffer end, set
+ // buffer end to ending address
+ subq a0, t7, t5 // compute copy source
+//
+// Do the copy.
+// t5 - source
+// a0 - dest
+// t9 - end of destination
+//
+// If the source is more than eight bytes away from the destination,
+// we can copy a quadword at a time. Otherwise, we must copy a byte
+// at a time to ensure that fills work correctly.
+//
+
+ subq t9, a0, t7 // compute number of bytes to copy
+ addq a5, t7, a5 // adjust bytes copied here, the only
+ // time this will not be correct is
+ // in an error condition.
+ subq a0, t5, t10 // test if source is >= 8 bytes away
+ cmpult t10, 8, t10 // from destination
+ bne t10, FillBytes // if so, do byte fill
+
+//
+// Write the low part of the first quadword out. This will cause the
+// destination to become qword aligned.
+//
+ ldq_u t10, 0(t5) // get low part of source qword
+ ldq_u t8, 7(t5) // get high part of source qword
+ extql t10, t5, t7
+ extqh t8, t5, t3
+ bis t3, t7, t10 // get aligned qword
+ ldq_u t7, 0(a0) // get low part of source destination
+ insql t10, a0, t3
+ mskql t7, a0, t4 // clear bytes in destination
+ bis t4, t3, t7 // merge qword into destination
+ stq_u t7, 0(a0) // store low part of quadword
+
+ addq a0, 8, t10 // compute qword-aligned destination
+ bic t10, 7, t10
+ subq t10, a0, t8
+ addq t5, t8, t5 // increment source
+ bis t10, zero, a0 // increment destination
+
+//
+// Recompute number of quadwords to copy now that the destination has
+// been qword aligned
+//
+ subq t9, a0, t7
+ cmovlt t7, t9, a0 // back up destination if we went too far
+ ble t7, 64f // no bytes remaining
+ srl t7, 3, t4
+ and t5, 7, t3 // get alignment of source
+ ldq_u t10, 0(t5)
+ bne t3, UnalignedQwordCopy
+ beq t4, 60f // no qwords remaining
+
+AlignedQwordLoop:
+ stq t10, 0(a0) // store qword
+ addq t5, 8, t5 // increment source
+ addq a0, 8, a0 // increment dest
+ subq t4, 1, t4 // decrement remaining qword
+ ldq t10, 0(t5) // get next qword
+ bne t4, AlignedQwordLoop
+ cmpult a0, t9, t4
+ beq t4, 64f // no bytes reamining
+//
+// Tail bytes are in t10, go ahead and store them.
+// We know we will not store beyond the containing qword of
+// the end of the buffer
+//
+60:
+ stq t10, 0(a0)
+ bis t9, zero, a0 // increment dest
+ br zero, 64f
+
+UnalignedQwordCopy:
+ beq t4, 65f // no qword remaining
+
+UnalignedQwordLoop:
+ ldq_u t8, 8(t5)
+ extql t10, t5, t10
+ extqh t8, t5, t7
+ bis t7, t10, t10
+ stq t10, 0(a0)
+ bis t8, zero, t10
+ addq t5, 8, t5 // increment source
+ addq a0, 8, a0 // increment dest
+ subq t4, 1, t4 // decrement remaining qwords
+ bne t4, UnalignedQwordLoop
+ cmpult a0, t9, t4
+ beq t4, 64f // no bytes remaining
+
+//
+// Low word of the tail bytes are in t10
+// Get the high part, then go ahead and store them.
+// We know we will not store beyond the containing qword
+// of the end of the buffer.
+//
+65:
+ ldq_u t8, 8(t5) // get high part of tail bytes
+ extql t10, t5, t7 // extract low part
+ extqh t8, t5, t4 // extract high part
+ bis t7, t4, t10 // merge
+ stq t10, 0(a0) // store result
+ bis t9, zero, a0 // increment dest.
+ br zero, 64f
+
+FillBytes:
+ ldq_u t10, 0(t5)
+ ldq_u t8, 0(a0)
+ extbl t10, t5, t7
+ insbl t7, a0, t10
+ mskbl t8, a0, t4
+ bis t10, t4, t7
+ stq_u t7, 0(a0)
+ addq a0, 1, a0
+ addq t5, 1, t5
+ cmpult a0, t9, t4
+ bne t4, FillBytes
+64:
+//
+// Token successfully copied.
+//
+ addq a2, 2, a2
+ subq t2, 1, t2 // decrement remaining bits
+ srl t1, 1, t1 // shift flag byte
+ beq t2, 10b // no more bits remaining
+
+ addq a0, t2, t3 // check for enough output bytes remaining
+ cmpule t3, a1, t4
+ beq t4, CopyTail
+
+ addq a2, 14, t3 // check for enough input bytes remaining
+ cmpule t3, a3, t4
+ beq t4, CopyTail
+
+ addq a2, t2, t3 // point to last byte.
+//
+// Get remaining bytes
+//
+ bis zero, zero, t0 // set # clear bits back to zero
+ ldq_u t5, 0(t3)
+ and a2, 7, t7
+ beq t7, 65f // source quadword aligned, no shift/merge required
+ extql t6, a2, t4
+ extqh t5, a2, t8
+ bis t4, t8, t5
+65:
+ bne t1, 25b // if any literal tokens remain, repeat
+
+ bis zero, 1, t9 // compute byte mask
+ sll t9, t2, t3
+ subq t3, 1, t9
+ zapnot t5, t9, t4 // get masked bytes to store
+ and a0, 7, t3 // get byte position of dest
+ addq t3, t2, t10 // compute ending offset
+ sll t9, t3, t9 // shift byte mask into position
+ ldq_u t7, 0(a0) // get low part of dest.
+ insql t4, t3, t6 // insert source bytes into position
+ zap t7, t9, t8 // clear dest
+ bis t8, t6, t7 // merge bytes to store
+ stq_u t7, 0(a0) // store merged result
+
+//
+// Check to see whether the bytes to store extend into the next
+// quadword of the destination.
+//
+ cmpult t10, 8, t9
+ bne t9, 70f // ending offset < 8, next quadword unaffected
+ insqh t4, t3, t6 // insert source bytes into position
+ stq_u t6, 8(a0) // store merged results
+
+70:
+ addq a0, t2, a0 // adjust destination pointer
+ addq a2, t2, a2 // adjust source pointer
+ addq a5, t2, a5 // adjust bytes copied
+ br zero, 10b
+
+UpdateFormat:
+ subq a0, a5, t10 // compute original pointer
+ subq v0, t10, t7 // compute current max displacement
+ sll t7, 1, t7 //
+ addq t7, t10, v0 // compute new max displacemnt
+ srl t11, 1, t11 // compute new length mask
+ subq t12, 1, t12 // compute new displacement shift
+ br zero, 50b // start again.
+
+//
+// a0 - the destination
+// a1 - the last byte of the destination
+// t1 - flag byte
+//
+CopyTailFlag:
+ ldil t2, 8 // set count of bits to process
+CopyTail:
+ cmpult a0, a1, t10
+ beq t10, SuccessExit // finished
+ cmpule a2, a3, t10
+ beq t10, SuccessExit // finished
+ blbc t1, CT15 // skip copy token
+ cmpeq a2, a3, t10
+ beq t10, CopyToken // more than one byte left
+ br zero, ErrorExit // only one byte left, error
+CT15:
+ ldq_u t10, 0(a2)
+ extbl t10, a2, t5
+ ldq_u t7, 0(a0)
+ insbl t5, a0, t6
+ mskbl t7, a0, t8
+ bis t6, t8, t7
+ stq_u t7, 0(a0)
+ addq a0, 1, a0
+ addq a2, 1, a2
+ srl t1, 1, t1
+ subq t2, 1, t2
+ addq a5, 1, a5
+ bne t2, CopyTail
+ cmpule a2, a3, t10
+ beq t10, SuccessExit // finished
+ ldq_u t0, 0(a2) // load flag byte and any subsequent bytes
+ extbl t0, a2, t1 // extract flag byte
+ addq a2, 1, a2
+ br zero, CopyTailFlag
+
+SuccessExit:
+ bis zero, zero, v0
+ stl a5, 0(a4)
+ ret zero, (ra)
+
+ErrorExit:
+ ldil v0, STATUS_BAD_COMPRESSION_BUFFER
+ ret zero, (ra)
+ .end LZNT1DecompressChunk
diff --git a/private/ntos/rtl/alpha/mvmem.s b/private/ntos/rtl/alpha/mvmem.s
new file mode 100644
index 000000000..c5ccc9a81
--- /dev/null
+++ b/private/ntos/rtl/alpha/mvmem.s
@@ -0,0 +1,1920 @@
+// TITLE("Compare, Move, Zero, and Fill Memory Support")
+//++
+//
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// mvmem.s
+//
+// Abstract:
+//
+// This module implements functions to compare, move, zero, and fill
+// blocks of memory. If the memory is aligned, then these functions
+// are very efficient.
+//
+// N.B. These routines MUST preserve all floating state since they are
+// frequently called from interrupt service routines that normally
+// do not save or restore floating state.
+//
+// Author:
+//
+// Joe Notarangelo 21-May-1992
+//
+// Environment:
+//
+// User or Kernel mode.
+//
+// Revision History:
+//
+// Monty VanderBilt 14-Feb-1996 Avoid memory loads and branch takens between
+// load lock and store conditional instructions
+// to conform with all alpha architecture rules.
+// Monty VanderBilt 27-Feb-1996 Added RtlZeroBytes and RtlFillBytes to support
+// byte granularity access when necessary.
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Compare Memory")
+//++
+//
+// ULONG
+// RtlCompareMemory (
+// IN PVOID Source1,
+// IN PVOID Source2,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function compares two blocks of memory and returns the number
+// of bytes that compared equal.
+//
+// Arguments:
+//
+// Source1 (a0) - Supplies a pointer to the first block of memory to
+// compare.
+//
+// Source2 (a1) - Supplies a pointer to the second block of memory to
+// compare.
+//
+// Length (a2) - Supplies the length, in bytes, of the memory to be
+// compared.
+//
+// Return Value:
+//
+// The number of bytes that compared equal is returned as the function
+// value. If all bytes compared equal, then the length of the orginal
+// block of memory is returned.
+//
+//--
+
+
+ LEAF_ENTRY(RtlCompareMemory)
+
+ bis a2, zero, v0 // save length of comparison
+ beq a2, 90f // (JAE) quit if nothing to compare
+ xor a0, a1, t0 // check for compatible alignment
+ and t0, 0x7, t0 // low bits only
+ bne t0, CompareUnaligned // if ne, incompatible alignment
+
+//
+// Compare memory aligned
+//
+
+CompareAligned: //
+
+//
+// compare memory until sources are aligned
+//
+ and a0, 0x7, t0 // get low bits
+ bne t0, 10f // if ne, sources not aligned yet
+ br zero, 30f // already aligned, predicted
+
+
+10:
+ ldq_u t1, 0(a0) // get unaligned quad at source 1
+ ldq_u t2, 0(a1) // get unaligned quad at source 2
+
+20:
+ extbl t1, t0, t4 // byte at t0 in source 1 quad
+ extbl t2, t0, t5 // byte at t0 in source 2 quad
+ xor t4, t5, t3 // t1 = t2 ?
+ bne t3, 110f // not equal, miscompare
+ subq a2, 1, a2 // decrement bytes to compare
+ beq a2, 90f // if eq, compare success
+ addq t0, 1, t0 // increment pointer within quad
+ cmpeq t0, 8, t3 // t0 = 8?, if so first quadword done
+ beq t3, 20b // continue while t0 < 8
+
+
+ addq a0, 8, a0 // increment to next quadword
+ addq a1, 8, a1 // increment source 2 to next also
+ bic a0, 7, a0 // align source 1 quadword
+ bic a1, 7, a1 // align source 2 quadword
+
+
+//
+// aligned block compare, compare blocks of 64 bytes
+//
+
+30:
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 50f // if eq, no 64 byte blocks
+
+//
+// N.B. loads from each of the sources were separated in case these
+// blocks are fighting for the cache
+//
+ .set noat
+40:
+ ldq t1, 0(a0) // t1 = source 1, quad 0
+ ldq t2, 8(a0) // t2 = source 1, quad 1
+ ldq t3, 16(a0) // t3 = source 1, quad 2
+ addq a1, 64, a1 // increment source 2 pointer
+ ldq t4, 24(a0) // t4 = source 1, quad 3
+
+ ldq t5, -64(a1) // t5 = source 2, quad 0
+ ldq a4, -56(a1) // a4 = source 2, quad 1
+ ldq a5, -48(a1) // a5 = source 2, quad 2
+ xor t1, t5, $at // quad 0 match?
+ bne $at, 200f // if ne[false], miscompare
+ ldq t5, -40(a1) // t5 = source 2, quad 3
+ ldq t1, 32(a0) // t1 = source 1, quad 4
+ xor t2, a4, $at // quad 1 match?
+ bne $at, 122f // if ne[false], miscompare
+ ldq t2, 40(a0) // t2 = source 1, quad 5
+ xor t3, a5, $at // quad 2 match?
+ bne $at, 124f // if ne[false], miscompare
+ ldq t3, 48(a0) // t3 = source 1, quad 6
+ xor t4, t5, $at // quad 3 match?
+ bne $at, 126f // if ne[false], miscompare
+ ldq t4, 56(a0) // t4 = source 1, quad 7
+
+ ldq t5, -32(a1) // t5 = source 2, quad 4
+ addq a0, 64, a0 // increment source 1 pointer
+ ldq a4, -24(a1) // a4 = source 2, quad 5
+ subq t0, 1, t0 // decrement blocks to compare
+ ldq a5, -16(a1) // a5 = source 2, quad 6
+ xor t1, t5, $at // quad 4 match?
+ bne $at, 130f // if ne[false], miscompare
+ ldq t5, -8(a1) // t5 = source 2, quad 7
+ xor t2, a4, $at // quad 5 match?
+ bne $at, 132f // if ne[false], miscompare
+ xor t3, a5, $at // quad 6 match?
+ bne $at, 134f // if ne[false], miscompare
+ xor t4, t5, $at // quad 7 match?
+ bne $at, 136f // if ne[false], miscompare
+ subq a2, 64, a2 // decrement bytes to compare
+ bne t0, 40b // if ne, more blocks to compare
+ .set at
+
+
+//
+// Compare quadwords
+//
+
+50:
+ srl a2, 3, t0 // t0 = number of quadwords to compare
+ beq t0, 70f // if eq, no quadwords to compare
+
+ .set noat
+60:
+ ldq t1, 0(a0) // t1 = quad from source 1
+ lda a0, 8(a0) // increment source 1 pointer
+ ldq t2, 0(a1) // t2 = quad from source 2
+ lda a1, 8(a1) // increment source 2 pointer
+ xor t1, t2, $at // are quadwords equal?
+ bne $at, 200f // if ne, miscompare
+ subq t0, 1, t0 // decrement quads to compare
+ subq a2, 8, a2 // decrement bytes to compare
+ bne t0, 60b // if ne, more quads to compare
+
+ .set at
+
+//
+// Compare bytes in last quadword
+//
+
+// a2 = number of bytes to compare, less than 8, greater than zero
+// a0, a1, quad-aligned to last quadword
+
+ beq a2, 80f // if eq, all bytes compared
+
+ .set noat
+70:
+ ldq t1, 0(a0) // t1 = quad at source 1
+ ldq t2, 0(a1) // t2 = quad at source 2
+ bis zero, 0xff, t0 // zap mask
+ sll t0, a2, t0 //
+ zap t1, t0, t1 // zero bytes not compared
+ zap t2, t0, t2 // same for source 2
+ xor t1, t2, $at // compare quadwords
+ bne $at, 200f // if ne, miscompare
+
+ .set at
+//
+// Successful compare
+// v0 already contains full length
+//
+
+80:
+ ret zero, (ra) // return
+
+
+//
+// Sources have incompatible alignment
+//
+CompareUnaligned:
+
+
+//
+// Compare until source 1 (a0) is aligned
+//
+
+ and a0, 0x7, t0 // get byte position of pointer
+ beq t0, 30f // if eq, already aligned
+
+ ldq_u t1, 0(a0) // get unaligned quad at a0
+
+10:
+ ldq_u t2, 0(a1) // get unaligned quad at a1
+ extbl t1, t0, t4 // get byte to compare from source 1
+ extbl t2, a1, t2 // get byte to compare from source 2
+ xor t4, t2, t3 // do bytes match?
+ bne t3, 110f // if ne, miscompare
+ subq a2, 1, a2 // decrement bytes to compare
+ beq a2, 90f // (JAE) quit if nothing left to compare
+ addq t0, 1, t0 // increment byte within source 1
+ addq a1, 1, a1 // increment source 2 pointer
+ cmpeq t0, 8, t3 // finished with source 1 quad?
+ beq t3, 10b // if eq[false], more to compare
+
+ addq a0, 7, a0 // point to next source 1 quad
+ bic a0, 7, a0 // align to quadword
+
+
+//
+// Compare 64-byte blocks
+//
+
+30:
+ srl a2, 6, t0 // t0 = number of blocks to compare
+ beq t0, 50f // if eq, no blocks to move
+
+ ldq_u t1, 0(a1) // get source 2 unaligned quad 1
+
+ .set noat
+40:
+ ldq_u t2, 7(a1) // get source 2 unaligned quad 2
+ addq a0, 64, a0 // increment source 1 pointer
+ ldq_u t3, 15(a1) // get source 2 unaligned quad 3
+ extql t1, a1, t1 // bytes from unaligned quad 1
+ extqh t2, a1, $at // bytes from unaligned quad 2
+ ldq_u t4, 23(a1) // get source 2 unaligned quad 4
+ bis t1, $at, t1 // t1 = quadword 1 (source 2)
+ ldq_u t5, 31(a1) // get source 2 unaligned quad 5
+ extql t2, a1, t2 // bytes from unaligned quad 2
+ extqh t3, a1, $at // bytes from unaligned quad 3
+ ldq a3, -64(a0) // a3 = quadword 1 (source 1)
+ bis t2, $at, t2 // t2 = quadword 2 (source 2)
+ ldq a4, -56(a0) // a4 = quadword 2 (source 1)
+ extql t3, a1, t3 // bytes from unaligned quad 3
+ extqh t4, a1, $at // bytes from unaligned quad 4
+ ldq a5, -48(a0) // a5 = quadword 3 (source 1)
+ bis t3, $at, t3 // t3 = quadword 3 (source 2)
+ extql t4, a1, t4 // bytes from unaligned quad 4
+ extqh t5, a1, $at // bytes from unaligned quad 5
+ subq t0, 1, t0 // decrement blocks to compare
+ bis t4, $at, t4 // t4 = quadword 4 (source 2)
+
+ xor t1, a3, $at // match on quadword 1?
+ ldq a3, -40(a0) // a3 = quadword 4 (source 1)
+ bne $at, 200f // if ne, miscompare quad 1
+ xor t2, a4, $at // match on quadword 2?
+ ldq_u t2, 39(a1) // get source 2 unaligned quad 6
+ bne $at, 122f // if ne, miscompare quad 2
+ xor t3, a5, $at // match on quadword 3?
+ ldq_u t3, 47(a1) // get source 2 unaligned quad 7
+ bne $at, 124f // if ne, miscompare quad 3
+ xor t4, a3, $at // match on quadword 4?
+ ldq_u t4, 55(a1) // get source 2 unaligned quad 8
+ bne $at, 126f // if ne, miscompare quad 4
+ ldq_u t1, 63(a1) // get source 2 unaligned quad 9
+
+ ldq a3, -32(a0) // a3 = quadword 5 (source 1)
+ extql t5, a1, t5 // bytes from unaligned quad 5
+ extqh t2, a1, $at // bytes from unaligned quad 6
+ ldq a4, -24(a0) // a4 = quadword 6 (source 1)
+ ldq a5, -16(a0) // a5 = quadword 7 (source 1)
+ bis t5, $at, t5 // t5 = quadword 5 (source 2)
+
+ xor t5, a3, $at // match on quadword 5?
+ ldq a3, -8(a0) // a3 = quadword 8 (source 1)
+ bne $at, 130f // if ne, miscompare quad 5
+ extql t2, a1, t2 // bytes from unaligned quad 6
+ extqh t3, a1, $at // bytes from unaligned quad 7
+ extql t3, a1, t3 // bytes from unaligned quad 7
+ bis t2, $at, t2 // t2 = quadword 6 (source 2)
+ xor t2, a4, $at // match on quadword 6?
+ bne $at, 132f // if ne, miscompare quad 6
+ extqh t4, a1, $at // bytes from unaligned quad 8
+ extql t4, a1, t4 // bytes from unaligned quad 8
+ bis t3, $at, t3 // t3 = quadword 7 (source 2)
+ xor t3, a5, $at // match on quadword 7?
+ bne $at, 134f // if ne, miscompare quad 7
+ extqh t1, a1, $at // bytes from unaligned quad 9
+ addq a1, 64, a1 // increment source 2 pointer
+ bis t4, $at, t4 // t4 = quadword 8 (source 2)
+ xor t4, a3, $at // match on quadword 8?
+ bne $at, 136f // if ne, miscompare quad 8
+ subq a2, 64, a2 // decrement number of bytes to compare
+ bne t0, 40b // if ne, more blocks to compare
+
+ .set at
+
+//
+// Compare quadwords
+//
+
+
+50:
+ srl a2, 3, t0 // t0 = number of quads to compare
+ beq t0, 70f // if eq, no quads to compare
+ ldq_u t1, 0(a1) // get unaligned quad 1 (source 2)
+
+ .set noat
+60:
+ ldq_u t2, 7(a1) // get unaligned quad 2 (source 2)
+ ldq t3, 0(a0) // t3 = quadword 1 (source 1)
+ extql t1, a1, t1 // get bytes from unaligned quad 1
+ extqh t2, a1, $at // get bytes from unaligned quad 2
+ addq a1, 8, a1 // increment source 2 pointer
+ bis t1, $at, t1 // t1 = quadword 1 (source 2)
+ xor t1, t3, $at // match on quadword?
+ bne $at, 200f // if ne, miscompare
+ subq t0, 1, t0 // decrement quadwords to compare
+ addq a0, 8, a0 // increment source 1 pointer
+ subq a2, 8, a2 // decrement bytes to compare
+ bis t2, zero, t1 // save low quadword for next loop
+ bne t0, 60b // if ne, more quads to compare
+
+ .set at
+
+//
+// Compare bytes for final quadword
+//
+
+70:
+ beq a2, 90f // if eq, comparison complete
+
+ ldq t1, 0(a0) // get quadword from source 1
+ bis zero, zero, t0 // t0 = byte position to compare
+
+ .set noat
+80:
+ ldq_u t2, 0(a1) // get unaligned quad from source 2
+ extbl t1, t0, t3 // t3 = byte from source 1
+ extbl t2, a1, t2 // t2 = byte from source 2
+ xor t3, t2, $at // match on byte?
+ bne $at, 100f // if ne, miscompare on byte
+ addq t0, 1, t0 // increment byte position
+ addq a1, 1, a1 // increment source 2 pointer
+ subq a2, 1, a2 // decrement bytes to compare
+ bne a2, 80b // if ne, more bytes to compare
+
+ .set at
+//
+// Successful full comparison
+//
+
+90:
+ ret zero, (ra) // return, v0 already set
+
+
+//
+// Miscompare on last quadword
+//
+
+100:
+ subq v0, a2, v0 // subtract bytes not compared
+ ret zero, (ra) // return
+
+//
+// Miscompare on first quadword, unaligned case
+//
+// v0 = total bytes to compare
+// a2 = bytes remaining to compare
+//
+
+110:
+ subq v0, a2, v0 // bytes compared successfully
+ ret zero, (ra) // return
+
+//
+// Miscompare on 64-byte block compare
+//
+
+122:
+ subq a2, 8, a2 // miscompare on quad 2
+ br zero, 200f // finish in common code
+
+124:
+ subq a2, 16, a2 // miscompare on quad 3
+ br zero, 200f // finish in common code
+
+126:
+ subq a2, 24, a2 // miscompare on quad 4
+ br zero, 200f // finish in common code
+
+130:
+ subq a2, 32, a2 // miscompare on quad 5
+ br zero, 200f // finish in common code
+
+132:
+ subq a2, 40, a2 // miscompare on quad 6
+ br zero, 200f // finish in common code
+
+134:
+ subq a2, 48, a2 // miscompare on quad 7
+ br zero, 200f // finish in common code
+
+136:
+ subq a2, 56, a2 // miscompare on quad 8
+ br zero, 200f // finish in common code
+
+//
+// Miscompare, determine number of bytes that successfully compared
+// $at = xor of relevant quads from sources, must be non-zero
+// a2 = number of bytes left to compare
+//
+ .set noat
+200:
+ cmpbge zero, $at, $at // $at = mask of non-zero bytes
+
+ //
+ // look for the first bit cleared in $at, this is the
+ // number of the first byte which differed
+ //
+ bis zero, zero, t0 // bit position to look for clear
+
+210:
+ blbc $at, 220f // if low clear, found difference
+ srl $at, 1, $at // check next bit
+ addq t0, 1, t0 // count bit position checked
+ br zero, 210b
+
+220:
+ subq v0, a2, v0 // subtract bytes yet to compare
+ addq v0, t0, v0 // add bytes that matched on last quad
+
+ ret zero, (ra)
+
+ .set at
+
+ .end RtlCompareMemory
+
+
+
+ SBTTL("Move Memory")
+//++
+//
+// VOID
+// RtlMoveMemory (
+// IN PVOID Destination,
+// IN PVOID Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function moves memory either forward or backward, aligned or
+// unaligned, in 64-byte blocks, followed by 8-byte blocks, followed
+// by any remaining bytes.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the destination address of
+// the move operation.
+//
+// Source (a1) - Supplies a pointer to the source address of the move
+// operation.
+//
+// Length (a2) - Supplies the length, in bytes, of the memory to be moved.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlMoveMemory)
+
+ beq a2, 80f // if eq, no bytes to move
+//
+// If the source address is less than the destination address and source
+// address plus the length of the move is greater than the destination
+// address, then the source and destination overlap such that the move
+// must be performed backwards.
+//
+
+ cmpult a0, a1, t0 // is destination less than source
+ bne t0, MoveForward // if eq [true] no overlap possible
+ addq a1, a2, t0 // compute source ending address
+ cmpult t0, a0, t1 // is source end less than dest.
+ beq t1, MoveBackward // if eq [false], overlap
+
+//
+// Move memory forward aligned and unaligned.
+//
+
+MoveForward: //
+ xor a0, a1, t0 // compare alignment bits
+ and t0, 0x7, t0 // isloate alignment comparison
+ bne t0, MoveForwardUnaligned // if ne, incompatible alignment
+
+//
+// Move memory forward aligned.
+//
+
+MoveForwardAligned: //
+
+//
+// Move bytes until source and destination are quadword aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, not quad aligned
+ br zero, 20f // predicted taken
+
+5:
+ ldq_u t2, 0(a0) // get unaligned quad from dest.
+ ldq_u t1, 0(a1) // get unaligned quadword from source
+10:
+ beq a2, 15f // if eq, all bytes moved
+ extbl t1, t0, t3 // t3 = byte from source
+ insbl t3, t0, t3 // t3 = byte from source, in position
+ mskbl t2, t0, t2 // clear position in dest. quad
+ bis t2, t3, t2 // merge in byte from source
+ subq a2, 1, a2 // decrement bytes to move
+ addq t0, 1, t0 // increment byte within quad
+ cmpeq t0, 8, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+15:
+ stq_u t2, 0(a0) // store merged destination bytes
+
+ addq a0, 7, a0 // move to next quadword
+ bic a0, 7, a0 // aligned quadword
+
+ addq a1, 7, a1 // move to next quadword
+ bic a1, 7, a1 // aligned quadword
+
+//
+// Check for 64-byte block moves
+//
+
+20:
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes
+
+30:
+ ldq t1, 0(a1) // load 64 bytes from source
+ addq a0, 64, a0 // increment destination pointer
+ ldq v0, 56(a1) //
+ ldq a3, 32(a1) //
+ stq t1, -64(a0) // write to destination
+ ldq t2, 8(a1) // into volatile registers
+ ldq t3, 16(a1) //
+ ldq t4, 24(a1) //
+ subq t0, 1, t0 // decrement number of blocks
+ stq t2, -56(a0) //
+ ldq a4, 40(a1) //
+ stq t3, -48(a0) //
+ ldq a5, 48(a1) //
+ stq t4, -40(a0) //
+ addq a1, 64, a1 // increment source pointer
+ stq a3, -32(a0) //
+ stq a4, -24(a0) //
+ stq a5, -16(a0) //
+ stq v0, -8(a0) //
+ bne t0, 30b // if ne, more blocks to copy
+
+//
+// Copy quadwords
+//
+
+40:
+ srl a2, 3, t0 // t0 = number of quadwords to move
+ beq t0, 60f // if eq no quadwords to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+50:
+ ldq t1, 0(a1) // load quadword from source
+ addq a1, 8, a1 // increment source pointer
+ stq t1, 0(a0) // store quadword to destination
+ addq a0, 8, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of quadwords
+ bne t0, 50b // if ne, more quadwords to move
+
+//
+// Move final residual bytes
+//
+
+60:
+ beq a2, 80f // if eq, no more bytes to move
+ ldq t1, 0(a1) // get last source quadword
+ ldq t2, 0(a0) // get last dest. quadword
+ bis zero, zero, t0 // t0 = next byte number to move
+
+70:
+ extbl t1, t0, t3 // extract byte from source
+ insbl t3, t0, t3 // t3 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position for dest.
+ bis t2, t3, t2 // merge in source byte
+ addq t0, 1, t0 // increment byte position
+ subq a2, 1, a2 // decrement bytes to move
+ bne a2, 70b // if ne => more bytes to move
+
+ stq t2, 0(a0) // store merged data
+
+//
+// Finish aligned MoveForward
+//
+
+80:
+ ret zero, (ra) // return
+
+
+
+//
+// Move memory forward unaligned.
+//
+
+MoveForwardUnaligned: //
+
+
+//
+// Move bytes until the destination is aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ beq t0, 100f // if eq, destination quad aligned
+
+ ldq_u t2, 0(a0) // get unaligned quad from dest
+
+90:
+ beq a2, 95f // if eq no more bytes to move
+ ldq_u t1, 0(a1) // get unaligned quad from source
+ extbl t1, a1, t1 // extract source byte
+ insbl t1, t0, t1 // t1 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position in dest.
+ bis t2, t1, t2 // merge in source byte
+ addq t0, 1, t0 // increment byte position
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ cmpeq t0, 8, t3 // t0 = 8? => quad finished
+ beq t3, 90b // if eq [false], more bytes to move
+95:
+ stq_u t2, 0(a0) // store merged quadword
+ addq a0, 7, a0 // increment to next quad
+ bic a0, 7, a0 // align next quadword
+
+//
+// Check for 64-byte blocks to move
+//
+
+100:
+ srl a2, 6, t0 // t0 = number of blocks to move
+ beq t0, 120f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes to move
+
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+
+110:
+ // get source data and merge it
+ // as we go
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quad #1
+ ldq_u t3, 15(a1) // t3 = third unaligned quad
+ extql t2, a1, t2 // extract applicable bytes from t2
+ extqh t3, a1, v0 // extract applicable bytes from t3
+ stq t1, 0(a0) // store quad #1
+ bis t2, v0, t2 // t2 = quad #2
+ ldq_u t4, 23(a1) // t4 = fourth unaligned quad
+ extql t3, a1, t3 // extract applicable bytes from t3
+ extqh t4, a1, v0 // extract applicable bytes from t4
+ stq t2, 8(a0) // store quad #2
+ bis t3, v0, t3 // t3 = quad #3
+ ldq_u t5, 31(a1) // t5 = fifth unaligned quad
+ extql t4, a1, t4 // extract applicable bytes from t4
+ extqh t5, a1, v0 // extract applicable bytes from t5
+ stq t3, 16(a0) // store quad #3
+ bis t4, v0, t4 // t4 = quad #4
+ ldq_u a3, 39(a1) // a3 = sixth unaligned quad
+ extql t5, a1, t5 // extract applicable bytes from t5
+ extqh a3, a1, v0 // extract applicable bytes from a3
+ stq t4, 24(a0) // store quad #4
+ bis t5, v0, t5 // t5 = quad #5
+ ldq_u a4, 47(a1) // a4 = seventh unaligned quad
+ extql a3, a1, a3 // extract applicable bytes from a3
+ extqh a4, a1, v0 // extract applicable bytes from a4
+ stq t5, 32(a0) // store quad #5
+ bis a3, v0, a3 // a3 = quad #6
+ ldq_u a5, 55(a1) // a5 = eighth unaligned quad
+ extql a4, a1, a4 // extract applicable bytes from a4
+ extqh a5, a1, v0 // extract applicable bytes from a5
+ stq a3, 40(a0) // store quad #6
+ bis a4, v0, a4 // a4 = quad #7
+ ldq_u t1, 63(a1) // t1 = ninth unaligned = 1st of next
+ extql a5, a1, a5 // extract applicable bytes from a5
+ extqh t1, a1, v0 // extract applicable bytes from t1
+ stq a4, 48(a0) // store quad #7
+ bis a5, v0, a5 // a5 = quad #8
+ addq a1, 64, a1 // increment source pointer
+ stq a5, 56(a0) // store quad #8
+ addq a0, 64, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of blocks
+ bne t0, 110b // if ne, more blocks to move
+
+//
+// Move unaligned source quads to aligned destination quads
+//
+
+120:
+ srl a2, 3, t0 // t0 = number of quads to move
+ beq t0, 140f // if eq no quads to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+130:
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ addq a0, 8, a0 // increment destination pointer
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quadword of data
+ stq t1, -8(a0) // store data to destination
+ addq a1, 8, a1 // increment source pointer
+ subq t0, 1, t0 // decrement quads to move
+ bis t2, zero, t1 // t1 = first of next unaligned pair
+ bne t0, 130b // if ne, more quads to move
+
+//
+// Move remaining bytes to final quadword
+//
+
+
+140:
+ beq a2, 160f // if eq no more bytes to move
+ ldq t2, 0(a0) // t2 = destination quadword
+ bis zero, zero, t3 // t3 = position for next insertion
+
+150:
+ ldq_u t1, 0(a1) // get unaligned source quad
+ extbl t1, a1, t1 // t1 = source byte
+ insbl t1, t3, t1 // t1 = source byte, in position
+ mskbl t2, t3, t2 // clear byte in destination
+ bis t2, t1, t2 // merge in source byte
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ addq t3, 1, t3 // increment destination position
+ bne a2, 150b // more bytes to move
+
+ stq t2, 0(a0) // store merged data
+
+//
+// Finish unaligned MoveForward
+//
+
+160:
+ ret zero, (ra) // return
+
+
+//
+// Move memory backward.
+//
+
+MoveBackward: //
+
+ addq a0, a2, a0 // compute ending destination address
+ addq a1, a2, a1 // compute ending source address
+ subq a0, 1, a0 // point to last destination byte
+ subq a1, 1, a1 // point to last source byte
+ xor a0, a1, t0 // compare alignment bits
+ and t0, 0x7, t0 // isolate alignment comparison
+ bne t0, MoveBackwardUnaligned // if ne, incompatible alignment
+
+//
+// Move memory backward aligned.
+//
+
+MoveBackwardAligned: //
+
+//
+// Move bytes until source and destination are quadword aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ cmpeq t0, 7, t1 // last byte position 7?
+ beq t1, 5f // if eq [false], not quad aligned
+ subq a0, 7, a0 // point to beginning of last quad
+ subq a1, 7, a1 // point to beginning of last quad
+ br zero, 30f // predicted taken
+
+5:
+ ldq_u t1, 0(a0) // get unaligned quad from dest.
+ ldq_u t2, 0(a1) // get unaligned quad from source
+
+10:
+ beq a2, 20f // if eq, all bytes moved
+ extbl t2, t0, t3 // t3 = byte from source
+ insbl t3, t0, t3 // t3 = byte from source, in position
+ mskbl t1, t0, t1 // clear position in destination
+ bis t1, t3, t1 // merge in byte from source
+ subq a2, 1, a2 // decrement bytes to move
+ subq t0, 1, t0 // decrement byte within quadword
+ cmplt t0, zero, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+
+20:
+ stq_u t1, 0(a0) // store merged destination bytes
+
+ subq a0, 8, a0 // move to previous quadword
+ bic a0, 7, a0 // aligned quadword
+
+ subq a1, 8, a1 // move to previous quadword
+ bic a1, 7, a1 // aligned quadword
+
+//
+// Check for 64-byte block moves
+//
+
+30:
+
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 50f // if eq, no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes
+
+40:
+ ldq t1, 0(a1) // load 64 bytes from source into
+ subq a0, 64, a0 // decrement destination pointer
+ ldq v0, -56(a1) //
+ ldq a3, -32(a1) //
+ stq t1, 64(a0) // write to destination
+ ldq t2, -8(a1) // into volatile registers
+ ldq a5, -48(a1) //
+ ldq a4, -40(a1) //
+ stq t2, 56(a0) //
+ ldq t3, -16(a1) //
+ ldq t4, -24(a1) //
+ subq a1, 64, a1 // decrement source pointer
+ stq t3, 48(a0) //
+ stq t4, 40(a0) //
+ stq a3, 32(a0) //
+ subq t0, 1, t0 // decrement number of blocks
+ stq a4, 24(a0) //
+ stq a5, 16(a0) //
+ stq v0, 8(a0) //
+ bne t0, 40b // if ne, more blocks to copy
+
+//
+// Copy quadwords
+//
+
+50:
+ srl a2, 3, t0 // t0 = number of quadwords to move
+ beq t0, 70f // if eq no quadwords to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+60:
+ ldq t1, 0(a1) // load quadword from source
+ subq a1, 8, a1 // decrement source pointer
+ stq t1, 0(a0) // store quadword to destination
+ subq a0, 8, a0 // decrement destination pointer
+ subq t0, 1, t0 // decrement quadwords to move
+ bne t0, 60b // if ne, more quadwords to move
+
+//
+// Move final residual bytes
+//
+
+70:
+ beq a2, 90f // if eq, no more bytes to move
+ ldq t1, 0(a1) // get last source quadword
+ ldq t2, 0(a0) // get last destination quadword
+ bis zero, 7, t0 // t0 = next byte number to move
+
+80:
+ extbl t1, t0, t3 // extract byte from source
+ insbl t3, t0, t3 // t3 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position for dest.
+ bis t2, t3, t2 // merge in source byte
+ subq t0, 1, t0 // decrement byte position
+ subq a2, 1, a2 // decrement bytes to move
+ bne a2, 80b // if ne, more bytes to move
+
+ stq t2, 0(a0) // write destination data
+//
+// Finish aligned MoveBackward
+//
+
+90:
+
+ ret zero, (ra) // return
+
+
+//
+// Move memory backward unaligned.
+//
+
+MoveBackwardUnaligned: //
+
+
+//
+// Move bytes until the destination is aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ cmpeq t0, 7, t1 // last byte of a quadword
+ beq t1, 95f // if eq[false], not aligned
+ subq a0, 7, a0 // align pointer to beginning of quad
+ br zero, 120f //
+
+95:
+ ldq_u t2, 0(a0) // get unaligned quad from dest.
+
+100:
+ beq a2, 110f // if eq, no more bytes to move
+ ldq_u t1, 0(a1) // get unaligned quad from source
+ extbl t1, a1, t1 // extract source byte
+ insbl t1, t0, t1 // t1 = source byte in position
+ mskbl t2, t0, t2 // clear byte position in dest.
+ bis t2, t1, t2 // merge source byte
+ subq t0, 1, t0 // decrement byte position
+ subq a1, 1, a1 // decrement source pointer
+ subq a2, 1, a2 // decrement number of bytes to move
+ cmplt t0, zero, t3 // t0 < 0? => quad finished
+ beq t3, 100b // if eq [false], more bytes to move
+
+110:
+ stq_u t2, 0(a0) // store merged quadword
+
+ subq a0, 8, a0 // decrement dest. to previous quad
+ bic a0, 7, a0 // align previous quadword
+
+//
+// Check for 64-byte blocks to move
+//
+
+120:
+
+ srl a2, 6, t0 // t0 = number of blocks to move
+ subq a1, 7, a1 // point to beginning of last quad
+ beq t0, 140f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes to move
+
+ ldq_u t1, 7(a1) // t1 = first unaligned quad
+
+130:
+ // get source data and merge it
+ // as we go
+ ldq_u t2, 0(a1) // t2 = second unaligned quad
+ extqh t1, a1, t1 // extract applicable bytes from t1
+ extql t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quad #1
+ ldq_u t3, -8(a1) // t3 = third unaligned quad
+ extqh t2, a1, t2 // extract applicable bytes from t2
+ extql t3, a1, v0 // extract applicable bytes from t3
+ stq t1, 0(a0) // store quad #1
+ bis t2, v0, t2 // t2 = quad #2
+ ldq_u t4, -16(a1) // t4 = fourth unaligned quad
+ extqh t3, a1, t3 // extract applicable bytes from t3
+ extql t4, a1, v0 // extract applicable bytes from t4
+ stq t2, -8(a0) // store quad #2
+ bis t3, v0, t3 // t3 = quad #3
+ ldq_u t5, -24(a1) // t5 = fifth unaligned quad
+ extqh t4, a1, t4 // extract applicable bytes from t4
+ extql t5, a1, v0 // extract applicable bytes from t5
+ stq t3, -16(a0) // store quad #3
+ bis t4, v0, t4 // t4 = quad #4
+ ldq_u a3, -32(a1) // a3 = sixth unaligned quad
+ extqh t5, a1, t5 // extract applicable bytes from t5
+ extql a3, a1, v0 // extract applicable bytes from a3
+ stq t4, -24(a0) // store quad #4
+ bis t5, v0, t5 // t5 = quad #5
+ ldq_u a4, -40(a1) // a4 = seventh unaligned quad
+ extqh a3, a1, a3 // extract applicable bytes from a3
+ extql a4, a1, v0 // extract applicable bytes from a4
+ stq t5, -32(a0) // store quad #5
+ bis a3, v0, a3 // a3 = quad #6
+ ldq_u a5, -48(a1) // a5 = eighth unaligned quad
+ extqh a4, a1, a4 // extract applicable bytes from a4
+ extql a5, a1, v0 // extract applicable bytes from a5
+ stq a3, -40(a0) // store quad #6
+ bis a4, v0, a4 // a4 = quad #7
+ ldq_u t1, -56(a1) // t1 = ninth unaligned = 1st of next
+ extqh a5, a1, a5 // extract applicable bytes from a5
+ extql t1, a1, v0 // extract applicable bytes from t1
+ stq a4, -48(a0) // store quad #7
+ bis a5, v0, a5 // a5 = quad #8
+ subq a1, 64, a1 // increment source pointer
+ stq a5, -56(a0) // store quad #8
+ subq a0, 64, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of blocks
+ bne t0, 130b // if ne, more blocks to move
+
+
+//
+// Move unaligned source quads to aligned destination quads
+//
+
+140:
+ srl a2, 3, t0 // t0 = number of quads to move
+ beq t0, 160f // if eq no quads to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+ ldq_u t1, 7(a1) // t1 = first unaligned quad
+
+150:
+ ldq_u t2, 0(a1) // t2 = second unaligned quad
+ subq a0, 8, a0 // decrement destination pointer
+ extqh t1, a1, t1 // extract applicable bytes from t1
+ extql t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quadword of data
+ stq t1, 8(a0) // store data to destination
+ subq a1, 8, a1 // decrement source pointer
+ subq t0, 1, t0 // decrement quads to move
+ bis t2, zero, t1 // t1 = first of next unaligned pair
+ bne t0, 150b // if ne, more quads to move
+
+//
+// Move remaining bytes to final quadword
+//
+
+160:
+ beq a2, 180f // if eq, no more bytes to move
+ ldq t2, 0(a0) // t2 = destination quadword
+ bis zero, 7, t0 // t0 = position for next insertion
+
+170:
+ subq a1, 1, a1 // decrement source pointer
+ ldq_u t1, 8(a1) // get unaligned source quad
+ extbl t1, a1, t1 // t1 = source byte
+ insbl t1, t0, t1 // t1 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position
+ bis t2, t1, t2 // merge in source byte
+ subq t0, 1, t0 // decrement byte position for dest.
+ subq a2, 1, a2 // decrement bytes to move
+ bne a2, 170b // if ne, more bytes to move
+
+ stq t2, 0(a0) //
+
+//
+// Finish unaligned MoveBackward
+//
+
+180:
+ ret zero, (ra) // return
+
+ .end RtlMoveMemory
+
+ SBTTL("Zero Memory")
+//++
+//
+// VOID
+// RtlZeroMemory (
+// IN PVOID Destination,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function zeros memory by first aligning the destination address to
+// a quadword boundary, and then zeroing 64-byte blocks, followed by 8-byte
+// blocks, followed by any remaining bytes.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to zero.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be zeroed.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlZeroMemory)
+
+ bis zero, zero, a2 // set fill pattern
+ br zero, RtlpFillMemory //
+
+
+ SBTTL("Fill Memory")
+//++
+//
+// VOID
+// RtlFillMemory (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN UCHAR Fill
+// )
+//
+// Routine Description:
+//
+// This function fills memory by first aligning the destination address to
+// a longword boundary, and then filling 32-byte blocks, followed by 4-byte
+// blocks, followed by any remaining bytes.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to fill.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be filled.
+//
+// Fill (a2) - Supplies the fill byte.
+//
+// N.B. The alternate entry memset expects the length and fill arguments
+// to be reversed. It also returns the Destination pointer
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(memset)
+
+ bis a0, zero, v0 // set return value
+ bis a1, zero, a3 // swap length and fill arguments
+ bis a2, zero, a1 //
+ bis a3, zero, a2 //
+
+ ALTERNATE_ENTRY(RtlFillMemory)
+
+ and a2, 0xff, a2 // clear excess bits
+ sll a2, 8, t0 // duplicate fill byte
+ bis a2, t0, a2 // generate fill word
+ sll a2, 16, t0 // duplicate fill word
+ bis a2, t0, a2 // generate fill longword
+ sll a2, 32, t0 // duplicate fill longword
+ bis a2, t0, a2 // generate fill quadword
+
+.align 3 // ensure quadword aligned target
+//
+// Fill memory with the pattern specified in register a2.
+//
+
+RtlpFillMemory: //
+
+//
+// Align destination to quadword
+//
+
+ beq a1, 80f // anything to fill? (paranoia)
+ and a0, 8-1, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, then not quad aligned
+ br zero, 20f // if eq, then quad aligned
+
+5:
+ ldq_u t1, 0(a0) // get unaligned quadword
+ // for first group of bytes
+10:
+ beq a1, 15f // if eq no more bytes to fill
+ insbl a2, t0, t2 // get fill byte into position
+ mskbl t1, t0, t1 // clear byte for fill
+ bis t1, t2, t1 // put in fill byte
+ addq t0, 1, t0 // increment to next byte position
+ subq a1, 1, a1 // decrement bytes to fill
+ cmpeq t0, 8, t2 // t0 = 8?
+ beq t2, 10b // if eq [false] more bytes to do
+
+15:
+ stq_u t1, 0(a0) // store modified bytes
+ addq a0, 7, a0 // move a0 to next quadword
+ bic a0, 7, a0 // align a0 to quadword
+
+//
+// Check for 64-byte blocks
+//
+
+20:
+ srl a1, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq then no 64 byte blocks
+ and a1, 64-1, a1 // a1 = residual bytes to fill
+
+30:
+ stq a2, 0(a0) // store 64 bytes
+ stq a2, 8(a0) //
+ stq a2, 16(a0) //
+ stq a2, 24(a0) //
+ stq a2, 32(a0) //
+ stq a2, 40(a0) //
+ stq a2, 48(a0) //
+ stq a2, 56(a0) //
+
+ subq t0, 1, t0 // decrement blocks remaining
+ addq a0, 64, a0 // increment destination pointer
+ bne t0, 30b // more blocks to write
+
+
+
+//
+// Fill aligned quadwords
+//
+
+40:
+ srl a1, 3, t0 // t0 = number of quadwords
+ bne t0, 55f // if ne quadwords left to fill
+ br zero, 60f // if eq no quadwords left
+
+55:
+ and a1, 8-1, a1 // a1 = residual bytes to fill
+
+50:
+ stq a2, 0(a0) // store quadword
+ subq t0, 1, t0 // decrement quadwords remaining
+ addq a0, 8, a0 // next quadword
+ bne t0, 50b // more quadwords to write
+
+
+//
+// Fill bytes for last quadword
+//
+
+60:
+ bne a1, 65f // if ne bytes remain to be filled
+ br zero, 80f // if eq no more bytes to fill
+
+65:
+ ldq t1, 0(a0) // get last quadword
+ bis zero, zero, t0 // t0 = byte position to start fill
+
+70:
+ beq a1, 75f // if eq, no more bytes to fill
+ insbl a2, t0, t2 // get fill byte into position
+ mskbl t1, t0, t1 // clear fill byte position
+ bis t1, t2, t1 // insert fill byte
+ addq t0, 1, t0 // increment byte within quad
+ subq a1, 1, a1 // decrement bytes to fill
+ cmpeq t0, 8, t3 // t0 = 8? => finished quad
+ beq t3, 70b // if eq [false] more bytes to fill
+
+75:
+ stq t1, 0(a0) // write merged quadword
+
+//
+// Finish up
+//
+
+80:
+ ret zero, (ra) // return
+
+
+ .end RtlZeroMemory
+
+ SBTTL("Fill Memory Ulong")
+//++
+//
+// VOID
+// RtlFillMemoryUlong (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN ULONG Pattern
+// )
+//
+// Routine Description:
+//
+// This function fills memory with the specified longowrd pattern by
+// filling 64-byte blocks followed by 8-byte blocks and finally
+// 4-byte blocks.
+//
+// N.B. This routine assumes that the destination address is aligned
+// on a longword boundary and that the length is an even multiple
+// of longwords.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to fill.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be filled.
+//
+// Pattern (a2) - Supplies the fill pattern.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlFillMemoryUlong)
+
+ bic a1, 3, a1 // make sure length is an even number
+ // of longwords
+ sll a2, 32, a3 // a3 = long pattern in upper 32 bits
+ srl a3, 32, t0 // clear upper bits, pattern in lower 32
+ bis a3, t0, a3 // a3 = quad version of fill pattern
+
+//
+// Make destination address quad-aligned
+//
+
+ and a0, 4, t0 // is a0 quad aligned?
+ beq t0, 10f // if eq, then a0 quad aligned
+ stl a2, 0(a0) // fill first longword
+ addq a0, 4, a0 // quad align a0
+ subq a1, 4, a1 // bytes remaining to store
+
+//
+// Check for 64-byte blocks to fill
+//
+
+10:
+ srl a1, 6, t0 // t0 = # 64-byte blocks to fill
+ beq t0, 30f // if eq no 64 byte blocks
+ and a1, 64-1, a1 // a1 = residual bytes
+
+20:
+ stq a3, 0(a0) // store 64 bytes
+ stq a3, 8(a0) //
+ stq a3, 16(a0) //
+ stq a3, 24(a0) //
+ stq a3, 32(a0) //
+ stq a3, 40(a0) //
+ stq a3, 48(a0) //
+ stq a3, 56(a0) //
+ subq t0, 1, t0 // t0 = blocks remaining
+ addq a0, 64, a0 // increment address pointer
+ bne t0, 20b // if ne more blocks to fill
+
+//
+// Fill 8 bytes at a time while we can, a1 = bytes remaining
+//
+
+30:
+ srl a1, 3, t0 // t0 = # quadwords to fill
+ beq t0, 50f // if eq no quadwords left
+ and a1, 8-1, a1 // a1 = residual bytes
+40:
+ stq a3, 0(a0) // store quadword
+ subq t0, 1, t0 // t0 = quadwords remaining
+ addq a0, 8, a0 // increment address pointer
+ bne t0, 40b // if ne more quadwords to fill
+
+//
+// Fill last 4 bytes
+//
+
+50:
+ beq a1, 60f // if eq no longwords remain
+ stl a2, 0(a0) // fill last longword
+
+//
+// Finish up
+//
+
+60:
+ ret zero, (ra) // return to caller
+
+
+ .end RtlFillMemoryUlong
+
+ SBTTL("Copy Memory With Byte Granularity")
+//++
+//
+// VOID
+// RtlCopyBytes (
+// IN PVOID Destination,
+// IN PVOID Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function copies non-overlapping memory, aligned or unaligned, in
+// 64-byte blocks, followed by 8-byte blocks, followed by any remaining
+// bytes. Unlike RtlCopyMemory or RtlMoveMemory the copy is done such
+// that byte granularity is assured for all platforms.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the destination address of
+// the move operation.
+//
+// Source (a1) - Supplies a pointer to the source address of the move
+// operation.
+//
+// Length (a2) - Supplies the length, in bytes, of the memory to be moved.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlCopyBytes)
+
+//
+// Move memory forward aligned and unaligned.
+//
+
+ xor a0, a1, t0 // compare alignment bits
+ and t0, 0x7, t0 // isolate alignment comparison
+ bne t0, CopyForwardUnaligned // if ne, incompatible alignment
+
+//
+// Source and Destination buffers have the same alignment. Move
+// bytes until done or source and destination are quadword aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, not quad aligned
+ br zero, 20f // predicted taken
+5:
+ bis zero, zero, t1 // t4 = destination byte zap mask
+ bis zero, 1, t2
+ sll t2, t0, t2 // t2 = next bit to set in zap mask
+10:
+ beq a2, 15f // if eq, all bits set
+ bis t1, t2, t1 // set bit in zap mask
+ sll t2, 1, t2 // set next higher bit for zap mask
+ subq a2, 1, a2 // decrement bytes to move
+ addq t0, 1, t0 // increment byte within quad
+ cmpeq t0, 8, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+15:
+ ldq_u t2, 0(a1) // get unaligned quadword from source
+ zapnot t2, t1, t2 // clear source bytes
+ bic a0, 7, a3 // a3 = quadword base of destination
+retry1:
+ ldq_l t0, 0(a3) // load destination quadword
+ zap t0, t1, t0 // clear destination bytes
+ or t0, t2, t0 // merge in bytes from source
+ stq_c t0, 0(a3) // store merged quadword conditional
+ beq t0, retry1f // if eq, retry failed interlock
+
+ addq a0, 7, a0 // move to next quadword
+ bic a0, 7, a0 // aligned quadword
+
+ addq a1, 7, a1 // move to next quadword
+ bic a1, 7, a1 // aligned quadword
+
+//
+// Check for 64-byte block moves
+//
+
+20:
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes
+
+30:
+ ldq t1, 0(a1) // load 64 bytes from source
+ addq a0, 64, a0 // increment destination pointer
+ ldq v0, 56(a1) //
+ ldq a3, 32(a1) //
+ stq t1, -64(a0) // write to destination
+ ldq t2, 8(a1) // into volatile registers
+ ldq t3, 16(a1) //
+ ldq t4, 24(a1) //
+ subq t0, 1, t0 // decrement number of blocks
+ stq t2, -56(a0) //
+ ldq a4, 40(a1) //
+ stq t3, -48(a0) //
+ ldq a5, 48(a1) //
+ stq t4, -40(a0) //
+ addq a1, 64, a1 // increment source pointer
+ stq a3, -32(a0) //
+ stq a4, -24(a0) //
+ stq a5, -16(a0) //
+ stq v0, -8(a0) //
+ bne t0, 30b // if ne, more blocks to copy
+
+//
+// Copy quadwords
+//
+
+40:
+ srl a2, 3, t0 // t0 = number of quadwords to move
+ beq t0, 60f // if eq no quadwords to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+50:
+ ldq t1, 0(a1) // load quadword from source
+ addq a1, 8, a1 // increment source pointer
+ stq t1, 0(a0) // store quadword to destination
+ addq a0, 8, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of quadwords
+ bne t0, 50b // if ne, more quadwords to move
+
+//
+// Move final residual bytes
+//
+
+60:
+ beq a2, 80f // if eq, no more bytes to move
+ mov a2, t0 // t0 = number of bytes to move
+ mov -1, t1 // t1 = bit mask
+ sll t0, 3, t0 // # of bytes to # of bits
+ srl t1, t0, t1 // clear t0 bits
+ sll t1, t0, t0 // move it back
+ ldq t1, 0(a1) // get last source quadword
+ bic t1, t0, t1 // clear bytes not copied
+ not t0, t0 // complement to clear destination
+retry2:
+ ldq_l t2, 0(a0) // get last destination quadword locked
+ bic t2, t0, t2 // clear bytes to be copied
+ bis t2, t1, t2 // move bytes from source
+ stq_c t2, 0(a0) // store merged quadword conditional
+ beq t2, retry2f // if eq, retry failed interlock
+
+//
+// Finish aligned MoveForward
+//
+
+80:
+ ret zero, (ra) // return
+
+//
+// Move memory forward unaligned.
+//
+
+CopyForwardUnaligned: //
+
+//
+// Move bytes until the destination is aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ beq t0, 100f // if eq, destination quad aligned
+ bis zero, zero, t1 // t4 = destination byte zap mask
+ bis zero, 1, t2
+ sll t2, t0, t2 // t2 = next bit to set in zap mask
+ mov zero, t4 // assemble destination bytes here
+90:
+ beq a2, 95f // if eq no more bytes to move
+ bis t1, t2, t1 // set bit in zap mask
+ sll t2, 1, t2 // set next higher bit for zap mask
+ ldq_u t5, 0(a1) // get unaligned quad from source
+ extbl t5, a1, t5 // extract source byte
+ insbl t5, t0, t5 // t5 = source byte, in position
+ or t4, t5, t4 // merge in source byte
+ addq t0, 1, t0 // increment byte position
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ cmpeq t0, 8, t3 // t0 = 8? => quad finished
+ beq t3, 90b // if eq [false], more bytes to move
+95:
+ bic a0, 0x7, a3 // a3 = quadword base of destination
+retry3:
+ ldq_l t0, 0(a3) // load destination quadword
+ zap t0, t1, t0 // clear destination bytes
+ or t0, t4, t0 // merge in bytes from source
+ stq_c t0, 0(a3) // store merged quadword conditional
+ beq t0, retry3f // if eq, retry failed interlock
+
+ addq a0, 7, a0 // increment to next quad
+ bic a0, 7, a0 // align next quadword
+
+//
+// Check for 64-byte blocks to move
+//
+
+100:
+ srl a2, 6, t0 // t0 = number of blocks to move
+ beq t0, 120f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes to move
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+110:
+ // get source data and merge it
+ // as we go
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quad #1
+ ldq_u t3, 15(a1) // t3 = third unaligned quad
+ extql t2, a1, t2 // extract applicable bytes from t2
+ extqh t3, a1, v0 // extract applicable bytes from t3
+ stq t1, 0(a0) // store quad #1
+ bis t2, v0, t2 // t2 = quad #2
+ ldq_u t4, 23(a1) // t4 = fourth unaligned quad
+ extql t3, a1, t3 // extract applicable bytes from t3
+ extqh t4, a1, v0 // extract applicable bytes from t4
+ stq t2, 8(a0) // store quad #2
+ bis t3, v0, t3 // t3 = quad #3
+ ldq_u t5, 31(a1) // t5 = fifth unaligned quad
+ extql t4, a1, t4 // extract applicable bytes from t4
+ extqh t5, a1, v0 // extract applicable bytes from t5
+ stq t3, 16(a0) // store quad #3
+ bis t4, v0, t4 // t4 = quad #4
+ ldq_u a3, 39(a1) // a3 = sixth unaligned quad
+ extql t5, a1, t5 // extract applicable bytes from t5
+ extqh a3, a1, v0 // extract applicable bytes from a3
+ stq t4, 24(a0) // store quad #4
+ bis t5, v0, t5 // t5 = quad #5
+ ldq_u a4, 47(a1) // a4 = seventh unaligned quad
+ extql a3, a1, a3 // extract applicable bytes from a3
+ extqh a4, a1, v0 // extract applicable bytes from a4
+ stq t5, 32(a0) // store quad #5
+ bis a3, v0, a3 // a3 = quad #6
+ ldq_u a5, 55(a1) // a5 = eighth unaligned quad
+ extql a4, a1, a4 // extract applicable bytes from a4
+ extqh a5, a1, v0 // extract applicable bytes from a5
+ stq a3, 40(a0) // store quad #6
+ bis a4, v0, a4 // a4 = quad #7
+ ldq_u t1, 63(a1) // t1 = ninth unaligned = 1st of next
+ extql a5, a1, a5 // extract applicable bytes from a5
+ extqh t1, a1, v0 // extract applicable bytes from t1
+ stq a4, 48(a0) // store quad #7
+ bis a5, v0, a5 // a5 = quad #8
+ addq a1, 64, a1 // increment source pointer
+ stq a5, 56(a0) // store quad #8
+ addq a0, 64, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of blocks
+ bne t0, 110b // if ne, more blocks to move
+
+//
+// Move unaligned source quads to aligned destination quads
+//
+
+120:
+ srl a2, 3, t0 // t0 = number of quads to move
+ beq t0, 140f // if eq no quads to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+130:
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ addq a0, 8, a0 // increment destination pointer
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quadword of data
+ stq t1, -8(a0) // store data to destination
+ addq a1, 8, a1 // increment source pointer
+ subq t0, 1, t0 // decrement quads to move
+ bis t2, zero, t1 // t1 = first of next unaligned pair
+ bne t0, 130b // if ne, more quads to move
+
+//
+// Move remaining bytes to final quadword
+//
+
+140:
+ beq a2, 160f // if eq no more bytes to move
+
+ mov zero, t3 // t3 = position for next insertion
+ mov zero, t4 // assemble destination bytes here
+ mov a2, t0 // t0 = number of bytes to move
+ mov -1, t1 // t1 = bit mask
+ sll t0, 3, t0 // # of bytes to # of bits
+ srl t1, t0, t1 // clear t0 bits
+ sll t1, t0, t0 // move it back
+ not t0, t0 // complement for destination clear mask
+150:
+ ldq_u t1, 0(a1) // get unaligned source quad
+ extbl t1, a1, t1 // t1 = source byte
+ insbl t1, t3, t1 // t1 = source byte, in position
+ bis t4, t1, t4 // merge in source byte
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ addq t3, 1, t3 // increment destination position
+ bne a2, 150b // more bytes to move
+retry4:
+ ldq_l t2, 0(a0) // get last destination quadword locked
+ bic t2, t0, t2 // clear bytes to be copied
+ bis t2, t4, t2 // move bytes from source
+ stq_c t2, 0(a0) // store merged quadword conditional
+ beq t2, retry4f // if eq, retry failed interlock
+
+//
+// Finish unaligned MoveForward
+//
+
+160:
+ ret zero, (ra) // return
+
+//
+// Out of line branches for failed store conditional.
+// Don't need to restore anything, just try again.
+//
+
+retry1f:
+ br retry1
+retry2f:
+ br retry2
+retry3f:
+ br retry3
+retry4f:
+ br retry4
+
+ .end RtlCopyBytes
+
+ SBTTL("Zero Bytes")
+//++
+//
+// VOID
+// RtlZeroBytes (
+// IN PVOID Destination,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function zeros memory by first aligning the destination address to
+// a quadword boundary, and then zeroing 64-byte blocks, followed by 8-byte
+// blocks, followed by any remaining bytes. Unlike RtlZeroMemory the copy is
+// done such that byte granularity is assured for all platforms.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to zero.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be zeroed.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlZeroBytes)
+
+ bis zero, zero, a2 // set fill pattern
+ br zero, RtlpFillBytes //
+
+
+ SBTTL("Fill Bytes")
+//++
+//
+// VOID
+// RtlFillBytes (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN UCHAR Fill
+// )
+//
+// Routine Description:
+//
+// This function fills memory by first aligning the destination address to
+// a longword boundary, and then filling 32-byte blocks, followed by 4-byte
+// blocks, followed by any remaining bytes. Unlike RtlFillMemory the copy is
+// done such that byte granularity is assured for all platforms.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to fill.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be filled.
+//
+// Fill (a2) - Supplies the fill byte.
+//
+// N.B. The alternate entry memset expects the length and fill arguments
+// to be reversed. It also returns the Destination pointer
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(RtlFillBytes)
+
+ and a2, 0xff, a2 // clear excess bits
+ sll a2, 8, t0 // duplicate fill byte
+ bis a2, t0, a2 // generate fill word
+ sll a2, 16, t0 // duplicate fill word
+ bis a2, t0, a2 // generate fill longword
+ sll a2, 32, t0 // duplicate fill longword
+ bis a2, t0, a2 // generate fill quadword
+
+.align 3 // ensure quadword aligned target
+//
+// Fill memory with the pattern specified in register a2.
+//
+
+RtlpFillBytes: //
+
+//
+// Align destination to quadword
+//
+
+ beq a1, 80f // anything to fill? (paranoia)
+ and a0, 8-1, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, then not quad aligned
+ br zero, 20f // if eq, then quad aligned
+
+5:
+ bis zero, zero, t1 // t4 = destination byte zap mask
+ bis zero, 1, t2
+ sll t2, t0, t2 // t2 = next bit to set in zap mask
+10:
+ beq a1, 15f // if eq, all bits set
+ bis t1, t2, t1 // set bit in zap mask
+ sll t2, 1, t2 // set next higher bit for zap mask
+ subq a1, 1, a1 // decrement bytes to fill
+ addq t0, 1, t0 // increment byte within quad
+ cmpeq t0, 8, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+15:
+ zapnot a2, t1, t2 // clear fill bytes
+ bic a0, 7, a3 // a3 = quadword base of destination
+retry5:
+ ldq_l t0, 0(a3) // load destination quadword
+ zap t0, t1, t0 // clear destination bytes
+ or t0, t2, t0 // merge in fill bytes
+ stq_c t0, 0(a3) // store merged quadword conditional
+ beq t0, retry5f // if eq, retry failed interlock
+
+ addq a0, 7, a0 // move a0 to next quadword
+ bic a0, 7, a0 // align a0 to quadword
+
+//
+// Check for 64-byte blocks
+//
+
+20:
+ srl a1, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq then no 64 byte blocks
+ and a1, 64-1, a1 // a1 = residual bytes to fill
+
+30:
+ stq a2, 0(a0) // store 64 bytes
+ stq a2, 8(a0) //
+ stq a2, 16(a0) //
+ stq a2, 24(a0) //
+ stq a2, 32(a0) //
+ stq a2, 40(a0) //
+ stq a2, 48(a0) //
+ stq a2, 56(a0) //
+
+ subq t0, 1, t0 // decrement blocks remaining
+ addq a0, 64, a0 // increment destination pointer
+ bne t0, 30b // more blocks to write
+
+
+
+//
+// Fill aligned quadwords
+//
+
+40:
+ srl a1, 3, t0 // t0 = number of quadwords
+ bne t0, 55f // if ne quadwords left to fill
+ br zero, 60f // if eq no quadwords left
+
+55:
+ and a1, 8-1, a1 // a1 = residual bytes to fill
+
+50:
+ stq a2, 0(a0) // store quadword
+ subq t0, 1, t0 // decrement quadwords remaining
+ addq a0, 8, a0 // next quadword
+ bne t0, 50b // more quadwords to write
+
+//
+// Fill bytes for last quadword
+//
+
+60:
+ beq a1, 80f // if eq no more bytes to fill
+
+ mov a1, t0 // t0 = number of bytes to move
+ mov -1, t1 // t1 = bit mask
+ sll t0, 3, t0 // # of bytes to # of bits
+ srl t1, t0, t1 // clear t0 bits
+ sll t1, t0, t0 // move it back
+ bic a2, t0, t1 // clear fill bytes not copied
+ not t0, t0 // complement to clear destination
+retry6:
+ ldq_l t2, 0(a0) // get last destination quadword locked
+ bic t2, t0, t2 // clear bytes to be copied
+ bis t2, t1, t2 // move bytes from source
+ stq_c t2, 0(a0) // store merged quadword conditional
+ beq t2, retry6f // if eq, retry failed interlock
+
+//
+// Finish up
+//
+
+80:
+ ret zero, (ra) // return
+
+//
+// Out of line branches for failed store conditional.
+// Don't need to restore anything, just try again.
+//
+
+retry5f:
+ br retry5
+retry6f:
+ br retry6
+
+ .end RtlZeroBytes
diff --git a/private/ntos/rtl/alpha/ntcurteb.s b/private/ntos/rtl/alpha/ntcurteb.s
new file mode 100644
index 000000000..22c9f1f88
--- /dev/null
+++ b/private/ntos/rtl/alpha/ntcurteb.s
@@ -0,0 +1,57 @@
+// TITLE("Get Current TEB Pointer")
+//++
+//
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// ntcurteb.s
+//
+// Abstract:
+//
+// This module implements the function to retrieve the current TEB pointer.
+//
+// Author:
+//
+// Joe Notarangelo 29-Jul-1992
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+//++
+//
+// PTEB
+// NtCurrentTeb(
+// VOID
+// )
+//
+// Routine Description:
+//
+// This function returns the current TEB pointer retrieved via an unprivileged
+// call pal. Since the call pal is unprivileged this routine is appropriate in
+// any mode.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// Current TEB pointer.
+//
+//--
+
+ LEAF_ENTRY(NtCurrentTeb)
+
+ GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) result in v0
+
+ ret zero, (ra) // return
+
+ .end NtCurrentTeb
diff --git a/private/ntos/rtl/alpha/ntrtlalp.h b/private/ntos/rtl/alpha/ntrtlalp.h
new file mode 100644
index 000000000..772cf749b
--- /dev/null
+++ b/private/ntos/rtl/alpha/ntrtlalp.h
@@ -0,0 +1,123 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ntrtlalp.h
+
+Abstract:
+
+ Alpha specific parts of ntrtlp.h.
+
+Author:
+
+ David N. Cutler (davec) 19-Apr-90
+
+Revision History:
+
+ Thomas Van Baak (tvb) 5-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+//
+// Define exception routine function prototypes.
+//
+
+EXCEPTION_DISPOSITION
+RtlpExecuteHandlerForException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN ULONG EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext,
+ IN PEXCEPTION_ROUTINE ExceptionRoutine
+ );
+
+EXCEPTION_DISPOSITION
+RtlpExecuteHandlerForUnwind (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN ULONG EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext,
+ IN PEXCEPTION_ROUTINE ExceptionRoutine
+ );
+
+//
+// Define procedure prototypes for exception filter and termination handler
+// execution routines.
+//
+
+LONG
+RtlpExecuteExceptionFilter (
+ PEXCEPTION_POINTERS ExceptionPointers,
+ EXCEPTION_FILTER ExceptionFilter,
+ ULONG EstablisherFrame
+ );
+
+VOID
+RtlpExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+//
+// Define function prototype for restore context.
+//
+
+VOID
+RtlpRestoreContext (
+ IN PCONTEXT Context
+ );
+
+#if DBG
+
+//
+// Define global flags to debug/validate exception handling for Alpha.
+//
+
+extern ULONG RtlDebugFlags;
+
+//
+// Print exception records as delivered by PALcode (KiDispatchException).
+//
+
+#define RTL_DBG_PAL_EXCEPTION 0x00001
+
+//
+// Software raised exceptions (RtlRaiseException, RtlRaiseStatus).
+//
+
+#define RTL_DBG_RAISE_EXCEPTION 0x00002
+
+//
+// Find a handler to take the exception (RtlDispatchException).
+//
+
+#define RTL_DBG_DISPATCH_EXCEPTION 0x00030
+#define RTL_DBG_DISPATCH_EXCEPTION_DETAIL 0x00020
+
+//
+// Call handlers and unwind to a target frame (RtlUnwind).
+//
+
+#define RTL_DBG_UNWIND 0x00300
+#define RTL_DBG_UNWIND_DETAIL 0x00200
+
+//
+// Climb one frame up the call stack (RtlVirtualUnwind).
+//
+
+#define RTL_DBG_VIRTUAL_UNWIND 0x03000
+#define RTL_DBG_VIRTUAL_UNWIND_DETAIL 0x02000
+
+//
+// Find the function entry for a given PC (RtlLookupFunctionEntry).
+//
+
+#define RTL_DBG_FUNCTION_ENTRY 0x30000
+#define RTL_DBG_FUNCTION_ENTRY_DETAIL 0x20000
+
+#endif // DBG
diff --git a/private/ntos/rtl/alpha/setjmp.s b/private/ntos/rtl/alpha/setjmp.s
new file mode 100644
index 000000000..830f9465e
--- /dev/null
+++ b/private/ntos/rtl/alpha/setjmp.s
@@ -0,0 +1,148 @@
+// TITLE("Set Jump")
+//++
+//
+// Copyright (c) 1993 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// setjmp.s
+//
+// Abstract:
+//
+// This module implements the Alpha acc compiler specific routine to
+// provide SAFE handling of setjmp/longjmp with respect to structured
+// exception handling.
+//
+// N.B. This function has been replaced by setjmp/setjmpex/longjmp in the
+// C runtime library. It remains here for backwards compatibility of
+// Beta 2 applications expecting setjmp and longjmp to be present in
+// ntdll, or for new acc compiled applications that link with ntdll
+// before libc.
+//
+// Author:
+//
+// David N. Cutler (davec) 2-Apr-1993
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 22-Apr-1993
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Set Jump")
+//++
+//
+// int
+// setjmp (
+// IN jmp_buf JumpBuffer
+// )
+//
+// Routine Description:
+//
+// This function implements a safe setjmp.
+//
+// Arguments:
+//
+// JumpBuffer (a0) - Supplies the address of a jump buffer to store the
+// jump information.
+//
+// N.B. This is an array of double's to force quadword alignment.
+//
+// Return Value:
+//
+// A value of zero is returned.
+//
+//--
+
+ .struct 0
+SjRa: .space 8 // saved return address
+SjS0: .space 8 // saved integer register s0
+SjFl: .space 8 // InFunction flag variable
+SjEf: .space 8 // EstablisherFrame(s) structure
+SjCx: .space ContextFrameLength // context frame
+SetjmpFrameLength:
+
+ NESTED_ENTRY(setjmp, SetjmpFrameLength, zero)
+
+ lda sp, -SetjmpFrameLength(sp) // allocate stack frame
+ stq ra, SjRa(sp) // save return address
+ stq s0, SjS0(sp) // save integer register s0
+
+ PROLOGUE_END
+
+//
+// Save the nonvolatile machine state.
+//
+
+ lda t0, SjCx(sp) // address of context record
+
+ .set noreorder
+ .set noat
+ stt f2, CxFltF2(t0) // save floating registers f2 - f9
+ stt f3, CxFltF3(t0) //
+ stt f4, CxFltF4(t0) //
+ stt f5, CxFltF5(t0) //
+ stt f6, CxFltF6(t0) //
+ stt f7, CxFltF7(t0) //
+ stt f8, CxFltF8(t0) //
+ stt f9, CxFltF9(t0) //
+
+ stq s0, CxIntS0(t0) // save integer registers s0 - fp/s6
+ stq s1, CxIntS1(t0) //
+ stq s2, CxIntS2(t0) //
+ stq s3, CxIntS3(t0) //
+ stq s4, CxIntS4(t0) //
+ stq s5, CxIntS5(t0) //
+ stq fp, CxIntFp(t0) //
+ .set at
+ .set reorder
+
+ stq gp, CxIntGp(t0) // save integer register gp
+ lda v0, SetjmpFrameLength(sp) // compute stack pointer of caller
+ stq v0, CxIntSp(t0) // save caller stack pointer
+ stq ra, CxIntRa(t0) // save return address
+ stq ra, CxFir(t0) // save continuation address
+ ldil t1, 2 // get acc safe setjmp flag
+ stl t1, JbType(a0) // set jump buffer context type
+ stl ra, JbPc(a0) // save target instruction address
+ mov a0, s0 // preserve jump buffer address
+
+//
+// Perform unwind to determine the virtual frame pointer of the caller.
+//
+
+ subl ra, 4, a0 // compute control PC address
+ bsr RtlLookupFunctionEntry // lookup function table address
+
+ ldq a0, SjRa(sp) // get return address
+ subl a0, 4, a0 // compute control PC address
+ mov v0, a1 // set address of function entry
+ lda a2, SjCx(sp) // address of context record
+ lda a3, SjFl(sp) // set address of in function variable
+ lda a4, SjEf(sp) // set frame pointers address
+ mov zero, a5 // set context pointer array address
+ bsr RtlVirtualUnwind // compute virtual frame pointer value
+
+//
+// Set return value, restore registers, deallocate stack frame, and return.
+//
+
+ ldl t0, SjEf(sp) // get virtual frame pointer
+ stl t0, JbFp(s0) // save virtual frame pointer address
+ mov zero, v0 // set return value
+
+ ldq s0, SjS0(sp) // restore integer register s0
+ ldq ra, SjRa(sp) // restore return address
+ lda sp, SetjmpFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end setjmp
diff --git a/private/ntos/rtl/alpha/tcmpmem.c b/private/ntos/rtl/alpha/tcmpmem.c
new file mode 100644
index 000000000..11ef83631
--- /dev/null
+++ b/private/ntos/rtl/alpha/tcmpmem.c
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tcmpmem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlCompareMemory
+ function by running an exhaustive test of every case of string offset,
+ compare length, and return value up to and a little beyond one 32-byte
+ cache line. This represents over one million test cases. It is assumed
+ any bugs that exist will be found within this range. If only the error
+ count summary is desired, type "tcmpmem > nul" instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 11-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+#define BUFFER_SIZE (MAX_OFFSET + MAX_LENGTH)
+
+UCHAR String1[BUFFER_SIZE];
+UCHAR String2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Expected;
+ ULONG Length;
+ ULONG Offset1;
+ ULONG Offset2;
+ ULONG Result;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlCompareMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ for (Offset1 = 0; Offset1 <= MAX_OFFSET; Offset1 += 1) {
+
+ //
+ // Copy the test pattern to Offset1 in String1 and then for each
+ // possible offset of String1, for each possible offset of String2,
+ // for each possible string compare length, and for each expected
+ // return value, make a call RtlCompareMemory.
+ //
+
+ FillPattern(&String1[Offset1], MAX_LENGTH);
+ for (Offset2 = 0; Offset2 <= MAX_OFFSET; Offset2 += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+ for (Expected = 0; Expected <= Length; Expected += 1) {
+
+ //
+ // Copy the test pattern starting at Offset2 in String2,
+ // change one byte at location `Expected', call
+ // RtlCompareMemory, and check that the function value
+ // is in fact the expected value.
+ //
+
+ FillPattern(&String2[Offset2], MAX_LENGTH);
+ String2[Offset2 + Expected] = ' ';
+ Result = RtlCompareMemory(&String1[Offset1],
+ &String2[Offset2],
+ Length);
+ TestCases += 1;
+ if (Result != Expected) {
+ ErrorCount += 1;
+
+ //
+ // The function failed to return the proper value.
+ //
+
+ printf("ERROR: Offset1 = %d, Offset2 = %d, Length = %d, Expected = %d, Result = %d\n",
+ Offset1, Offset2, Length, Expected, Result);
+ printf(" String1[Offset1] = %lx: <%.*s>\n",
+ &String1[Offset1], Length, &String1[Offset1]);
+ printf(" String2[Offset2] = %lx: <%.*s>\n",
+ &String2[Offset2], Length, &String2[Offset2]);
+ printf("\n");
+ }
+ }
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlCompareMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/tfilmem.c b/private/ntos/rtl/alpha/tfilmem.c
new file mode 100644
index 000000000..0a347599c
--- /dev/null
+++ b/private/ntos/rtl/alpha/tfilmem.c
@@ -0,0 +1,79 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tfilmem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlFillMemory
+ function by running an exhaustive test of every case of string offset
+ and length up to and a little beyond one 32-byte cache line. This
+ represents several thousand test cases. It is assumed any bugs that
+ exist will be found within this range. If only the error count summary
+ is desired, type "tfilmem > nul" instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 13-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+#define BUFFER_SIZE (MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN)
+
+UCHAR Buffer1[BUFFER_SIZE];
+UCHAR Buffer2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Length;
+ ULONG Offset;
+ ULONG Result;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlFillMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+
+ FillPattern(Buffer1, BUFFER_SIZE);
+ FillPattern(Buffer2, BUFFER_SIZE);
+ LocalFillMemory(&Buffer1[Offset], Length, '@');
+ RtlFillMemory(&Buffer2[Offset], Length, '@');
+
+ Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE);
+
+ TestCases += 1;
+ if (Result != BUFFER_SIZE) {
+ ErrorCount += 1;
+
+ printf("ERROR: Offset = %d, Length = %d\n", Offset, Length);
+ printf("Buffers differ starting at byte %d:\n", Result);
+ printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1);
+ printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2);
+ printf("\n");
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlFillMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/tmovmem.c b/private/ntos/rtl/alpha/tmovmem.c
new file mode 100644
index 000000000..3b381d9ba
--- /dev/null
+++ b/private/ntos/rtl/alpha/tmovmem.c
@@ -0,0 +1,129 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tmovmem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlMoveMemory
+ function by running an exhaustive test of every case of string offset,
+ move length, and relative overlap of the two strings up to and a little
+ beyond one 32-byte cache line. This represents several hundred thousand
+ test cases. It is assumed any bugs that exist will be found within this
+ range. If only the error count summary is desired, type "tmovmem > nul"
+ instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 13-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+//
+// Two strings are defined within a large buffer. The target string is
+// initially below the source string with a small gap between the two
+// strings. A margin around the strings ensures any bytes accidentally
+// changed outside the strings are detectable. As the length of the strings
+// and the offset of the source string are varied, the target string wanders
+// from well below, through, and well above the source string.
+//
+
+#define MIN_OVERLAP (-(MAX_LENGTH + MAX_MARGIN))
+#define MAX_OVERLAP (MAX_LENGTH + MAX_MARGIN - 1)
+
+#define BUFFER_SIZE (MAX_MARGIN + MAX_LENGTH + MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN + MAX_LENGTH + MAX_MARGIN)
+
+UCHAR Buffer0[BUFFER_SIZE];
+UCHAR Buffer1[BUFFER_SIZE];
+UCHAR Buffer2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Length;
+ ULONG Offset;
+ LONG Overlap;
+ ULONG Result;
+ ULONG Source;
+ ULONG Target;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlMoveMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ //
+ // Make a call to RtlMoveMemory for all possible source string offsets
+ // within a cache line, for a large set of string lengths, and a wide
+ // range of positions of the target string relative to the source string,
+ // including all possible overlapping string configurations.
+ //
+
+ for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+ for (Overlap = MIN_OVERLAP; Overlap <= MAX_OVERLAP; Overlap += 1) {
+
+ //
+ // The same string configuration is made in two different
+ // buffers. RtlMoveMemory is used on the two strings in one
+ // buffer and the trusted LocalMoveMemory on the two strings
+ // in the other buffer. The entire buffers are compared to
+ // determine if the two move functions agree.
+ //
+
+ FillPattern(Buffer1, BUFFER_SIZE);
+ FillPattern(Buffer2, BUFFER_SIZE);
+
+ Source = MAX_MARGIN + MAX_LENGTH + MAX_MARGIN + Offset;
+ Target = Source + Overlap;
+
+ LocalMoveMemory(&Buffer1[Target], &Buffer1[Source], Length);
+ RtlMoveMemory(&Buffer2[Target], &Buffer2[Source], Length);
+
+ Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE);
+
+ TestCases += 1;
+ if (Result != BUFFER_SIZE) {
+ ErrorCount += 1;
+
+ printf("ERROR: Offset = %d, Length = %d, Overlap = %d\n",
+ Offset, Length, Overlap);
+ printf("RtlMoveMemory( &Buffer[ %d ], &Buffer[ %d ], %d )\n",
+ Target, Source, Length);
+
+ FillPattern(Buffer0, BUFFER_SIZE);
+ printf(" Original Source = %lx: <%.*s>\n",
+ &Buffer0[Source], Length, &Buffer0[Source]);
+ printf(" Expected Target = %lx: <%.*s>\n",
+ &Buffer1[Target], Length, &Buffer1[Target]);
+ printf(" Actual Target = %lx: <%.*s>\n",
+ &Buffer2[Target], Length, &Buffer2[Target]);
+ printf("\n");
+ printf("Buffers differ starting at byte %d:\n", Result);
+ printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1);
+ printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2);
+ printf("\n");
+ }
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlMoveMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/trampoln.s b/private/ntos/rtl/alpha/trampoln.s
new file mode 100644
index 000000000..b37d471e7
--- /dev/null
+++ b/private/ntos/rtl/alpha/trampoln.s
@@ -0,0 +1,524 @@
+// TITLE("Trampoline Code For User Mode APC and Exception Dispatching")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// trampoln.s
+//
+// Abstract:
+//
+// This module implements the trampoline code necessary to dispatch user
+// mode APCs and exceptions.
+//
+// Author:
+//
+// David N. Cutler (davec) 3-Apr-1990
+//
+// Environment:
+//
+// User mode only.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 11-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Define length of exception dispatcher stack frame.
+//
+
+#define ExceptionDispatcherFrameLength (ExceptionRecordLength + ContextFrameLength)
+
+ SBTTL("User APC Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the APC dispatcher.
+//
+//--
+
+//
+// N.B. This function specifies its own private exception handler.
+//
+ EXCEPTION_HANDLER(KiUserApcHandler)
+
+ NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero);
+
+ .set noreorder
+ .set noat
+ stq sp, CxIntSp(sp) // save stack pointer
+ stq ra, CxIntRa(sp) // save return address
+ stq ra, CxFir(sp) // set continuation address
+ stq fp, CxIntFp(sp) // save integer register fp
+ stq gp, CxIntGp(sp) // save integer register gp
+
+ stq s0, CxIntS0(sp) // save integer registers s0 - s5
+ stq s1, CxIntS1(sp) //
+ stq s2, CxIntS2(sp) //
+ stq s3, CxIntS3(sp) //
+ stq s4, CxIntS4(sp) //
+ stq s5, CxIntS5(sp) //
+
+ stt f2, CxFltF2(sp) // save floating registers f2 - f9
+ stt f3, CxFltF3(sp) //
+ stt f4, CxFltF4(sp) //
+ stt f5, CxFltF5(sp) //
+ stt f6, CxFltF6(sp) //
+ stt f7, CxFltF7(sp) //
+ stt f8, CxFltF8(sp) //
+ stt f9, CxFltF9(sp) //
+
+ mov sp, fp // set frame pointer
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+//++
+//
+// VOID
+// KiUserApcDispatcher (
+// IN PVOID NormalContext,
+// IN PVOID SystemArgument1,
+// IN PVOID SystemArgument2,
+// IN PKNORMAL_ROUTINE NormalRoutine
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to deliver an APC
+// in user mode. The context frame for this routine was built when the
+// APC interrupt was processed and contains the entire machine state of
+// the current thread. The specified APC routine is called and then the
+// machine state is restored and execution is continued.
+//
+// Arguments:
+//
+// a0 - Supplies the normal context parameter that was specified when the
+// APC was initialized.
+//
+// a1 - Supplies the first argument that was provided by the executive when
+// the APC was queued.
+//
+// a2 - Supplies the second argument that was provided by the executive
+// when the APC was queued.
+//
+// a3 - Supplies the address of the function that is to be called.
+//
+// N.B. Register sp supplies a pointer to a context frame.
+//
+// N.B. Register fp supplies the same value as sp and is used as a frame
+// pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Fir address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+ ALTERNATE_ENTRY(KiUserApcDispatcher)
+
+ jsr ra, (a3) // call specified APC routine
+
+ mov fp, a0 // set address of context frame
+ ldil a1, TRUE // set test alert argument true
+ bsr ra, ZwContinue // execute system service to continue
+ mov v0, s0 // save status value
+
+//
+// Unsuccessful completion after attempting to continue execution. Use the
+// return status as the exception code, set noncontinuable exception and
+// attempt to raise another exception. Note there is no return from raise
+// status.
+//
+
+10: mov s0, a0 // set status value
+ bsr ra, RtlRaiseStatus // raise exception
+ br zero, 10b // loop on return
+
+ .end KiUserApcDispatch
+
+ SBTTL("User APC Exception Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// KiUserApcHandler (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN ULONG EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PDISPATCHER_CONTEXT DispatcherContext
+//
+// Routine Description:
+//
+// This function is called when an exception occurs in an APC routine
+// or one of its dynamic descendents, or when an unwind through the
+// APC dispatcher is in progress. If an unwind is in progress, then test
+// alert is called to ensure that all currently queued APCs are executed.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// ExceptionContinueSearch is returned as the function value.
+//
+//--
+
+ .struct 0
+HdRa: .space 8 // saved return address
+ .space 1 * 8 // required for 16-byte stack alignment
+HandlerFrameLength: // length of handler frame
+
+ NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero)
+
+ lda sp, -HandlerFrameLength(sp) // allocate stack frame
+ stq ra, HdRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// The following code is equivalent to:
+//
+// EXCEPTION_DISPOSITION
+// KiUserApcHandler(IN PEXCEPTION_RECORD ExceptionRecord)
+// {
+// if (IS_UNWINDING(ExceptionRecord->ExceptionFlags)) {
+// NtTestAlert();
+// }
+// return ExceptionContinueSearch
+// }
+//
+
+ ldl t0, ErExceptionFlags(a0) // get exception flags
+ and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress
+ beq t0, 10f // if eq, no unwind in progress
+
+ bsr ra, ZwTestAlert // test for alert pending
+
+10: ldil v0, ExceptionContinueSearch // set disposition value
+ ldq ra, HdRa(sp) // restore return address
+ lda sp, HandlerFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end KiUserApcHandler
+
+
+ SBTTL("User Callback Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the exception dispatcher.
+//
+//--
+
+ NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero);
+.set noreorder
+ stq sp, CkSp(sp)
+ stq ra, CkRa(sp)
+.set reorder
+ PROLOGUE_END
+//++
+//
+// VOID
+// KiUserCallbackDispatcher (
+// VOID
+// )
+//
+// Routine Description:
+//
+// This routine is entered on a callout from kernel mode to execute a
+// user mode callback function. All arguments for this function have
+// been placed on the stack.
+//
+// Arguments:
+//
+// (sp + 16) - Supplies a value of zero for alignment.
+//
+// (sp + 24) - Supplies the API number of the callback function that is
+// executed.
+//
+// (sp + 32) - Supplies a pointer to the input buffer.
+//
+// (sp + 40) - Supplies the input buffer length.
+//
+// Return Value:
+//
+// This function returns to kernel mode.
+//
+//--
+
+ ALTERNATE_ENTRY(KiUserCallbackDispatcher)
+
+ ldl a0, CkBuffer(sp) // get input buffer address
+ ldl a1, CkLength(sp) // get input buffer length
+ ldl t0, CkApiNumber(sp) // get API number
+ GET_THREAD_ENVIRONMENT_BLOCK // get TEB in v0
+ ldl t5, TePeb(v0) // get PEB in t5
+ ldl t2, PeKernelCallbackTable(t5) // get address of callback table
+ s4addl t0, t2, t3 // get address of callback
+ ldl t4, 0(t3) // get callback pointer
+ jsr ra, (t4) // call specified function
+
+//
+// If a return from the callback function occurs, then the output buffer
+// address and length are returned as NULL.
+//
+
+ bis zero,zero,a0 // set zero buffer address
+ bis zero,zero,a1 // set zero buffer length
+ bis v0, zero, a2 // set completion status
+ bsr ra, ZwCallbackReturn // return to kernel mode
+
+//
+// Unsuccessful completion after attempting to return to kernel mode. Use
+// the return status as the exception code, set noncontinuable exception and
+// attempt to raise another exception. Note there is no return from raise
+// status.
+//
+
+ bis v0, zero, s0 // save status value
+10: bis s0, zero, a0 // set status value
+ bsr ra, RtlRaiseStatus // raise exception
+ br zero, 10b // loop on return
+
+ .end KiUserCallbackDispatch
+
+ SBTTL("User Exception Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the exception dispatcher.
+//
+// When reverse executed, this prologue will restore all integer registers,
+// rather than just the non-volatile registers. This is necessary for proper
+// unwinding through the call to the exception dispatcher when non-standard
+// calls have been used in frames at or above the exception frame. Non-leaf
+// functions using a non-standard call are allowed to save the return address
+// register in another integer register instead of on the stack.
+//
+//--
+
+ NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrameLength, zero);
+
+ .set noreorder
+ .set noat
+ stq sp, CxIntSp(sp) // save stack pointer
+ stq ra, CxIntRa(sp) // save return address
+ stq ra, CxFir(sp) // set continuation address
+
+ stq v0, CxIntV0(sp) // save integer register v0
+ stq t0, CxIntT0(sp) // save integer registers t0 - t6
+ stq t1, CxIntT1(sp) //
+ stq t2, CxIntT2(sp) //
+ stq t3, CxIntT3(sp) //
+ stq t4, CxIntT4(sp) //
+ stq t5, CxIntT5(sp) //
+ stq t6, CxIntT6(sp) //
+ stq t7, CxIntT7(sp) //
+
+ stq s0, CxIntS0(sp) // save integer registers s0 - s5
+ stq s1, CxIntS1(sp) //
+ stq s2, CxIntS2(sp) //
+ stq s3, CxIntS3(sp) //
+ stq s4, CxIntS4(sp) //
+ stq s5, CxIntS5(sp) //
+ stq fp, CxIntFp(sp) // save integer register fp
+
+ stq a0, CxIntA0(sp) // save integer registers a0 - a5
+ stq a1, CxIntA1(sp) //
+ stq a2, CxIntA2(sp) //
+ stq a3, CxIntA3(sp) //
+ stq a4, CxIntA4(sp) //
+ stq a5, CxIntA5(sp) //
+
+ stq t8, CxIntT8(sp) // save integer registers t8 - t11
+ stq t9, CxIntT9(sp) //
+ stq t10, CxIntT10(sp) //
+ stq t11, CxIntT11(sp) //
+
+ stq t12, CxIntT12(sp) // save integer register t12
+ stq AT, CxIntAt(sp) // save integer register AT
+ stq gp, CxIntGp(sp) // save integer register gp
+
+ stt f2, CxFltF2(sp) // save floating registers f2 - f9
+ stt f3, CxFltF3(sp) //
+ stt f4, CxFltF4(sp) //
+ stt f5, CxFltF5(sp) //
+ stt f6, CxFltF6(sp) //
+ stt f7, CxFltF7(sp) //
+ stt f8, CxFltF8(sp) //
+ stt f9, CxFltF9(sp) //
+
+ mov sp, fp // set frame pointer
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+//++
+//
+// VOID
+// KiUserExceptionDispatcher (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to dispatch a user
+// mode exception. If a frame based handler handles the exception, then
+// the execution is continued. Otherwise last chance processing is performed.
+//
+// Arguments:
+//
+// s0 - Supplies a pointer to an exception record.
+//
+// s1 - Supplies a pointer to a context frame.
+//
+// fp - Supplies the same value as sp and is used as a frame pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Fir address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+
+ ALTERNATE_ENTRY(KiUserExceptionDispatcher)
+
+ mov s0, a0 // set address of exception record
+ mov s1, a1 // set address of context frame
+ bsr ra, RtlDispatchException // attempt to dispatch the exception
+
+//
+// If the return status is TRUE, then the exception was handled and execution
+// should be continued with the NtContinue service in case the context was
+// changed. If the return status is FALSE, then the exception was not handled
+// and NtRaiseException is called to perform last chance exception processing.
+//
+
+ beq v0, 10f // if eq [false], perform last chance processing
+
+//
+// Continue execution.
+//
+
+ mov s1, a0 // set address of context frame
+ ldil a1, FALSE // set test alert argument false
+ bsr ra, ZwContinue // execute system service to continue
+ br zero, 20f // join common code
+
+//
+// Last chance processing.
+//
+
+10: mov s0, a0 // set address of exception record
+ mov s1, a1 // set address of context frame
+ ldil a2, FALSE // set first chance argument false
+ bsr ra, ZwRaiseException // perform last chance processing
+
+//
+// Common code for unsuccessful completion of the continue or last chance
+// service. Use the return status (which is now in v0) as the exception code,
+// set noncontinuable exception and attempt to raise another exception. Note
+// the stack grows and eventually this loop will end.
+//
+
+20: lda sp, -ExceptionRecordLength(sp) // allocate exception record
+ mov sp, a0 // get address of actual record
+ stl v0, ErExceptionCode(a0) // set exception code
+ ldil t0, EXCEPTION_NONCONTINUABLE // set noncontinuable flag
+ stl t0, ErExceptionFlags(a0) // store exception flags
+ stl s0, ErExceptionRecord(a0) // set associated exception record
+ stl zero, ErNumberParameters(a0) // set number of parameters
+ bsr ra, RtlRaiseException // raise exception
+ br zero, 20b // loop on error
+
+ .end KiUserExceptionDispatch
+
+//++
+//
+// NTSTATUS
+// KiRaiseUserExceptionDispatcher (
+// IN NTSTATUS ExceptionCode
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to raise a user
+// mode exception.
+//
+// Arguments:
+//
+// v0 - Supplies the status code to be raised.
+//
+// Return Value:
+//
+// ExceptionCode
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Fir address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+
+ .struct 0
+RaiseRa: .space 8 // saved return address
+RaiseV0: .space 8 // saved S0
+RaiseExr: .space ExceptionRecordLength // exception record for RtlRaiseException
+RaiseFrameLength: // length of handler frame
+
+ NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero)
+ lda sp, -RaiseFrameLength(sp) // allocate stack frame
+ stq ra, RaiseRa(sp) // save return address
+ PROLOGUE_END
+
+ stq v0, RaiseV0(sp) // save function return status
+ stl v0, ErExceptionCode+RaiseExr(sp) // set exception code
+ stl zero, ErExceptionFlags+RaiseExr(sp) // set exception flags
+ stl zero, ErExceptionRecord+RaiseExr(sp) // set exception record
+ stl ra, ErExceptionAddress+RaiseExr(sp) // set exception address
+ stl zero, ErNumberParameters+RaiseExr(sp)
+
+ lda a0, RaiseExr(sp) // set argument to RtlRaiseException
+ bsr ra, RtlRaiseException // attempt to raise the exception
+
+ ldq v0, RaiseV0(sp) // return status
+
+ ldq ra, RaiseRa(sp) // restore ra
+ lda sp, RaiseFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end KiRaiseUserExceptionDispatch
diff --git a/private/ntos/rtl/alpha/tzermem.c b/private/ntos/rtl/alpha/tzermem.c
new file mode 100644
index 000000000..c56f1620a
--- /dev/null
+++ b/private/ntos/rtl/alpha/tzermem.c
@@ -0,0 +1,79 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tzermem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlZeroMemory
+ function by running an exhaustive test of every case of string offset
+ and length up to and a little beyond one 32-byte cache line. This
+ represents several thousand test cases. It is assumed any bugs that
+ exist will be found within this range. If only the error count summary
+ is desired, type "tzermem > nul" instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 13-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+#define BUFFER_SIZE (MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN)
+
+UCHAR Buffer1[BUFFER_SIZE];
+UCHAR Buffer2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Length;
+ ULONG Offset;
+ ULONG Result;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlZeroMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+
+ FillPattern(Buffer1, BUFFER_SIZE);
+ FillPattern(Buffer2, BUFFER_SIZE);
+ LocalZeroMemory(&Buffer1[Offset], Length);
+ RtlZeroMemory(&Buffer2[Offset], Length);
+
+ Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE);
+
+ TestCases += 1;
+ if (Result != BUFFER_SIZE) {
+ ErrorCount += 1;
+
+ printf("ERROR: Offset = %d, Length = %d\n", Offset, Length);
+ printf("Buffers differ starting at byte %d:\n", Result);
+ printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1);
+ printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2);
+ printf("\n");
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlZeroMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/unwindr.c b/private/ntos/rtl/alpha/unwindr.c
new file mode 100644
index 000000000..ff8c4a562
--- /dev/null
+++ b/private/ntos/rtl/alpha/unwindr.c
@@ -0,0 +1,871 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ unwindr.c
+
+Abstract:
+
+ This module implements two alternate versions of the unwind function
+ required by Alpha AXP during the GEM compiler transition period. The
+ code is adapted from RtlUnwind (exdsptch.c). These functions can be
+ deleted if GEM uses scope table based structured exception handling
+ and can materialize virtual frame pointers.
+
+Author:
+
+ Thomas Van Baak (tvb) 18-Nov-1992
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Define local macros.
+//
+// Raise noncontinuable exception with associated exception record.
+//
+
+#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \
+ EXCEPTION_RECORD ExceptionRecordn; \
+ \
+ ExceptionRecordn.ExceptionCode = Status; \
+ ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \
+ ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \
+ ExceptionRecordn.NumberParameters = 0; \
+ RtlRaiseException(&ExceptionRecordn); \
+ }
+
+//
+// The low 2 bits of ExceptionHandler are flags bits and not part of the
+// exception handler address.
+//
+
+#define IS_HANDLER_DEFINED(FunctionEntry) \
+ (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0)
+
+#if DBG
+
+//
+// Maintain a short history of PC's for malformed function table errors.
+//
+
+#define PC_HISTORY_DEPTH 4
+
+//
+// Definition of global flag to debug/validate exception handling.
+// See ntrtlalp.h for the bit definitions in this flag word.
+//
+
+ULONG RtlDebugFlags;
+
+#endif
+
+#define Virtual VirtualFramePointer
+#define Real RealFramePointer
+
+VOID
+RtlUnwindRfp (
+ IN PVOID TargetRealFrame OPTIONAL,
+ IN PVOID TargetIp OPTIONAL,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. If the TargetRealFrame parameter is not specified, then the exit
+ unwind flag is also set in the exception flags of the exception record.
+ A backward scan through the procedure call frames is then performed to
+ find the target of the unwind operation.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ This function is identical to RtlUnwind except that the TargetRealFrame
+ parameter is the real frame pointer instead of the virtual frame pointer.
+
+Arguments:
+
+ TargetRealFrame - Supplies an optional pointer to the call frame that is
+ the target of the unwind. If this parameter is not specified, then an
+ exit unwind is performed.
+
+ TargetIp - Supplies an optional instruction address that specifies the
+ continuation address of the unwind. This address is ignored if the
+ target frame parameter is not specified.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ CONTEXT ContextRecord2;
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LastPc;
+ ULONG LowLimit;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("\nRtlUnwindRfp(TargetRealFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n",
+ TargetRealFrame, TargetIp, ReturnValue);
+ }
+#endif
+ //
+ // Get current stack limits, capture the current context, virtually
+ // unwind to the caller of this routine, get the initial PC value, and
+ // set the unwind target address.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlCaptureContext(&ContextRecord1);
+ ControlPc = (ULONG)ContextRecord1.IntRa;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = LastPc;
+ ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp;
+
+ //
+ // If an exception record is not specified, then build a local exception
+ // record for use in calling exception handlers during the unwind operation.
+ //
+
+ if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
+ ExceptionRecord = &ExceptionRecord1;
+ ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // If the target frame of the unwind is specified, then a normal unwind
+ // is being performed. Otherwise, an exit unwind is being performed.
+ //
+
+ ExceptionFlags = EXCEPTION_UNWINDING;
+ if (ARGUMENT_PRESENT(TargetRealFrame) == FALSE) {
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
+ }
+
+ //
+ // Scan backward through the call frame hierarchy and call exception
+ // handlers until the target frame of the unwind is reached.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindRfp: Loop: FrameDepth = %d, Rfp = %lx, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, EstablisherFrame.Real, ContextRecord1.IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then copy the
+ // context record, virtually unwind to the caller of the current
+ // routine to obtain the real frame pointer of the establisher and
+ // check if there is an exception handler for the frame.
+ //
+
+ if (FunctionEntry != NULL) {
+ RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT));
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // If the real and virtual frame pointers are not within the
+ // specified stack limits, the frame pointers are unaligned, or
+ // the target frame is below the real frame and an exit unwind is
+ // not being performed, then raise the exception STATUS_BAD_STACK.
+ // Otherwise, check to determine if the current routine has an
+ // exception handler.
+ //
+
+ if ((EstablisherFrame.Real < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ (EstablisherFrame.Real > EstablisherFrame.Virtual) ||
+ ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) &&
+ ((ULONG)TargetRealFrame < EstablisherFrame.Real)) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0) ||
+ ((EstablisherFrame.Real & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
+ DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" TargetRealFrame = %08lx\n", TargetRealFrame);
+ if ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) &&
+ ((ULONG)TargetRealFrame < EstablisherFrame.Real)) {
+ DbgPrint(" TargetRealFrame is below EstablisherFrame.Real!\n");
+ }
+ DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
+ (ULONG)ContextRecord2.IntSp);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n",
+ LastPc, ControlPc);
+ DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
+#endif
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlUnwindRfp: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+}
+#endif
+
+ //
+ // The frame has an exception handler.
+ //
+ // The control PC, establisher frame pointer, the address
+ // of the function table entry, and the address of the
+ // context record are all stored in the dispatcher context.
+ // This information is used by the unwind linkage routine
+ // and can be used by the exception handler itself.
+ //
+ // A linkage routine written in assembler is used to actually
+ // call the actual exception handler. This is required by the
+ // exception handler that is associated with the linkage
+ // routine so it can have access to two sets of dispatcher
+ // context when it is called.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = &ContextRecord2;
+
+ //
+ // Call the exception handler.
+ //
+
+ do {
+
+ //
+ // If the establisher frame is the target of the unwind
+ // operation, then set the target unwind flag.
+ //
+
+ if ((ULONG)TargetRealFrame == EstablisherFrame.Real) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindRfp: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ &ContextRecord2,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindRfp: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ //
+ // Clear target unwind and collided unwind flags.
+ //
+
+ ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
+ EXCEPTION_TARGET_UNWIND);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search.
+ //
+ // Continue the search for a handler or continue
+ // execution.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is collided unwind.
+ //
+ // Set the target of the current unwind to the context
+ // record of the previous unwind, virtually unwind to
+ // the caller of the old routine, and reexecute the
+ // exception handler from the collided frame with the
+ // collided unwind flag set in the exception record.
+ //
+
+ case ExceptionCollidedUnwind :
+ ControlPc = DispatcherContext.ControlPc;
+ FunctionEntry = DispatcherContext.FunctionEntry;
+ RtlMoveMemory(&ContextRecord1,
+ DispatcherContext.ContextRecord,
+ sizeof(CONTEXT));
+
+ ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp;
+ RtlMoveMemory(&ContextRecord2,
+ &ContextRecord1,
+ sizeof(CONTEXT));
+
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+
+ } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ LastPc = (ULONG)ContextRecord1.IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (LastPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (unwind).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+ DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
+#endif
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = LastPc;
+
+ } while ((EstablisherFrame.Real < HighLimit) &&
+ (EstablisherFrame.Real != (ULONG)TargetRealFrame));
+
+ //
+ // If the establisher stack pointer is equal to the target frame
+ // pointer, then continue execution. Otherwise, an exit unwind was
+ // performed or the target of the unwind did not exist and the
+ // debugger and subsystem are given a second chance to handle the
+ // unwind.
+ //
+
+ if (EstablisherFrame.Real == (ULONG)TargetRealFrame) {
+ ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindRfp: finished unwinding, and calling RtlpRestoreContext\n");
+ }
+#endif
+ RtlpRestoreContext(&ContextRecord2);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindRfp: finished unwinding, but calling ZwRaiseException\n");
+ }
+#endif
+ ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE);
+ }
+}
+
+VOID
+RtlUnwindReturn (
+ IN PVOID TargetFrame,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. A backward scan through the procedure call frames is then
+ performed to find the target of the unwind operation. When the target
+ frame is reached, a return is made to the caller of the target frame
+ with the return value specified by the return value parameter.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ This function is identical to RtlUnwind except control resumes in the
+ caller of the target frame, not in the target frame itself.
+
+Arguments:
+
+ TargetFrame - Supplies an optional pointer to the call frame that is the
+ target of the unwind.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ CONTEXT ContextRecord2;
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LastPc;
+ ULONG LowLimit;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("\nRtlUnwindReturn(TargetFrame = %lx,, ReturnValue = %lx)\n",
+ TargetFrame, ReturnValue);
+ }
+#endif
+ //
+ // Get current stack limits, capture the current context, virtually
+ // unwind to the caller of this routine, get the initial PC value, and
+ // set the unwind target address.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlCaptureContext(&ContextRecord1);
+ ControlPc = (ULONG)ContextRecord1.IntRa;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = LastPc;
+ ContextRecord1.Fir = 0;
+
+ //
+ // If an exception record is not specified, then build a local exception
+ // record for use in calling exception handlers during the unwind operation.
+ //
+
+ if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
+ ExceptionRecord = &ExceptionRecord1;
+ ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // A target frame of the unwind is specified so a normal unwind is
+ // being performed.
+ //
+
+ ExceptionFlags = EXCEPTION_UNWINDING;
+
+ //
+ // Scan backward through the call frame hierarchy and call exception
+ // handlers until the target frame of the unwind is reached.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindReturn: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, ContextRecord1.IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then copy the
+ // context record, virtually unwind to the caller of the current
+ // routine to obtain the virtual frame pointer of the establisher and
+ // check if there is an exception handler for the frame.
+ //
+
+ if (FunctionEntry != NULL) {
+ RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT));
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // If the virtual frame pointer is not within the specified stack
+ // limits, the virtual frame pointer is unaligned, or the target
+ // frame is below the virtual frame and an exit unwind is not being
+ // performed, then raise the exception STATUS_BAD_STACK. Otherwise,
+ // check to determine if the current routine has an exception
+ // handler.
+ //
+
+ if ((EstablisherFrame.Virtual < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
+ DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" TargetFrame = %08lx\n", TargetFrame);
+ if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual)) {
+ DbgPrint(" TargetFrame is below EstablisherFrame!\n");
+ }
+ DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
+ (ULONG)ContextRecord2.IntSp);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n",
+ LastPc, ControlPc);
+ DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
+#endif
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlUnwindReturn: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+}
+#endif
+
+ //
+ // The frame has an exception handler.
+ //
+ // The control PC, establisher frame pointer, the address
+ // of the function table entry, and the address of the
+ // context record are all stored in the dispatcher context.
+ // This information is used by the unwind linkage routine
+ // and can be used by the exception handler itself.
+ //
+ // A linkage routine written in assembler is used to actually
+ // call the actual exception handler. This is required by the
+ // exception handler that is associated with the linkage
+ // routine so it can have access to two sets of dispatcher
+ // context when it is called.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = &ContextRecord2;
+
+ //
+ // Call the exception handler.
+ //
+
+ do {
+
+ //
+ // If the establisher frame is the target of the unwind
+ // operation, then set the target unwind flag.
+ //
+
+ if ((ULONG)TargetFrame == EstablisherFrame.Virtual) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindReturn: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ &ContextRecord2,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindReturn: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ //
+ // Clear target unwind and collided unwind flags.
+ //
+
+ ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
+ EXCEPTION_TARGET_UNWIND);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search.
+ //
+ // Continue the search for a handler or continue
+ // execution.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is collided unwind.
+ //
+ // Set the target of the current unwind to the context
+ // record of the previous unwind, virtually unwind to
+ // the caller of the old routine, and reexecute the
+ // exception handler from the collided frame with the
+ // collided unwind flag set in the exception record.
+ //
+
+ case ExceptionCollidedUnwind :
+ ControlPc = DispatcherContext.ControlPc;
+ FunctionEntry = DispatcherContext.FunctionEntry;
+ RtlMoveMemory(&ContextRecord1,
+ DispatcherContext.ContextRecord,
+ sizeof(CONTEXT));
+
+ ContextRecord1.Fir = 0;
+ RtlMoveMemory(&ContextRecord2,
+ &ContextRecord1,
+ sizeof(CONTEXT));
+
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+
+ } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ LastPc = (ULONG)ContextRecord1.IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (LastPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (unwind).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+ DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
+#endif
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = LastPc;
+
+ } while ((EstablisherFrame.Virtual < HighLimit) &&
+ (EstablisherFrame.Virtual != (ULONG)TargetFrame));
+
+ //
+ // If the establisher stack pointer is equal to the target frame pointer,
+ // then continue execution at the point where the call to the target frame
+ // was made. Otherwise the target of the unwind did not exist and the
+ // debugger and subsystem are given a second chance to handle the unwind.
+ //
+
+ if ((ULONG)ContextRecord1.IntSp == (ULONG)TargetFrame) {
+ ContextRecord1.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+
+ //
+ // Set the continuation address to the address after the point where
+ // control left the previous frame and entered the target frame.
+ //
+
+ ContextRecord1.Fir = (ULONGLONG)(LONG)LastPc + 4;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindReturn: finished unwinding, and calling RtlpRestoreContext\n");
+ }
+#endif
+ RtlpRestoreContext(&ContextRecord1);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindReturn: finished unwinding, but calling ZwRaiseException\n");
+ }
+#endif
+ ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE);
+ }
+}
diff --git a/private/ntos/rtl/alpha/xcptmisc.s b/private/ntos/rtl/alpha/xcptmisc.s
new file mode 100644
index 000000000..45141b0d5
--- /dev/null
+++ b/private/ntos/rtl/alpha/xcptmisc.s
@@ -0,0 +1,501 @@
+// TITLE("Miscellaneous Exception Handling")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// xcptmisc.s
+//
+// Abstract:
+//
+// This module implements miscellaneous routines that are required to
+// support exception handling. Functions are provided to call an exception
+// handler for an exception, call an exception handler for unwinding, call
+// an exception filter, call a termination handler, and get the caller's
+// stack limits.
+//
+// Author:
+//
+// David N. Cutler (davec) 12-Sep-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 7-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Define call frame for calling exception handlers.
+//
+
+ .struct 0
+CfRa: .space 8 // saved return address
+CfA3: .space 8 // save area for argument a3
+ .space 0 * 8 // 16-byte stack alignment
+CfFrameLength: // length of stack frame
+
+ SBTTL("Execute Handler for Exception")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpExecuteHandlerForException (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN ULONG EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PDISPATCHER_CONTEXT DispatcherContext,
+// IN PEXCEPTION_ROUTINE ExceptionRoutine
+// )
+//
+// Routine Description:
+//
+// This function allocates a call frame, stores the establisher frame
+// pointer in the frame, establishes an exception handler, and then calls
+// the specified exception handler as an exception handler. If a nested
+// exception occurs, then the exception handler of this function is called
+// and the establisher frame pointer is returned to the exception dispatcher
+// via the dispatcher context parameter. If control is returned to this
+// routine, then the frame is deallocated and the disposition status is
+// returned to the exception dispatcher.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of the exception handler that is to be called.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// ExceptionRoutine (a4) - Supplies a pointer to the exception handler that
+// is to be called.
+//
+// Return Value:
+//
+// The disposition value returned by the specified exception handler is
+// returned as the function value.
+//
+//--
+
+//
+// N.B. This function specifies its own private exception handler.
+//
+
+ EXCEPTION_HANDLER(RtlpExceptionHandler)
+
+ NESTED_ENTRY(RtlpExecuteHandlerForException, CfFrameLength, zero)
+
+ lda sp, -CfFrameLength(sp) // allocate stack frame
+ stq ra, CfRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// Save the address of the dispatcher context record in our stack frame so
+// that our own exception handler (not the one we're calling) can retrieve it.
+//
+
+ stq a3, CfA3(sp) // save address of dispatcher context
+
+//
+// Now call the exception handler and return its return value as ours.
+//
+
+ bic a4, 3, a4 // clear low-order bits (IEEE mode)
+ jsr ra, (a4) // call exception handler
+
+ ldq ra, CfRa(sp) // restore return address
+ lda sp, CfFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end RtlpExecuteHandlerForException
+
+ SBTTL("Local Exception Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpExceptionHandler (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN ULONG EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PDISPATCHER_CONTEXT DispatcherContext
+// )
+//
+// Routine Description:
+//
+// This function is called when a nested exception occurs. Its function
+// is to retrieve the establisher frame pointer from its establisher's
+// call frame, store this information in the dispatcher context record,
+// and return a disposition value of nested exception.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// A disposition value ExceptionNestedException is returned if an unwind
+// is not in progress. Otherwise a value of ExceptionContinueSearch is
+// returned.
+//
+//--
+
+ LEAF_ENTRY(RtlpExceptionHandler)
+
+ ldl t0, ErExceptionFlags(a0) // get exception flags
+ and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress
+ bne t0, 10f // if neq, unwind in progress
+
+//
+// Unwind is not in progress - return nested exception disposition.
+//
+
+//
+// Convert the given establisher virtual frame pointer (a1) to a real frame
+// pointer (the value of a1 minus CfFrameLength) and retrieve the pointer to
+// the dispatcher context that earlier was stored in the stack frame.
+//
+
+ ldq t0, -CfFrameLength + CfA3(a1) // get dispatcher context address
+
+ ldl t1, DcEstablisherFrame(t0) // copy the establisher frame pointer
+ stl t1, DcEstablisherFrame(a3) // to current dispatcher context
+
+ ldil v0, ExceptionNestedException // set disposition value
+ ret zero, (ra) // return
+
+//
+// Unwind is in progress - return continue search disposition.
+//
+
+10: ldil v0, ExceptionContinueSearch // set disposition value
+ ret zero, (ra) // return
+
+ .end RtlpExceptionHandler
+
+ SBTTL("Execute Handler for Unwind")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpExecuteHandlerForUnwind (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN PVOID EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PVOID DispatcherContext,
+// IN PEXCEPTION_ROUTINE ExceptionRoutine
+// )
+//
+// Routine Description:
+//
+// This function allocates a call frame, stores the establisher frame
+// pointer and the context record address in the frame, establishes an
+// exception handler, and then calls the specified exception handler as
+// an unwind handler. If a collided unwind occurs, then the exception
+// handler of this function is called and the establisher frame pointer
+// and context record address are returned to the unwind dispatcher via
+// the dispatcher context parameter. If control is returned to this routine,
+// then the frame is deallocated and the disposition status is returned to
+// the unwind dispatcher.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of the exception handler that is to be called.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// ExceptionRoutine (a4) - Supplies a pointer to the exception handler that
+// is to be called.
+//
+// Return Value:
+//
+// The disposition value returned by the specified exception handler is
+// returned as the function value.
+//
+//--
+
+//
+// N.B. This function specifies its own private exception handler.
+//
+
+ EXCEPTION_HANDLER(RtlpUnwindHandler)
+
+ NESTED_ENTRY(RtlpExecuteHandlerForUnwind, CfFrameLength, zero)
+
+ lda sp, -CfFrameLength(sp) // allocate stack frame
+ stq ra, CfRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// Save the address of the dispatcher context record in our stack frame so
+// that our own exception handler (not the one we're calling) can retrieve it.
+//
+
+ stq a3, CfA3(sp) // save address of dispatcher context
+
+//
+// Now call the exception handler and return its return value as our return
+// value.
+//
+
+ bic a4, 3, a4 // clear low-order bits (IEEE mode)
+ jsr ra, (a4) // call exception handler
+
+ ldq ra, CfRa(sp) // restore return address
+ lda sp, CfFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end RtlpExecuteHandlerForUnwind
+
+ SBTTL("Local Unwind Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpUnwindHandler (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN PVOID EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PVOID DispatcherContext
+// )
+//
+// Routine Description:
+//
+// This function is called when a collided unwind occurs. Its function
+// is to retrieve the establisher dispatcher context, copy it to the
+// current dispatcher context, and return a disposition value of nested
+// unwind.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// A disposition value ExceptionCollidedUnwind is returned if an unwind is
+// in progress. Otherwise, a value of ExceptionContinueSearch is returned.
+//
+//--
+
+ LEAF_ENTRY(RtlpUnwindHandler)
+
+ ldl t0, ErExceptionFlags(a0) // get exception flags
+ and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress
+ beq t0, 10f // if eq, unwind not in progress
+
+//
+// Unwind is in progress - return collided unwind disposition.
+//
+
+//
+// Convert the given establisher virtual frame pointer (a1) to a real frame
+// pointer (the value of a1 minus CfFrameLength) and retrieve the pointer to
+// the dispatcher context that earlier was stored in the stack frame.
+//
+
+ ldq t0, -CfFrameLength + CfA3(a1) // get dispatcher context address
+
+ ldl t1, DcControlPc(t0) // copy the entire dispatcher
+ ldl t2, DcFunctionEntry(t0) // context of the establisher
+ ldl t3, DcEstablisherFrame(t0) // frame...
+ ldl t4, DcContextRecord(t0) //
+
+ stl t1, DcControlPc(a3) // to the current dispatcher
+ stl t2, DcFunctionEntry(a3) // context (it's four words
+ stl t3, DcEstablisherFrame(a3) // long).
+ stl t4, DcContextRecord(a3) //
+
+ ldil v0, ExceptionCollidedUnwind // set disposition value
+ ret zero, (ra) // return
+
+//
+// Unwind is not in progress - return continue search disposition.
+//
+
+10: ldil v0, ExceptionContinueSearch // set disposition value
+ ret zero, (ra) // return
+
+ .end RtlpUnwindHandler
+
+ SBTTL("Execute Exception Filter")
+//++
+//
+// ULONG
+// RtlpExecuteExceptionFilter (
+// PEXCEPTION_POINTERS ExceptionPointers,
+// EXCEPTION_FILTER ExceptionFilter,
+// ULONG EstablisherFrame
+// )
+//
+// Routine Description:
+//
+// This function sets the static link and transfers control to the specified
+// exception filter routine.
+//
+// Arguments:
+//
+// ExceptionPointers (a0) - Supplies a pointer to the exception pointers
+// structure.
+//
+// ExceptionFilter (a1) - Supplies the address of the exception filter
+// routine.
+//
+// EstablisherFrame (a2) - Supplies the establisher frame pointer.
+//
+// Return Value:
+//
+// The value returned by the exception filter routine.
+//
+//--
+
+ LEAF_ENTRY(RtlpExecuteExceptionFilter)
+
+//
+// The protocol for calling exception filters used by the acc C-compiler is
+// that the uplevel frame pointer is passed in register v0 and the pointer
+// to the exception pointers structure is passed in register a0. The Gem
+// compiler expects the static link in t0. Here we do both.
+//
+
+ mov a2, v0 // set static link
+ mov a2, t0 // set alternate static link
+ jmp zero, (a1) // transfer control to exception filter
+
+ .end RtlpExecuteExceptionFilter
+
+ SBTTL("Execute Termination Handler")
+//++
+//
+// VOID
+// RtlpExecuteTerminationHandler (
+// BOOLEAN AbnormalTermination,
+// TERMINATION_HANDLER TerminationHandler,
+// ULONG EstablisherFrame
+// )
+//
+// Routine Description:
+//
+// This function sets the static link and transfers control to the specified
+// termination handler routine.
+//
+// Arguments:
+//
+// AbnormalTermination (a0) - Supplies a boolean value that determines
+// whether the termination is abnormal.
+//
+// TerminationHandler (a1) - Supplies the address of the termination handler
+// routine.
+//
+// EstablisherFrame (a2) - Supplies the establisher frame pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlpExecuteTerminationHandler)
+
+//
+// The protocol for calling termination handlers used by the acc C-compiler
+// is that the uplevel frame pointer is passed in register v0 and the boolean
+// abnormal termination value is passed in register a0. The Gem compiler
+// expects the static link in t0. Here we do both.
+//
+
+ mov a2, v0 // set static link
+ mov a2, t0 // set alternate static link
+ jmp zero, (a1) // transfer control to termination handler
+
+ .end RtlpExecuteTerminationHandler
+
+ SBTTL("Get Stack Limits")
+//++
+//
+// VOID
+// RtlpGetStackLimits (
+// OUT PULONG LowLimit,
+// OUT PULONG HighLimit
+// )
+//
+// Routine Description:
+//
+// This function returns the current stack limits based on the current
+// processor mode.
+//
+// Arguments:
+//
+// LowLimit (a0) - Supplies a pointer to a variable that is to receive
+// the low limit of the stack.
+//
+// HighLimit (a1) - Supplies a pointer to a variable that is to receive
+// the high limit of the stack.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlpGetStackLimits)
+#if defined(NTOS_KERNEL_RUNTIME)
+
+//
+// Current mode is kernel - compute stack limits.
+//
+
+ GET_INITIAL_KERNEL_STACK // get initial kernel stack in v0
+
+ mov v0, t1 // copy high limit of kernel stack
+ GET_CURRENT_THREAD // get current thread in v0
+ ldl t2, ThStackLimit(v0) // get low limit of kernel stack
+#else
+
+//
+// Current mode is user - get stack limits from the TEB.
+//
+
+ GET_THREAD_ENVIRONMENT_BLOCK // get address of TEB in v0
+
+ ldl t1, TeStackBase(v0) // get high limit of user stack
+ ldl t2, TeStackLimit(v0) // get low limit of user stack
+#endif
+
+ stl t2, 0(a0) // store low stack limit
+ stl t1, 0(a1) // store high stack limit
+ ret zero, (ra) // return
+
+ .end RtlpGetStackLimits