summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/mips
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/mips
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/mips')
-rw-r--r--private/ntos/rtl/mips/chandler.c220
-rw-r--r--private/ntos/rtl/mips/chkstk.s88
-rw-r--r--private/ntos/rtl/mips/context.c296
-rw-r--r--private/ntos/rtl/mips/debugstb.s271
-rw-r--r--private/ntos/rtl/mips/exdsptch.c2072
-rw-r--r--private/ntos/rtl/mips/getcalr.c179
-rw-r--r--private/ntos/rtl/mips/largeint.s947
-rw-r--r--private/ntos/rtl/mips/lzntmips.s1325
-rw-r--r--private/ntos/rtl/mips/nlssup.c109
-rw-r--r--private/ntos/rtl/mips/ntrtlmip.h41
-rw-r--r--private/ntos/rtl/mips/stringsp.c122
-rw-r--r--private/ntos/rtl/mips/trampoln.s522
-rw-r--r--private/ntos/rtl/mips/xcptmisc.s356
-rw-r--r--private/ntos/rtl/mips/xxcaptur.s314
-rw-r--r--private/ntos/rtl/mips/xxmvmem.s1440
15 files changed, 8302 insertions, 0 deletions
diff --git a/private/ntos/rtl/mips/chandler.c b/private/ntos/rtl/mips/chandler.c
new file mode 100644
index 000000000..9c84d6218
--- /dev/null
+++ b/private/ntos/rtl/mips/chandler.c
@@ -0,0 +1,220 @@
+/*++
+
+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:
+
+--*/
+
+#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
+ );
+
+VOID
+__C_ExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+EXCEPTION_DISPOSITION
+__C_specific_handler (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext
+ )
+
+/*++
+
+Routine Description:
+
+ This function scans the scope tables associated with the specified
+ procedure and calls exception and termination handlers as necessary.
+
+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 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);
+
+ //
+ // 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.
+ // are called.
+ //
+
+ if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // Scan the scope table and call the appropriate exception filter
+ // routines.
+ //
+
+ ExceptionPointers.ExceptionRecord = ExceptionRecord;
+ ExceptionPointers.ContextRecord = ContextRecord;
+ 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. Otherwise,
+ // continue the search for an exception filter.
+ //
+
+ if (Value < 0) {
+ return ExceptionContinueExecution;
+
+ } else if (Value > 0) {
+ 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 = 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 termination handlers.
+ //
+ // 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.
+ //
+
+ if (ScopeTable->ScopeRecord[Index].JumpTarget != 0) {
+ if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) {
+ break;
+ }
+
+ } else {
+ DispatcherContext->ControlPc =
+ ScopeTable->ScopeRecord[Index].EndAddress + 4;
+ TerminationHandler =
+ (TERMINATION_HANDLER)ScopeTable->ScopeRecord[Index].HandlerAddress;
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ (ULONG)EstablisherFrame);
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Continue search for exception or termination handlers.
+ //
+
+ return ExceptionContinueSearch;
+}
diff --git a/private/ntos/rtl/mips/chkstk.s b/private/ntos/rtl/mips/chkstk.s
new file mode 100644
index 000000000..6de5018b7
--- /dev/null
+++ b/private/ntos/rtl/mips/chkstk.s
@@ -0,0 +1,88 @@
+// TITLE("Runtime Stack Checking")
+//++
+//
+// Copyright (c) 1991 Microsoft Corporation
+//
+// Module Name:
+//
+// chkstk.s
+//
+// Abstract:
+//
+// This module implements runtime stack checking.
+//
+// Author:
+//
+// David N. Cutler (davec) 14-Mar-1991
+//
+// Environment:
+//
+// User mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksmips.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 attempt to expand the stack. If the
+// attempt succeeds, then another page is committed. Otherwise, a stack
+// overflow exception is raised. It is the responsiblity of the caller to
+// handle this exception.
+//
+// N.B. This routine is called using a calling sequence that assumes that
+// all registers are preserved.
+//
+// Arguments:
+//
+// Allocation (t8) - Supplies the size of the allocation on the stack.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ NESTED_ENTRY(_RtlCheckStack, 0, ra)
+
+ sw t7,0(sp) // save temporary register
+ sw t8,4(sp) // save allocation size
+ sw t9,8(sp) // save temporary register
+
+ PROLOGUE_END
+
+ .set noreorder
+ .set noat
+ li t9,UsPcr // get address of user PCR
+ lw t9,PcTeb(t9) // get address of environment block
+ subu t8,sp,t8 // compute new bottom of stack
+ lw t9,TeStackLimit(t9) // get low stack address
+ sltu t7,t8,t9 // new stack address within limits?
+ beq zero,t7,20f // if eq, stack within limits
+ li t7,~(PAGE_SIZE - 1) // set address mask
+ and t8,t8,t7 // round down new stack address
+10: subu t9,t9,PAGE_SIZE // compute next address to check
+ bne t8,t9,10b // if ne, more pages to probe
+ sw zero,0(t9) // check stack address
+20: lw t7,0(sp) // restore temporary register
+ lw t8,4(sp) // restore allocation size
+ j ra // return
+ lw t9,8(sp) // restore temporary register
+ .set at
+ .set reorder
+
+ .end _RtlCheckStack
diff --git a/private/ntos/rtl/mips/context.c b/private/ntos/rtl/mips/context.c
new file mode 100644
index 000000000..ef9b521c9
--- /dev/null
+++ b/private/ntos/rtl/mips/context.c
@@ -0,0 +1,296 @@
+/*++
+
+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.
+
+--*/
+
+#include <ntos.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:
+
+ Context - Supplies a pointer to a context record that is to be initialized.
+
+ 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 & 0x7) != 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.
+ //
+
+ Context->XIntZero = 0;
+ Context->XIntAt = 1;
+ Context->XIntV0 = 2;
+ Context->XIntV1 = 3;
+ Context->XIntA0 = 4;
+ Context->XIntA1 = 5;
+ Context->XIntA2 = 6;
+ Context->XIntA3 = 7;
+ Context->XIntT0 = 8;
+ Context->XIntT1 = 9;
+ Context->XIntT2 = 10;
+ Context->XIntT3 = 11;
+ Context->XIntT4 = 12;
+ Context->XIntT5 = 13;
+ Context->XIntT6 = 14;
+ Context->XIntT7 = 15;
+ Context->XIntS0 = 16;
+ Context->XIntS1 = 17;
+ Context->XIntS2 = 18;
+ Context->XIntS3 = 19;
+ Context->XIntS4 = 20;
+ Context->XIntS5 = 21;
+ Context->XIntS6 = 22;
+ Context->XIntS7 = 23;
+ Context->XIntT8 = 24;
+ Context->XIntT9 = 25;
+ Context->XIntS8 = 30;
+ Context->XIntLo = 0;
+ Context->XIntHi = 0;
+
+ //
+ // Initialize the floating point registers to contain zero in their upper
+ // half and the integer value of their register number in the lower half.
+ //
+
+ Context->FltF0 = 0;
+ Context->FltF1 = 0;
+ Context->FltF2 = 2;
+ Context->FltF3 = 0;
+ Context->FltF4 = 4;
+ Context->FltF5 = 0;
+ Context->FltF6 = 6;
+ Context->FltF7 = 0;
+ Context->FltF8 = 8;
+ Context->FltF9 = 0;
+ Context->FltF10 = 10;
+ Context->FltF11 = 0;
+ Context->FltF12 = 12;
+ Context->FltF13 = 0;
+ Context->FltF14 = 14;
+ Context->FltF15 = 0;
+ Context->FltF16 = 16;
+ Context->FltF17 = 0;
+ Context->FltF18 = 18;
+ Context->FltF19 = 0;
+ Context->FltF20 = 20;
+ Context->FltF21 = 0;
+ Context->FltF22 = 22;
+ Context->FltF23 = 0;
+ Context->FltF24 = 24;
+ Context->FltF25 = 0;
+ Context->FltF26 = 26;
+ Context->FltF27 = 0;
+ Context->FltF28 = 28;
+ Context->FltF29 = 0;
+ Context->FltF30 = 30;
+ Context->FltF31 = 0;
+ Context->Fsr = 0;
+
+ //
+ // Initialize the control registers.
+ //
+ // N.B. The register gp is estabished at thread startup by the loader.
+ //
+
+ Context->XIntGp = 0;
+ Context->XIntSp = (LONG)InitialSp;
+ Context->XIntRa = 1;
+ Context->Fir = (ULONG)InitialPc;
+ Context->Psr = 0;
+ Context->ContextFlags = CONTEXT_FULL;
+
+ //
+ // Set the initial context of the thread in a machine specific way.
+ //
+
+ Context->XIntA0 = (LONG)Parameter;
+ Context->XIntSp -= KTRAP_FRAME_ARGUMENTS;
+}
+
+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 - s7).
+
+Arguments:
+
+ Process - Supplies an open handle to the target process.
+
+ Thread - Supplies an open handle to the target thread within the target
+ process.
+
+ CallSize - 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 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 NewSp;
+
+ if (ArgumentCount > 8) {
+ 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 cuurent 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.XIntV0 = (LONG)STATUS_ALERTED;
+ }
+
+ //
+ // Pass the parameters to the other thread via the non-volatile registers
+ // s0 - s7. The context record is passed on the stack of the target thread.
+ //
+
+ NewSp = (ULONG)(Context.XIntSp - sizeof(CONTEXT));
+ Status = NtWriteVirtualMemory(Process,
+ (PVOID)NewSp,
+ &Context,
+ sizeof(CONTEXT),
+ NULL);
+
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+
+ return Status;
+ }
+
+ Context.XIntSp = (LONG)NewSp;
+ if (PassContext) {
+ Context.XIntS0 = (LONG)NewSp;
+ RtlMoveMemory(&Context.XIntS1, Arguments, ArgumentCount * sizeof(ULONG));
+
+ } else {
+ RtlMoveMemory(&Context.XIntS0, Arguments, ArgumentCount * sizeof(ULONG));
+ }
+
+ //
+ // Set the address of the target code into FIR and set the thread context
+ // to cause the target procedure to be executed.
+ //
+
+ Context.Fir = (ULONG)CallSite;;
+ Status = NtSetContextThread(Thread, &Context);
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+
+ return Status;
+}
diff --git a/private/ntos/rtl/mips/debugstb.s b/private/ntos/rtl/mips/debugstb.s
new file mode 100644
index 000000000..4a305eaa1
--- /dev/null
+++ b/private/ntos/rtl/mips/debugstb.s
@@ -0,0 +1,271 @@
+// 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 trap r31,r29,r0 instruction with a special value in
+// R31. The simulator decodes this trap instruction and dispatches to the
+// correct piece of code in the simulator based on the value in R31. See
+// the simscal.c source file in the simulator source directory.
+//
+// Author:
+//
+// Steven R. Wood (stevewo) 3-Aug-1989
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksmips.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_BREAKPOINT
+ j ra
+
+ .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:
+//
+// None.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(DbgBreakPointWithStatus)
+
+ ALTERNATE_ENTRY(RtlpBreakWithStatusInstruction)
+ break DEBUG_STOP_BREAKPOINT
+ j ra
+
+ .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 DEBUG_STOP_BREAKPOINT
+ j ra
+
+ .end DbgUserBreakPoint
+
+//++
+//
+// 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)
+
+ lhu a3,StrMaximumLength(a1) // set maximum length of input string
+ lw a2,StrBuffer(a1) // set address of input string
+ lhu a1,StrLength(a0) // set length of output string
+ lw a0,StrBuffer(a0) // set address of output string
+ break DEBUG_PROMPT_BREAKPOINT // execute a debug prompt breakpoint
+ j ra // return
+
+ .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_BREAKPOINT
+ j 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_BREAKPOINT
+ j ra
+
+ .end DebugUnLoadImageSymbols
+
+#endif
+
+//++
+//
+// 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)
+
+ lhu a1,StrLength(a0) // set length of output string
+ lw a0,StrBuffer(a0) // set address of output string
+ break DEBUG_PRINT_BREAKPOINT // execute a debug print breakpoint
+ j ra // return
+
+ .end DebugPrint
+
+#endif
diff --git a/private/ntos/rtl/mips/exdsptch.c b/private/ntos/rtl/mips/exdsptch.c
new file mode 100644
index 000000000..9c9b4d3b6
--- /dev/null
+++ b/private/ntos/rtl/mips/exdsptch.c
@@ -0,0 +1,2072 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ exdsptch.c
+
+Abstract:
+
+ This module implements the dispatching of exception and the unwinding of
+ procedure call frames.
+
+Author:
+
+ David N. Cutler (davec) 11-Sep-1990
+
+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); \
+ }
+
+//
+// Define stack register and zero register numbers.
+//
+
+#define RA 0x1f // integer register 31
+#define SP 0x1d // integer register 29
+#define ZERO 0x0 // integer register 0
+
+//
+// Define saved register masks.
+//
+
+#define SAVED_FLOATING_MASK 0xfff00000 // saved floating registers
+#define SAVED_INTEGER_MASK 0xf3ffff02 // saved integer registers
+
+//
+// Define private function prototypes.
+//
+
+VOID
+RtlpRestoreContext (
+ IN PCONTEXT Context,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL
+ );
+
+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,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ );
+
+ULONG
+RtlpVirtualUnwind32 (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ );
+
+
+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 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. 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;
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ ULONG EstablisherFrame;
+ ULONG ExceptionFlags;
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG Index;
+ BOOLEAN InFunction;
+ ULONG HighLimit;
+ 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.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT));
+ ControlPc = ContextRecord1.Fir;
+ ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE;
+ NestedFrame = 0;
+
+ //
+ // 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 {
+
+ //
+ // 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 | 1,
+ 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 < LowLimit) || (EstablisherFrame > HighLimit) ||
+ ((EstablisherFrame & 0x7) != 0)) {
+ ExceptionFlags |= EXCEPTION_STACK_INVALID;
+ break;
+
+ } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) {
+
+ //
+ // 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;
+ DispatcherContext.ContextRecord = ContextRecord;
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // If requested log exception.
+ //
+
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ Index = RtlpLogExceptionHandler(ExceptionRecord,
+ ContextRecord,
+ ControlPc,
+ FunctionEntry,
+ sizeof(RUNTIME_FUNCTION));
+ }
+
+ Disposition =
+ RtlpExecuteHandlerForException(ExceptionRecord,
+ EstablisherFrame,
+ ContextRecord,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+
+ 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) {
+ 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 {
+ 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.XIntRa - 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) {
+ break;
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ ControlPc = NextPc;
+ } while ((ULONG)ContextRecord1.XIntSp < HighLimit);
+
+ //
+ // Set final exception flags and return exception not handled.
+ //
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+ 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;
+ USHORT i;
+
+ //
+ // Search for the image that includes the specified PC value.
+ //
+
+ ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase);
+
+ //
+ // 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 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 {
+
+ //
+ // The capability exists for more than one function entry
+ // to map to the same function. This permits a function to
+ // have discontiguous code segments described by separate
+ // function table entries. If the ending prologue address
+ // is not within the limits of the begining and ending
+ // address of the function able entry, then the prologue
+ // ending address is the address of a function table entry
+ // that accurately describes the ending prologue address.
+ //
+
+ if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
+ (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) {
+ FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress;
+ }
+
+ return FunctionEntry;
+ }
+ }
+ }
+ }
+
+ //
+ // A function table entry for the specified PC was not found.
+ //
+
+ 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.
+
+--*/
+
+{
+
+ 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;
+ ULONG 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.XIntRa - 4);
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc | 1,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ContextRecord.Fir = 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.
+
+--*/
+
+{
+
+ 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;
+ ULONG 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.XIntRa - 4);
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc | 1,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ContextRecord.Fir = 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.
+
+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.
+
+ ContextRecord - Supplies a pointer to a context record that can be used
+ to store context druing the unwind operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ ULONG EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG HighLimit;
+ ULONG LowLimit;
+ ULONG NextPc;
+
+ //
+ // 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->XIntRa - 4);
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc | 1,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = NextPc;
+ ContextRecord->Fir = (ULONG)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 {
+
+ //
+ // 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,
+ &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 < LowLimit) || (EstablisherFrame > HighLimit) ||
+ ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame)) ||
+ ((EstablisherFrame & 0x7) != 0)) {
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) {
+
+ //
+ // 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;
+ 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) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord->XIntV0 = (LONG)ReturnValue;
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame,
+ ContextRecord,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+
+ //
+ // 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 != (ULONG)TargetFrame) {
+ NextPc = RtlVirtualUnwind(ControlPc | 1,
+ 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 = (ULONG)TargetIp;
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ EstablisherFrame = 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 {
+
+ //
+ // If the target frame has not been reached, then virtually unwind to the
+ // caller of the current routine and update the context record.
+ //
+
+ if (EstablisherFrame != (ULONG)TargetFrame) {
+ NextPc = RtlVirtualUnwind(ControlPc | 1,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ }
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ NextPc = (ULONG)(ContextRecord->XIntRa - 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) {
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+ // N.B. Make sure the address is in the delay slot of the jal
+ // to prevent the boundary condition of the return address
+ // being at the front of a try body.
+ //
+
+ ControlPc = NextPc;
+
+ } while ((EstablisherFrame < HighLimit) &&
+ (EstablisherFrame != (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 == (ULONG)TargetFrame) {
+ ContextRecord->XIntV0 = (LONG)ReturnValue;
+ RtlpRestoreContext(ContextRecord, ExceptionRecord);
+
+ } else {
+ ZwRaiseException(ExceptionRecord, ContextRecord, FALSE);
+ }
+}
+
+ULONG
+RtlVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ )
+
+/*++
+
+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 routine handles 64-bit context records.
+
+Arguments:
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ N.B. The low order bit of this argument is used to denote the
+ context record type. If the low order bit is clear, then
+ the context record contains 32-bit information. Otherwise,
+ it contains 64-bit information.
+
+ 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.
+
+--*/
+
+{
+
+ ULONG Address;
+ ULONG DecrementOffset;
+ ULONG DecrementRegister;
+ PULONG FloatingRegister;
+ ULONG Function;
+ MIPS_INSTRUCTION Instruction;
+ PULONGLONG IntegerRegister;
+ ULONG NextPc;
+ LONG Offset;
+ ULONG Opcode;
+ ULONG Rd;
+ BOOLEAN RestoredRa;
+ BOOLEAN RestoredSp;
+ ULONG Rs;
+ ULONG Rt;
+
+ //
+ // If the low order bit of the control PC is clear, then the context
+ // record format is 32-bit. Otherwise, the context record format is
+ // 64-bits.
+ //
+
+ if ((ControlPc & 1) == 0) {
+ return RtlpVirtualUnwind32(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ InFunction,
+ EstablisherFrame,
+ ContextPointers);
+
+ } else {
+
+ //
+ // Set the base address of the integer and floating register arrays.
+ //
+
+ FloatingRegister = &ContextRecord->FltF0;
+ IntegerRegister = &ContextRecord->XIntZero;
+
+ //
+ // If the instruction at the point where control left the specified
+ // function is a return, then any saved registers have been restored
+ // with the possible exception of the stack pointer and the control
+ // PC is not considered to be in the function (i.e., an epilogue).
+ //
+
+ ControlPc &= ~1;
+ if (*((PULONG)ControlPc) == JUMP_RA) {
+ *InFunction = FALSE;
+ Instruction.Long = *((PULONG)ControlPc + 1);
+ Opcode = Instruction.i_format.Opcode;
+ Offset = Instruction.i_format.Simmediate;
+ Rd = Instruction.r_format.Rd;
+ Rs = Instruction.i_format.Rs;
+ Rt = Instruction.i_format.Rt;
+ Function = Instruction.r_format.Function;
+
+ //
+ // If the opcode is an add immediate unsigned op and both the
+ // source and destination registers are SP, then add the signed
+ // offset value to SP. Otherwise, if the opcode is a special op,
+ // the operation is an add unsigned, and the source and destination
+ // registers are both SP, then add the register specified by Rd to
+ // SP.
+ //
+
+ if ((Opcode == ADDIU_OP) && (Rt == SP) && (Rs == SP)) {
+ IntegerRegister[SP] += Offset;
+
+ } else if ((Opcode == SPEC_OP) && (Function == ADDU_OP) &&
+ (Rd == SP) && (Rs == SP)) {
+ IntegerRegister[SP] += IntegerRegister[Rt];
+ }
+
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ return (ULONG)ContextRecord->XIntRa;
+ }
+
+ //
+ // If the address where control left the specified function is outside
+ // the limits 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., it is within the prologue).
+ //
+
+ if ((ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= FunctionEntry->PrologEndAddress)) {
+ *InFunction = TRUE;
+ ControlPc = FunctionEntry->PrologEndAddress;
+
+ } else {
+ *InFunction = FALSE;
+ }
+
+ //
+ // Scan backward through the prologue and reload callee registers that
+ // were stored.
+ //
+
+ DecrementRegister = 0;
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ NextPc = (ULONG)(ContextRecord->XIntRa - 4);
+ RestoredRa = FALSE;
+ RestoredSp = FALSE;
+ while (ControlPc > FunctionEntry->BeginAddress) {
+
+ //
+ // Get instruction value, decode fields, case of opcode value, and
+ // reverse store operations.
+ //
+
+ ControlPc -= 4;
+ Instruction.Long = *((PULONG)ControlPc);
+ Opcode = Instruction.i_format.Opcode;
+ Offset = Instruction.i_format.Simmediate;
+ Rd = Instruction.r_format.Rd;
+ Rs = Instruction.i_format.Rs;
+ Rt = Instruction.i_format.Rt;
+ Address = (ULONG)(Offset + IntegerRegister[Rs]);
+ if (Opcode == SW_OP) {
+
+ //
+ // Store word.
+ //
+ // If the base register is SP and the source register is an
+ // integer register, then reload the register value.
+ //
+
+ if (Rs == SP) {
+ IntegerRegister[Rt] = *((PLONG)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, this
+ // is an interrupt or exception and the return PC should be
+ // biased by 4. Otherwise, if the destination register is
+ // SP and this is the first time that SP is being restored,
+ // then set the establisher frame pointer.
+ //
+
+ if (Rt == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)(ContextRecord->XIntRa - 4);
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+
+ } else if (Rt == SP) {
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ 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->XIntegerContext[Rt] = (PULONGLONG)Address;
+ }
+ }
+
+ } else if (Opcode == SD_OP) {
+
+ //
+ // Store double.
+ //
+ // If the base register is SP and the source register is an
+ // integer register, then reload the register value.
+ //
+
+ if (Rs == SP) {
+ IntegerRegister[Rt] = *((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, this
+ // is an interrupt or exception and the return PC should be
+ // biased by 4. Otherwise, if the destination register is
+ // SP and this is the first time that SP is being restored,
+ // then set the establisher frame pointer.
+ //
+
+ if (Rt == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)(ContextRecord->XIntRa - 4);
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+
+ } else if (Rt == SP) {
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ RestoredSp = TRUE;
+ }
+ }
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination register contents
+ // are stored.
+ //
+ // N.B. The low order bit of the address is set to indicate
+ // a store double operation.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->XIntegerContext[Rt] = (PLONGLONG)((ULONG)Address | 1);
+ }
+ }
+
+ } else if (Opcode == SWC1_OP) {
+
+ //
+ // Store word coprocessor 1.
+ //
+ // If the base register is SP and the source register is a
+ // floating register, then reload the register value.
+ //
+
+ if (Rs == SP) {
+ FloatingRegister[Rt] = *((PULONG)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[Rt] = (PULONG)Address;
+ }
+ }
+
+ } else if (Opcode == SDC1_OP) {
+
+ //
+ // Store double coprocessor 1.
+ //
+ // If the base register is SP and the source register is a
+ // floating register, then reload the register and the next
+ // register values.
+ //
+
+ if (Rs == SP) {
+ FloatingRegister[Rt] = *((PULONG)Address);
+ FloatingRegister[Rt + 1] = *((PULONG)(Address + 4));
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination registers contents
+ // are stored.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->FloatingContext[Rt] = (PULONG)Address;
+ ContextPointers->FloatingContext[Rt + 1] = (PULONG)(Address + 4);
+ }
+ }
+
+ } else if (Opcode == ADDIU_OP) {
+
+ //
+ // Add immediate unsigned.
+ //
+ // If both the source and destination registers are SP, then
+ // a standard stack allocation was performed and the signed
+ // displacement value should be subtracted from SP. Otherwise,
+ // if the destination register is the decrement register and
+ // the source register is zero, then add the decrement value
+ // to SP.
+ //
+
+ if ((Rs == SP) && (Rt == SP)) {
+ IntegerRegister[SP] -= Offset;
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ RestoredSp = TRUE;
+ }
+
+ } else if ((Rt == DecrementRegister) && (Rs == ZERO)) {
+ IntegerRegister[SP] += Offset;
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ RestoredSp = TRUE;
+ }
+ }
+
+ } else if (Opcode == ORI_OP) {
+
+ //
+ // Or immediate.
+ //
+ // If both the destination and source registers are the decrement
+ // register, then save the decrement value. Otherwise, if the
+ // destination register is the decrement register and the source
+ // register is zero, then add the decrement value to SP.
+ //
+
+ if ((Rs == DecrementRegister) && (Rt == DecrementRegister)) {
+ DecrementOffset = (Offset & 0xffff);
+
+ } else if ((Rt == DecrementRegister) && (Rs == ZERO)) {
+ IntegerRegister[SP] += (Offset & 0xffff);
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = (ULONG)ContextRecord->XIntSp;
+ RestoredSp = TRUE;
+ }
+ }
+
+ } else if (Opcode == SPEC_OP) {
+
+ //
+ // Special operation.
+ //
+ // The real opcode is in the function field of special opcode
+ // instructions.
+ //
+
+ Function = Instruction.r_format.Function;
+ if ((Function == ADDU_OP) || (Function == OR_OP)) {
+
+ //
+ // Add unsigned or an or operation.
+ //
+ // If one of the source registers is ZERO, then the
+ // operation is a move operation and the destination
+ // register should be moved to the appropriate source
+ // register.
+ //
+
+ if (Rt == ZERO) {
+ IntegerRegister[Rs] = IntegerRegister[Rd];
+
+ //
+ // 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, this an interrupt or exception and the
+ // return PC should be biased by 4.
+ //
+
+ if (Rs == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)(ContextRecord->XIntRa - 4);
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+ }
+
+ } else if (Rs == ZERO) {
+ IntegerRegister[Rt] = IntegerRegister[Rd];
+
+ //
+ // 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, this an interrupt or exception and the
+ // return PC should be biased by 4.
+ //
+
+ if (Rt == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)(ContextRecord->XIntRa - 4);
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+ }
+ }
+
+ } else if (Function == SUBU_OP) {
+
+ //
+ // Subtract unsigned.
+ //
+ // If the destination register is SP and the source register
+ // is SP, then a stack allocation greater than 32kb has been
+ // performed and source register number of the decrement must
+ // be saved for later use.
+ //
+
+ if ((Rd == SP) && (Rs == SP)) {
+ DecrementRegister = Rt;
+ }
+ }
+
+ } else if (Opcode == LUI_OP) {
+
+ //
+ // Load upper immediate.
+ //
+ // If the destination register is the decrement register, then
+ // compute the decrement value, add it from SP, and clear the
+ // decrement register number.
+ //
+
+ if (Rt == DecrementRegister) {
+ DecrementRegister = 0;
+ IntegerRegister[SP] += (LONG)(DecrementOffset + (Offset << 16));
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = (ULONG)(ContextRecord->XIntSp);
+ RestoredSp = TRUE;
+ }
+ }
+ }
+ }
+
+ //
+ // Make sure that integer register zero is really zero.
+ //
+
+ ContextRecord->XIntZero = 0;
+ return NextPc;
+ }
+}
+
+ULONG
+RtlpVirtualUnwind32 (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ )
+
+/*++
+
+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 routine handles 32-bit context records.
+
+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.
+
+--*/
+
+{
+
+ ULONG Address;
+ ULONG DecrementOffset;
+ ULONG DecrementRegister;
+ PULONG FloatingRegister;
+ ULONG Function;
+ MIPS_INSTRUCTION Instruction;
+ PULONG IntegerRegister;
+ ULONG NextPc;
+ LONG Offset;
+ ULONG Opcode;
+ ULONG Rd;
+ BOOLEAN RestoredRa;
+ BOOLEAN RestoredSp;
+ ULONG Rs;
+ ULONG Rt;
+
+ //
+ // Set the base address of the integer and floating register arrays.
+ //
+
+ FloatingRegister = &ContextRecord->FltF0;
+ IntegerRegister = &ContextRecord->IntZero;
+
+ //
+ // If the instruction at the point where control left the specified
+ // function is a return, then any saved registers have been restored
+ // with the possible exception of the stack pointer and the control
+ // PC is not considered to be in the function (i.e., an epilogue).
+ //
+
+ if (*((PULONG)ControlPc) == JUMP_RA) {
+ *InFunction = FALSE;
+ Instruction.Long = *((PULONG)ControlPc + 1);
+ Opcode = Instruction.i_format.Opcode;
+ Offset = Instruction.i_format.Simmediate;
+ Rd = Instruction.r_format.Rd;
+ Rs = Instruction.i_format.Rs;
+ Rt = Instruction.i_format.Rt;
+ Function = Instruction.r_format.Function;
+
+ //
+ // If the opcode is an add immediate unsigned op and both the source
+ // and destination registers are SP, then add the signed offset value
+ // to SP. Otherwise, if the opcode is a special op, the operation is
+ // an add unsigned, and the source and destination registers are both
+ // SP, then add the register specified by Rd to SP.
+ //
+
+ if ((Opcode == ADDIU_OP) && (Rt == SP) && (Rs == SP)) {
+ IntegerRegister[SP] += Offset;
+
+ } else if ((Opcode == SPEC_OP) && (Function == ADDU_OP) &&
+ (Rd == SP) && (Rs == SP)) {
+ IntegerRegister[SP] += IntegerRegister[Rt];
+ }
+
+ *EstablisherFrame = ContextRecord->IntSp;
+ return ContextRecord->IntRa;
+ }
+
+ //
+ // If the address where control left the specified function is outside
+ // the limits 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., it is within the prologue).
+ //
+
+ if ((ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= FunctionEntry->PrologEndAddress)) {
+ *InFunction = TRUE;
+ ControlPc = FunctionEntry->PrologEndAddress;
+
+ } else {
+ *InFunction = FALSE;
+ }
+
+ //
+ // Scan backward through the prologue and reload callee registers that
+ // were stored.
+ //
+
+ DecrementRegister = 0;
+ *EstablisherFrame = ContextRecord->IntSp;
+ NextPc = ContextRecord->IntRa - 4;
+ RestoredRa = FALSE;
+ RestoredSp = FALSE;
+ while (ControlPc > FunctionEntry->BeginAddress) {
+
+ //
+ // Get instruction value, decode fields, case of opcode value, and
+ // reverse store operations.
+ //
+
+ ControlPc -= 4;
+ Instruction.Long = *((PULONG)ControlPc);
+ Opcode = Instruction.i_format.Opcode;
+ Offset = Instruction.i_format.Simmediate;
+ Rd = Instruction.r_format.Rd;
+ Rs = Instruction.i_format.Rs;
+ Rt = Instruction.i_format.Rt;
+ Address = Offset + IntegerRegister[Rs];
+ if (Opcode == SW_OP) {
+
+ //
+ // Store word.
+ //
+ // If the base register is SP and the source register is an
+ // integer register, then reload the register value.
+ //
+
+ if (Rs == SP) {
+ IntegerRegister[Rt] = *((PULONG)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, this
+ // is an interrupt or exception and the return PC should be
+ // biased by 4. Otherwise, if the destination register is
+ // SP and this is the first time that SP is being restored,
+ // then set the establisher frame pointer.
+ //
+
+ if (Rt == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+
+ } else if (Rt == SP) {
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = 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->XIntegerContext[Rt] = (PULONGLONG)Address;
+ }
+ }
+
+ } else if (Opcode == SWC1_OP) {
+
+ //
+ // Store word coprocessor 1.
+ //
+ // If the base register is SP and the source register is a
+ // floating register, then reload the register value.
+ //
+
+ if (Rs == SP) {
+ FloatingRegister[Rt] = *((PULONG)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[Rt] = (PULONG)Address;
+ }
+ }
+
+ } else if (Opcode == SDC1_OP) {
+
+ //
+ // Store double coprocessor 1.
+ //
+ // If the base register is SP and the source register is a
+ // floating register, then reload the register and the next
+ // register values.
+ //
+
+ if (Rs == SP) {
+ FloatingRegister[Rt] = *((PULONG)Address);
+ FloatingRegister[Rt + 1] = *((PULONG)(Address + 4));
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination registers contents
+ // are stored.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->FloatingContext[Rt] = (PULONG)Address;
+ ContextPointers->FloatingContext[Rt + 1] = (PULONG)(Address + 4);
+ }
+ }
+
+ } else if (Opcode == ADDIU_OP) {
+
+ //
+ // Add immediate unsigned.
+ //
+ // If both the source and destination registers are SP, then
+ // a standard stack allocation was performed and the signed
+ // displacement value should be subtracted from SP. Otherwise,
+ // if the destination register is the decrement register and
+ // the source register is zero, then add the decrement value
+ // to SP.
+ //
+
+ if ((Rs == SP) && (Rt == SP)) {
+ IntegerRegister[SP] -= Offset;
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+
+ } else if ((Rt == DecrementRegister) && (Rs == ZERO)) {
+ IntegerRegister[SP] += Offset;
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+ }
+
+ } else if (Opcode == ORI_OP) {
+
+ //
+ // Or immediate.
+ //
+ // If both the destination and source registers are the decrement
+ // register, then save the decrement value. Otherwise, if the
+ // destination register is the decrement register and the source
+ // register is zero, then add the decrement value to SP.
+ //
+
+ if ((Rs == DecrementRegister) && (Rt == DecrementRegister)) {
+ DecrementOffset = (Offset & 0xffff);
+
+ } else if ((Rt == DecrementRegister) && (Rs == ZERO)) {
+ IntegerRegister[SP] += (Offset & 0xffff);
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+ }
+
+ } else if (Opcode == SPEC_OP) {
+
+ //
+ // Special operation.
+ //
+ // The real opcode is in the function field of special opcode
+ // instructions.
+ //
+
+ Function = Instruction.r_format.Function;
+ if ((Function == ADDU_OP) || (Function == OR_OP)) {
+
+ //
+ // Add unsigned or an or operation.
+ //
+ // If one of the source registers is ZERO, then the
+ // operation is a move operation and the destination
+ // register should be moved to the appropriate source
+ // register.
+ //
+
+ if (Rt == ZERO) {
+ IntegerRegister[Rs] = IntegerRegister[Rd];
+
+ //
+ // 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, this an interrupt or exception and the
+ // return PC should be biased by 4.
+ //
+
+ if (Rs == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+ }
+
+ } else if (Rs == ZERO) {
+ IntegerRegister[Rt] = IntegerRegister[Rd];
+
+ //
+ // 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, this an interrupt or exception and the
+ // return PC should be biased by 4.
+ //
+
+ if (Rt == RA) {
+ if (RestoredRa == FALSE) {
+ NextPc = ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ }
+ }
+ }
+
+ } else if (Function == SUBU_OP) {
+
+ //
+ // Subtract unsigned.
+ //
+ // If the destination register is SP and the source register
+ // is SP, then a stack allocation greater than 32kb has been
+ // performed and source register number of the decrement must
+ // be saved for later use.
+ //
+
+ if ((Rd == SP) && (Rs == SP)) {
+ DecrementRegister = Rt;
+ }
+ }
+
+ } else if (Opcode == LUI_OP) {
+
+ //
+ // Load upper immediate.
+ //
+ // If the destination register is the decrement register, then
+ // compute the decrement value, add it from SP, and clear the
+ // decrement register number.
+ //
+
+ if (Rt == DecrementRegister) {
+ DecrementRegister = 0;
+ IntegerRegister[SP] += (DecrementOffset + (Offset << 16));
+ if (RestoredSp == FALSE) {
+ *EstablisherFrame = ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+ }
+ }
+ }
+
+ //
+ // Make sure that integer register zero is really zero.
+ //
+
+ ContextRecord->IntZero = 0;
+ return NextPc;
+}
+
+ULONG
+RtlpVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ )
+
+/*++
+
+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 | 1,
+ FunctionEntry,
+ &LocalContext,
+ InFunction,
+ EstablisherFrame,
+ ContextPointers);
+}
diff --git a/private/ntos/rtl/mips/getcalr.c b/private/ntos/rtl/mips/getcalr.c
new file mode 100644
index 000000000..1b9df34fa
--- /dev/null
+++ b/private/ntos/rtl/mips/getcalr.c
@@ -0,0 +1,179 @@
+/*++
+
+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
+
+--*/
+#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 routine that called the routine
+ that called this routine, and 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 A and B 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.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord;
+ ULONG EstablisherFrame;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG NextPc;
+ ULONG HighLimit, LowLimit;
+
+ //
+ // 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.XIntRa;
+
+ //
+ // 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).
+ //
+
+ 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 | 1,
+ 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.XIntSp < 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 | 1,
+ 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.XIntSp < 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 | 1,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ *CallersCallersPc = (PVOID)NextPc;
+ }
+ }
+ }
+
+ return;
+}
+
+USHORT
+RtlCaptureStackBackTrace(
+ IN ULONG FramesToSkip,
+ IN ULONG FramesToCapture,
+ OUT PVOID *BackTrace,
+ OUT PULONG BackTraceHash
+ )
+{
+ return 0;
+}
diff --git a/private/ntos/rtl/mips/largeint.s b/private/ntos/rtl/mips/largeint.s
new file mode 100644
index 000000000..d051d2fc7
--- /dev/null
+++ b/private/ntos/rtl/mips/largeint.s
@@ -0,0 +1,947 @@
+// TITLE("Large Integer Arithmetic")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+//
+// Module Name:
+//
+// largeint.s
+//
+// Abstract:
+//
+// This module implements routines for performing extended integer
+// arithmtic.
+//
+// Author:
+//
+// David N. Cutler (davec) 18-Apr-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksmips.h"
+
+ 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.
+//
+// Arguments:
+//
+// Addend1 (a2, a3) - Supplies the first addend value.
+//
+// Addend2 (4 * 4(sp), 4 * 5(sp)) - Supplies the second addend value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerAdd)
+
+ lw t0,4 * 4(sp) // get low part of addend2 value
+ lw t1,4 * 5(sp) // get high part of addend2 value
+ addu t0,t0,a2 // add low parts of large integer
+ addu t1,t1,a3 // add high parts of large integer
+ sltu t2,t0,a2 // generate carry from low part
+ addu t1,t1,t2 // add carry to high part
+ sw t0,0(a0) // store low part of result
+ sw t1,4(a0) // store high part of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlLargeIntegerAdd
+
+ SBTTL("Convert Long to Large Integer")
+//++
+//
+// LARGE_INTEGER
+// RtlConvertLongToLargeInteger (
+// IN LONG SignedInteger
+// )
+//
+// Routine Description:
+//
+// This function converts the a signed integer to a signed large integer
+// and returns the result.
+//
+// Arguments:
+//
+// SignedInteger (a1) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertLongToLargeInteger)
+
+ sra a2,a1,31 // compute high part of result
+ sw a1,0(a0) // store low part of result
+ sw a2,4(a0) // store high part of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlConvertLongToLargeInteger
+
+ SBTTL("Convert Ulong to Large Integer")
+//++
+//
+// LARGE_INTEGER
+// RtlConvertUlongToLargeInteger (
+// IN LONG UnsignedInteger
+// )
+//
+// Routine Description:
+//
+// This function converts the an unsigned integer to a signed large
+// integer and returns the result.
+//
+// Arguments:
+//
+// UnsignedInteger (a1) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertUlongToLargeInteger)
+
+ sw a1,0(a0) // store low part of result
+ sw zero,4(a0) // store high part of result
+ move v0,a0 // set function return register
+ j 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 an signed integer and
+// returns a signed large integer result.
+//
+// Arguments:
+//
+// Multiplicand (a1) - Supplies the multiplicand value.
+//
+// Multiplier (a2) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedIntegerMultiply)
+
+ mult a1,a2 // multiply longword value
+ mflo t0 // get low 32-bits of result
+ mfhi t1 // get high 32-bits of result
+ sw t0,0(a0) // set low part of result
+ sw t1,4(a0) // set high part of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlEnlargedIntegerMultiply)
+
+ 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 (a1) - Supplies the multiplicand value.
+//
+// Multiplier (a2) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedMultiply)
+
+ multu a1,a2 // multiply longword value
+ mflo t0 // get low 32-bits of result
+ mfhi t1 // get high 32-bits of result
+ sw t0,0(a0) // set low part of result
+ sw t1,4(a0) // set high part of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlEnlargedUnsignedMultiply)
+
+ SBTTL("Enlarged Unsigned Divide")
+//++
+//
+// ULONG
+// RtlEnlargedUnsignedDivide (
+// IN ULARGE_INTEGER Dividend,
+// IN ULONG Divisor,
+// IN PULONG Remainder.
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned long
+// and returns the resultant quotient and optionally the remainder.
+//
+// N.B. It is assumed that no overflow will occur.
+//
+// Arguments:
+//
+// Dividend (a0, a1) - Supplies the dividend value.
+//
+// Divisor (a2) - Supplies the divisor value.
+//
+// Remainder (a3) - Supplies an optional pointer to a variable that
+// receives the remainder.
+//
+// Return Value:
+//
+// The unsigned long integer quotient is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedDivide)
+
+ sltu v1,a1,a2 // check if overflow will occur
+ beq zero,a2,20f // if eq, attempted division by zero
+ dsll a1,a1,32 // left justify hihg part of dividend
+ beq zero,v1,30f // if eq, overflow will occur
+ dsll a0,a0,32 // zero extend low part of dividend
+ dsrl a0,a0,32 //
+ or a0,a0,a1 // merge high and low part of dividend
+ ddivu a0,a2 // compute quotient value
+ mflo v0 // set quotient value
+ beq zero,a3,10f // if eq, remainder not requested
+ mfhi a0 // load remainder
+ sw a0,0(a3) // store longword remainder
+10: j ra // return
+
+
+20: break DIVIDE_BY_ZERO_BREAKPOINT // attempted division by zero
+ j ra //
+
+30: break DIVIDE_OVERFLOW_BREAKPOINT // division value overflows result
+ j ra //
+
+ .end RtlEnlargedUnsignedDivide
+
+ SBTTL("Extended Large Integer Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedLargeIntegerDivide (
+// IN LARGE_INTEGER Dividend,
+// IN ULONG Divisor,
+// IN PULONG Remainder.
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned long
+// and returns the resultant quotient and optionally the remainder.
+//
+// Arguments:
+//
+// Dividend (a2, a3) - Supplies the dividend value.
+//
+// Divisor (4 * 4(sp)) - Supplies the divisor value.
+//
+// Remainder (4 * 5(sp)- Supplies an optional pointer to a variable
+// that receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedLargeIntegerDivide)
+
+ .set noreorder
+ .set noat
+ move v0,a0 // set function return register
+ lw a1,4 * 4(sp) // get divisor value
+ lw t0,4 * 5(sp) // get address to store remainder
+ beq zero,a1,30f // if eq, attempted division by zero
+ li t1,63 // set loop count
+ move t2,zero // clear partial remainder
+10: sra t3,t2,31 // replicate partial remainder high bit
+ sll t2,t2,1 // shift next dividend bit
+ srl t4,a3,31 // into the partial remainder
+ or t2,t2,t4 //
+ sll a3,a3,1 // double left shift dividend
+ srl t4,a2,31 //
+ or a3,a3,t4 //
+ sltu t4,t2,a1 // check if partial remainder less
+ subu t4,t4,1 // convert to 0 or -1
+ or t4,t4,t3 // merge with partial remainder high bit
+ and t5,t4,a1 // select divisor or 0
+ sll a2,a2,1 //
+ subu a2,a2,t4 // merge quotient bit
+ subu t2,t2,t5 // subtract out divisor
+ bne zero,t1,10b // if ne, more iterations to go
+ subu t1,t1,1 // decrement iteration count
+ beq zero,t0,20f // if eq, remainder not requested
+ sw a2,0(a0) // store low part of quotient
+ sw t2,0(t0) // store longword remainder
+20: j ra // return
+ sw a3,4(a0) // store high part of quotient
+ .set at
+ .set reorder
+
+30: break DIVIDE_BY_ZERO_BREAKPOINT // attempted division by zero
+ j ra //
+
+ .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 (a2, a3) - Supplies the dividend value.
+//
+// MagicDivisor (4 * 4(sp), 4 * 5(sp)) - Supplies the magic divisor value
+// which is a 64-bit multiplicative reciprocal.
+//
+// Shiftcount (4 * 6(sp)) - Supplies the right shift adjustment value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedMagicDivide)
+
+ move t0,a2 // assume dividend is positive
+ move t1,a3 //
+ bgez a3,10f // if gez, positive dividend
+ subu t0,zero,t0 // negate low part of dividend
+ subu t1,zero,t1 // negate high part of dividend
+ sltu t2,zero,t0 // set borrow from high part
+ subu t1,t1,t2 // subtract out out borrow
+10: lw a1,4 * 4(sp) // get low part of magic dividor
+ lw t2,4 * 5(sp) // get high part of magic divisor
+ lbu v0,4 * 6(sp) // get shift count
+
+//
+// Compute low 32-bits of dividend times low 32-bits of divisor.
+//
+
+ multu t0,a1 //
+ mfhi t3 // save high 32-bits of product
+
+//
+// Compute low 32-bits of dividend time high 32-bits of divisor.
+//
+
+ multu t0,t2 //
+ mflo t4 // save low 32-bits of product
+ mfhi t5 // save high 32-bits of product
+
+//
+// Compute high 32-bits of dividend times low 32-bits of divisor.
+//
+
+ multu t1,a1 //
+ mflo t6 // save loow 32-bits of product
+ mfhi t7 // save high 32-bits of product
+
+//
+// Compute high 32-bits of dividend times high 32-bits of divisor.
+//
+
+ multu t1,t2 //
+ mflo t8 // save low 32-bits of product
+ mfhi t9 // save high 32-bits of product
+
+//
+// Add partial results to form high 64-bits of result.
+//
+
+ addu t0,t3,t4 //
+ sltu t1,t0,t4 // generate carry
+ addu t0,t0,t6 //
+ sltu t2,t0,t6 // generate carry
+ addu t2,t1,t2 // combine carries
+ addu t1,t2,t5 //
+ sltu t2,t1,t5 // generate carry
+ addu t1,t1,t7 //
+ sltu t3,t1,t7 // generate carry
+ addu t2,t2,t3 // combine carries
+ addu t1,t1,t8 //
+ sltu t3,t1,t8 // generate carry
+ addu t2,t2,t3 // combine carries
+ addu t2,t2,t9 //
+
+//
+// Right shift the result by the specified shift count and negate result
+// if necessary.
+//
+
+ li v1,32 // compute left shift count
+ subu v1,v1,v0 //
+ bgtz v1,20f // if gtz, shift less that 32-bits
+
+//
+// Shift count is greater than or equal 32 bits - high half of result is zero,
+// low half is the high half shifted right by remaining count.
+//
+
+ move t1,zero // set high half of result
+ srl t0,t2,v0 // set low half of result
+ b 30f //
+
+//
+// Shift count is less than 32-bits - high half of result is the high half
+// of product shifted right by count, low half of result is the shifted out
+// bits of the high half combined with the rigth shifted low half of the
+// product.
+//
+
+20: srl t0,t1,v0 // shift low half right count bits
+ srl t1,t2,v0 // shift high half right count bits
+ beq zero,v0,30f // if eq, no more shifts necessary
+ sll t2,t2,v1 // isolate shifted out bits of high half
+ or t0,t0,t2 // combine bits for low half of result
+
+//
+// Negate result if neccessary.
+//
+
+30: bgez a3,40f // if gez, positive result
+ subu t0,zero,t0 // negate low half of result
+ subu t1,zero,t1 // negate high half of result
+ beq zero,t0,40f // if eq, negation complete
+ subu t1,t1,1 // convert high part to ones complement
+40: sw t0,0(a0) // store low half of result
+ sw t1,4(a0) // store high half of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlExtendedMagicDivide
+
+ SBTTL("Large Integer Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerDivide (
+// IN LARGE_INTEGER Dividend,
+// IN LARGE_INTEGER Divisor,
+// IN PLARGE_INTEGER Remainder.
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned
+// large and returns the resultant quotient and optionally the remainder.
+//
+// Arguments:
+//
+// Dividend (a2, a3) - Supplies the dividend value.
+//
+// Divisor (4 * 4(sp), 4 * 5(sp)) - Supplies the divisor value.
+//
+// Remainder (4 * 6(sp)- Supplies an optional pointer to a variable
+// that receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerDivide)
+
+ .set noreorder
+ .set noat
+ move v0,a0 // set function return register
+ lw a1,4 * 4(sp) // get low part of divisor
+ lw t0,4 * 5(sp) // get high part of divisor
+ lw t1,4 * 6(sp) // get address to store remainder
+ or v1,t0,a1 // combine low and high parts
+ beq zero,v1,60f // if eq, attempted division by zero
+ li t2,63 // set loop count
+ move t3,zero // clear partial remainder
+ move t4,zero //
+10: sll t4,t4,1 // shift next dividend bit
+ srl t5,t3,31 // into the partial remainder
+ or t4,t4,t5 //
+ sll t3,t3,1 //
+ srl t5,a3,31 //
+ or t3,t3,t5 //
+ sll a3,a3,1 // double left shift dividend
+ srl t5,a2,31 //
+ or a3,a3,t5 //
+ sltu t5,t4,t0 // check if partial remainder less
+ beq zero,t5,20f // if eq, partial remainder not less
+ sll a2,a2,1 //
+ bne zero,t2,10b // if ne, more iterations to go
+ subu t2,t2,1 // decrement iteration count
+ beq zero,t1,50f // if eq, remainder not requested
+ sw a2,0(a0) // store low part of quotient
+ sw t3,0(t1) // store large integer remainder
+ sw t4,4(t1) //
+ j ra // return
+ sw a3,4(a0) // store high part of quotient
+
+20: bne t0,t4,30f // if ne, partial remainder greater
+ sltu t5,t3,a1 // check is partial remainder less
+ bne zero,t5,40f // if ne, partial remainder less
+ nop //
+30: or a2,a2,1 // merge quotient bit
+ subu t4,t4,t0 // subtract out divisor high part
+ sltu t5,t3,a1 // set borrow from high part
+ subu t4,t4,t5 // subtract borrow from high part
+ subu t3,t3,a1 // subtract out divisor low
+40: bne zero,t2,10b // if ne, more iterations to go
+ subu t2,t2,1 // decrement iteration count
+ beq zero,t1,50f // if eq, remainder not requested
+ sw a2,0(a0) // store low part of quotient
+ sw t3,0(t1) // store large integer remainder
+ sw t4,4(t1) //
+50: j ra // return
+ sw a3,4(a0) // store high part of quotient
+ .set at
+ .set reorder
+
+60: break DIVIDE_BY_ZERO_BREAKPOINT // attempted division by zero
+ j ra //
+
+ .end RtlLargeIntegerDivide
+
+ 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.
+//
+// Arguments:
+//
+// Multiplicand (a2, a3) - Supplies the multiplicand value.
+//
+// Multiplier (4 * 4(sp)) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedIntegerMultiply)
+
+ lw a1,4 * 4(sp) // get multiplier value
+ xor t9,a1,a3 // compute sign of result
+ move t0,a1 // assume multiplier positive
+ bgez a1,10f // if gez, positive multiplier
+ subu t0,zero,t0 // negate multiplier
+10: move t1,a2 // assume multiplicand positive
+ move t2,a3 //
+ bgez a3,20f // if gez, positive multiplicand
+ subu t1,zero,t1 // negate multiplicand
+ subu t2,zero,t2 //
+ sltu t3,zero,t1 // compute borrow from high part
+ subu t2,t2,t3 // subtract out borrow
+
+//
+// Compute low 32-bits of multiplier times the low 32-bit of multiplicand.
+//
+
+20: multu t0,t1 //
+ mflo t4 // save low 32-bits of product
+ mfhi t5 // save high 32-bits of product
+
+//
+// Compute low 32-bits of multiplier times the high 32-bits of multiplicand.
+//
+
+ multu t0,t2 //
+ mflo t6 // save low 32-bits of product
+ mfhi t7 // save high 32-bits of product
+
+//
+// Add partial results to form high 64-bits of result.
+//
+
+ addu t5,t5,t6 //
+ sltu t3,t5,t6 // generate carry
+ addu t6,t3,t7 //
+
+//
+// Negate result if neccessary.
+//
+
+ bgez t9,40f // if gez, positive result
+ subu t4,zero,t4 // negate low half of result
+ subu t5,zero,t5 // negate high half of result
+ subu t6,zero,t6 // negate extended part of result
+ beq zero,t4,30f // if eq, negation complete
+ subu t5,t5,1 // convert high part to ones complement
+ subu t6,t6,1 // convert extended part to ones complement
+ b 40f //
+
+30: beq zero,t5,40f // if eq, negation complete
+ subu t6,t6,1 // convert extended part to ones complement
+
+//
+// Store final result.
+//
+
+40: sw t4,0(a0) // store low half of result
+ sw t5,4(a0) // store high half of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlExtendedIntegerMultiply
+
+ 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.
+//
+// Arguments:
+//
+// Subtrahend (a2, a3) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerNegate)
+
+ subu a2,zero,a2 // negate low part of subtrahend
+ subu a3,zero,a3 // negate high part of subtrahend
+ sltu t0,zero,a2 // compute borrow from high part
+ subu a3,a3,t0 // subtract borrow from high part
+ sw a2,0(a0) // store low part of result
+ sw a3,4(a0) // store high part of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlLargeIntegerNegate
+
+ 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.
+//
+// Arguments:
+//
+// Minuend (a2, a3) - Supplies the minuend value.
+//
+// Subtrahend (4 * 4(sp), 4 * 5(sp)) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerSubtract)
+
+ lw a1,4 * 4(sp) // get low part of subtrahend
+ lw t2,4 * 5(sp) // get high part of subtrahend
+ subu t0,a2,a1 // subtract low parts
+ subu t1,a3,t2 // subtract high parts
+ sltu t3,a2,a1 // generate borrow from high part
+ subu t1,t1,t3 // subtract borrow
+ sw t0,0(a0) // store low part of result
+ sw t1,4(a0) // store high part of result
+ move v0,a0 // set function return register
+ j ra // return
+
+ .end RtlLargeIntegerSubtract
+
+ 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.
+//
+// N.B. No test is made for significant bits shifted out of the result.
+//
+// Arguments:
+//
+// LargeInteger (a2, a3) - Supplies the large integer to be shifted.
+//
+// ShiftCount (4 * 4(sp)) - Supplies the left shift count.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftLeft)
+
+ lbu a1,4 * 4(sp) // get shift count
+ move v0,a0 // set function return register
+ and a1,a1,0x3f // truncate shift count mod 64
+
+//
+// Left shift the operand by the specified shift count.
+//
+
+ li v1,32 // compute right shift count
+ subu v1,v1,a1 //
+ bgtz v1,10f // if gtz, shift less that 32-bits
+
+//
+// Shift count is greater than or equal 32 bits - low half of result is zero,
+// high half is the low half shifted left by remaining count.
+//
+
+ sll a3,a2,a1 // set high half of result
+ sw zero,0(a0) // store low part of reuslt
+ sw a3,4(a0) // store high part of result
+ j ra // return
+
+//
+// Shift count is less than 32-bits - high half of result is the high half
+// of operand shifted left by count combined with the low half of the operand
+// shifted right, low half of result is the low half shifted left.
+//
+
+10: sll a3,a3,a1 // shift high half left count bits
+ beq zero,a1,20f // if eq, no more shifts necessary
+ srl t0,a2,v1 // isolate shifted out bits of low half
+ sll a2,a2,a1 // shift low half left count bits
+ or a3,a3,t0 // combine bits for high half of result
+20: sw a2,0(a0) // store low part of reuslt
+ sw a3,4(a0) // store high part of result
+ j 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 (a2, a3) - Supplies the large integer to be shifted.
+//
+// ShiftCount (4 * 4(sp)) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftRight)
+
+ lbu a1,4 * 4(sp) // get shift count
+ move v0,a0 // set function return register
+ and a1,a1,0x3f // truncate shift count mod 64
+
+//
+// Right shift the operand by the specified shift count.
+//
+
+ li v1,32 // compute left shift count
+ subu v1,v1,a1 //
+ bgtz v1,10f // if gtz, shift less that 32-bits
+
+//
+// Shift count is greater than or equal 32 bits - high half of result is
+// zero, low half is the high half shifted right by remaining count.
+//
+
+ srl a2,a3,a1 // set low half of result
+ sw a2,0(a0) // store low part of reuslt
+ sw zero,4(a0) // store high part of result
+ j ra // return
+
+//
+// Shift count is less than 32-bits - high half of result is the high half
+// of operand shifted right by count, low half of result is the shifted out
+// bits of the high half combined with the right shifted low half of the
+// operand.
+//
+
+10: srl a2,a2,a1 // shift low half right count bits
+ beq zero,a1,20f // if eq, no more shifts necessary
+ sll t0,a3,v1 // isolate shifted out bits of high half
+ srl a3,a3,a1 // shift high half right count bits
+ or a2,a2,t0 // combine bits for low half of result
+20: sw a2,0(a0) // store low part of reuslt
+ sw a3,4(a0) // store high part of result
+ j ra // return
+
+ .end RtlLargeIntegerShiftRight
+
+ 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 (a1, a2) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a3) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by a0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerArithmeticShift)
+
+ lbu a1,4 * 4(sp) // get shift count
+ move v0,a0 // set function return register
+ and a1,a1,0x3f // truncate shift count mod 64
+
+//
+// Right shift the operand by the specified shift count.
+//
+
+ li v1,32 // compute left shift count
+ subu v1,v1,a1 //
+ bgtz v1,10f // if gtz, shift less that 32-bits
+
+//
+// Shift count is greater than or equal 32 bits - high half of result is
+// zero, low half is the high half shifted right by remaining count.
+//
+
+ sra a2,a3,a1 // set low half of result
+ sra a3,a3,31 // set high half of result
+ sw a2,0(a0) // store low part of reuslt
+ sw a3,4(a0) // store high part of result
+ j ra // return
+
+//
+// Shift count is less than 32-bits - high half of result is the high half
+// of operand shifted right by count, low half of result is the shifted out
+// bits of the high half combined with the right shifted low half of the
+// operand.
+//
+
+10: srl a2,a2,a1 // shift low half right count bits
+ beq zero,a1,20f // if eq, no more shifts necessary
+ sll t0,a3,v1 // isolate shifted out bits of high half
+ sra a3,a3,a1 // shift high half right count bits
+ or a2,a2,t0 // combine bits for low half of result
+20: sw a2,0(a0) // store low part of reuslt
+ sw a3,4(a0) // store high part of result
+ j ra // return
+
+ .end RtlLargeIntegerArithmeticShift
diff --git a/private/ntos/rtl/mips/lzntmips.s b/private/ntos/rtl/mips/lzntmips.s
new file mode 100644
index 000000000..b97699bc2
--- /dev/null
+++ b/private/ntos/rtl/mips/lzntmips.s
@@ -0,0 +1,1325 @@
+// TITLE("LZ Decompression")
+//++
+//
+// Copyright (c) 1994 Microsoft Corporation
+//
+// Module Name:
+//
+// lznt1m.s
+//
+// Abstract:
+//
+// This module implements the decompression engine needed
+// to support file system compression.
+//
+// Author:
+//
+// Mark Enstrom (marke) 21-Nov-1994
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksmips.h"
+
+
+
+// #define FORMAT412 0
+// #define FORMAT511 1
+// #define FORMAT610 2
+// #define FORMAT79 3
+// #define FORMAT88 4
+// #define FORMAT97 5
+// #define FORMAT106 6
+// #define FORMAT115 7
+// #define FORMAT124 8
+//
+// 4/12 5/11 6/10 7/9 8/8 9/7 10/6 11/5 12/4
+//
+// ULONG FormatMaxLength[] = { 4098, 2050, 1026, 514, 258, 130, 66, 34, 18 };
+// ULONG FormatMaxDisplacement[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
+//
+// width table for LZ length and offset encoding
+//
+
+
+ SBTTL("LZNT1DecompressChunk")
+//++
+//
+// 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.
+//
+// The following code takes advantage of three distinct observations.
+// First, literal tokens occur at least twice as often as copy tokens.
+// This argues for having a "fall-through" being the case where a literal
+// token is found. We structure the main decomposition loop in eight
+// pieces where the first piece is a sequence of literal-test fall-throughs
+// and the remainder are a copy token followed by 7,6,...,0 literal-test
+// fall-throughs. Each test examines a particular bit in the tag byte
+// and jumps to the relevant code piece.
+//
+// The second observation involves performing bounds checking only
+// when needed. Bounds checking the compressed buffer need only be done
+// when fetching the tag byte. If there is not enough room left in the
+// input for a tag byte and 8 (worst case) copy tokens, a branch is made
+// to a second loop that handles a byte-by-byte "safe" copy to finish
+// up the decompression. Similarly, at the head of the loop a check is
+// made to ensure that there is enough room in the output buffer for 8
+// literal bytes. If not enough room is left, then the second loop is
+// used. Finally, after performing each copy, the output-buffer check
+// is made as well since a copy may take the destination pointer
+// arbitrarily close to the end of the destination.
+//
+// The third observation is an examination of CPU time while disk
+// decompression is in progress. CPU utilization is only less than
+// 25% peak. This means this routine should be written to minimize
+// latency instead of bandwidth. For this reason, taken branches are
+// avoided at the cost of code size and loop unrolling is not done.
+//
+// Arguments:
+//
+// a0 - UncompressedBuffer - Pointer to start of destination buffer
+// a1 - EndOfUncompressedBufferPlus1 - One byte beyond uncompressed buffer
+// a2 - CompressedBuffer - Pointer to buffer of compressed data (this pointer
+// has been adjusted to point past the chunk header)
+// a3 - EndOfCompressedBufferPlus1 - One byte beyond compressed buffer
+// (sp) - FinalUncompressedChunkSize - return bytes written to
+// UncompressedBuffer
+//
+// Return Value:
+//
+// None
+//
+//--
+
+ .struct 0
+LzS0: .space 4 // saved internal register s0
+LzRA: .space 4 // saved internal register ra
+ .space 4*2 // fill to keep stack 16-byte aligned
+LzFrameLength: // Length of Stack frame
+ .space 4*4 // parameter 0-3 space
+LzFinal:.space 4 // argument FinalUncompressedChunkSize
+
+
+ NESTED_ENTRY(LZNT1DecompressChunk, LzFrameLength, zero)
+
+ subu sp,sp,LzFrameLength
+
+ sw s0,LzS0(sp)
+ sw ra,LzRA(sp)
+
+ PROLOGUE_END
+
+//
+// make copy of UncompressedBuffer for
+// current output pointer
+//
+
+ move t4,a0
+
+//
+// Initialize variables used in keeping track of the
+// LZ Copy Token format. t9 is used to store the maximum
+// displacement for each phase of LZ decoding
+// (see explanation of format in LZNT1.c). This displacement
+// is added to the start of the CompressedBuffer address
+// so that a boundary crossing can be detected.
+//
+
+ li t9,0x10 // t9 = Max Displacement for LZ
+ addu t8,t9,a0 // t8 = Format boundary
+ li t7,0xffff >> 4 // t7 = length mask
+ li t6,12 // t6 = offset shift count
+
+//
+// Initialize variables to track safe copy limits for
+// CompressedBuffer and UncopmressedBuffer. This allows
+// execution of the quick Flag check below without
+// checking for crossing the end of either buffer.
+// From CompressedBuffer, one input pass includes 1 flag byte
+// and up to 8 two byte copy tokens ( 1+2*8).
+// To the un-compressed buffer, 8 literal bytes may be written,
+// any copy-token bits set will cause an explicit length check
+// in the LzCopy section
+//
+
+ subu v0,a1,8 // safe end of UncompressedBuffer
+ subu v1,a3,1+2*8 // safe end of CompressedBuffer
+
+Top:
+
+//
+// make sure safe copy can be performed for at least 8 literal bytes
+//
+
+ bgt a2,v1,SafeCheckStart // safe check
+ bgt t4,v0,SafeCheckStart // safe check
+ lbu s0,0(a2) // load flag byte
+
+//
+// fall-through for copying 8 bytes.
+//
+
+
+ sll t0,s0,31-0 // shift proper flag bit into sign bit
+ lbu t1,1(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy0 // if sign bit is set, go to copy routine
+ sb t1,0(t4) // store literal byte to dst
+
+ sll t0,s0,31-1 // shift proper flag bit into sign bit
+ lbu t1,2(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy1 // if sign bit is set, go to copy routine
+ sb t1,1(t4) // store literal byte to dst
+
+ sll t0,s0,31-2 // shift proper flag bit into sign bit
+ lbu t1,3(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy2 // if sign bit is set, go to copy routine
+ sb t1,2(t4) // store literal byte to dst
+
+ sll t0,s0,31-3 // shift proper flag bit into sign bit
+ lbu t1,4(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy3 // if sign bit is set, go to copy routine
+ sb t1,3(t4) // store literal byte to dst
+
+ sll t0,s0,31-4 // shift proper flag bit into sign bit
+ lbu t1,5(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy4 // if sign bit is set, go to copy routine
+ sb t1,4(t4) // store literal byte to dst
+
+ sll t0,s0,31-5 // shift proper flag bit into sign bit
+ lbu t1,6(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy5 // if sign bit is set, go to copy routine
+ sb t1,5(t4) // store literal byte to dst
+
+ sll t0,s0,31-6 // shift proper flag bit into sign bit
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+
+ addu t4,t4,8
+ b Top
+
+
+
+
+LzCopy0:
+
+//
+// LzCopy0
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,2(a2) // load second byte of copy token
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,7 // seven bits left in current flag byte
+ addu a2,a2,2 // Make a2 point to next src byte
+ srl s0,s0,1 // shift flag byte into next position
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy. Continue flag check at position 1
+//
+
+ subu t4,t4,1 // unbias output pointer
+
+ sll t0,s0,31-1 // rotate flag bit into sign position
+ lbu t1,2(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy1 // if sign bit is set, go to copy routine
+ sb t1,1(t4) // store literal byte to dst
+
+
+ sll t0,s0,31-2 // shift proper flag bit into sign bit
+ lbu t1,3(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy2 // if sign bit is set, go to copy routine
+ sb t1,2(t4) // store literal byte to dst
+
+ sll t0,s0,31-3 // shift proper flag bit into sign bit
+ lbu t1,4(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy3 // if sign bit is set, go to copy routine
+ sb t1,3(t4) // store literal byte to dst
+
+ sll t0,s0,31-4 // shift proper flag bit into sign bit
+ lbu t1,5(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy4 // if sign bit is set, go to copy routine
+ sb t1,4(t4) // store literal byte to dst
+
+ sll t0,s0,31-5 // shift proper flag bit into sign bit
+ lbu t1,6(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy5 // if sign bit is set, go to copy routine
+ sb t1,5(t4) // store literal byte to dst
+
+ sll t0,s0,31-6 // shift proper flag bit into sign bit
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+
+
+LzCopy1:
+
+//
+// LzCopy1
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,3(a2) // load second byte of copy token
+ addu t4,t4,1 // mov t4 to point to byte 1
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,6 // six bits left in current flag byte
+ addu a2,a2,3 // Make a2 point to next src byte
+ srl s0,s0,2 // shift flag byte into position
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy. Continue flag check at position 2
+//
+
+ subu t4,t4,2 // un-bias input pointer
+
+ sll t0,s0,31-2 // rotate flag into position for sign check
+ lbu t1,3(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy2 // if sign bit is set, go to copy routine
+ sb t1,2(t4) // store literal byte to dst
+
+ sll t0,s0,31-3 // shift proper flag bit into sign bit
+ lbu t1,4(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy3 // if sign bit is set, go to copy routine
+ sb t1,3(t4) // store literal byte to dst
+
+ sll t0,s0,31-4 // shift proper flag bit into sign bit
+ lbu t1,5(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy4 // if sign bit is set, go to copy routine
+ sb t1,4(t4) // store literal byte to dst
+
+ sll t0,s0,31-5 // shift proper flag bit into sign bit
+ lbu t1,6(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy5 // if sign bit is set, go to copy routine
+ sb t1,5(t4) // store literal byte to dst
+
+ sll t0,s0,31-6 // shift proper flag bit into sign bit
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+
+LzCopy2:
+
+//
+// LzCopy2
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,4(a2) // load second byte of copy token
+ addu t4,t4,2 // mov t4 to point to byte 1
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,5 // five bits left in current flag byte
+ addu a2,a2,4 // Make a2 point to next src byte
+ srl s0,s0,3 // shift flag byte into positin
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy
+// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here)
+//
+
+ subu t4,t4,3 // un-bias output pointer
+
+ sll t0,s0,31-3 // rotate flag into position for sign check
+ lbu t1,4(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy3 // if sign bit is set, go to copy routine
+ sb t1,3(t4) // store literal byte to dst
+
+ sll t0,s0,31-4 // shift proper flag bit into sign bit
+ lbu t1,5(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy4 // if sign bit is set, go to copy routine
+ sb t1,4(t4) // store literal byte to dst
+
+ sll t0,s0,31-5 // shift proper flag bit into sign bit
+ lbu t1,6(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy5 // if sign bit is set, go to copy routine
+ sb t1,5(t4) // store literal byte to dst
+
+ sll t0,s0,31-6 // shift proper flag bit into sign bit
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+
+
+LzCopy3:
+
+//
+// LzCopy3
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,5(a2) // load second byte of copy token
+ addu t4,t4,3 // mov t4 to point to byte 1
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,4 // four bits left in current flag byte
+ addu a2,a2,5 // Make a2 point to next src byte
+ srl s0,s0,4 // shift flag byte into positin
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy
+// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here)
+//
+
+ subu t4,t4,4 // un-bias output pointer
+
+ sll t0,s0,31-4 // rotate flag into position for sign check
+ lbu t1,5(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy4 // if sign bit is set, go to copy routine
+ sb t1,4(t4) // store literal byte to dst
+
+ sll t0,s0,31-5 // shift proper flag bit into sign bit
+ lbu t1,6(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy5 // if sign bit is set, go to copy routine
+ sb t1,5(t4) // store literal byte to dst
+
+ sll t0,s0,31-6 // shift proper flag bit into sign bit
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+
+LzCopy4:
+
+//
+// LzCopy4
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,6(a2) // load second byte of copy token
+ addu t4,t4,4 // mov t4 to point to byte 1
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,3 // three bits left in current flag byte
+ addu a2,a2,6 // Make a2 point to next src byte
+ srl s0,s0,5 // shift flag byte so that next bit is in positin 0
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy
+// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here)
+//
+
+ subu t4,t4,5 // un-bias output pointer
+
+ sll t0,s0,31-5 // rotate flag into position for sign check
+ lbu t1,6(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy5 // if sign bit is set, go to copy routine
+ sb t1,5(t4) // store literal byte to dst
+
+ sll t0,s0,31-6 // shift proper flag bit into sign bit
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+LzCopy5:
+
+//
+// LzCopy5
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,7(a2) // load second byte of copy token
+ addu t4,t4,5 // mov t4 to point to byte 1
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,2 // two bits left in current flag byte
+ addu a2,a2,7 // Make a2 point to next src byte
+ srl s0,s0,6 // shift flag byte so that next bit is in positin 0
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy
+// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here)
+//
+
+ subu t4,t4,6 // un-bias output pointer
+
+ sll t0,s0,31-6 // rotate flag into position for sign check
+ lbu t1,7(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy6 // if sign bit is set, go to copy routine
+ sb t1,6(t4) // store literal byte to dst
+
+ sll t0,s0,31-7 // shift proper flag bit into sign bit
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+
+LzCopy6:
+
+//
+// LzCopy6
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+ lbu t2,8(a2) // load second byte of copy token
+ addu t4,t4,6 // mov t4 to point to byte 1
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+ ble t4,v0,10f // skip if still in safe boundry
+ li t5,1 // one bit left in current flag byte
+ addu a2,a2,8 // Make a2 point to next src byte
+ srl s0,s0,7 // shift flag byte into position
+ b SafeCheckLoop
+10:
+
+//
+// adjust t4 back to position it would be if this was a liternal byte
+// copy
+// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here)
+//
+
+ subu t4,t4,7 // un-bias output pointer
+
+ sll t0,s0,31-7 // rotate flag into position for sign check
+ lbu t1,8(a2) // load literal or CopyToken[0]
+ bltz t0,LzCopy7 // if sign bit is set, go to copy routine
+ sb t1,7(t4)
+
+ addu a2,a2,9 // inc src addr
+ addu t4,t4,8
+ b Top
+
+
+LzCopy7:
+
+//
+// LzCopy7
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of current flag byte
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+// This routine is special since it is for the last bit in the flag
+// byte. The InputPointer(a2) and OutputPointer(t4) are biased at
+// the top of this segment and don't need to be biased again
+//
+//
+
+ lbu t2,9(a2) // load second byte of copy token
+ addu t4,t4,7 // mov t4 to point to byte 7
+ addu a2,a2,10 // a2 points to next actual src byte
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length (End Address)
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess
+
+//
+// if t4 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ bgt t4,v0,SafeCheckStart // branch to safe-copy setup
+
+//
+// t4 and a2 are alreadt corrected
+// jump back tostart of quick loop
+//
+
+ b Top
+
+
+
+//
+// Near the end of either compressed or uncompressed buffers,
+// check buffer limits before any load or store
+//
+
+SafeCheckStart:
+
+ beq a2,a3,LzSuccess // check for end of CompressedBuffer
+ lbu s0,0(a2) // load next flag byte
+ addu a2,a2,1 // inc src addr to literal/CopyFlag[0]
+ li t5,8 // loop count
+
+SafeCheckLoop:
+
+ beq a2,a3,LzSuccess // check for end of CompressedBuffer
+ beq t4,a1,LzSuccess // check for end of UncompressedBuffer
+ sll t0,s0,31 // shift flag bit into sign bit
+
+ lbu t1,0(a2) // load literal or CopyToken[0]
+ addu a2,a2,1 // inc CompressedBuffer adr
+ bltz t0,LzSafeCopy // if sign bit, go to safe copy routine
+
+ sb t1,0(t4) // store literal byte
+ addu t4,t4,1 // inc UncompressedBuffer
+
+SafeCheckReentry:
+
+ srl s0,s0,1 // move next bit into position
+ addu t5,t5,-1
+ bne t5,zero,SafeCheckLoop // check for more bits in flag byte
+
+ b SafeCheckStart // get next flag byte
+
+
+LzSafeCopy:
+
+//
+// LzSafeCopy
+//
+// t1 - CopyToken[0]
+// a2 - CompressedBuffer address of CopyToken[1]
+// t4 - UncomressedBuffer address at start of flag byte check
+// s0 - Flag byte
+//
+// load copy token, (first byte already loaded in delay slot),
+// then combine into a 16 bit field
+//
+
+
+ beq a2,a3,LzCompressError // check for end of CompressedBuffer, error case
+ lbu t2,0(a2) // load second byte of copy token
+ addu a2,a2,1 // fix-up src addr for return to switch
+ sll t2,t2,8 // shift second byte into high 16
+ or t2,t1,t2 // combine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ subu t0,t8,t4 // if t4 < t8 then
+ bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return
+
+//
+// Extract offset and length from copy token
+//
+
+ and t0,t2,t7 // t0 = length from field
+ addu t0,t0,3 // t0 = real length
+ srl t1,t2,t6 // t1 = offset
+ addu t1,t1,1 // t1 = real offset
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+
+ subu t2,t4,a0 // t2 = current offset into output buffer
+ bgt t1,t2,LzCompressError // error in compressed data
+
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ addu t2,t4,t0 // CurrentPointer + Length
+ ble t2,a1,10f // Fix length if it would over-run buffer
+
+ move t2,a1 // new length is end of buffer
+
+10:
+
+//
+// copy t0 bytes bytes from [t4-t1] to [t4]
+//
+
+ subu t3,t4,t1 // t1 = OutputPointer - Offset
+20:
+ lbu t0,0(t3) // load src
+ sb t0,0(t4) // store to dst
+ addu t4,t4,1 // inc dst addr
+ addu t3,t3,1 // inc src addr
+ bne t4,t2,20b // loop till done
+
+//
+// if t4 = a1, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+30:
+ beq t4,a1,LzSuccess // Done
+
+ b SafeCheckReentry // Not done yet, continue with flag check
+
+LzSuccess:
+
+//
+// calculate how many bytes have been moved to the uncompressed
+// buffer, then set good return value
+//
+
+ lw t0,LzFinal(sp) // address of variable to receive length
+ subu t1,t4,a0 // bytes stored
+ sw t1,0(t0) // store length
+10:
+ li v0,STATUS_SUCCESS // STATUS_SUCCESS
+
+LzComplete:
+
+ lw ra,LzRA(sp)
+ lw s0,LzS0(sp)
+
+ addu sp,sp,LzFrameLength
+
+ j ra
+
+//
+// fatal error in compressed data format
+//
+
+LzCompressError:
+ li v0,STATUS_BAD_COMPRESSION_BUFFER
+ b LzComplete
+
+
+//
+// at least one format boundry has been crossed, set up new bouandry
+// then jump back to the check routine to make sure new boundry is
+// correct
+//
+
+LzAdjustBoundary:
+
+ sll t9,t9,1 // next length boundary
+ addu t8,t9,a0 // t8 = next offset boundary
+ srl t7,t7,1 // reduce width of length mask
+ subu t6,t6,1 // reduce shift count to isolate offset
+ subu t0,t8,t4 // if t4 < t8 then
+ bltz t0,LzAdjustBoundary // branch to boundry adjust, re-check
+
+ j ra
+
+
+ .end LZNT1DecompressChunk
+
+
diff --git a/private/ntos/rtl/mips/nlssup.c b/private/ntos/rtl/mips/nlssup.c
new file mode 100644
index 000000000..4c108a4a7
--- /dev/null
+++ b/private/ntos/rtl/mips/nlssup.c
@@ -0,0 +1,109 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ nlssup.c
+
+Abstract:
+
+ This module defines CPU specific routines for converting NLS strings.
+
+Author:
+
+ Steve Wood (stevewo) 31-Mar-1989
+
+Revision History:
+
+
+--*/
+
+#include "ntrtlp.h"
+
+ WCHAR
+RtlAnsiCharToUnicodeChar(
+ IN OUT PCHAR *SourceCharacter
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates the specified ansi character to unicode and
+ returns the unicode value. The purpose for this routine is to allow
+ for character by character ansi to unicode translation. The
+ translation is done with respect to the current system locale
+ information.
+
+
+Arguments:
+
+ SourceCharacter - Supplies a pointer to an ansi character pointer.
+ Through two levels of indirection, this supplies an ansi
+ character that is to be translated to unicode. After
+ translation, the ansi character pointer is modified to point to
+ the next character to be converted. This is done to allow for
+ dbcs ansi characters.
+
+Return Value:
+
+ Returns the unicode equivalent of the specified ansi character.
+
+--*/
+
+{
+ WCHAR UnicodeCharacter;
+
+ //
+ // Note that this needs to reference the translation table !
+ //
+
+ UnicodeCharacter = **SourceCharacter;
+ (*SourceCharacter)++;
+ return UnicodeCharacter;
+}
+
+
+ VOID
+RtlpAnsiPszToUnicodePsz(
+ IN PCHAR AnsiString,
+ IN WCHAR *UnicodeString,
+ IN USHORT AnsiStringLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates the specified ansi character to unicode and
+ returns the unicode value. The purpose for this routine is to allow
+ for character by character ansi to unicode translation. The
+ translation is done with respect to the current system locale
+ information.
+
+
+Arguments:
+
+ AnsiString - Supplies a pointer to the ANSI string to convert to unicode.
+ UnicodeString - Supplies a pointer to a buffer to hold the unicode string
+ AnsiStringLength - Supplies the length of the ANSI string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG Index;
+ PCHAR AnsiChar;
+
+ AnsiChar = AnsiString;
+ Index = 0;
+ while(Index < AnsiStringLength ) {
+ UnicodeString[Index] = RtlAnsiCharToUnicodeChar(&AnsiChar);
+ Index++;
+ }
+ UnicodeString[Index] = UNICODE_NULL;
+}
diff --git a/private/ntos/rtl/mips/ntrtlmip.h b/private/ntos/rtl/mips/ntrtlmip.h
new file mode 100644
index 000000000..cb844325e
--- /dev/null
+++ b/private/ntos/rtl/mips/ntrtlmip.h
@@ -0,0 +1,41 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ntrtlmip.h
+
+Abstract:
+
+ MIPS specific parts of ntrtlp.h
+
+Author:
+
+ David N. Cutler (davec) 19-Apr-90
+
+Revision History:
+
+--*/
+
+//
+// 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
+ );
diff --git a/private/ntos/rtl/mips/stringsp.c b/private/ntos/rtl/mips/stringsp.c
new file mode 100644
index 000000000..3cf3f2d5f
--- /dev/null
+++ b/private/ntos/rtl/mips/stringsp.c
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ stingsup.c
+
+Abstract:
+
+ This module defines CPU specific routines for manipulating NT strings.
+
+Author:
+
+ Steve Wood (stevewo) 31-Mar-1989
+
+Revision History:
+
+
+--*/
+
+#include "nt.h"
+#include "ntrtl.h"
+
+
+VOID
+RtlInitAnsiString(
+ OUT PANSI_STRING DestinationString,
+ IN PSZ SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlInitAnsiString function initializes an NT counted string.
+ The DestinationString is initialized to point to the SourceString
+ and the Length and MaximumLength fields of DestinationString are
+ initialized to the length of the SourceString, which is zero if
+ SourceString is not specified.
+
+Arguments:
+
+ DestinationString - Pointer to the counted string to initialize
+
+ SourceString - Optional pointer to a null terminated string that
+ the counted string is to point to.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ USHORT Length;
+ Length = 0;
+ DestinationString->Length = 0;
+ DestinationString->Buffer = SourceString;
+ if (ARGUMENT_PRESENT( SourceString )) {
+ while (*SourceString++) {
+ Length++;
+ }
+
+ DestinationString->Length = Length;
+
+ DestinationString->MaximumLength = (SHORT)(Length+1);
+ }
+ else {
+ DestinationString->MaximumLength = 0;
+ }
+}
+
+
+VOID
+RtlInitUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PWSTR SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlInitUnicodeString function initializes an NT counted
+ unicode string. The DestinationString is initialized to point to
+ the SourceString and the Length and MaximumLength fields of
+ DestinationString are initialized to the length of the SourceString,
+ which is zero if SourceString is not specified.
+
+Arguments:
+
+ DestinationString - Pointer to the counted string to initialize
+
+ SourceString - Optional pointer to a null terminated unicode string that
+ the counted string is to point to.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ USHORT Length = 0;
+ DestinationString->Length = 0;
+ DestinationString->Buffer = SourceString;
+ if (ARGUMENT_PRESENT( SourceString )) {
+ while (*SourceString++) {
+ Length += sizeof(*SourceString);
+ }
+
+ DestinationString->Length = Length;
+
+ DestinationString->MaximumLength = Length+(USHORT)sizeof(UNICODE_NULL);
+ }
+ else {
+ DestinationString->MaximumLength = 0;
+ }
+}
diff --git a/private/ntos/rtl/mips/trampoln.s b/private/ntos/rtl/mips/trampoln.s
new file mode 100644
index 000000000..dd5654f11
--- /dev/null
+++ b/private/ntos/rtl/mips/trampoln.s
@@ -0,0 +1,522 @@
+// TITLE("Trampoline Code For User Mode APC and Exception Dispatching")
+//++
+//
+// Copyright (c) 1990 Microsoft 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:
+//
+//--
+
+#include "ksmips.h"
+
+//
+// Define length of exception dispatcher stack frame.
+//
+
+#define ExceptionDispatcherFrame (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.
+//
+//--
+
+ EXCEPTION_HANDLER(KiUserApcHandler)
+
+ NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero);
+
+ .set noreorder
+ .set noat
+ sd sp,CxXIntSp(sp) // save stack pointer
+ sd ra,CxXIntRa(sp) // save return address
+ sw ra,CxFir(sp) // save return address
+ sd s8,CxXIntS8(sp) // save integer register s8
+ sd gp,CxXIntGp(sp) // save integer register gp
+ sd s0,CxXIntS0(sp) // save integer registers s0 - s7
+ sd s1,CxXIntS1(sp) //
+ sd s2,CxXIntS2(sp) //
+ sd s3,CxXIntS3(sp) //
+ sd s4,CxXIntS4(sp) //
+ sd s5,CxXIntS5(sp) //
+ sd s6,CxXIntS6(sp) //
+ sd s7,CxXIntS7(sp) //
+ sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31
+ sdc1 f22,CxFltF22(sp) //
+ sdc1 f24,CxFltF24(sp) //
+ sdc1 f26,CxFltF26(sp) //
+ sdc1 f28,CxFltF28(sp) //
+ sdc1 f30,CxFltF30(sp) //
+ move s8,sp // 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 provied 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 that address of the function that is to be called.
+//
+// sp - Supplies a pointer to a context frame.
+//
+// s8 - Supplies the same value as sp and is used as a frame pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(KiUserApcDispatcher)
+
+ jal a3 // call specified APC routine
+ move a0,s8 // set address of context frame
+ li a1,1 // set test alert argument true
+ jal ZwContinue // execute system service to continue
+
+//
+// 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 their is not return from raise
+// status.
+//
+
+ move s0,v0 // save status value
+10: move a0,s0 // set status value
+ jal RtlRaiseStatus // raise exception
+ b 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 and when an unwind thought 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
+ .space 4 * 4 // argument save area
+HdRa: .space 4 // saved return address
+ .space 3 * 4 //
+HandlerFrameLength: // length of handler frame
+
+ NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero)
+
+ subu sp,sp,HandlerFrameLength // allocate stack frame
+ sw ra,HdRa(sp) // save return address
+
+ PROLOGUE_END
+
+ lw t0,ErExceptionFlags(a0) // get exception flags
+ and t0,t0,EXCEPTION_UNWIND // check if unwind in progress
+ beq zero,t0,10f // if eq, no unwind in progress
+ jal ZwTestAlert // test for alert pending
+10: li v0,ExceptionContinueSearch // set disposition value
+ lw ra,HdRa(sp) // restore return address
+ addu sp,sp,HandlerFrameLength // deallocate stack frame
+ j 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 APC dispatcher.
+//
+//--
+
+// EXCEPTION_HANDLER(KiUserCallbackHandler)
+
+ NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero);
+
+ .set noreorder
+ .set noat
+ sd sp,CkSp(sp) // save stack pointer
+ sd ra,CkRa(sp) // save return address
+ .set at
+ .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 + ApiNumber) - Supplies the API number of the callback function
+// that is executed.
+//
+// (sp + Buffer) - Supplies a pointer to the input buffer.
+//
+// (sp + Length) - Supplies the input buffer length.
+//
+// Return Value:
+//
+// This function returns to kernel mode.
+//
+//--
+
+
+ ALTERNATE_ENTRY(KiUserCallbackDispatcher)
+
+ lw a0,CkBuffer(sp) // get input buffer address
+ lw a1,CkLength(sp) // get input buffer length
+ lw t0,CkApiNumber(sp) // get API number
+ li t1,UsPcr // get user PCR page address
+ lw t1,PcTeb(t1) // get address of TEB
+ lw t2,TePeb(t1) // get address of PEB
+ lw t3,PeKernelCallbackTable(t2) // get address of callback table
+ sll t0,t0,2 // compute address of table entry
+ addu t3,t3,t0 //
+ lw t3,0(t3) // get address of callback routine
+ jal t3 // call specified function
+
+//
+// If a return from the callback function occurs, then the output buffer
+// address and length are returned as NULL.
+//
+
+ move a0,zero // set zero buffer address
+ move a1,zero // set zero buffer lenfth
+ move a2,v0 // set completion status
+ jal 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 their is not return from raise
+// status.
+//
+
+10: move a0,v0 // set status value
+ jal RtlRaiseStatus // raise exception
+ b 10b // loop on return
+
+ .end KiUserCallbackDispatch
+
+ SBTTL("User Callback Exception Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// KiUserCallbackHandler (
+// 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 a user callback
+// routine or one of its dynamic descendents.
+//
+// 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.
+//--
+
+ NESTED_ENTRY(KiUserCallbackHandler, HandlerFrameLength, zero)
+
+ subu sp,sp,HandlerFrameLength // allocate stack frame
+ sw ra,HdRa(sp) // save return address
+
+ PROLOGUE_END
+
+ lw t0,ErExceptionFlags(a0) // get exception flags
+ and t0,t0,EXCEPTION_UNWIND // check if unwind in progress
+ bne zero,t0,10f // if ne, unwind in progress
+ li v0,ExceptionContinueSearch // set disposition value
+ addu sp,sp,HandlerFrameLength // deallocate stack frame
+ j ra // return
+
+//
+// There is an attempt to unwind through a callback frame. If this were
+// allowed, then a kernel callback frame would be abandoned on the kernel
+// stack. Force a callback return.
+//
+
+10: lw a2,ErExceptionCode(a0) // get exception code
+ move a0,zero // set zero buffer address
+ move a1,zero // set zero buffer lenfth
+ jal 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.
+//
+
+20: move a0,v0 // set status value
+ jal RtlRaiseStatus // raise exception
+ b 20b // loop on return
+
+ .end KiUserCallbackHandler
+
+ SBTTL("User Exception Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the exception dispatcher.
+//
+//--
+
+ NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrame, zero);
+
+ .set noreorder
+ .set noat
+ sd sp,CxXIntSp(sp) // save stack pointer
+ sd ra,CxXIntRa(sp) // save return address
+ sw ra,CxFir(sp) // save return address
+ sd s8,CxXIntS8(sp) // save integer register s8
+ sd gp,CxXIntGp(sp) // save integer register gp
+ sd s0,CxXIntS0(sp) // save integer registers s0 - s7
+ sd s1,CxXIntS1(sp) //
+ sd s2,CxXIntS2(sp) //
+ sd s3,CxXIntS3(sp) //
+ sd s4,CxXIntS4(sp) //
+ sd s5,CxXIntS5(sp) //
+ sd s6,CxXIntS6(sp) //
+ sd s7,CxXIntS7(sp) //
+ sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31
+ sdc1 f22,CxFltF22(sp) //
+ sdc1 f24,CxFltF24(sp) //
+ sdc1 f26,CxFltF26(sp) //
+ sdc1 f28,CxFltF28(sp) //
+ sdc1 f30,CxFltF30(sp) //
+ move s8,sp // 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. Else last chance processing is performed.
+//
+// Arguments:
+//
+// s0 - Supplies a pointer to an exception record.
+//
+// s1 - Supplies a pointer to a context frame.
+//
+// s8 - Supplies the same value as sp and is used as a frame pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(KiUserExceptionDispatcher)
+
+ move a0,s0 // set address of exception record
+ move a1,s1 // set address of context record
+ jal 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 zero,v0,10f // if eq, perform last chance processing
+
+//
+// Continue execution.
+//
+
+ move a0,s1 // set address of context frame
+ li a1,0 // set test alert argument false
+ jal ZwContinue // execute system service to continue
+ b 20f // join common code
+
+//
+// Last chance processing.
+//
+
+10: move a0,s0 // set address of exception record
+ move a1,s1 // set address of context frame
+ move a2,zero // set first chance FALSE
+ jal ZwRaiseException // perform last chance processing
+
+//
+// Common code for nonsuccessful completion of the continue or last chance
+// service. Use the return status as the exception code, set noncontinuable
+// exception and attempt to raise another exception. Note the stack grows
+// and eventually this loop will end.
+//
+
+20: move s1,v0 // save status value
+30: subu sp,sp,ExceptionRecordLength + (4 * 4) // allocate exception record
+ addu a0,sp,4 * 4 // compute address of actual record
+ sw s1,ErExceptionCode(a0) // set exception code
+ li t0,EXCEPTION_NONCONTINUABLE // set noncontinuable flag
+ sw t0,ErExceptionFlags(a0) //
+ sw s0,ErExceptionRecord(a0) // set associated exception record
+ sw zero,ErNumberParameters(a0) // set number of parameters
+ jal RtlRaiseException // raise exception
+ b 20b // loop of 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.
+//
+// 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.
+//
+// Arguments:
+//
+// v0 - Supplies the status code to be raised.
+//
+// Return Value:
+//
+// ExceptionCode
+//
+//--
+
+ .struct 0
+ .space 4 * 4 // argument save area
+RaiseRa:.space 4 // saved return address
+RaiseV0:.space 4 // saved S0
+RaiseEr:.space ExceptionRecordLength // exception record for RtlRaiseException
+RaiseFrameLength: // length of handler frame
+
+ NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero)
+
+ subu sp,sp,RaiseFrameLength // allocate stack frame
+ sw ra,RaiseRa(sp) // save return address
+
+ PROLOGUE_END
+
+ sw v0,RaiseV0(sp) // save function return status
+ sw v0,ErExceptionCode + RaiseEr(sp) // set exception code
+ sw zero,ErExceptionFlags + RaiseEr(sp) // set exception flags
+ sw zero,ErExceptionRecord + RaiseEr(sp) // clear exception record
+ sw ra,ErExceptionAddress + RaiseEr(sp) // set exception address
+ sw zero,ErNumberParameters+RaiseEr(sp) // set number of parameters
+ addu a0,sp,RaiseEr // compute exception record address
+ jal RtlRaiseException // attempt to raise the exception
+ lw v0,RaiseV0(sp) // restore function status
+ lw ra,RaiseRa(sp) // restore return address
+ addu sp,sp,RaiseFrameLength // deallocate stack frame
+ j ra // return
+
+ .end KiRaiseUserExceptionDispatch
diff --git a/private/ntos/rtl/mips/xcptmisc.s b/private/ntos/rtl/mips/xcptmisc.s
new file mode 100644
index 000000000..c31a09e24
--- /dev/null
+++ b/private/ntos/rtl/mips/xcptmisc.s
@@ -0,0 +1,356 @@
+// TITLE("Miscellaneous Exception Handling")
+//++
+//
+// Copyright (c) 1990 Microsoft 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:
+//
+//--
+
+#include "ksmips.h"
+
+//
+// Define call frame for calling exception handlers.
+//
+
+ .struct 0
+CfArg: .space 4 * 4 // argument register save area
+ .space 3 * 4 // fill for alignment
+CfRa: .space 4 // saved return address
+CfFrameLength: // length of stack frame
+CfA0: .space 4 // caller argument save area
+CfA1: .space 4 //
+CfA2: .space 4 //
+CfA3: .space 4 //
+CfExr: .space 4 // address of exception routine
+
+ 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 (4 * 4(sp)) - 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.
+//
+//--
+
+ EXCEPTION_HANDLER(RtlpExceptionHandler)
+
+ NESTED_ENTRY(RtlpExecuteHandlerForException, CfFrameLength, zero)
+
+ subu sp,sp,CfFrameLength // allocate stack frame
+ sw ra,CfRa(sp) // save return address
+
+ PROLOGUE_END
+
+ lw t0,CfExr(sp) // get address of exception routine
+ sw a3,CfA3(sp) // save address of dispatcher context
+ jal t0 // call exception exception handler
+ lw ra,CfRa(sp) // restore return address
+ addu sp,sp,CfFrameLength // deallocate stack frame
+ j 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)
+
+ lw t0,ErExceptionFlags(a0) // get exception flags
+ and t0,t0,EXCEPTION_UNWIND // check if unwind in progress
+ bne zero,t0,10f // if neq, unwind in progress
+
+//
+// Unwind is not in progress - return nested exception disposition.
+//
+
+ lw t0,CfA3 - CfA0(a1) // get dispatcher context address
+ li v0,ExceptionNestedException // set disposition value
+ lw t1,DcEstablisherFrame(t0) // copy the establisher frame pointer
+ sw t1,DcEstablisherFrame(a3) // to current dispatcher context
+ j ra // return
+
+//
+// Unwind is in progress - return continue search disposition.
+//
+
+10: li v0,ExceptionContinueSearch // set disposition value
+ j 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 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 (4 * 4(sp)) - 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.
+//
+//--
+
+ EXCEPTION_HANDLER(RtlpUnwindHandler)
+
+ NESTED_ENTRY(RtlpExecuteHandlerForUnwind, CfFrameLength, zero)
+
+ subu sp,sp,CfFrameLength // allocate stack frame
+ sw ra,CfRa(sp) // save return address
+
+ PROLOGUE_END
+
+ lw t0,CfExr(sp) // get address of exception routine
+ sw a3,CfA3(sp) // save address of dispatcher context
+ jal t0 // call exception unwind handler
+ lw ra,CfRa(sp) // restore return address
+ addu sp,sp,CfFrameLength // deallocate stack frame
+ j 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)
+
+ lw t0,ErExceptionFlags(a0) // get exception flags
+ and t0,t0,EXCEPTION_UNWIND // check if unwind in progress
+ beq zero,t0,10f // if eq, unwind not in progress
+
+//
+// Unwind is in progress - return collided unwind disposition.
+//
+
+ lw t0,CfA3 - CfA0(a1) // get dispatcher context address
+ li v0,ExceptionCollidedUnwind // set disposition value
+ lw t1,DcControlPc(t0) // Copy the establisher frames'
+ lw t2,DcFunctionEntry(t0) // dispatcher context to the current
+ lw t3,DcEstablisherFrame(t0) // dispatcher context
+ lw t4,DcContextRecord(t0) //
+ sw t1,DcControlPc(a3) //
+ sw t2,DcFunctionEntry(a3) //
+ sw t3,DcEstablisherFrame(a3) //
+ sw t4,DcContextRecord(a3) //
+ j ra // return
+
+//
+// Unwind is not in progress - return continue search disposition.
+//
+
+10: li v0,ExceptionContinueSearch // set disposition value
+ j ra // return
+ .end RtlpUnwindHandler
+
+ 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)
+
+ li t0,UsPcr // get address of user PCR
+ bgez sp,10f // if gez, current mode is user
+
+//
+// Current mode is kernel - compute stack limits.
+//
+
+ lw t1,KiPcr + PcInitialStack(zero) // get high limit kernel stack
+ lw t2,KiPcr + PcStackLimit(zero) // get low limit kernel stack
+ b 20f // finish in commom code
+
+//
+// Current mode is user - get stack limits from the TEB.
+//
+
+10: lw t0,PcTeb(t0) // get address of TEB
+ lw t2,TeStackLimit(t0) // get low limit of user stack
+ lw t1,TeStackBase(t0) // get high limit of user stack
+20: sw t2,0(a0) // store low stack limit
+ sw t1,0(a1) // store high stack limit
+ j ra // return
+
+ .end RtlpGetStackLimits
diff --git a/private/ntos/rtl/mips/xxcaptur.s b/private/ntos/rtl/mips/xxcaptur.s
new file mode 100644
index 000000000..9adfb4242
--- /dev/null
+++ b/private/ntos/rtl/mips/xxcaptur.s
@@ -0,0 +1,314 @@
+// TITLE("Capture and Restore Context")
+//++
+//
+// Copyright (c) 1990 Microsoft 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:
+//
+//--
+
+#include "ksmips.h"
+
+//
+// Define local symbols.
+//
+
+#define UserPsr (1 << PSR_CU1) | (0xff << PSR_INTMASK) | (1 << PSR_UX) | \
+ (2 << PSR_KSU) | (1 << PSR_IE) // constant user PSR value
+
+ SBTTL("Capture Context")
+//++
+//
+// VOID
+// RtlCaptureContext (
+// OUT PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This function captures the context of the caller in the specified
+// context record.
+//
+// Arguments:
+//
+// ContextRecord (a0) - Supplies the address of a context record.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlCaptureContext)
+
+//
+// Save integer registers zero, at - t9, s8, gp, sp, ra, lo, and hi.
+//
+
+ .set noreorder
+ .set noat
+ sd zero,CxXIntZero(a0) //
+ sd AT,CxXIntAt(a0) //
+ sd v0,CxXIntV0(a0) //
+ mflo v0 //
+ sd v1,CxXIntV1(a0) //
+ mfhi v1 //
+ sd v0,CxXIntLo(a0) //
+ sd v1,CxXIntHi(a0) //
+ sd a0,CxXIntA0(a0) //
+ sd a1,CxXIntA1(a0) //
+ sd a2,CxXIntA2(a0) //
+ sd a3,CxXIntA3(a0) //
+ sd t0,CxXIntT0(a0) //
+ sd t1,CxXIntT1(a0) //
+ sd t2,CxXIntT2(a0) //
+ sd t3,CxXIntT3(a0) //
+ sd t4,CxXIntT4(a0) //
+ sd t5,CxXIntT5(a0) //
+ sd t6,CxXIntT6(a0) //
+ sd t7,CxXIntT7(a0) //
+ sd s0,CxXIntS0(a0) //
+ sd s1,CxXIntS1(a0) //
+ sd s2,CxXIntS2(a0) //
+ sd s3,CxXIntS3(a0) //
+ sd s4,CxXIntS4(a0) //
+ sd s5,CxXIntS5(a0) //
+ sd s6,CxXIntS6(a0) //
+ sd s7,CxXIntS7(a0) //
+ sd t8,CxXIntT8(a0) //
+ sd t9,CxXIntT9(a0) //
+ sd zero,CxXIntK0(a0) //
+ sd zero,CxXIntK1(a0) //
+ sd s8,CxXIntS8(a0) //
+ sd gp,CxXIntGp(a0) //
+ sd sp,CxXIntSp(a0) //
+ sd ra,CxXIntRa(a0) //
+
+//
+// Save floating status and floating registers f0 - f31.
+//
+
+ cfc1 v0,fsr //
+ sdc1 f0,CxFltF0(a0) //
+ sdc1 f2,CxFltF2(a0) //
+ sdc1 f4,CxFltF4(a0) //
+ sdc1 f6,CxFltF6(a0) //
+ sdc1 f8,CxFltF8(a0) //
+ sdc1 f10,CxFltF10(a0) //
+ sdc1 f12,CxFltF12(a0) //
+ sdc1 f14,CxFltF14(a0) //
+ sdc1 f16,CxFltF16(a0) //
+ sdc1 f18,CxFltF18(a0) //
+ sdc1 f20,CxFltF20(a0) //
+ sdc1 f22,CxFltF22(a0) //
+ sdc1 f24,CxFltF24(a0) //
+ sdc1 f26,CxFltF26(a0) //
+ sdc1 f28,CxFltF28(a0) //
+ sdc1 f30,CxFltF30(a0) //
+ .set at
+ .set reorder
+
+//
+// Save control information and set context flags.
+//
+
+ sw v0,CxFsr(a0) // save floating status
+ sw ra,CxFir(a0) // set continuation address
+ li v0,UserPsr // set constant user processor status
+ bgez sp,10f // if gez, called from user mode
+
+ .set noreorder
+ .set noat
+ mfc0 v0,psr // get current processor status
+ nop //
+ .set at
+ .set reorder
+
+10: sw v0,CxPsr(a0) // set processor status
+ li t0,CONTEXT_FULL // set context control flags
+ sw t0,CxContextFlags(a0) //
+ j ra // return
+
+ .end RtlCaptureContext
+
+ SBTTL("Restore Context")
+//++
+//
+// VOID
+// RtlpRestoreContext (
+// IN PCONTEXT ContextRecord,
+// IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function restores the context of the caller to the specified
+// context.
+//
+// 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.
+//
+// ExceptionRecord (a1) - Supplies an optional pointer to an exception
+// record.
+//
+// Return Value:
+//
+// None.
+//
+// N.B. There is no return from this routine.
+//
+//--
+
+ LEAF_ENTRY(RtlpRestoreContext)
+
+//
+// If an exception record is specified and the exception status is
+// STATUS_LONGJUMP, then restore the nonvolatile registers to their
+// state at the call to setjmp before restoring the context record.
+//
+
+ li t0,STATUS_LONGJUMP // get long jump status code
+ beq zero,a1,5f // if eq, no exception record
+ lw t1,ErExceptionCode(a1) // get exception code
+ lw a2,ErExceptionInformation(a1) // get address of jump buffer
+ bne t0,t1,5f // if ne, not a long jump
+ ldc1 f20,JbFltF20(a2) // move floating registers f20 - f31
+ ldc1 f22,JbFltF22(a2) //
+ ldc1 f24,JbFltF24(a2) //
+ ldc1 f26,JbFltF26(a2) //
+ ldc1 f28,JbFltF28(a2) //
+ ldc1 f30,JbFltF30(a2) //
+ sdc1 f20,CxFltF20(a0) //
+ sdc1 f22,CxFltF22(a0) //
+ sdc1 f24,CxFltF24(a0) //
+ sdc1 f26,CxFltF26(a0) //
+ sdc1 f28,CxFltF28(a0) //
+ sdc1 f30,CxFltF30(a0) //
+ lw s0,JbIntS0(a2) // move integer registers s0 - s8
+ lw s1,JbIntS1(a2) //
+ lw s2,JbIntS2(a2) //
+ lw s3,JbIntS3(a2) //
+ lw s4,JbIntS4(a2) //
+ lw s5,JbIntS5(a2) //
+ lw s6,JbIntS6(a2) //
+ lw s7,JbIntS7(a2) //
+ lw s8,JbIntS8(a2) //
+ sd s0,CxXIntS0(a0) //
+ sd s1,CxXIntS1(a0) //
+ sd s2,CxXIntS2(a0) //
+ sd s3,CxXIntS3(a0) //
+ sd s4,CxXIntS4(a0) //
+ sd s5,CxXIntS5(a0) //
+ sd s6,CxXIntS6(a0) //
+ sd s7,CxXIntS7(a0) //
+ sd s8,CxXIntS8(a0) //
+ lw v0,JbFir(a2) // move fir and sp
+ lw v1,JbIntSp(a2) //
+ sw v0,CxFir(a0) //
+ sd v0,CxXIntRa(a0) //
+ sd v1,CxXIntSp(a0) //
+
+//
+// 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.
+//
+
+5: bltz ra,10f // if lt, kernel mode restore
+ move a1,zero // set test alert argument
+ jal ZwContinue // continue execution
+
+//
+// Save the address of the context record and contuation address in
+// registers t0 and t1. These registers are not restored.
+//
+
+10: move t0,a0 // save context record address
+ lw t1,CxFir(t0) // get continuation address
+
+//
+// Restore floating status and floating registers f0 - f31.
+//
+
+ .set noreorder
+ .set noat
+ lw v0,CxFsr(t0) // restore floating status
+ ctc1 v0,fsr //
+ ldc1 f0,CxFltF0(t0) // restore floating registers f0 - f31
+ ldc1 f2,CxFltF2(t0) //
+ ldc1 f4,CxFltF4(t0) //
+ ldc1 f6,CxFltF6(t0) //
+ ldc1 f8,CxFltF8(t0) //
+ ldc1 f10,CxFltF10(t0) //
+ ldc1 f12,CxFltF12(t0) //
+ ldc1 f14,CxFltF14(t0) //
+ ldc1 f16,CxFltF16(t0) //
+ ldc1 f18,CxFltF18(t0) //
+ ldc1 f20,CxFltF20(t0) //
+ ldc1 f22,CxFltF22(t0) //
+ ldc1 f24,CxFltF24(t0) //
+ ldc1 f26,CxFltF26(t0) //
+ ldc1 f28,CxFltF28(t0) //
+ ldc1 f30,CxFltF30(t0) //
+
+//
+// Restore integer registers and continue execution.
+//
+
+ ld v0,CxXIntLo(t0) // restore multiply/divide registers
+ ld v1,CxXIntHi(t0) //
+ mtlo v0 //
+ mthi v1 //
+ ld AT,CxXIntAt(t0) // restore integer registers at - a3
+ ld v0,CxXIntV0(t0) //
+ ld v1,CxXIntV1(t0) //
+ ld a0,CxXIntA0(t0) //
+ ld a1,CxXIntA1(t0) //
+ ld a2,CxXIntA2(t0) //
+ ld a3,CxXIntA3(t0) //
+ ld t2,CxXIntT2(t0) // restore integer registers t2 - t7
+ ld t3,CxXIntT3(t0) //
+ ld t4,CxXIntT4(t0) //
+ ld t5,CxXIntT5(t0) //
+ ld t6,CxXIntT6(t0) //
+ ld t7,CxXIntT7(t0) //
+ ld s0,CxXIntS0(t0) // restore integer registers s0 - s7
+ ld s1,CxXIntS1(t0) //
+ ld s2,CxXIntS2(t0) //
+ ld s3,CxXIntS3(t0) //
+ ld s4,CxXIntS4(t0) //
+ ld s5,CxXIntS5(t0) //
+ ld s6,CxXIntS6(t0) //
+ ld s7,CxXIntS7(t0) //
+ ld t8,CxXIntT8(t0) // restore integer registers t8 and t9
+ ld t9,CxXIntT9(t0) //
+ ld s8,CxXIntS8(t0) // restore integer register s8
+ ld gp,CxXIntGp(t0) // restore integer register gp
+ ld sp,CxXIntSp(t0) //
+ j t1 // continue execution
+ ld ra,CxXIntRa(t0) //
+ .set at
+ .set reorder
+
+ .end RtlRestoreContext
diff --git a/private/ntos/rtl/mips/xxmvmem.s b/private/ntos/rtl/mips/xxmvmem.s
new file mode 100644
index 000000000..40e2ddaac
--- /dev/null
+++ b/private/ntos/rtl/mips/xxmvmem.s
@@ -0,0 +1,1440 @@
+// TITLE("Compare, Move, Zero, and Fill Memory Support")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+//
+// Module Name:
+//
+// xxmvmem.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:
+//
+// David N. Cutler (davec) 11-Apr-1990
+//
+// Environment:
+//
+// User or Kernel mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksmips.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)
+
+ addu a3,a0,a2 // compute ending address of source1
+ move v0,a2 // save length of comparison
+ and t0,a2,32 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ addu t4,a0,t1 // compute ending block address
+ beq zero,t1,100f // if eq, no 32-byte block to compare
+ or t0,a0,a1 // merge and isolate alignment bits
+ and t0,t0,0x3 //
+ bne zero,t0,CompareUnaligned // if ne, unalignment comparison
+
+//
+// Compare memory aligned.
+//
+
+CompareAligned: //
+
+ .set noreorder
+ .set noat
+10: lw t0,0(a0) // compare 32-byte block
+ lw t1,0(a1) //
+ lw t2,4(a0) //
+ bne t0,t1,90f // if ne, first word not equal
+ lw t3,4(a1) //
+ lw t0,8(a0) //
+ bne t2,t3,20f // if ne, second word not equal
+ lw t1,8(a1) //
+ lw t2,12(a0) //
+ bne t0,t1,30f // if ne, third word not equal
+ lw t3,12(a1) //
+ lw t0,16(a0) //
+ bne t2,t3,40f // if ne, fourth word not equal
+ lw t1,16(a1) //
+ lw t2,20(a0) //
+ bne t0,t1,50f // if ne, fifth word not equal
+ lw t3,20(a1) //
+ lw t0,24(a0) //
+ bne t2,t3,60f // if ne, sixth word not equal
+ lw t1,24(a1) //
+ lw t2,28(a0) //
+ bne t0,t1,70f // if ne, seventh word not equal
+ lw t3,28(a1) //
+ addu a0,a0,32 // advance source1 to next block
+ bne t2,t3,80f // if ne, eighth word not equal
+ nop //
+ bne a0,t4,10b // if ne, more 32-byte blocks to compare
+ addu a1,a1,32 // update source2 address
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 100f //
+
+//
+// Compare memory unaligned.
+//
+
+CompareUnaligned: //
+ and t0,a0,0x3 // isolate source1 alignment
+ bne zero,t0,CompareUnalignedS1 // if ne, source1 unaligned
+
+//
+// Source1 is aligned and Source2 is unaligned.
+//
+
+CompareUnalignedS2: //
+
+ .set noreorder
+ .set noat
+10: lw t0,0(a0) // compare 32-byte block
+ lwr t1,0(a1) //
+ lwl t1,3(a1) //
+ lw t2,4(a0) //
+ bne t0,t1,90f // if ne, first word not equal
+ lwr t3,4(a1) //
+ lwl t3,7(a1) //
+ lw t0,8(a0) //
+ bne t2,t3,20f // if ne, second word not equal
+ lwr t1,8(a1) //
+ lwl t1,11(a1) //
+ lw t2,12(a0) //
+ bne t0,t1,30f // if ne, third word not equal
+ lwr t3,12(a1) //
+ lwl t3,15(a1) //
+ lw t0,16(a0) //
+ bne t2,t3,40f // if ne, fourth word not equal
+ lwr t1,16(a1) //
+ lwl t1,19(a1) //
+ lw t2,20(a0) //
+ bne t0,t1,50f // if ne, fifth word not equal
+ lwr t3,20(a1) //
+ lwl t3,23(a1) //
+ lw t0,24(a0) //
+ bne t2,t3,60f // if ne, sixth word not equal
+ lwr t1,24(a1) //
+ lwl t1,27(a1) //
+ lw t2,28(a0) //
+ bne t0,t1,70f // if ne, seventh word not equal
+ lwr t3,28(a1) //
+ lwl t3,31(a1) //
+ addu a0,a0,32 // advance source1 to next block
+ bne t2,t3,80f // if ne, eighth word not equal
+ nop //
+ bne a0,t4,10b // if ne, more 32-byte blocks to compare
+ addu a1,a1,32 // update source2 address
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 100f //
+
+//
+// Source1 is unaligned, check Source2 alignment.
+//
+
+CompareUnalignedS1: //
+ and t0,a1,0x3 // isolate Source2 alignment
+ bne zero,t0,CompareUnalignedS1AndS2 // if ne, Source2 unaligned
+
+//
+// Source1 is unaligned and Source2 is aligned.
+//
+
+ .set noreorder
+ .set noat
+10: lwr t0,0(a0) // compare 32-byte block
+ lwl t0,3(a0) //
+ lw t1,0(a1) //
+ lwr t2,4(a0) //
+ lwl t2,7(a0) //
+ bne t0,t1,90f // if ne, first word not equal
+ lw t3,4(a1) //
+ lwr t0,8(a0) //
+ lwl t0,11(a0) //
+ bne t2,t3,20f // if ne, second word not equal
+ lw t1,8(a1) //
+ lwr t2,12(a0) //
+ lwl t2,15(a0) //
+ bne t0,t1,30f // if ne, third word not equal
+ lw t3,12(a1) //
+ lwr t0,16(a0) //
+ lwl t0,19(a0) //
+ bne t2,t3,40f // if ne, fourth word not equal
+ lw t1,16(a1) //
+ lwr t2,20(a0) //
+ lwl t2,23(a0) //
+ bne t0,t1,50f // if ne, fifth word not equal
+ lw t3,20(a1) //
+ lwr t0,24(a0) //
+ lwl t0,27(a0) //
+ bne t2,t3,60f // if ne, sixth word not equal
+ lw t1,24(a1) //
+ lwr t2,28(a0) //
+ lwl t2,31(a0) //
+ bne t0,t1,70f // if ne, seventh word not equal
+ lw t3,28(a1) //
+ addu a0,a0,32 // advance source1 to next block
+ bne t2,t3,80f // if ne, eighth word not equal
+ nop //
+ bne a0,t4,10b // if ne, more 32-byte blocks to compare
+ addu a1,a1,32 // update source2 address
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 100f //
+
+//
+// Source1 and Source2 are unaligned.
+//
+
+CompareUnalignedS1AndS2: //
+
+ .set noreorder
+ .set noat
+10: lwr t0,0(a0) // compare 32-byte block
+ lwl t0,3(a0) //
+ lwr t1,0(a1) //
+ lwl t1,3(a1) //
+ lwr t2,4(a0) //
+ lwl t2,7(a0) //
+ bne t0,t1,90f // if ne, first word not equal
+ lwr t3,4(a1) //
+ lwl t3,7(a1) //
+ lwr t0,8(a0) //
+ lwl t0,11(a0) //
+ bne t2,t3,20f // if ne, second word not equal
+ lwr t1,8(a1) //
+ lwl t1,11(a1) //
+ lwr t2,12(a0) //
+ lwl t2,15(a0) //
+ bne t0,t1,30f // if ne, third word not equal
+ lwr t3,12(a1) //
+ lwl t3,15(a1) //
+ lwr t0,16(a0) //
+ lwl t0,19(a0) //
+ bne t2,t3,40f // if ne, fourth word not equal
+ lwr t1,16(a1) //
+ lwl t1,19(a1) //
+ lwr t2,20(a0) //
+ lwl t2,23(a0) //
+ bne t0,t1,50f // if ne, fifth word not equal
+ lwr t3,20(a1) //
+ lwl t3,23(a1) //
+ lwr t0,24(a0) //
+ lwl t0,27(a0) //
+ bne t2,t3,60f // if ne, sixth word not equal
+ lwr t1,24(a1) //
+ lwl t1,27(a1) //
+ lwr t2,28(a0) //
+ lwl t2,31(a0) //
+ bne t0,t1,70f // if ne, seventh word not equal
+ lwr t3,28(a1) //
+ lwl t3,31(a1) //
+ addu a0,a0,32 // advance source1 to next block
+ bne t2,t3,80f // if ne, eighth word not equal
+ nop //
+ bne a0,t4,10b // if ne, more 32-byte blocks to compare
+ addu a1,a1,32 // update source2 address
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 100f //
+
+//
+// Adjust source1 and source2 pointers dependent on position of miscompare in
+// block.
+//
+
+20: addu a0,a0,4 // mismatch on second word
+ addu a1,a1,4 //
+ b 90f //
+
+30: addu a0,a0,8 // mismatch on third word
+ addu a1,a1,8 //
+ b 90f //
+
+40: addu a0,a0,12 // mistmatch on fourth word
+ addu a1,a1,12 //
+ b 90f //
+
+50: addu a0,a0,16 // mismatch on fifth word
+ addu a1,a1,16 //
+ b 90f //
+
+60: addu a0,a0,20 // mismatch on sixth word
+ addu a1,a1,20 //
+ b 90f //
+
+70: addu a0,a0,24 // mismatch on seventh word
+ addu a1,a1,24 //
+ b 90f //
+
+80: subu a0,a0,4 // mismatch on eighth word
+ addu a1,a1,28 //
+90: subu a2,a3,a0 // compute remaining bytes
+
+//
+// Compare 1-byte blocks.
+//
+
+100: addu t2,a0,a2 // compute ending block address
+ beq zero,a2,120f // if eq, no bytes to zero
+110: lb t0,0(a0) // compare 1-byte block
+ lb t1,0(a1) //
+ addu a1,a1,1 // advance pointers to next block
+ bne t0,t1,120f // if ne, byte not equal
+ addu a0,a0,1 //
+ bne a0,t2,110b // if ne, more 1-byte block to zero
+
+120: subu t0,a3,a0 // compute number of bytes not compared
+ subu v0,v0,t0 // compute number of byte that matched
+ j ra // return
+
+ .end RtlCompareMemory
+
+ SBTTL("Equal Memory")
+//++
+//
+// ULONG
+// RtlEqualMemory (
+// IN PVOID Source1,
+// IN PVOID Source2,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function compares two blocks of memory for equality.
+//
+// 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:
+//
+// If all bytes in the source strings match, then a value of TRUE is
+// returned. Otherwise, FALSE is returned.
+//
+//--
+
+ LEAF_ENTRY(RtlEqualMemory)
+
+ li v0,FALSE // set return value FALSE
+ addu a3,a0,a2 // compute ending address of source1
+ and t0,a2,16 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ addu t4,a0,t1 // compute ending block address
+ beq zero,t1,20f // if eq, no 16-byte block to compare
+ or t0,a0,a1 // merge and isolate alignment bits
+ and t0,t0,0x3 //
+ bne zero,t0,EqualUnaligned // if ne, unalignment comparison
+
+//
+// Compare memory aligned.
+//
+
+EqualAligned: //
+
+ .set noreorder
+ .set noat
+10: lw t0,0(a0) // compare 16-byte block
+ lw t1,0(a1) //
+ lw t2,4(a0) //
+ bne t0,t1,50f // if ne, first word not equal
+ lw t3,4(a1) //
+ lw t0,8(a0) //
+ bne t2,t3,50f // if ne, second word not equal
+ lw t1,8(a1) //
+ lw t2,12(a0) //
+ bne t0,t1,50f // if ne, third word not equal
+ lw t3,12(a1) //
+ bne t2,t3,50f // if ne, eighth word not equal
+ addu a0,a0,16 // advance source1 to next block
+ bne a0,t4,10b // if ne, more blocks to compare
+ addu a1,a1,16 // advance source2 to next block
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 20f //
+
+//
+// Compare memory unaligned.
+//
+
+EqualUnaligned: //
+ and t0,a0,0x3 // isolate source1 alignment
+ bne zero,t0,EqualUnalignedS1 // if ne, source1 unaligned
+
+//
+// Source1 is aligned and Source2 is unaligned.
+//
+
+EqualUnalignedS2: //
+
+ .set noreorder
+ .set noat
+10: lw t0,0(a0) // compare 16-byte block
+ lwr t1,0(a1) //
+ lwl t1,3(a1) //
+ lw t2,4(a0) //
+ bne t0,t1,50f // if ne, first word not equal
+ lwr t3,4(a1) //
+ lwl t3,7(a1) //
+ lw t0,8(a0) //
+ bne t2,t3,50f // if ne, second word not equal
+ lwr t1,8(a1) //
+ lwl t1,11(a1) //
+ lw t2,12(a0) //
+ bne t0,t1,50f // if ne, third word not equal
+ lwr t3,12(a1) //
+ lwl t3,15(a1) //
+ bne t2,t3,50f // if ne, fourth word not equal
+ addu a0,a0,16 // advance source1 to next block
+ bne a0,t4,10b // if ne, more blocks to compare
+ addu a1,a1,16 // advance source2 to next block
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 20f //
+
+//
+// Source1 is unaligned, check Source2 alignment.
+//
+
+EqualUnalignedS1: //
+ and t0,a1,0x3 // isolate Source2 alignment
+ bne zero,t0,EqualUnalignedS1AndS2 // if ne, Source2 unaligned
+
+//
+// Source1 is unaligned and Source2 is aligned.
+//
+
+ .set noreorder
+ .set noat
+10: lwr t0,0(a0) // compare 16-byte block
+ lwl t0,3(a0) //
+ lw t1,0(a1) //
+ lwr t2,4(a0) //
+ lwl t2,7(a0) //
+ bne t0,t1,50f // if ne, first word not equal
+ lw t3,4(a1) //
+ lwr t0,8(a0) //
+ lwl t0,11(a0) //
+ bne t2,t3,50f // if ne, second word not equal
+ lw t1,8(a1) //
+ lwr t2,12(a0) //
+ lwl t2,15(a0) //
+ bne t0,t1,50f // if ne, third word not equal
+ lw t3,12(a1) //
+ bne t2,t3,50f // if ne, fourth word not equal
+ addu a0,a0,16 // advance source1 to next block
+ bne a0,t4,10b // if ne, more blocks to compare
+ addu a1,a1,16 // advance source2 to next block
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+ b 20f //
+
+//
+// Source1 and Source2 are unaligned.
+//
+
+EqualUnalignedS1AndS2: //
+
+ .set noreorder
+ .set noat
+10: lwr t0,0(a0) // compare 16-byte block
+ lwl t0,3(a0) //
+ lwr t1,0(a1) //
+ lwl t1,3(a1) //
+ lwr t2,4(a0) //
+ lwl t2,7(a0) //
+ bne t0,t1,50f // if ne, first word not equal
+ lwr t3,4(a1) //
+ lwl t3,7(a1) //
+ lwr t0,8(a0) //
+ lwl t0,11(a0) //
+ bne t2,t3,50f // if ne, second word not equal
+ lwr t1,8(a1) //
+ lwl t1,11(a1) //
+ lwr t2,12(a0) //
+ lwl t2,15(a0) //
+ bne t0,t1,50f // if ne, third word not equal
+ lwr t3,12(a1) //
+ lwl t3,15(a1) //
+ bne t2,t3,50f // if ne, fourth word not equal
+ addu a0,a0,16 // advance source1 to next block
+ bne a0,t4,10b // if ne, more blocks to compare
+ addu a1,a1,16 // advance source2 to next block
+ .set at
+ .set reorder
+
+ subu a2,a3,a0 // compute remaining bytes
+
+//
+// Compare 1-byte blocks.
+//
+
+20: addu t2,a0,a2 // compute ending block address
+ beq zero,a2,40f // if eq, no bytes to zero
+30: lb t0,0(a0) // compare 1-byte block
+ lb t1,0(a1) //
+ addu a1,a1,1 // advance pointers to next block
+ bne t0,t1,50f // if ne, byte not equal
+ addu a0,a0,1 //
+ bne a0,t2,30b // if ne, more 1-byte block to zero
+40: li v0,TRUE // set return value TRUE
+
+50: j ra // return
+
+ .end RtlEqualMemory
+
+ SBTTL("Move Memory")
+//++
+//
+// PVOID
+// RtlMoveMemory (
+// IN PVOID Destination,
+// IN PVOID Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function moves memory either forward or backward, aligned or
+// unaligned, in 32-byte blocks, followed by 4-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:
+//
+// The Destination address is returned as the function value.
+//
+// N.B. The C runtime entry points memmove and memcpy are equivalent to
+// RtlMoveMemory htus alternate entry points are provided for these
+// routines.
+//--
+
+ LEAF_ENTRY(RtlMoveMemory)
+
+ ALTERNATE_ENTRY(memcpy)
+ ALTERNATE_ENTRY(memmove)
+
+ move v0,a0 // set return value
+
+//
+// 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.
+//
+
+10: bgeu a1,a0,MoveForward // if geu, no overlap possible
+ addu t0,a1,a2 // compute source ending address
+ bgtu t0,a0,MoveBackward // if gtu, source and destination overlap
+
+//
+// Move memory forward aligned and unaligned.
+//
+
+MoveForward: //
+ sltu t0,a2,8 // check if less than eight bytes
+ bne zero,t0,50f // if ne, less than eight bytes to move
+ xor t0,a0,a1 // compare alignment bits
+ and t0,t0,0x7 // isolate alignment comparison
+ bne zero,t0,MoveForwardUnaligned // if ne, incompatible alignment
+
+//
+// Move memory forward aligned.
+//
+
+MoveForwardAligned: //
+ subu t0,zero,a0 // compute bytes until aligned
+ and t0,t0,0x7 // isolate residual byte count
+ subu a2,a2,t0 // reduce number of bytes to move
+ beq zero,t0,10f // if eq, already aligned
+ ldr t1,0(a1) // move unaligned bytes
+ sdr t1,0(a0) //
+ addu a0,a0,t0 // align destination address
+ addu a1,a1,t0 // align source address
+
+//
+// Check for 32-byte blocks to move.
+//
+
+10: and t0,a2,32 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ addu t8,a0,t1 // compute ending block address
+ beq zero,t1,30f // if eq, no 32-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Check for odd number of 32-byte blocks to move.
+//
+
+ and t0,t1,1 << 5 // test if even number of 32-byte blocks
+ beq zero,t0,20f // if eq, even number of 32-byte blocks
+
+//
+// Move one 32-byte block quadword aligned.
+//
+
+ .set noreorder
+ ld t0,0(a1) // move 32-byte block
+ ld t1,8(a1) //
+ ld t2,16(a1) //
+ ld t3,24(a1) //
+ sd t0,0(a0) //
+ sd t1,8(a0) //
+ sd t2,16(a0) //
+ sd t3,24(a0) //
+ addu a0,a0,32 // advance pointers to next block
+ beq a0,t8,30f // if eq, end of block
+ addu a1,a1,32 //
+ .set reorder
+
+//
+// Move 64-byte blocks quadword aligned.
+//
+
+ .set noreorder
+20: ld t0,0(a1) // move 64-byte block
+ ld t1,8(a1) //
+ ld t2,16(a1) //
+ ld t3,24(a1) //
+ ld t4,32(a1) //
+ ld t5,40(a1) //
+ ld t6,48(a1) //
+ ld t7,56(a1) //
+ sd t0,0(a0) //
+ sd t1,8(a0) //
+ sd t2,16(a0) //
+ sd t3,24(a0) //
+ sd t4,32(a0) //
+ sd t5,40(a0) //
+ sd t6,48(a0) //
+ sd t7,56(a0) //
+ addu a0,a0,64 // advance pointers to next block
+ bne a0,t8,20b // if ne, more 64-byte blocks to zero
+ addu a1,a1,64 //
+ .set reorder
+
+//
+// Check for 4-byte blocks to move.
+//
+
+30: and t0,a2,4 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ addu t2,a0,t1 // compute ending block address
+ beq zero,t1,50f // if eq, no 4-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Move 4-byte block.
+//
+
+ .set noreorder
+40: lw t0,0(a1) // move 4-byte block
+ addu a0,a0,4 // advance pointers to next block
+ sw t0,-4(a0) //
+ bne a0,t2,40b // if ne, more 4-byte blocks to zero
+ addu a1,a1,4 //
+ .set reorder
+
+//
+// Move 1-byte blocks.
+//
+
+50: addu t2,a0,a2 // compute ending block address
+ beq zero,a2,70f // if eq, no bytes to zero
+
+ .set noreorder
+60: lb t0,0(a1) // move 1-byte block
+ addu a0,a0,1 // advance pointers to next block
+ sb t0,-1(a0) //
+ bne a0,t2,60b // if ne, more 1-byte block to zero
+ addu a1,a1,1 //
+ .set reorder
+
+70: j ra // return
+
+//
+// Move memory forward unaligned.
+//
+
+MoveForwardUnaligned: //
+ subu t0,zero,a0 // compute bytes until aligned
+ and t0,t0,0x7 // isolate residual byte count
+ subu a2,a2,t0 // reduce number of bytes to move
+ beq zero,t0,10f // if eq, already aligned
+ ldr t1,0(a1) // move unaligned bytes
+ ldl t1,7(a1) //
+ sdr t1,0(a0) //
+ addu a0,a0,t0 // align destination address
+ addu a1,a1,t0 // update source address
+
+//
+// Check for 32-byte blocks to move.
+//
+
+10: and t0,a2,32 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ addu t8,a0,t1 // compute ending block address
+ beq zero,t1,30f // if eq, no 32-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Check for odd number of 32-byte blocks to move.
+//
+
+ and t0,t1,1 << 5 // test if even number of 32-byte blocks
+ beq zero,t0,20f // if eq, even number of 32-byte blocks
+
+//
+// Move one 32-byte block quadword aligned.
+//
+
+ .set noreorder
+ ldr t0,0(a1) // move 32-byte block
+ ldl t0,7(a1) //
+ ldr t1,8(a1) //
+ ldl t1,15(a1) //
+ ldr t2,16(a1) //
+ ldl t2,23(a1) //
+ ldr t3,24(a1) //
+ ldl t3,31(a1) //
+ sd t0,0(a0) //
+ sd t1,8(a0) //
+ sd t2,16(a0) //
+ sd t3,24(a0) //
+ addu a0,a0,32 // advance pointers to next block
+ beq a0,t8,30f // if eq, end of block
+ addu a1,a1,32 //
+ .set reorder
+
+//
+// Move 64-byte block.
+//
+
+ .set noreorder
+20: ldr t0,0(a1) // move 64-byte block
+ ldl t0,7(a1) //
+ ldr t1,8(a1) //
+ ldl t1,15(a1) //
+ ldr t2,16(a1) //
+ ldl t2,23(a1) //
+ ldr t3,24(a1) //
+ ldl t3,31(a1) //
+ ldr t4,32(a1) //
+ ldl t4,39(a1) //
+ ldr t5,40(a1) //
+ ldl t5,47(a1) //
+ ldr t6,48(a1) //
+ ldl t6,55(a1) //
+ ldr t7,56(a1) //
+ ldl t7,63(a1) //
+ sd t0,0(a0) //
+ sd t1,8(a0) //
+ sd t2,16(a0) //
+ sd t3,24(a0) //
+ sd t4,32(a0) //
+ sd t5,40(a0) //
+ sd t6,48(a0) //
+ sd t7,56(a0) //
+ addu a0,a0,64 // advance pointers to next block
+ bne a0,t8,20b // if ne, more 32-byte blocks to zero
+ addu a1,a1,64 //
+ .set reorder
+
+//
+// Check for 4-byte blocks to move.
+//
+
+30: and t0,a2,4 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ addu t2,a0,t1 // compute ending block address
+ beq zero,t1,50f // if eq, no 4-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Move 4-byte block.
+//
+
+ .set noreorder
+40: lwr t0,0(a1) // move 4-byte block
+ lwl t0,3(a1) //
+ addu a0,a0,4 // advance pointers to next block
+ sw t0,-4(a0) //
+ bne a0,t2,40b // if ne, more 4-byte blocks to zero
+ addu a1,a1,4 //
+ .set reorder
+
+//
+// Move 1-byte blocks.
+//
+
+50: addu t2,a0,a2 // compute ending block address
+ beq zero,a2,70f // if eq, no bytes to zero
+
+ .set noreorder
+60: lb t0,0(a1) // move 1-byte block
+ addu a0,a0,1 // advance pointers to next block
+ sb t0,-1(a0) //
+ bne a0,t2,60b // if ne, more 1-byte block to zero
+ addu a1,a1,1 //
+ .set reorder
+
+70: j ra // return
+
+//
+// Move memory backward.
+//
+
+MoveBackward: //
+ addu a0,a0,a2 // compute ending destination address
+ addu a1,a1,a2 // compute ending source address
+ sltu t0,a2,8 // check if less than eight bytes
+ bne zero,t0,50f // if ne, less than eight bytes to move
+ xor t0,a0,a1 // compare alignment bits
+ and t0,t0,0x7 // isolate alignment comparison
+ bne zero,t0,MoveBackwardUnaligned // if ne, incompatible alignment
+
+//
+// Move memory backward aligned.
+//
+
+MoveBackwardAligned: //
+ and t0,a0,0x7 // isolate residual byte count
+ subu a2,a2,t0 // reduce number of bytes to move
+ beq zero,t0,10f // if eq, already aligned
+ ldl t1,-1(a1) // move unaligned bytes
+ sdl t1,-1(a0) //
+ subu a0,a0,t0 // align destination address
+ subu a1,a1,t0 // align source address
+
+//
+// Check for 32-byte blocks to move.
+//
+
+10: and t0,a2,32 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ subu t8,a0,t1 // compute ending block address
+ beq zero,t1,30f // if eq, no 32-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Check for odd number of 32-byte blocks to move.
+//
+
+ and t0,t1,1 << 5 // test if even number of 32-byte blocks
+ beq zero,t0,20f // if eq, even number of 32-byte blocks
+
+//
+// Move one 32-byte block quadword aligned.
+//
+
+ .set noreorder
+ ld t0,-8(a1) // move 32-byte block
+ ld t1,-16(a1) //
+ ld t2,-24(a1) //
+ ld t3,-32(a1) //
+ sd t0,-8(a0) //
+ sd t1,-16(a0) //
+ sd t2,-24(a0) //
+ sd t3,-32(a0) //
+ subu a0,a0,32 // advance pointers to next block
+ beq a0,t8,30f // if eq, end of block
+ subu a1,a1,32 //
+ .set reorder
+
+//
+// Move 64-byte blocks quadword aligned.
+//
+
+ .set noreorder
+20: ld t0,-8(a1) // move 64-byte block
+ ld t1,-16(a1) //
+ ld t2,-24(a1) //
+ ld t3,-32(a1) //
+ ld t4,-40(a1) //
+ ld t5,-48(a1) //
+ ld t6,-56(a1) //
+ ld t7,-64(a1) //
+ sd t0,-8(a0) //
+ sd t1,-16(a0) //
+ sd t2,-24(a0) //
+ sd t3,-32(a0) //
+ sd t4,-40(a0) //
+ sd t5,-48(a0) //
+ sd t6,-56(a0) //
+ sd t7,-64(a0) //
+ subu a0,a0,64 // advance pointers to next block
+ bne a0,t8,20b // if ne, more 64-byte blocks to zero
+ subu a1,a1,64 //
+ .set reorder
+
+//
+// Check for 4-byte blocks to move.
+//
+
+30: and t0,a2,4 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ subu t2,a0,t1 // compute ending block address
+ beq zero,t1,50f // if eq, no 4-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Move 4-byte block.
+//
+
+ .set noreorder
+40: lw t0,-4(a1) // move 4-byte block
+ subu a0,a0,4 // advance pointers to next block
+ sw t0,0(a0) //
+ bne a0,t2,40b // if ne, more 4-byte blocks to zero
+ subu a1,a1,4 //
+ .set reorder
+
+//
+// Move 1-byte blocks.
+//
+
+50: subu t2,a0,a2 // compute ending block address
+ beq zero,a2,70f // if eq, no bytes to zero
+
+ .set noreorder
+60: lb t0,-1(a1) // move 1-byte block
+ subu a0,a0,1 // advance pointers to next block
+ sb t0,0(a0) //
+ bne a0,t2,60b // if ne, more 1-byte block to zero
+ subu a1,a1,1 //
+ .set reorder
+
+70: j ra // return
+
+//
+// Move memory backward unaligned.
+//
+
+MoveBackwardUnaligned: //
+ and t0,a0,0x7 // isolate residual byte count
+ subu a2,a2,t0 // reduce number of bytes to move
+ beq zero,t0,10f // if eq, already aligned
+ ldl t1,-1(a1) // move unaligned bytes
+ ldr t1,-8(a1) //
+ sdl t1,-1(a0) //
+ subu a0,a0,t0 // align destination address
+ subu a1,a1,t0 // update source address
+
+//
+// Check for 32-byte blocks to move.
+//
+
+10: and t0,a2,32 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ subu t8,a0,t1 // compute ending block address
+ beq zero,t1,30f // if eq, no 32-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Check for odd number of 32-byte blocks to move.
+//
+
+ and t0,t1,1 << 5 // test if even number of 32-byte blocks
+ beq zero,t0,20f // if eq, even number of 32-byte blocks
+
+//
+// Move one 32-byte block.
+//
+
+ .set noreorder
+ ldr t0,-8(a1) // move 32-byte block
+ ldl t0,-1(a1) //
+ ldr t1,-16(a1) //
+ ldl t1,-9(a1) //
+ ldr t2,-24(a1) //
+ ldl t2,-17(a1) //
+ ldr t3,-32(a1) //
+ ldl t3,-25(a1) //
+ sd t0,-8(a0) //
+ sd t1,-16(a0) //
+ sd t2,-24(a0) //
+ sd t3,-32(a0) //
+ subu a0,a0,32 // advance pointers to next block
+ beq a0,t8,30f // if eq, end of block
+ subu a1,a1,32 //
+ .set reorder
+
+//
+// Move 32-byte block.
+//
+
+ .set noreorder
+20: ldr t0,-8(a1) // move 32-byte block
+ ldl t0,-1(a1) //
+ ldr t1,-16(a1) //
+ ldl t1,-9(a1) //
+ ldr t2,-24(a1) //
+ ldl t2,-17(a1) //
+ ldr t3,-32(a1) //
+ ldl t3,-25(a1) //
+ ldr t4,-40(a1) //
+ ldl t4,-33(a1) //
+ ldr t5,-48(a1) //
+ ldl t5,-41(a1) //
+ ldr t6,-56(a1) //
+ ldl t6,-49(a1) //
+ ldr t7,-64(a1) //
+ ldl t7,-57(a1) //
+ sd t0,-8(a0) //
+ sd t1,-16(a0) //
+ sd t2,-24(a0) //
+ sd t3,-32(a0) //
+ sd t4,-40(a0) //
+ sd t5,-48(a0) //
+ sd t6,-56(a0) //
+ sd t7,-64(a0) //
+ subu a0,a0,64 // advance pointers to next block
+ bne a0,t8,20b // if ne, more 64-byte blocks to zero
+ subu a1,a1,64 //
+ .set reorder
+
+//
+// Check for 4-byte blocks to move.
+//
+
+30: and t0,a2,4 - 1 // isolate residual bytes
+ subu t1,a2,t0 // subtract out residual bytes
+ subu t2,a0,t1 // compute ending block address
+ beq zero,t1,50f // if eq, no 4-byte block to zero
+ move a2,t0 // set residual number of bytes
+
+//
+// Move 4-byte block.
+//
+
+ .set noreorder
+40: lwr t0,-4(a1) // move 4-byte block
+ lwl t0,-1(a1) //
+ subu a0,a0,4 // advance pointers to next block
+ sw t0,0(a0) //
+ bne a0,t2,40b // if ne, more 4-byte blocks to zero
+ subu a1,a1,4 //
+ .set reorder
+
+//
+// Move 1-byte blocks.
+//
+
+50: subu t2,a0,a2 // compute ending block address
+ beq zero,a2,70f // if eq, no bytes to zero
+
+ .set noreorder
+60: lb t0,-1(a1) // move 1-byte block
+ subu a0,a0,1 // advance pointers to next block
+ sb t0,0(a0) //
+ bne a0,t2,60b // if ne, more 1-byte block to zero
+ subu a1,a1,1 //
+ .set reorder
+
+70: j ra // return
+
+ .end RtlMoveMemory
+
+ SBTTL("Zero Memory")
+//++
+//
+// PVOID
+// RtlZeroMemory (
+// IN PVOID Destination,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function zeros memory by first aligning the destination address to
+// a longword boundary, and then zeroing 32-byte blocks, followed by 4-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:
+//
+// The destination address is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(RtlZeroMemory)
+
+ move a2,zero // set fill pattern
+ b RtlpFillMemory //
+
+
+ SBTTL("Fill Memory")
+//++
+//
+// PVOID
+// 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.
+//
+// Return Value:
+//
+// The destination address is returned as the function value.
+//
+//--
+
+ ALTERNATE_ENTRY(memset)
+
+ move a3,a1 // swap length and fill arguments
+ move a1,a2 //
+ move a2,a3 //
+
+ ALTERNATE_ENTRY(RtlFillMemory)
+
+ and a2,a2,0xff // clear excess bits
+ sll t0,a2,8 // duplicate fill byte
+ or a2,a2,t0 // generate fill word
+ sll t0,a2,16 // duplicate fill word
+ or a2,a2,t0 // generate fill longword
+
+//
+// Fill memory with the pattern specified in register a2.
+//
+
+RtlpFillMemory: //
+ move v0,a0 // set return value
+ dsll a2,a2,32 // duplicate pattern to 64-bits
+ dsrl t0,a2,32 //
+ or a2,a2,t0 //
+ subu t0,zero,a0 // compute bytes until aligned
+ and t0,t0,0x7 // isolate residual byte count
+ subu t1,a1,t0 // reduce number of bytes to fill
+ blez t1,60f // if lez, less than 8 bytes to fill
+ move a1,t1 // set number of bytes to fill
+ beq zero,t0,10f // if eq, already aligned
+ sdr a2,0(a0) // fill unaligned bytes
+ addu a0,a0,t0 // align destination address
+
+//
+// Check for 32-byte blocks to fill.
+//
+
+10: and t0,a1,32 - 1 // isolate residual bytes
+ subu t1,a1,t0 // subtract out residual bytes
+ addu t2,a0,t1 // compute ending block address
+ beq zero,t1,40f // if eq, no 32-byte blocks to fill
+ move a1,t0 // set residual number of bytes
+
+//
+// Fill 32-byte blocks.
+//
+
+ and t0,a0,1 << 2 // check if destintion quadword aligned
+ beq zero,t0,20f // if eq, yes
+ sw a2,0(a0) // store destination longword
+ addu a0,a0,4 // align destination address
+ addu a1,a1,t1 // recompute bytes to fill
+ subu a1,a1,4 // reduce count by 4
+ b 10b //
+
+//
+// The destination is quadword aligned.
+//
+
+20: and t0,t1,1 << 5 // test if even number of 32-byte blocks
+ beq zero,t0,30f // if eq, even number of 32-byte blocks
+
+//
+// Fill one 32-byte block.
+//
+
+ .set noreorder
+ .set noat
+ sd a2,0(a0) // fill 32-byte block
+ sd a2,8(a0) //
+ sd a2,16(a0) //
+ addu a0,a0,32 // advance pointer to next block
+ beq a0,t2,40f // if ne, no 64-byte blocks to fill
+ sd a2,-8(a0) //
+ .set at
+ .set reorder
+
+//
+// Fill 64-byte block.
+//
+
+ .set noreorder
+ .set noat
+30: sd a2,0(a0) // fill 32-byte block
+ sd a2,8(a0) //
+ sd a2,16(a0) //
+ sd a2,24(a0) //
+ sd a2,32(a0) //
+ sd a2,40(a0) //
+ sd a2,48(a0) //
+ addu a0,a0,64 // advance pointer to next block
+ bne a0,t2,30b // if ne, more 32-byte blocks to fill
+ sd a2,-8(a0) //
+ .set at
+ .set reorder
+
+//
+// Check for 4-byte blocks to fill.
+//
+
+40: and t0,a1,4 - 1 // isolate residual bytes
+ subu t1,a1,t0 // subtract out residual bytes
+ addu t2,a0,t1 // compute ending block address
+ beq zero,t1,60f // if eq, no 4-byte block to fill
+ move a1,t0 // set residual number of bytes
+
+//
+// Fill 4-byte blocks.
+//
+
+ .set noreorder
+50: addu a0,a0,4 // advance pointer to next block
+ bne a0,t2,50b // if ne, more 4-byte blocks to fill
+ sw a2,-4(a0) // fill 4-byte block
+ .set reorder
+
+//
+// Check for 1-byte blocks to fill.
+//
+
+60: addu t2,a0,a1 // compute ending block address
+ beq zero,a1,80f // if eq, no bytes to fill
+
+//
+// Fill 1-byte blocks.
+//
+
+ .set noreorder
+70: addu a0,a0,1 // advance pointer to next block
+ bne a0,t2,70b // if ne, more 1-byte block to fill
+ sb a2,-1(a0) // fill 1-byte block
+ .set reorder
+
+80: j ra // return
+
+ .end RtlZeroMemory
+
+ SBTTL("Fill Memory Ulong")
+//++
+//
+// PVOID
+// RtlFillMemoryUlong (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN ULONG Pattern
+// )
+//
+// Routine Description:
+//
+// This function fills memory with the specified longowrd pattern by
+// filling 32-byte blocks followed by 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:
+//
+// The destination address is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(RtlFillMemoryUlong)
+
+ move v0,a0 // set function value
+ srl a1,a1,2 // make sure length is an even number
+ sll a1,a1,2 // of longwords
+ dsll a2,a2,32 // duplicate pattern to 64-bits
+ dsrl t0,a2,32 //
+ or a2,a2,t0 //
+
+//
+// Check for 32-byte blocks to fill.
+//
+
+10: and t0,a1,32 - 1 // isolate residual bytes
+ subu t1,a1,t0 // subtract out residual bytes
+ addu t2,a0,t1 // compute ending block address
+ beq zero,t1,40f // if eq, no 32-byte blocks to fill
+ move a1,t0 // set residual number of bytes
+
+//
+// Fill 32-byte blocks.
+//
+
+ and t0,a0,1 << 2 // check if destintion quadword aligned
+ beq zero,t0,20f // if eq, yes
+ sw a2,0(a0) // store destination longword
+ addu a0,a0,4 // align destination address
+ addu a1,a1,t1 // recompute bytes to fill
+ subu a1,a1,4 // reduce count by 4
+ b 10b //
+
+//
+// The destination is quadword aligned.
+//
+
+20: and t0,t1,1 << 5 // test if even number of 32-byte blocks
+ beq zero,t0,30f // if eq, even number of 32-byte blocks
+
+//
+// Fill one 32-byte block.
+//
+
+ .set noreorder
+ .set noat
+ sd a2,0(a0) // fill 32-byte block
+ sd a2,8(a0) //
+ sd a2,16(a0) //
+ addu a0,a0,32 // advance pointer to next block
+ beq a0,t2,40f // if ne, no 64-byte blocks to fill
+ sd a2,-8(a0) //
+ .set at
+ .set reorder
+
+//
+// Fill 64-byte block.
+//
+
+ .set noreorder
+ .set noat
+30: sd a2,0(a0) // fill 32-byte block
+ sd a2,8(a0) //
+ sd a2,16(a0) //
+ sd a2,24(a0) //
+ sd a2,32(a0) //
+ sd a2,40(a0) //
+ sd a2,48(a0) //
+ addu a0,a0,64 // advance pointer to next block
+ bne a0,t2,30b // if ne, more 32-byte blocks to fill
+ sd a2,-8(a0) //
+ .set at
+ .set reorder
+
+//
+// Check for 4-byte blocks to fill.
+//
+
+40: addu t2,a1,a0 // compute ending block address
+ beq zero,a1,60f // if eq, no 4-byte block to fill
+
+//
+// Fill 4-byte blocks.
+//
+
+ .set noreorder
+50: addu a0,a0,4 // advance pointer to next block
+ bne a0,t2,50b // if ne, more 4-byte blocks to fill
+ sw a2,-4(a0) // fill 4-byte block
+ .set reorder
+
+60: j ra // return
+
+ .end RtlFillMemoryUlong