MODULE Machine;
IMPORT SYSTEM, Trace;
CONST
Version = "A2 Revision 4186 (17.06.2011)";
MaxCPU* = 8;
DefaultObjectFileExtension* = ".Obx";
MTTR* = 12; MMX* = 23; HTT* = 28;
MaxDisks = 2;
HeapAdr = 100000H;
MaxMemTop = SHORT(80000000H);
DefaultDMASize = 20;
CONST
StrongChecks = FALSE;
Stats* = FALSE;
TimeCount = 0 ;
TraceOutput* = 0;
Memory* = 1;
Heaps* = 2;
Interrupts* = 3 ;
Modules* = 4;
Objects* = 5;
Processors* = 6;
KernelLog* = 7;
Preemption* = 31;
MaxLocks = 8;
LowestLock = 0; HighestLock = MaxLocks-1;
CONST
TraceVerbose = FALSE;
AddressSize = SYSTEM.SIZEOF(SYSTEM.ADDRESS);
SetSize = MAX (SET) + 1;
Ok* = 0;
K = 1024; M = 100000H;
PS = 4096;
PSlog2 = 12;
RS = 4*M;
PTEs = RS DIV PS;
ReservedPages = 8;
NilAdr* = -1;
NodeSP = 0;
NodeNext = AddressSize;
NodePrev = AddressSize*2;
MinSP = AddressSize*3; MaxSP = PS;
MapAreaAdr = SHORT(80000000H);
MapAreaSize = 64*M;
IntelAreaAdr = SHORT(0FEE00000H);
IntelAreaSize = 00001000H;
StackAreaAdr = MapAreaAdr+MapAreaSize;
StackAreaSize = IntelAreaAdr-StackAreaAdr;
KernelStackSize = 2*PS;
MaxUserStackSize = 128*K;
InitUserStackSize = PS;
UserStackGuardSize = PS;
MaxUserStacks = StackAreaSize DIV MaxUserStackSize;
LowAdr = PS;
LinkAdr = M;
StaticBlockSize = 32;
BlockHeaderSize = 2 * AddressSize;
RecordDescSize = 4 * AddressSize;
TSSOfs = 6;
StackOfs = TSSOfs + MaxCPU;
GDTSize = StackOfs + MaxCPU;
KernelCodeSel = 1*8;
KernelStackSel = 2*8;
UserCodeSel = 3*8 + 3;
DataSel = 4*8;
UserStackSel = 5*8 + 3;
KernelTR = TSSOfs*8;
PageNotPresent = 0;
KernelPage = 3;
UserPage = 7;
HeapMin = 50;
HeapMax = 95;
ExpandRate = 1;
Threshold = 10;
InitialHeapIncrement = 4096;
HeaderSize = 40H;
EndBlockOfs = 38H;
MemoryBlockOfs = BlockHeaderSize + RecordDescSize + BlockHeaderSize;
CONST
DE* = 0; DB* = 1; NMI* = 2; BP* = 3; OVF* = 4; BR* = 5; UD* = 6; NM* = 7;
DF* = 8; TS* = 10; NP* = 11; SSF* = 12; GP* = 13; PF* = 14; MF*= 16; AC*= 17; MC* = 18;
IRQ0* = 32;
IRQ2 = IRQ0 + 2;
IRQ7 = IRQ0 + 7;
IRQ8 = IRQ0 + 8;
IRQ15 = 47;
MaxIRQ* = IRQ15;
MPKC* = 49;
SoftInt* = 58;
MPIPCLocal* = 59;
MPTMR* = 60;
MPIPC* = 61;
MPERR* = 62;
MPSPU* = 63;
IDTSize = 64;
MaxNumHandlers = 16;
TraceSpurious = FALSE;
HandleSpurious = TRUE OR TraceSpurious;
IntA0 = 020H; IntA1 = 021H;
IntB0 = 0A0H; IntB1 = 0A1H;
IFBit* = 9; VMBit* = 17;
KernelLevel* = 0; UserLevel* = 3;
Second* = 1000;
CONST
Self* = 0; FrontBarrier* = 1; BackBarrier* = 2;
TraceApic = FALSE;
TraceProcessor = FALSE;
ClockRateDelay = 50;
TimerClock = 1193180;
CONST
TraceV24 = 2; TraceScreen = 0;
TraceWidth = 80; TraceHeight = 25;
TraceLen = TraceWidth * SYSTEM.SIZEOF (INTEGER);
TraceSize = TraceLen * TraceHeight;
TYPE
Vendor* = ARRAY 13 OF CHAR;
IDMap* = ARRAY 16 OF SHORTINT;
TYPE
Stack* = RECORD
low: SYSTEM.ADDRESS;
adr*: SYSTEM.ADDRESS;
high*: SYSTEM.ADDRESS;
END;
TSSDesc = RECORD
Link: LONGINT;
ESP0: LONGINT;
ESS0: LONGINT;
ESP1: LONGINT;
ESS1: LONGINT;
ESP2: LONGINT;
ESS2: LONGINT;
CR3: LONGINT;
EIP: LONGINT;
EFLAGS: SET;
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI: LONGINT;
ES, CS, SS, DS, FS, GS: LONGINT;
LDT: LONGINT;
TaskAttributes: INTEGER;
IOBitmapOffset: INTEGER
END;
Startup* = PROCEDURE;
SegDesc = RECORD
low, high: LONGINT
END;
GDT = ARRAY GDTSize OF SegDesc;
Range* = RECORD
adr*: SYSTEM.ADDRESS; size*: SYSTEM.SIZE;
END;
TYPE
State* = RECORD
EDI*, ESI*, ERR*, ESP0*, EBX*, EDX*, ECX*, EAX*: LONGINT;
INT*, BP*, PC*, CS*: LONGINT;
FLAGS*: SET;
SP*, SS*: LONGINT;
ES*, DS*, FS*, GS*: LONGINT;
END;
ExceptionState* = RECORD
halt*: SYSTEM.ADDRESS;
pf*: SYSTEM.ADDRESS;
locks*: SET;
SP*: SYSTEM.ADDRESS;
SS*, ES*, DS*, FS*, GS*: LONGINT;
CR*: ARRAY 5 OF LONGINT;
DR*: ARRAY 8 OF LONGINT;
FPU*: ARRAY 7 OF SET
END;
Handler* = PROCEDURE {DELEGATE} (VAR state: State);
HandlerRec = RECORD
valid: BOOLEAN;
handler: Handler
END;
GateDescriptor = RECORD
offsetBits0to15: INTEGER;
selector: INTEGER;
gateType: INTEGER;
offsetBits16to31: INTEGER
END;
IDT = ARRAY IDTSize OF GateDescriptor;
SSEState* = ARRAY (512+16) OF CHAR;
TYPE
MemoryBlock* = POINTER TO MemoryBlockDesc;
MemoryBlockDesc* = RECORD
next- {UNTRACED}: MemoryBlock;
startAdr-: SYSTEM.ADDRESS;
size-: SYSTEM.SIZE;
beginBlockAdr-, endBlockAdr-: SYSTEM.ADDRESS
END;
TYPE
EventHandler = PROCEDURE (id: LONGINT; CONST state: State);
Message* = POINTER TO RECORD END;
BroadcastHandler = PROCEDURE (id: LONGINT; CONST state: State; msg: Message);
TimeArray = ARRAY MaxCPU OF HUGEINT;
Address32* = LONGINT;
VAR
lowTop*: SYSTEM.ADDRESS;
memTop*: SYSTEM.ADDRESS;
dmaSize*: SYSTEM.SIZE;
configMP: SYSTEM.ADDRESS;
revMP: CHAR;
featureMP: ARRAY 5 OF CHAR;
version-: ARRAY 64 OF CHAR;
SSESupport-: BOOLEAN;
SSE2Support-: BOOLEAN;
SSE3Support-: BOOLEAN;
SSSE3Support-: BOOLEAN;
SSE41Support-: BOOLEAN;
SSE42Support-: BOOLEAN;
SSE5Support-: BOOLEAN;
AVXSupport-: BOOLEAN;
features-, features2-: SET;
fcr*: SET;
mhz*: HUGEINT;
chs: ARRAY MaxDisks OF RECORD cyls, hds, spt: LONGINT END;
initRegs: ARRAY 2 OF LONGINT;
config: ARRAY 2048 OF CHAR;
bootFlag: SYSTEM.ADDRESS;
idAdr: SYSTEM.ADDRESS;
map: IDMap;
bootID: LONGINT;
numberOfProcessors: LONGINT;
coresPerProcessor : LONGINT;
threadsPerCore : LONGINT;
CONST
CacheLineSize = 128;
TYPE
Lock = RECORD
locked : BOOLEAN;
filler : ARRAY CacheLineSize - 1 OF CHAR;
END;
VAR
lock: ARRAY MaxLocks OF Lock;
proc-, trapState-: ARRAY MaxCPU OF RECORD
locksHeld-: SET;
state-: SET;
preemptCount-: LONGINT;
padding : ARRAY CacheLineSize - 12 OF CHAR;
END;
padding : ARRAY 92 OF CHAR;
trapLocksBusy-: SET;
maxTime: LONGINT;
VAR
gdt: GDT;
procm: ARRAY MaxCPU OF RECORD
tss: TSSDesc;
sp: SYSTEM.ADDRESS;
stack: Stack
END;
kernelPD: SYSTEM.ADDRESS;
freeLowPage: SYSTEM.ADDRESS;
freeLowPages, freeHighPages, totalPages: LONGINT;
mapTop: SYSTEM.ADDRESS;
heapEndAdr: SYSTEM.ADDRESS;
topPageNum: LONGINT;
pageHeapAdr: SYSTEM.ADDRESS;
pageStackAdr: SYSTEM.ADDRESS;
freeStack: ARRAY (MaxUserStacks+SetSize-1) DIV SetSize OF SET;
freeStackIndex: LONGINT;
Nbigskips-: LONGINT;
Nfilled-: LONGINT;
NnewStacks-, NnewStackLoops-, NnewStackInnerLoops-, NdisposeStacks-,
NlostPages-, NreservePagesUsed-, NmaxUserStacks-: LONGINT;
VAR
idt: IDT;
glue: ARRAY IDTSize OF ARRAY 15 OF CHAR;
intHandler: ARRAY IDTSize, MaxNumHandlers OF HandlerRec;
stateTag: SYSTEM.ADDRESS;
default: HandlerRec;
ticks*: LONGINT;
VAR
ipcBusy, ipcFlags, ipcFrontBarrier, ipcBackBarrier: SET;
ipcHandler: BroadcastHandler;
ipcMessage: Message;
numProcessors-: LONGINT;
maxProcessors: LONGINT;
allProcessors-: SET;
localAPIC: SYSTEM.ADDRESS;
apicVer: ARRAY MaxCPU OF LONGINT;
started: ARRAY MaxCPU OF BOOLEAN;
busHz0, busHz1: ARRAY MaxCPU OF LONGINT;
timer: EventHandler;
timerRate: LONGINT;
stopped: BOOLEAN;
idMap: IDMap;
revIDmap: ARRAY MaxCPU OF SHORTINT;
time: TimeArray;
eventCount, eventMax: LONGINT;
event: Handler;
expandMin, heapMinKB, heapMaxKB : SYSTEM.SIZE;
gcThreshold-: SYSTEM.SIZE;
memBlockHead-{UNTRACED}, memBlockTail-{UNTRACED}: MemoryBlock;
traceProcessorProc*: EventHandler;
traceProcessor: BOOLEAN;
Timeslice*: Handler;
start*: PROCEDURE;
VAR
traceMode: SET;
traceBase: SYSTEM.ADDRESS;
tracePos: SYSTEM.SIZE;
tracePort: LONGINT;
traceColor: SHORTINT;
PROCEDURE ID* (): LONGINT;
CODE {SYSTEM.i386}
MOV EAX, idAdr
LEA EBX, map
MOV EAX, [EAX]
SHR EAX, 24
AND EAX, 15
MOV AL, [EBX+EAX]
END ID;
PROCEDURE -SpinHint*;
CODE {SYSTEM.i386}
XOR ECX, ECX ; just in case some processor interprets REP this way
REP NOP ; PAUSE instruction; NOP on pre-P4 processors, Spin Loop Hint on P4 and after
END SpinHint;
PROCEDURE -LessThan* (a, b: SYSTEM.ADDRESS): BOOLEAN;
CODE {SYSTEM.i386}
POP EBX
POP EAX
CMP EAX, EBX
SETB AL
END LessThan;
PROCEDURE -LessOrEqual* (a, b: SYSTEM.ADDRESS): BOOLEAN;
CODE {SYSTEM.i386}
POP EBX
POP EAX
CMP EAX, EBX
SETBE AL
END LessOrEqual;
PROCEDURE -GreaterThan* (a, b: SYSTEM.ADDRESS): BOOLEAN;
CODE {SYSTEM.i386}
POP EBX
POP EAX
CMP EAX, EBX
SETA AL
END GreaterThan;
PROCEDURE -GreaterOrEqual* (a, b: SYSTEM.ADDRESS): BOOLEAN;
CODE {SYSTEM.i386}
POP EBX
POP EAX
CMP EAX, EBX
SETAE AL
END GreaterOrEqual;
PROCEDURE Fill32* (destAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE; filler: LONGINT);
CODE {SYSTEM.i386}
MOV EDI, [EBP+destAdr]
MOV ECX, [EBP+size]
MOV EAX, [EBP+filler]
TEST ECX, 3
JZ ok
PUSH 8 ; ASSERT failure
INT 3
ok:
SHR ECX, 2
CLD
REP STOSD
END Fill32;
PROCEDURE -GetTimer* (): HUGEINT;
CODE {SYSTEM.Pentium}
RDTSC ; set EDX:EAX
END GetTimer;
PROCEDURE -DisableInterrupts* (): SET;
CODE {SYSTEM.i386}
PUSHFD
CLI
POP EAX
END DisableInterrupts;
PROCEDURE -RestoreInterrupts* (s: SET);
CODE {SYSTEM.i386}
POPFD
END RestoreInterrupts;
PROCEDURE -InterruptsEnabled* (): BOOLEAN;
CODE {SYSTEM.i386}
PUSHFD
POP EAX
SHR EAX, 9
AND AL, 1
END InterruptsEnabled;
PROCEDURE MulH* (h, g: HUGEINT): HUGEINT;
CODE {SYSTEM.i386}
MOV EDX, [EBP+12] ; y_hi
MOV ECX, [EBP+20] ; x_hi
OR EDX, ECX ; are x_hi and y_hi both zeros?
MOV EDX, [EBP+16] ; x_lo
MOV EAX, [EBP+8] ; y_lo
JNZ fullMul ; yes, requires full multiplication
MUL EDX ; EDX:EAX := y_lo * x_lo
JMP exit ; done, return to caller
fullMul: ; full multiplication is required
MUL ECX ; EAX := LO(y_lo*x_hi)
MOV EBX, EAX ; keep the result
MOV EAX, [EBP+12] ; y_hi
MUL DWORD [EBP+16] ; EAX := LO(y_hi*x_lo)
ADD EBX, EAX ; EBX := LO(y_lo*x_hi) + LO(y_hi*x_lo)
MOV EAX, [EBP+8] ; y_lo
MUL DWORD [EBP+16] ; EDX := HI(y_lo*x_lo), EAX := LO(y_lo*x_lo)
ADD EDX, EBX ; EDX := y_lo*x_hi + y_hi*x_lo + HI(y_lo*x_lo)
exit:
END MulH;
PROCEDURE DivH* (x, y: HUGEINT): HUGEINT;
CODE {SYSTEM.i386}
MOV ECX, [EBP+12] ; y-hi
MOV EBX, [EBP+8] ; y-lo
MOV EDX, [EBP+20] ; x-hi
MOV EAX, [EBP+16] ; x-lo
MOV ESI, ECX ; y-hi
XOR ESI, EDX ; y-hi ^ x-hi
SAR ESI, 31 ; (quotient < 0) ? -1 : 0
MOV EDI, EDX ; x-hi
SAR EDI, 31 ; (x < 0) ? -1 : 0
XOR EAX, EDI ; if (x < 0)
XOR EDX, EDI ; compute 1s complement of x
SUB EAX, EDI ; if (x < 0)
SBB EDX, EDI ; compute 2s complement of x
MOV EDI, ECX ; y-hi
SAR EDI, 31 ; (y < 0) ? -1 : 0
XOR EBX, EDI ; if (y < 0)
XOR ECX, EDI ; compute 1s complement of y
SUB EBX, EDI ; if (y < 0)
SBB ECX, EDI ; compute 2s complement of y
JNZ bigDivisor ; y > 2^32-1
CMP EDX, EBX ; only one division needed ? (ECX = 0)
JAE twoDivs ; need two divisions
DIV EBX ; EAX = quotient-lo
MOV EDX, ECX ; EDX = quotient-hi = 0
; quotient in EDX:EAX
XOR EAX, ESI ; if (quotient < 0)
XOR EDX, ESI ; compute 1s complement of result
SUB EAX, ESI ; if (quotient < 0)
SBB EDX, ESI ; compute 2s complement of result
JMP exit ; done, return to caller
twoDivs:
MOV ECX, EAX ; save x-lo in ECX
MOV EAX, EDX ; get x-hi
XOR EDX, EDX ; zero extend it into EDX:EAX
DIV EBX ; quotient-hi in EAX
XCHG EAX, ECX ; ECX = quotient-hi, EAX = x-lo
DIV EBX ; EAX = quotient-lo
MOV EDX, ECX ; EDX = quotient-hi
; quotient in EDX:EAX
JMP makeSign ; make quotient signed
bigDivisor:
SUB ESP, 12 ; create three local variables
MOV [ESP], EAX ; x-lo
MOV [ESP+4], EBX ; y-lo
MOV [ESP+8], EDX ; x-hi
MOV EDI, ECX ; save y-hi
SHR EDX, 1 ; shift both
RCR EAX, 1 ; y and
ROR EDI, 1 ; and x
RCR EBX, 1 ; right by 1 bit
BSR ECX, ECX ; ECX = number of remaining shifts
SHRD EBX, EDI, CL ; scale down y and
SHRD EAX, EDX, CL ; x such that y
SHR EDX, CL ; less than 2^32 (i.e. fits in EBX)
ROL EDI, 1 ; restore original y-hi
DIV EBX ; compute quotient
MOV EBX, [ESP] ; x-lo
MOV ECX, EAX ; save quotient
IMUL EDI, EAX ; quotient * y hi-word (low only)
MUL DWORD [ESP+4] ; quotient * y lo-word
ADD EDX, EDI ; EDX:EAX = quotient * y
SUB EBX, EAX ; x-lo - (quot.*y)-lo
MOV EAX, ECX ; get quotient
MOV ECX, [ESP+8] ; x-hi
SBB ECX, EDX ; subtract y * quot. from x
SBB EAX, 0 ; adjust quotient if remainder negative
XOR EDX, EDX ; clear hi-word of quotient
ADD ESP, 12 ; remove local variables
makeSign:
XOR EAX, ESI ; if (quotient < 0)
XOR EDX, ESI ; compute 1s complement of result
SUB EAX, ESI ; if (quotient < 0)
SBB EDX, ESI ; compute 2s complement of result
exit:
END DivH;
PROCEDURE -ASHH* (h: HUGEINT; n: LONGINT): HUGEINT;
CODE {SYSTEM.i386}
POP ECX
POP EAX
POP EDX
CMP ECX, 0
JL right
AND ECX, 63 ; limit count, like ASH
JZ exit
ll:
SHL EAX, 1
RCL EDX, 1
DEC ECX
JNZ ll
JMP exit
right:
NEG ECX
AND ECX, 63 ; limit count, like ASH
JZ exit
lr:
SAR EDX, 1
RCR EAX, 1
DEC ECX
JNZ lr
exit:
END ASHH;
PROCEDURE -LInt2ToHInt* (high, low: LONGINT): HUGEINT;
CODE {SYSTEM.i386}
POP EAX
POP EDX
END LInt2ToHInt;
PROCEDURE -HIntToLReal* (h: HUGEINT): LONGREAL;
CODE {SYSTEM.i386, SYSTEM.FPU}
FILD QWORD [ESP]
FWAIT
ADD ESP, 8
END HIntToLReal;
PROCEDURE -SetFCR (s: SET);
CODE {SYSTEM.i386, SYSTEM.FPU}
FLDCW [ESP] ; parameter s
POP EAX
END SetFCR;
PROCEDURE -FCR (): SET;
CODE {SYSTEM.i386, SYSTEM.FPU}
PUSH 0
FNSTCW [ESP]
FWAIT
POP EAX
END FCR;
PROCEDURE -InitFPU;
CODE {SYSTEM.i386, SYSTEM.FPU}
FNINIT
END InitFPU;
PROCEDURE SetupFPU*;
BEGIN
InitFPU; SetFCR(fcr)
END SetupFPU;
PROCEDURE -SetupFlags;
CODE {SYSTEM.i386}
PUSHFD
AND DWORD [ESP], 0FFF8802AH
OR DWORD [ESP], 3000H
POPFD
END SetupFlags;
PROCEDURE -Setup486Flags;
CODE {SYSTEM.i386, SYSTEM.Privileged}
MOV EAX, CR0
OR EAX, 00040020H
AND EAX, 0FFFEFFFFH
MOV CR0, EAX
END Setup486Flags;
PROCEDURE -Setup586Flags;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
MOV EAX, CR4
BTR EAX, 2 ; clear TSD
MOV CR4, EAX
END Setup586Flags;
PROCEDURE SetupSSE2Ext;
CONST
FXSRFlag = 24;
SSEFlag = 25;
SSE2Flag = 26;
SSE3Flag = 0;
SSSE3Flag =9;
SSE41Flag =19;
SSE42Flag =20;
SSE5Flag = 11;
AVXFlag = 28;
BEGIN
SSE2Support := FALSE;
SSE3Support := FALSE;
SSSE3Support := FALSE;
SSE41Support := FALSE;
SSE42Support := FALSE;
SSE5Support := FALSE;
AVXSupport := FALSE;
IF SSEFlag IN features THEN
SSESupport := TRUE;
IF SSE2Flag IN features THEN SSE2Support := TRUE;
(*PH 04/11*)
IF SSE3Flag IN features2 THEN SSE3Support := TRUE;
IF SSSE3Flag IN features2 THEN SSSE3Support := TRUE END;
IF SSE41Flag IN features2 THEN SSE41Support := TRUE;
IF SSE42Flag IN features2 THEN SSE42Support := TRUE END;
END;
IF SSE5Flag IN features2 THEN SSE5Support := TRUE END;
IF AVXFlag IN features2 THEN AVXSupport := TRUE END;
END;
END;
IF FXSRFlag IN features THEN InitSSE END;
END;
END SetupSSE2Ext;
PROCEDURE -InitSSE;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
MOV EAX, CR4
OR EAX, 00000200H ; set bit 9 (OSFXSR)
AND EAX, 0FFFFFBFFH ; delete bit 10 (OSXMMEXCPT)
MOV CR4, EAX
END InitSSE;
PROCEDURE -DisableMathTaskEx;
CODE {SYSTEM.i386, SYSTEM.Privileged}
MOV EAX,CR0
AND AL, 0F5H
MOV CR0, EAX;
END DisableMathTaskEx;
PROCEDURE -DisableEmulation;
CODE {SYSTEM.i386, SYSTEM.Privileged}
MOV EAX, CR0
AND AL, 0FBH
MOV CR0, EAX
END DisableEmulation;
PROCEDURE CPUID*(function : LONGINT; VAR eax, ebx, ecx, edx : SET);
CODE {SYSTEM.i386, SYSTEM.Pentium}
MOV EAX, [EBP+function] ; CPUID function parameter
MOV ESI, [EBP+ecx] ; copy ecx into ECX (sometimes used as input parameter)
MOV ECX, [ESI]
CPUID ; execute CPUID
MOV ESI, [EBP+eax] ; copy EAX into eax;
MOV [ESI], EAX
MOV ESI, [EBP+ebx] ; copy EBX into ebx
MOV [ESI], EBX
MOV ESI, [EBP+ecx] ; copy ECX into ecx
MOV [ESI], ECX
MOV ESI, [EBP+edx] ; copy EDX into edx
MOV [ESI], EDX
END CPUID;
PROCEDURE CpuIdSupported*() : BOOLEAN;
CODE {SYSTEM.i386}
PUSHFD ; save EFLAGS
POP EAX ; store EFLAGS in EAX
MOV EBX, EAX ; save EBX for later testing
XOR EAX, 00200000H ; toggle bit 21
PUSH EAX ; push to stack
POPFD ; save changed EAX to EFLAGS
PUSHFD ; push EFLAGS to TOS
POP EAX ; store EFLAGS in EAX
CMP EAX, EBX ; see if bit 21 has changed
SETNE AL; ; return TRUE if bit 21 has changed, FALSE otherwise
END CpuIdSupported;
PROCEDURE InitProcessor*;
BEGIN
SetupFlags;
Setup486Flags;
Setup586Flags;
DisableMathTaskEx;
DisableEmulation;
SetupFPU;
SetupSSE2Ext
END InitProcessor;
PROCEDURE InitAPICIDAdr* (adr: SYSTEM.ADDRESS; CONST m: IDMap);
VAR s: SET;
BEGIN
s := DisableInterrupts ();
idAdr := adr; map := m;
RestoreInterrupts (s)
END InitAPICIDAdr;
PROCEDURE InitBoot;
VAR
largestFunction, i: LONGINT;
eax, ebx, ecx, edx : SET;
logicalProcessorCount : LONGINT;
u: ARRAY 8 OF CHAR; vendor : Vendor;
PROCEDURE GetString(VAR string : ARRAY OF CHAR; offset : LONGINT; register : SET);
BEGIN
string[offset] :=CHR(SYSTEM.VAL(LONGINT, register * {0..7}));
string[offset+1] := CHR(SYSTEM.VAL(LONGINT, SYSTEM.LSH(register * {8..15}, -8)));
string[offset+2] := CHR(SYSTEM.VAL(LONGINT, SYSTEM.LSH(register * {16..23}, -16)));
string[offset+3] := CHR(SYSTEM.VAL(LONGINT, SYSTEM.LSH(register * {24..31}, -24)));
END GetString;
BEGIN
vendor := "Unknown"; features := {}; features2 := {};
coresPerProcessor := 1; threadsPerCore := 1;
IF CpuIdSupported() THEN
CPUID(0, eax, ebx, ecx, edx);
largestFunction := SYSTEM.VAL(LONGINT, eax);
ASSERT(LEN(vendor) >= 13);
GetString(vendor, 0, ebx); GetString(vendor, 4, edx); GetString(vendor, 8, ecx); vendor[12] := 0X;
IF (largestFunction >= 1) THEN
CPUID(1, eax, ebx, ecx, edx);
features := SYSTEM.VAL(SET, edx);
features2 := SYSTEM.VAL(SET, ecx);
IF (HTT IN features) THEN
logicalProcessorCount := SYSTEM.VAL(LONGINT, SYSTEM.LSH(ebx * {16..23}, -16));
IF (vendor = "GenuineIntel") THEN
IF (largestFunction >= 4) THEN
ecx := SYSTEM.VAL(SET, 0);
CPUID(4, eax, ebx, ecx, edx);
coresPerProcessor := SYSTEM.VAL(LONGINT, SYSTEM.LSH(eax * {26..31}, -26)) + 1;
threadsPerCore := logicalProcessorCount DIV coresPerProcessor;
ELSE
threadsPerCore := logicalProcessorCount;
END;
ELSIF (vendor = "AuthenticAMD") THEN
CPUID(SHORT(80000000H), eax, ebx, ecx, edx);
largestFunction := SYSTEM.VAL(LONGINT, eax - {31});
IF (largestFunction >= 8) THEN
CPUID(SHORT(80000008H), eax, ebx, ecx, edx);
coresPerProcessor := SYSTEM.VAL(LONGINT, ecx * {0..7}) + 1;
threadsPerCore := logicalProcessorCount DIV coresPerProcessor;
ELSIF (largestFunction >= 1) THEN
CPUID(SHORT(80000001H), eax, ebx, ecx, edx);
IF 1 IN ecx THEN
coresPerProcessor := logicalProcessorCount;
threadsPerCore := 1;
END;
ELSE
END;
ELSE
Trace.String("Machine: "); Trace.Yellow; Trace.String("Warning: Cannot detect hyperthreading, unknown CPU vendor ");
Trace.String(vendor); Trace.Ln; Trace.Default;
END;
END;
END;
END;
Trace.String("Machine: "); Trace.Int(coresPerProcessor, 0); Trace.String(" cores per physical package, ");
Trace.Int(threadsPerCore, 0); Trace.String(" threads per core.");
Trace.Ln;
InitFPU;
fcr := (FCR () - {0, 2, 3, 10, 11}) + {0 .. 5, 8, 9};
bootID := 0; map[0] := 0;
idAdr := SYSTEM.ADR (bootID);
GetConfig ("MHz", u);
i := 0; mhz := StrToInt (i, u);
END InitBoot;
PROCEDURE GetConfig* (CONST name: ARRAY OF CHAR; VAR val: ARRAY OF CHAR);
VAR i, src: LONGINT; ch: CHAR;
BEGIN
ASSERT (name[0] # "=");
src := 0;
LOOP
ch := config[src];
IF ch = 0X THEN EXIT END;
i := 0;
LOOP
ch := config[src];
IF (ch # name[i]) OR (name[i] = 0X) THEN EXIT END;
INC (i); INC (src)
END;
IF (ch = 0X) & (name[i] = 0X) THEN
i := 0;
REPEAT
INC (src); ch := config[src]; val[i] := ch; INC (i);
IF i = LEN(val) THEN val[i - 1] := 0X; RETURN END
UNTIL ch = 0X;
val[i] := 0X; RETURN
ELSE
WHILE ch # 0X DO
INC (src); ch := config[src]
END;
INC (src);
REPEAT
ch := config[src]; INC (src)
UNTIL ch = 0X
END
END;
val[0] := 0X
END GetConfig;
PROCEDURE GetDiskCHS* (d: LONGINT; VAR cyls, hds, spt: LONGINT);
BEGIN
cyls := chs[d].cyls; hds := chs[d].hds; spt := chs[d].spt
END GetDiskCHS;
PROCEDURE GetInit* (n: LONGINT; VAR val: LONGINT);
BEGIN
val := initRegs[n]
END GetInit;
PROCEDURE StrToInt* (VAR i: LONGINT; CONST s: ARRAY OF CHAR): LONGINT;
VAR vd, vh, sgn, d: LONGINT; hex: BOOLEAN;
BEGIN
vd := 0; vh := 0; hex := FALSE;
IF s[i] = "-" THEN sgn := -1; INC (i) ELSE sgn := 1 END;
LOOP
IF (s[i] >= "0") & (s[i] <= "9") THEN d := ORD (s[i])-ORD ("0")
ELSIF (CAP (s[i]) >= "A") & (CAP (s[i]) <= "F") THEN d := ORD (CAP (s[i]))-ORD ("A") + 10; hex := TRUE
ELSE EXIT
END;
vd := 10*vd + d; vh := 16*vh + d;
INC (i)
END;
IF CAP (s[i]) = "H" THEN hex := TRUE; INC (i) END;
IF hex THEN vd := vh END;
RETURN sgn * vd
END StrToInt;
PROCEDURE -Wait*;
CODE {SYSTEM.i386}
JMP 0
JMP 0
JMP 0
END Wait;
PROCEDURE Reboot;
CODE {SYSTEM.i386, SYSTEM.Privileged}
PUSH 0
PUSH 0
LIDT [ESP]
INT 3
END Reboot;
PROCEDURE -Cli*;
CODE{SYSTEM.i386}
CLI
END Cli;
PROCEDURE -Sti*;
CODE{SYSTEM.i386}
STI
END Sti;
PROCEDURE Shutdown* (reboot: BOOLEAN);
VAR i: LONGINT;
BEGIN
Cli;
IF reboot THEN
Portout8 (70H, 8FX);
Wait; Portout8 (71H, 0X);
Wait; Portout8 (70H, 0DX);
Wait; Portout8 (64H, 0FEX);
FOR i := 1 TO 10000 DO END;
Reboot
END;
LOOP END
END Shutdown;
PROCEDURE GetPar (p: SYSTEM.ADDRESS; ofs: LONGINT): LONGINT;
VAR ch: CHAR;
BEGIN
SYSTEM.GET (p + 12 + ofs, ch);
RETURN ORD (ch)
END GetPar;
PROCEDURE ReadBootTable (bt: SYSTEM.ADDRESS);
VAR i, p: SYSTEM.ADDRESS; j, d, type, addr, size, heapSize: LONGINT; ch: CHAR;
BEGIN
heapSize := 0; lowTop := 0;
p := bt; d := 0;
LOOP
SYSTEM.GET (p, type);
IF type = -1 THEN
EXIT
ELSIF type = 3 THEN
SYSTEM.GET (p + 8, addr); SYSTEM.GET (p + 12, size);
lowTop := addr + size
ELSIF type = 4 THEN
SYSTEM.GET (p + 8, addr); SYSTEM.GET (p + 12, size);
IF addr = HeapAdr THEN heapSize := size END
ELSIF type = 5 THEN
IF d < MaxDisks THEN
chs[d].cyls := GetPar (p, 0) + 100H * GetPar (p, 1);
chs[d].hds := GetPar (p, 2); chs[d].spt := GetPar (p, 14);
INC (d)
END
ELSIF type = 8 THEN
i := p + 8; j := 0;
LOOP
SYSTEM.GET (i, ch); config[j] := ch; INC (i); INC (j);
IF ch = 0X THEN EXIT END;
REPEAT SYSTEM.GET (i, ch); config[j] := ch; INC (i); INC (j) UNTIL ch = 0X;
REPEAT SYSTEM.GET (i, ch); config[j] := ch; INC (i); INC (j) UNTIL ch = 0X
END
END;
SYSTEM.GET (p + 4, size); INC (p, size)
END;
ASSERT((heapSize # 0) & (lowTop # 0));
memTop := HeapAdr + heapSize
END ReadBootTable;
PROCEDURE GetNVByte* (ofs: LONGINT): CHAR;
VAR c: CHAR;
BEGIN
Portout8 (70H, CHR(ofs)); Wait; Portin8(71H, c);
RETURN c
END GetNVByte;
PROCEDURE PutNVByte* (ofs: LONGINT; val: CHAR);
BEGIN
Portout8 (70H, CHR(ofs)); Wait; Portout8 (71H, val)
END PutNVByte;
PROCEDURE ChecksumMP* (adr: SYSTEM.ADDRESS; size: SYSTEM.SIZE): LONGINT;
VAR sum: LONGINT; x: SYSTEM.ADDRESS; ch: CHAR;
BEGIN
sum := 0;
FOR x := adr TO adr + size-1 DO
SYSTEM.GET (x, ch);
sum := (sum + ORD(ch)) MOD 256
END;
RETURN sum
END ChecksumMP;
PROCEDURE SearchMem (adr: SYSTEM.ADDRESS; size: SYSTEM.SIZE): SYSTEM.ADDRESS;
VAR x, len: LONGINT; ch: CHAR;
BEGIN
WHILE size > 0 DO
SYSTEM.GET (adr, x);
IF x = 05F504D5FH THEN
SYSTEM.GET (adr + 8, ch); len := ORD(ch)*16;
IF len > 0 THEN
SYSTEM.GET (adr + 9, ch);
IF (ch = 1X) OR (ch >= 4X) THEN
IF ChecksumMP(adr, len) = 0 THEN
RETURN adr
END
END
END
END;
INC (adr, 16); DEC (size, 16)
END;
RETURN NilAdr
END SearchMem;
PROCEDURE SearchMP;
VAR adr: SYSTEM.ADDRESS;
BEGIN
adr := 0;
SYSTEM.GET (040EH, SYSTEM.VAL (INTEGER, adr));
adr := adr*16;
IF adr < 100000H THEN adr := SearchMem(adr, 1024)
ELSE adr := NilAdr
END;
IF adr = NilAdr THEN
adr := SearchMem(lowTop + (-lowTop) MOD 10000H - 1024, 1024);
IF adr = NilAdr THEN
adr := SearchMem(memTop - 1024, 1024);
IF adr = NilAdr THEN
adr := SearchMem(0E0000H, 20000H)
END
END
END;
IF adr = NilAdr THEN
revMP := 0X; configMP := NilAdr
ELSE
SYSTEM.GET (adr + 9, revMP);
SYSTEM.MOVE(adr + 11, SYSTEM.ADR(featureMP[0]), 5);
configMP := SYSTEM.GET32 (adr + 4);
IF configMP = 0 THEN configMP := NilAdr END
END
END SearchMP;
PROCEDURE AllocateDMA;
VAR old: SYSTEM.ADDRESS;
BEGIN
old := lowTop;
dmaSize := DefaultDMASize*1024;
ASSERT((dmaSize >= 0) & (dmaSize <= 65536));
IF (lowTop-dmaSize) DIV 65536 # (lowTop-1) DIV 65536 THEN
DEC (lowTop, lowTop MOD 65536)
END;
DEC (lowTop, dmaSize);
dmaSize := old - lowTop
END AllocateDMA;
PROCEDURE IsRAM(adr: SYSTEM.ADDRESS): BOOLEAN;
CONST Pattern1 = SHORT(0BEEFC0DEH); Pattern2 = SHORT(0AA55FF00H);
VAR save, x: LONGINT; ok: BOOLEAN;
BEGIN
ok := FALSE;
SYSTEM.GET (adr, save);
SYSTEM.PUT (adr, Pattern1);
x := Pattern2;
SYSTEM.GET (adr, x);
IF x = Pattern1 THEN
SYSTEM.PUT (adr, Pattern2);
x := Pattern1;
SYSTEM.GET (adr, x);
ok := (x = Pattern2)
END;
SYSTEM.PUT (adr, save);
RETURN ok
END IsRAM;
PROCEDURE CheckMemory;
CONST M = 100000H; ExtMemAdr = M; Step = M;
VAR s: ARRAY 16 OF CHAR; adr: SYSTEM.ADDRESS; i: LONGINT;
BEGIN
GetConfig("ExtMemSize", s);
IF s[0] # 0X THEN
i := 0; memTop := ExtMemAdr + StrToInt(i, s) * M;
Trace.String("Machine: Memory: ");
ELSE
Trace.String("Machine: Detecting memory... ");
IF memTop >= 15*M THEN
adr := memTop-4;
WHILE (SYSTEM.LSH(memTop, -12) < SYSTEM.LSH(MaxMemTop, -12)) & IsRAM(adr) DO
memTop := adr + 4;
INC (adr, Step)
END;
IF (memTop <= 0) THEN memTop := 2047 * M ; END;
END
END;
Trace.Green; Trace.IntSuffix(memTop, 0, "B"); Trace.Ln; Trace.Default;
END CheckMemory;
PROCEDURE InitLocks;
VAR i: LONGINT; s: ARRAY 12 OF CHAR;
BEGIN
IF TimeCount # 0 THEN
GetConfig("LockTimeout", s);
i := 0; maxTime := StrToInt(i, s);
IF maxTime > MAX(LONGINT) DIV 1000000 THEN
maxTime := MAX(LONGINT)
ELSE
maxTime := maxTime * 1000000
END
END;
FOR i := 0 TO MaxCPU-1 DO
proc[i].locksHeld := {}; proc[i].preemptCount := 0
END;
FOR i := 0 TO MaxLocks-1 DO
lock[i].locked := FALSE
END
END InitLocks;
PROCEDURE -GetFlags (): SET;
CODE {SYSTEM.i386}
PUSHFD
POP EAX
END GetFlags;
PROCEDURE -SetFlags (s: SET);
CODE {SYSTEM.i386}
POPFD
END SetFlags;
PROCEDURE -PushFlags*;
CODE {SYSTEM.i386}
PUSHFD
END PushFlags;
PROCEDURE -PopFlags*;
CODE {SYSTEM.i386}
POPFD
END PopFlags;
PROCEDURE AcquirePreemption* (): LONGINT;
VAR id: LONGINT;
BEGIN
PushFlags; Cli;
id := ID ();
INC (proc[id].preemptCount);
PopFlags;
RETURN id
END AcquirePreemption;
PROCEDURE ReleasePreemption*;
VAR id: LONGINT;
BEGIN
PushFlags; Cli;
id := ID ();
IF StrongChecks THEN
ASSERT(proc[id].preemptCount > 0)
END;
DEC (proc[id].preemptCount);
PopFlags
END ReleasePreemption;
PROCEDURE PreemptCount* (id: LONGINT): LONGINT;
BEGIN
IF StrongChecks THEN
ASSERT(id = ID ())
END;
RETURN proc[id].preemptCount
END PreemptCount;
PROCEDURE AcquireSpinTimeout(VAR locked: BOOLEAN; count: LONGINT; flags: SET): CHAR;
CODE {SYSTEM.i386}
MOV ESI, [EBP+flags] ; ESI := flags
MOV EDI, [EBP+count] ; EDI := count
MOV EBX, [EBP+locked] ; EBX := ADR(locked)
MOV AL, 1 ; AL := 1
CLI ; switch interrupts off before acquiring lock
test:
CMP [EBX], AL ; locked? { AL = 1 }
JE wait ; yes, go wait
XCHG [EBX], AL ; set and read the lock atomically. LOCK prefix implicit.
CMP AL, 1 ; was locked?
JNE exit ; no, we have it now, interrupts are off, and AL # 1
wait:
; ASSERT(AL = 1)
XOR ECX, ECX ; just in case some processor interprets REP this way
REP NOP ; PAUSE instruction; see SpinHint
TEST ESI, 200H ; bit 9 - IF
JZ intoff
STI ; restore interrupt state quickly to allow pending interrupts (e.g. Processors.StopAll/Broadcast)
NOP ; NOP required, otherwise STI; CLI not interruptable
CLI ; disable interrupts
intoff:
DEC EDI ; counter
JNZ test ; not timed out yet
OR EDI, [EBP+count] ; re-fetch original value & set flags
JZ test ; if count = 0, retry forever
; timed out (AL = 1)
exit:
END AcquireSpinTimeout;
PROCEDURE Acquire* (level: LONGINT);
VAR id, i: LONGINT; flags: SET; start: HUGEINT;
BEGIN
id := AcquirePreemption ();
flags := GetFlags ();
IF StrongChecks THEN
ASSERT(~(9 IN flags) OR (proc[id].locksHeld = {}));
ASSERT(~(level IN proc[id].locksHeld))
END;
IF (TimeCount = 0) OR (maxTime = 0) THEN
IF AcquireSpinTimeout(lock[level].locked, 0, flags) = 0X THEN END;
ELSE
start := GetTimer ();
WHILE AcquireSpinTimeout(lock[level].locked, TimeCount, flags) = 1X DO
IF GetTimer () - start > maxTime THEN
trapState := proc;
trapLocksBusy := {};
FOR i := 0 TO MaxLocks-1 DO
IF lock[i].locked THEN INCL(trapLocksBusy, i) END
END;
HALT(1301)
END
END
END;
IF proc[id].locksHeld = {} THEN
proc[id].state := flags
END;
INCL(proc[id].locksHeld, level);
IF StrongChecks THEN
ASSERT((level = 0) OR (proc[id].locksHeld * {0..level-1} = {}))
END
END Acquire;
PROCEDURE Release* (level: LONGINT);
VAR id: LONGINT; flags: SET;
BEGIN
id := ID ();
IF StrongChecks THEN
ASSERT(~(9 IN GetFlags ()));
ASSERT(lock[level].locked);
ASSERT(level IN proc[id].locksHeld)
END;
EXCL(proc[id].locksHeld, level);
IF proc[id].locksHeld = {} THEN
flags := proc[id].state ELSE flags := GetFlags ()
END;
lock[level].locked := FALSE;
SetFlags(flags);
ReleasePreemption
END Release;
PROCEDURE AcquireAll*;
VAR lock: LONGINT;
BEGIN
FOR lock := HighestLock TO LowestLock BY -1 DO Acquire(lock) END
END AcquireAll;
PROCEDURE ReleaseAll*;
VAR lock: LONGINT;
BEGIN
FOR lock := LowestLock TO HighestLock DO Release(lock) END
END ReleaseAll;
PROCEDURE BreakAll* (): SET;
VAR id, level: LONGINT; released: SET;
BEGIN
id := AcquirePreemption ();
PushFlags; Cli;
released := {};
FOR level := 0 TO MaxLocks-1 DO
IF level IN proc[id].locksHeld THEN
lock[level].locked := FALSE;
EXCL(proc[id].locksHeld, level);
INCL(released, level)
END
END;
IF proc[id].preemptCount > 1 THEN INCL(released, Preemption) END;
proc[id].preemptCount := 0;
PopFlags;
RETURN released
END BreakAll;
PROCEDURE AcquireObject* (VAR locked: BOOLEAN);
CODE {SYSTEM.i386}
PUSHFD
MOV EBX, [EBP+locked] ; EBX := ADR(locked)
MOV AL, 1
test:
CMP [EBX], AL ; locked? { AL = 1 }
JNE try
XOR ECX, ECX ; just in case some processor interprets REP this way
STI
REP NOP ; PAUSE instruction; see SpinHint
CLI
JMP test
try:
XCHG [EBX], AL ; set and read the lock atomically. LOCK prefix implicit.
CMP AL, 1 ; was locked?
JE test ; yes, try again
POPFD
END AcquireObject;
PROCEDURE ReleaseObject* (VAR locked: BOOLEAN);
CODE {SYSTEM.i386}
MOV EBX, [EBP+locked] ; EBX := ADR(locked)
MOV BYTE [EBX], 0
END ReleaseObject;
PROCEDURE LoadGDT(base: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
CODE {SYSTEM.i386, SYSTEM.Privileged}
SHL DWORD [EBP+size], 16
MOV EBX, 2
LGDT [EBP+EBX+size]
END LoadGDT;
PROCEDURE LoadSegRegs(data: LONGINT);
CODE {SYSTEM.i386}
MOV EAX, [EBP+data]
MOV DS, AX
MOV ES, AX
XOR EAX, EAX
MOV FS, AX
MOV GS, AX
END LoadSegRegs;
PROCEDURE -CS* (): LONGINT;
CODE {SYSTEM.i386}
XOR EAX, EAX
MOV AX, CS
END CS;
PROCEDURE NewLowPage(VAR adr: SYSTEM.ADDRESS);
BEGIN
adr := freeLowPage;
IF freeLowPage # NilAdr THEN
SYSTEM.GET (freeLowPage, freeLowPage);
DEC(freeLowPages)
END
END NewLowPage;
PROCEDURE NewDirectPage(VAR adr: SYSTEM.ADDRESS);
BEGIN
IF pageHeapAdr # heapEndAdr THEN
DEC(pageHeapAdr, PS); adr := pageHeapAdr;
DEC(freeHighPages)
ELSE
adr := NilAdr
END
END NewDirectPage;
PROCEDURE NewPage(VAR physAdr: SYSTEM.ADDRESS);
VAR sp, prev: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET(pageStackAdr + NodeSP, sp);
ASSERT((sp >= MinSP) & (sp <= MaxSP) & (sp MOD AddressSize = 0));
IF sp > MinSP THEN
DEC(sp, AddressSize);
SYSTEM.GET (pageStackAdr+sp, physAdr);
SYSTEM.PUT (pageStackAdr+NodeSP, sp);
SYSTEM.GET (pageStackAdr+NodePrev, prev);
IF (sp = MinSP) & (prev # NilAdr) THEN
pageStackAdr := prev
END;
DEC(freeHighPages)
ELSE
NewDirectPage(physAdr)
END
END NewPage;
PROCEDURE DisposePage(physAdr: SYSTEM.ADDRESS);
VAR sp, next, newAdr: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET (pageStackAdr + NodeSP, sp);
ASSERT((sp >= MinSP) & (sp <= MaxSP) & (sp MOD AddressSize = 0));
IF sp = MaxSP THEN
SYSTEM.GET (pageStackAdr + NodeNext, next);
IF next # NilAdr THEN
pageStackAdr := next;
SYSTEM.GET (pageStackAdr+NodeSP, sp);
ASSERT(sp = MinSP)
ELSE
NewDirectPage(newAdr);
IF newAdr = NilAdr THEN
NewLowPage(newAdr);
IF newAdr = NilAdr THEN
IF Stats THEN INC(NlostPages) END;
RETURN
ELSE
IF Stats THEN INC(NreservePagesUsed) END
END
END;
sp := MinSP;
SYSTEM.PUT (newAdr + NodeNext, next);
SYSTEM.PUT (newAdr + NodePrev, pageStackAdr);
pageStackAdr := newAdr
END
END;
SYSTEM.PUT (pageStackAdr + sp, physAdr);
SYSTEM.PUT (pageStackAdr + NodeSP, sp + AddressSize);
INC(freeHighPages)
END DisposePage;
PROCEDURE NewVirtual(VAR virtAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
BEGIN
ASSERT(size MOD PS = 0);
IF mapTop+size > MapAreaAdr+MapAreaSize THEN
virtAdr := NilAdr
ELSE
virtAdr := mapTop;
INC(mapTop, size)
END
END NewVirtual;
PROCEDURE DisposeVirtual(virtAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
END DisposeVirtual;
PROCEDURE MapPage(virtAdr, phys: SYSTEM.ADDRESS): BOOLEAN;
VAR i, pt: SYSTEM.ADDRESS;
BEGIN
i := virtAdr DIV RS MOD PTEs;
SYSTEM.GET (kernelPD + AddressSize*i, pt);
IF ODD(pt) THEN
DEC(pt, pt MOD PS)
ELSE
NewPage(pt);
IF pt = NilAdr THEN RETURN FALSE END;
SYSTEM.PUT (kernelPD + AddressSize*i, pt + UserPage);
Fill32(pt, PTEs*AddressSize, PageNotPresent)
END;
SYSTEM.PUT (pt + AddressSize*(virtAdr DIV PS MOD PTEs), phys);
RETURN TRUE
END MapPage;
PROCEDURE MappedPage(virtAdr: SYSTEM.ADDRESS): SYSTEM.ADDRESS;
VAR pt: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET (kernelPD + AddressSize*(virtAdr DIV RS MOD PTEs), pt);
IF ODD(pt) THEN
SYSTEM.GET (pt - pt MOD PS + AddressSize*(virtAdr DIV PS MOD PTEs), pt);
RETURN pt
ELSE
RETURN 0
END
END MappedPage;
PROCEDURE UnmapPage(virtAdr: SYSTEM.ADDRESS): SYSTEM.ADDRESS;
VAR t, pt: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET (kernelPD + AddressSize*(virtAdr DIV RS MOD PTEs), pt);
IF ODD(pt) THEN
pt := pt - pt MOD PS + AddressSize*(virtAdr DIV PS MOD PTEs);
SYSTEM.GET (pt, t);
SYSTEM.PUT (pt, NIL);
RETURN t
ELSE
RETURN 0
END
END UnmapPage;
PROCEDURE MapDirect(virtAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE; phys: SYSTEM.ADDRESS): BOOLEAN;
BEGIN
ASSERT((virtAdr MOD PS = 0) & (size MOD PS = 0));
WHILE size # 0 DO
IF ~ODD(MappedPage(virtAdr)) THEN
IF ~MapPage(virtAdr, phys) THEN RETURN FALSE END
END;
INC(virtAdr, PS); INC(phys, PS); DEC(size, PS)
END;
RETURN TRUE
END MapDirect;
PROCEDURE ExpandNow(try: LONGINT): BOOLEAN;
VAR size: SYSTEM.SIZE;
BEGIN
size := SYSTEM.LSH(memBlockTail.endBlockAdr - memBlockHead.beginBlockAdr, -10);
RETURN (~ODD(try) OR (size < heapMinKB)) & (size < heapMaxKB)
END ExpandNow;
PROCEDURE ExpandHeap*(try: LONGINT; size: SYSTEM.SIZE; VAR memBlock: MemoryBlock; VAR beginBlockAdr, endBlockAdr: SYSTEM.ADDRESS);
BEGIN
IF ExpandNow(try) THEN
IF size < expandMin THEN size := expandMin END;
beginBlockAdr := memBlockHead.endBlockAdr;
endBlockAdr := beginBlockAdr;
INC(endBlockAdr, size);
SetHeapEndAdr(endBlockAdr);
memBlock := memBlockHead;
ELSE
beginBlockAdr := memBlockHead.endBlockAdr;
endBlockAdr := memBlockHead.endBlockAdr;
memBlock := NIL
END
END ExpandHeap;
PROCEDURE SetMemoryBlockEndAddress*(memBlock: MemoryBlock; endBlockAdr: SYSTEM.ADDRESS);
BEGIN
ASSERT(endBlockAdr >= memBlock.beginBlockAdr);
memBlock.endBlockAdr := endBlockAdr
END SetMemoryBlockEndAddress;
PROCEDURE FreeMemBlock*(memBlock: MemoryBlock);
BEGIN
HALT(515)
END FreeMemBlock;
PROCEDURE SetHeapEndAdr(VAR endAdr: SYSTEM.ADDRESS);
VAR n, m: SYSTEM.SIZE;
BEGIN
Acquire(Memory);
n := SYSTEM.LSH(endAdr+(PS-1), -PSlog2) - SYSTEM.LSH(heapEndAdr, -PSlog2);
m := SYSTEM.LSH(pageHeapAdr, -PSlog2) - SYSTEM.LSH(heapEndAdr, -PSlog2) - ReservedPages;
IF n > m THEN n := m END;
IF n > 0 THEN INC(heapEndAdr, n*PS); DEC(freeHighPages, n) END;
endAdr := heapEndAdr;
Release(Memory)
END SetHeapEndAdr;
PROCEDURE MapPhysical*(physAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE; VAR virtAdr: SYSTEM.ADDRESS);
VAR ofs: SYSTEM.ADDRESS;
BEGIN
IF (SYSTEM.LSH(physAdr, -PSlog2) <= topPageNum) &
(SYSTEM.LSH(physAdr+size-1, -PSlog2) <= topPageNum) &
(SYSTEM.LSH(physAdr, -PSlog2) >= SYSTEM.LSH(LowAdr, -PSlog2)) THEN
virtAdr := physAdr
ELSE
ofs := physAdr MOD PS;
DEC(physAdr, ofs); INC(size, ofs);
INC(size, (-size) MOD PS);
Acquire(Memory);
NewVirtual(virtAdr, size);
IF virtAdr # NilAdr THEN
IF ~MapDirect(virtAdr, size, physAdr + UserPage) THEN
DisposeVirtual(virtAdr, size);
virtAdr := NilAdr
END
END;
Release(Memory);
IF TraceVerbose THEN
Acquire (TraceOutput);
Trace.String("Mapping ");
Trace.IntSuffix(size, 1, "B"); Trace.String(" at ");
Trace.Address (physAdr); Trace.String (" - "); Trace.Address (physAdr+size-1);
IF virtAdr = NilAdr THEN
Trace.String(" failed")
ELSE
Trace.String (" to "); Trace.Address (virtAdr);
IF ofs # 0 THEN Trace.String (", offset "); Trace.Int(ofs, 0) END
END;
Trace.Ln;
Release (TraceOutput);
END;
IF virtAdr # NilAdr THEN INC(virtAdr, ofs) END
END
END MapPhysical;
PROCEDURE UnmapPhysical*(virtAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
END UnmapPhysical;
PROCEDURE PhysicalAdr*(adr: SYSTEM.ADDRESS; size: SYSTEM.SIZE): SYSTEM.ADDRESS;
VAR physAdr, mapped, expected: SYSTEM.ADDRESS;
BEGIN
IF (SYSTEM.LSH(adr, -PSlog2) <= topPageNum) & (SYSTEM.LSH(adr+size-1, -PSlog2) <= topPageNum) THEN
RETURN adr
ELSE
Acquire(Memory);
mapped := MappedPage(adr);
Release(Memory);
IF ODD(mapped) & (size > 0) THEN
physAdr := mapped - mapped MOD PS + adr MOD PS;
DEC(size, PS - adr MOD PS);
IF size > 0 THEN
expected := SYSTEM.LSH(mapped, -PSlog2)+1;
LOOP
INC(adr, PS);
Acquire(Memory);
mapped := MappedPage(adr);
Release(Memory);
IF ~ODD(mapped) OR (SYSTEM.LSH(mapped, -PSlog2) # expected) THEN
physAdr := NilAdr; EXIT
END;
DEC(size, PS);
IF size <= 0 THEN EXIT END;
INC(expected)
END
ELSE
END
ELSE
physAdr := NilAdr
END;
RETURN physAdr
END
END PhysicalAdr;
PROCEDURE TranslateVirtual*(virtAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE; VAR num: LONGINT; VAR physAdr: ARRAY OF Range);
VAR ofs, phys1: SYSTEM.ADDRESS; size1: SYSTEM.SIZE;
BEGIN
Acquire(Memory);
num := 0;
LOOP
IF size = 0 THEN EXIT END;
IF num = LEN(physAdr) THEN num := 0; EXIT END;
ofs := virtAdr MOD PS;
size1 := PS - ofs;
IF size1 > size THEN size1 := size END;
phys1 := MappedPage(virtAdr);
IF ~ODD(phys1) THEN num := 0; EXIT END;
physAdr[num].adr := phys1 - phys1 MOD PS + ofs;
physAdr[num].size := size1; INC(num);
INC(virtAdr, size1); DEC(size, size1)
END;
IF num = 0 THEN physAdr[0].adr := NilAdr; physAdr[0].size := 0 END;
Release(Memory)
END TranslateVirtual;
PROCEDURE GetFreeK*(VAR total, lowFree, highFree: SYSTEM.SIZE);
CONST KperPage = PS DIV 1024;
BEGIN
Acquire(Memory);
total := totalPages * KperPage;
lowFree := freeLowPages * KperPage;
highFree := freeHighPages * KperPage;
Release(Memory)
END GetFreeK;
PROCEDURE ExtendStack*(VAR s: Stack; virtAdr: SYSTEM.ADDRESS): BOOLEAN;
VAR phys: SYSTEM.ADDRESS; ok: BOOLEAN;
BEGIN
Acquire(Memory);
ok := FALSE;
IF (virtAdr < s.high) & (virtAdr >= s.low) THEN
DEC(virtAdr, virtAdr MOD PS);
IF Stats & (virtAdr < s.adr-PS) THEN INC(Nbigskips) END;
IF ODD(MappedPage(virtAdr)) THEN
ok := TRUE
ELSE
NewPage(phys);
IF phys # NilAdr THEN
IF MapPage(virtAdr, phys + UserPage) THEN
IF virtAdr < s.adr THEN
s.adr := virtAdr
ELSE
IF Stats THEN INC(Nfilled) END
END;
ok := TRUE
ELSE
DisposePage(phys)
END
END
END
END;
Release(Memory);
RETURN ok
END ExtendStack;
PROCEDURE NewStack*(VAR s: Stack; process: ANY; VAR initSP: SYSTEM.ADDRESS);
VAR adr, phys: SYSTEM.ADDRESS; old: LONGINT; free: SET;
BEGIN
ASSERT(InitUserStackSize = PS);
Acquire(Memory);
IF Stats THEN INC(NnewStacks) END;
old := freeStackIndex;
LOOP
IF Stats THEN INC(NnewStackLoops) END;
free := freeStack[freeStackIndex];
IF free # {} THEN
adr := 0; WHILE ~(adr IN free) DO INC(adr) END;
IF Stats THEN INC(NnewStackInnerLoops, adr+1) END;
EXCL(freeStack[freeStackIndex], adr);
adr := StackAreaAdr + (freeStackIndex*SetSize + adr)*MaxUserStackSize;
EXIT
END;
INC(freeStackIndex);
IF freeStackIndex = LEN(freeStack) THEN freeStackIndex := 0 END;
IF freeStackIndex = old THEN HALT(1503) END
END;
NewPage(phys); ASSERT(phys # NilAdr);
s.high := adr + MaxUserStackSize; s.low := adr + UserStackGuardSize;
s.adr := s.high - InitUserStackSize;
initSP := s.high-AddressSize;
IF ~MapPage(s.adr, phys + UserPage) THEN HALT(99) END;
SYSTEM.PUT (initSP, process);
Release(Memory)
END NewStack;
PROCEDURE -GetProcessPtr* (): ANY;
CONST Mask = -MaxUserStackSize; Ofs = MaxUserStackSize-4;
CODE {SYSTEM.i386}
MOV EAX, Mask
AND EAX, ESP
MOV EAX, [EAX+Ofs]
POP EBX; pointer returned via stack!
MOV [EBX], EAX
END GetProcessPtr;
PROCEDURE WorkingOnKernelStack* (): BOOLEAN;
VAR id: LONGINT; sp: SYSTEM.ADDRESS;
BEGIN
ASSERT(KernelStackSize # MaxUserStackSize - UserStackGuardSize);
sp := CurrentSP ();
id := ID ();
RETURN (sp >= procm[id].stack.low) & (sp <= procm[id].stack.high)
END WorkingOnKernelStack;
PROCEDURE DisposeStack*(CONST s: Stack);
VAR adr, phys: SYSTEM.ADDRESS;
BEGIN
GlobalFlushTLB;
Acquire(Memory);
IF Stats THEN INC(NdisposeStacks) END;
adr := s.adr;
REPEAT
phys := UnmapPage(adr);
IF ODD(phys) THEN DisposePage(phys - phys MOD PS) END;
INC(adr, PS)
UNTIL adr = s.high;
adr := (adr - MaxUserStackSize - StackAreaAdr) DIV MaxUserStackSize;
INCL(freeStack[adr DIV 32], adr MOD 32);
Release(Memory)
END DisposeStack;
PROCEDURE ValidStack*(CONST s: Stack; sp: SYSTEM.ADDRESS): BOOLEAN;
VAR valid: BOOLEAN;
BEGIN
Acquire(Memory);
valid := (sp MOD 4 = 0) & (sp >= s.adr) & (sp <= s.high);
WHILE valid & (sp < s.high) DO
valid := ODD(MappedPage(sp));
INC(sp, PS)
END;
Release(Memory);
RETURN valid
END ValidStack;
PROCEDURE UpdateState*;
VAR id: LONGINT;
BEGIN
ASSERT(CS () MOD 4 = 0);
id := ID ();
ASSERT(procm[id].stack.high # 0);
procm[id].sp := CurrentBP ()
END UpdateState;
PROCEDURE GetKernelStacks*(VAR stack: ARRAY OF Stack);
VAR i: LONGINT;
BEGIN
FOR i := 0 TO MaxCPU-1 DO
stack[i].adr := procm[i].sp;
stack[i].high := procm[i].stack.high
END
END GetKernelStacks;
PROCEDURE InitPages;
VAR i, j, phys, lTop, mTop: SYSTEM.ADDRESS;
BEGIN
mTop := memTop;
DEC(mTop, mTop MOD PS);
topPageNum := SYSTEM.LSH(mTop-1, -PSlog2);
lTop := lowTop;
DEC(lTop, lTop MOD PS);
SYSTEM.GET (LinkAdr + EndBlockOfs, heapEndAdr);
pageHeapAdr := mTop;
freeHighPages := SYSTEM.LSH(pageHeapAdr, -PSlog2) - SYSTEM.LSH(heapEndAdr, -PSlog2);
IF TraceVerbose THEN
Trace.String("Kernel: "); Trace.Address (LinkAdr); Trace.String(" .. ");
Trace.Address (heapEndAdr-1); Trace.Ln;
Trace.String ("High: "); Trace.Address (heapEndAdr); Trace.String(" .. ");
Trace.Address (pageHeapAdr-1); Trace.String(" = "); Trace.Int (freeHighPages, 0);
Trace.StringLn (" free pages")
END;
NewDirectPage(pageStackAdr); ASSERT(pageStackAdr # NilAdr);
SYSTEM.PUT (pageStackAdr+NodeSP, SYSTEM.VAL (SYSTEM.ADDRESS, MinSP));
SYSTEM.PUT (pageStackAdr+NodeNext, SYSTEM.VAL (SYSTEM.ADDRESS, NilAdr));
SYSTEM.PUT (pageStackAdr+NodePrev, SYSTEM.VAL (SYSTEM.ADDRESS, NilAdr));
freeLowPage := NilAdr; freeLowPages := 0;
i := lTop DIV PS; j := LowAdr DIV PS;
IF TraceVerbose THEN
Trace.String("Low: "); Trace.Address (j*PS); Trace.String (".."); Trace.Address (i*PS-1)
END;
REPEAT
DEC(i); phys := i*PS;
SYSTEM.PUT (phys, freeLowPage);
freeLowPage := phys; INC(freeLowPages)
UNTIL i = j;
IF TraceVerbose THEN
Trace.String(" = "); Trace.Int(freeLowPages, 1); Trace.StringLn (" free pages")
END;
totalPages := SYSTEM.LSH(memTop - M + lowTop + dmaSize + PS, -PSlog2);
ASSERT((StackAreaAdr MOD MaxUserStackSize = 0) & (StackAreaSize MOD MaxUserStackSize = 0));
FOR i := 0 TO LEN(freeStack)-1 DO freeStack[i] := {0..SetSize-1} END;
FOR i := MaxUserStacks TO LEN(freeStack)*SetSize-1 DO EXCL(freeStack[i DIV SetSize], i MOD SetSize) END;
freeStackIndex := 0;
mapTop := MapAreaAdr;
NewPage(kernelPD); ASSERT(kernelPD # NilAdr);
Fill32(kernelPD, PTEs*4, PageNotPresent);
IF ~MapDirect(LowAdr, mTop-LowAdr, LowAdr + UserPage) THEN HALT(99) END
END InitPages;
PROCEDURE GenMemSegDesc(type, base, limit, dpl: LONGINT; page: BOOLEAN; VAR sd: SegDesc);
VAR s: SET;
BEGIN
sd.low := ASH(base MOD 10000H, 16) + limit MOD 10000H;
s := SYSTEM.VAL (SET, ASH(ASH(base, -24), 24) + ASH(ASH(limit, -16), 16) +
ASH(dpl, 13) + ASH(type, 9) + ASH(base, -16) MOD 100H);
s := s + {12, 15, 22};
IF page THEN INCL(s, 23) END;
sd.high := SYSTEM.VAL (LONGINT, s)
END GenMemSegDesc;
PROCEDURE GenTSSDesc(base: SYSTEM.ADDRESS; limit, dpl: LONGINT; VAR sd: SegDesc);
VAR s: SET;
BEGIN
sd.low := ASH(base MOD 10000H, 16) + limit MOD 10000H;
s := SYSTEM.VAL (SET, ASH(ASH(base, -24), 24) + ASH(ASH(limit, -16), 16) +
ASH(dpl, 13) + ASH(base, -16) MOD 100H);
s := s + {8, 11, 15};
sd.high := SYSTEM.VAL (LONGINT, s)
END GenTSSDesc;
PROCEDURE InitSegments;
VAR i: LONGINT;
BEGIN
gdt[0].low := 0; gdt[0].high := 0;
GenMemSegDesc(5, 0, M-1, 0, TRUE, gdt[1]);
GenMemSegDesc(1, 0, M-1, 0, TRUE, gdt[2]);
GenMemSegDesc(7, 0, M-1, 0, TRUE, gdt[3]);
GenMemSegDesc(1, 0, M-1, 3, TRUE, gdt[4]);
GenMemSegDesc(3, 0, M DIV PS, 3, TRUE, gdt[5]);
FOR i := 0 TO MaxCPU-1 DO
GenTSSDesc(SYSTEM.ADR(procm[i].tss), SYSTEM.SIZEOF(TSSDesc)-1, 0, gdt[TSSOfs+i]);
procm[i].sp := 0; procm[i].stack.high := 0
END
END InitSegments;
PROCEDURE EnableSegments;
BEGIN
LoadGDT(SYSTEM.ADR(gdt[0]), SYSTEM.SIZEOF(GDT)-1);
LoadSegRegs(DataSel)
END EnableSegments;
PROCEDURE NewKernelStack(VAR stack: Stack);
VAR phys, virt: SYSTEM.ADDRESS; size: SYSTEM.SIZE;
BEGIN
size := KernelStackSize;
NewVirtual(virt, size + PS);
ASSERT(virt # NilAdr, 1502);
INC(virt, PS);
stack.low := virt;
stack.adr := virt;
REPEAT
NewPage(phys); ASSERT(phys # NilAdr);
IF ~MapPage(virt, phys + KernelPage) THEN HALT(99) END;
DEC(size, PS); INC(virt, PS)
UNTIL size = 0;
stack.high := virt
END NewKernelStack;
PROCEDURE -SetTR(tr: SYSTEM.ADDRESS);
CODE {SYSTEM.i386, SYSTEM.Privileged}
POP EAX
LTR AX
END SetTR;
PROCEDURE -EnableMM(pd, esp: SYSTEM.ADDRESS);
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
POP EBX ; esp
POP EAX ; pd
MOV CR3, EAX ; page directory ptr
MOV ECX, [EBP+4] ; caller0 return
MOV EDX, [EBP] ; caller0 EBP
MOV EDX, [EDX+4] ; caller1 return
MOV EAX, CR0
OR EAX, 80000000H ; set PG bit
MOV CR0, EAX ; enable virtual addressing (old stack no longer usable)
JMP 0 ; flush queue
WBINVD
MOV DWORD [EBX-4], 0 ; not UserStackSel (cf. GetUserStack)
MOV [EBX-8], EDX ; caller1 return on new stack
MOV DWORD [EBX-12], 0 ; caller1 EBP on new stack
LEA EBP, [EBX-12] ; new stack top
MOV ESP, EBP
JMP ECX ; back to caller0 (whose locals are now inaccessible!)
END EnableMM;
PROCEDURE InitMemory*;
VAR id: LONGINT;
BEGIN
EnableSegments;
id := ID ();
NewKernelStack(procm[id].stack);
procm[id].sp := 0;
Fill32(SYSTEM.ADR(procm[id].tss), SYSTEM.SIZEOF(TSSDesc), 0);
procm[id].tss.ESP0 := procm[id].stack.high;
procm[id].tss.ESS0 := KernelStackSel;
procm[id].tss.IOBitmapOffset := -1;
SetTR(KernelTR + id*8);
EnableMM(kernelPD, procm[id].tss.ESP0)
END InitMemory;
PROCEDURE InitBootPage*(start: Startup; VAR physAdr: SYSTEM.ADDRESS);
CONST BootOfs = 800H;
VAR adr, a: SYSTEM.ADDRESS;
BEGIN
Acquire(Memory);
NewLowPage(physAdr);
Release(Memory);
ASSERT((physAdr # NilAdr) & (physAdr >= 0) & (physAdr < M) & (physAdr MOD PS = 0));
adr := physAdr + BootOfs;
a := adr;
SYSTEM.PUT32(a, 0100012EBH); INC(a, 4); SYSTEM.PUT32(a, 000080000H); INC(a, 4);
SYSTEM.PUT32(a, 000000000H); INC(a, 4); SYSTEM.PUT32(a, 000170000H); INC(a, 4);
SYSTEM.PUT32(a, 000000000H); INC(a, 4); SYSTEM.PUT32(a, 0010F2EFAH); INC(a, 4);
SYSTEM.PUT32(a, 02E08081EH); INC(a, 4); SYSTEM.PUT32(a, 00E16010FH); INC(a, 4);
SYSTEM.PUT32(a, 0E0010F08H); INC(a, 4); SYSTEM.PUT32(a, 0010F010CH); INC(a, 4);
SYSTEM.PUT32(a, 0B800EBF0H); INC(a, 4); SYSTEM.PUT32(a, 0D08E0010H); INC(a, 4);
SYSTEM.PUT32(a, 0C08ED88EH); INC(a, 4); SYSTEM.PUT32(a, 00800BC66H); INC(a, 4);
SYSTEM.PUT32(a, 033660000H); INC(a, 4); SYSTEM.PUT32(a, 0FF2E66C0H); INC(a, 4);
SYSTEM.PUT32(a, 09008022EH); INC(a, 4);
SYSTEM.PUT32(adr+2, SYSTEM.VAL (LONGINT, start));
SYSTEM.PUT32(adr+16, SYSTEM.ADR(gdt[0]));
SYSTEM.PUT8(physAdr, 0EAX);
SYSTEM.PUT32(physAdr + 1, ASH(physAdr, 16-4) + BootOfs)
END InitBootPage;
PROCEDURE InitAPICArea*(adr: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
BEGIN
ASSERT((size = PS) & (adr >= IntelAreaAdr) & (adr+size-1 < IntelAreaAdr+IntelAreaSize));
IF ~MapDirect(adr, size, adr + UserPage) THEN HALT(99) END
END InitAPICArea;
PROCEDURE SetGCParams*;
VAR size, t: SYSTEM.SIZE;
BEGIN
GetFreeK(size, t, t);
heapMinKB := size * HeapMin DIV 100;
heapMaxKB := size * HeapMax DIV 100;
expandMin := size * ExpandRate DIV 100 * 1024;
IF expandMin < 0 THEN expandMin := MAX(LONGINT) END;
gcThreshold := size * Threshold DIV 100 * 1024;
IF gcThreshold < 0 THEN gcThreshold := MAX(LONGINT) END
END SetGCParams;
PROCEDURE GetStaticHeap*(VAR beginBlockAdr, endBlockAdr, freeBlockAdr: SYSTEM.ADDRESS);
VAR memBlockAdr, endAdr: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET(LinkAdr + EndBlockOfs, freeBlockAdr);
ASSERT(freeBlockAdr MOD StaticBlockSize = 0);
memBlockAdr := LinkAdr + HeaderSize + MemoryBlockOfs;
memBlockHead := SYSTEM.VAL(MemoryBlock, memBlockAdr);
ASSERT(memBlockHead.beginBlockAdr MOD StaticBlockSize = 0);
endAdr := memBlockHead.endBlockAdr;
INC(endAdr, InitialHeapIncrement);
SetHeapEndAdr(endAdr);
memBlockHead.endBlockAdr := endAdr;
ASSERT((memBlockHead.endBlockAdr - memBlockHead.beginBlockAdr) MOD StaticBlockSize = 0);
memBlockTail := memBlockHead;
beginBlockAdr := memBlockHead.beginBlockAdr;
endBlockAdr := memBlockHead.endBlockAdr
END GetStaticHeap;
PROCEDURE ValidHeapAddress*(p: SYSTEM.ADDRESS): BOOLEAN;
BEGIN
RETURN GreaterOrEqual(p,memBlockHead.beginBlockAdr) & LessOrEqual(p,memBlockTail.endBlockAdr)
END ValidHeapAddress;
PROCEDURE JumpToUserLevel*(userEBP: SYSTEM.ADDRESS);
CODE {SYSTEM.i386}
PUSH UserStackSel ; SS3
PUSH DWORD [EBP+userEBP] ; ESP3
PUSHFD ; EFLAGS3
PUSH UserCodeSel ; CS3
CALL L1 ; PUSH L1 (EIP3)
L1:
ADD DWORD [ESP], BYTE 5; adjust EIP3 to L2 (L2-L1 = 5)
IRETD ; switch to level 3 and continue at following instruction
L2:
POP EBP ; from level 3 stack (refer to Objects.NewProcess)
RET 8 ; jump to body of first active object
END JumpToUserLevel;
PROCEDURE Ensure32BitAddress*(adr: SYSTEM.ADDRESS): LONGINT;
BEGIN
RETURN adr
END Ensure32BitAddress;
PROCEDURE Is32BitAddress*(adr: SYSTEM.ADDRESS): BOOLEAN;
BEGIN RETURN SYSTEM.VAL (Address32, adr) = adr;
END Is32BitAddress;
PROCEDURE Unexpected(VAR state: State);
VAR int: LONGINT; isr, irr: CHAR;
BEGIN
int := state.INT;
IF HandleSpurious & ((int >= IRQ0) & (int <= MaxIRQ) OR (int = MPSPU)) THEN
IF (int >= IRQ8) & (int <= IRQ15) THEN
Portout8 (IntB0, 0BX); Portin8(IntB0, isr);
Portout8 (IntB0, 0AX); Portin8(IntB0, irr)
ELSIF (int >= IRQ0) & (int <= IRQ7) THEN
Portout8 (IntA0, 0BX); Portin8(IntA0, isr);
Portout8 (IntA0, 0AX); Portin8(IntA0, irr)
ELSE
isr := 0X; irr := 0X
END;
IF TraceSpurious THEN
Acquire (TraceOutput);
Trace.String("INT"); Trace.Int(int, 1);
Trace.Hex(ORD(isr), -3); Trace.Hex(ORD(irr), -2); Trace.Ln;
Release (TraceOutput);
END
ELSE
Acquire (TraceOutput);
Trace.StringLn ("Unexpected interrupt");
Trace.Memory(SYSTEM.ADR(state), SYSTEM.SIZEOF(State)-4*4);
IF int = 3 THEN
LOOP END
ELSE
Release (TraceOutput);
SetEAX(int);
HALT(1801)
END
END
END Unexpected;
PROCEDURE -InEnableIRQ (int: LONGINT);
CODE {SYSTEM.i386}
POP EBX
CMP EBX, IRQ7
JG cont2
IN AL, IntA1
SUB EBX, IRQ0
BTR EAX, EBX
OUT IntA1, AL
JMP end
cont2:
IN AL, IntB1
SUB EBX, IRQ8
BTR EAX, EBX
OUT IntB1, AL
end:
END InEnableIRQ;
PROCEDURE -InDisableIRQ (int: LONGINT);
CODE {SYSTEM.i386}
POP EBX
CMP EBX, IRQ7
JG cont2
IN AL, IntA1
SUB EBX, IRQ0
BTS EAX, EBX
OUT IntA1, AL
JMP end
cont2:
IN AL, IntB1
SUB EBX, IRQ8
BTS EAX, EBX
OUT IntB1, AL
end:
END InDisableIRQ;
PROCEDURE EnableIRQ* (int: LONGINT);
BEGIN
ASSERT((int >= IRQ0) & (int <= IRQ15) & (int # IRQ2));
Acquire(Interrupts);
InEnableIRQ(int);
Release(Interrupts)
END EnableIRQ;
PROCEDURE DisableIRQ* (int: LONGINT);
BEGIN
ASSERT((int >= IRQ0) & (int <= IRQ15) & (int # IRQ2));
Acquire(Interrupts);
InDisableIRQ(int);
Release(Interrupts)
END DisableIRQ;
PROCEDURE InstallHandler* (h: Handler; int: LONGINT);
VAR i: LONGINT; unexpected: Handler;
BEGIN
ASSERT(default.valid);
ASSERT(int # IRQ2);
Acquire(Interrupts);
i := 0;
unexpected := Unexpected;
IF intHandler[int, 0].handler # unexpected THEN
WHILE (i < MaxNumHandlers - 1) & intHandler[int, i].valid DO
INC(i)
END;
IF i < MaxNumHandlers - 1 THEN
intHandler[int, i].valid := TRUE;
intHandler[int, i].handler := h;
ELSE
Acquire(TraceOutput);
Trace.String("Machine.InstallHandler: handler could not be installed for interrupt "); Trace.Int(int, 0);
Trace.String(" - too many handlers per interrupt number"); Trace.Ln;
Release(TraceOutput)
END
ELSE
intHandler[int, 0].handler := h;
IF (int >= IRQ0) & (int <= IRQ15) THEN InEnableIRQ(int) END
END;
Release(Interrupts)
END InstallHandler;
PROCEDURE RemoveHandler* (h: Handler; int: LONGINT);
VAR i, j, foundIndex: LONGINT;
BEGIN
ASSERT(default.valid);
Acquire(Interrupts);
i := 0;
foundIndex := -1;
WHILE (i < MaxNumHandlers - 1) & intHandler[int, i].valid DO
IF intHandler[int, i].handler = h THEN foundIndex := i END;
INC(i)
END;
IF foundIndex # -1 THEN
FOR j := foundIndex TO i - 2 DO
intHandler[int, j] := intHandler[int, j + 1]
END
END;
IF ~intHandler[int, 0].valid THEN
intHandler[int, 0] := default;
IF (int >= IRQ0) & (int <= IRQ15) THEN DisableIRQ(int) END
END;
Release(Interrupts)
END RemoveHandler;
PROCEDURE GetCR0to4(VAR cr: ARRAY OF LONGINT);
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
MOV EDI, [EBP+cr]
MOV EAX, CR0
XOR EBX, EBX ; CR1 is not documented
MOV ECX, CR2
MOV EDX, CR3
MOV [EDI], EAX
MOV [EDI+4], EBX
MOV [EDI+8], ECX
MOV [EDI+12], EDX
MOV EAX, CR4 ; Pentium only
MOV [EDI+16], EAX
END GetCR0to4;
PROCEDURE GetDR0to7(VAR dr: ARRAY OF LONGINT);
CODE {SYSTEM.i386, SYSTEM.Privileged}
MOV EDI, [EBP+dr]
MOV EAX, DR0
MOV EBX, DR1
MOV ECX, DR2
MOV EDX, DR3
MOV [EDI], EAX
MOV [EDI+4], EBX
MOV [EDI+8], ECX
MOV [EDI+12], EDX
XOR EAX, EAX ; DR4 is not documented
XOR EBX, EBX ; DR5 is not documented
MOV ECX, DR6
MOV EDX, DR7
MOV [EDI+16], EAX
MOV [EDI+20], EBX
MOV [EDI+24], ECX
MOV [EDI+28], EDX
END GetDR0to7;
PROCEDURE GetSegments(VAR ss, es, ds, fs, gs: LONGINT);
CODE {SYSTEM.i386}
XOR EAX, EAX
MOV EBX, [EBP+ss]
MOV AX, SS
MOV [EBX], EAX
MOV EBX, [EBP+es]
MOV AX, ES
MOV [EBX], EAX
MOV EBX, [EBP+ds]
MOV AX, DS
MOV [EBX], EAX
MOV EBX, [EBP+fs]
MOV AX, FS
MOV [EBX], EAX
MOV EBX, [EBP+gs]
MOV AX, GS
MOV [EBX], EAX
END GetSegments;
PROCEDURE -CLTS;
CODE {SYSTEM.i386, SYSTEM.Privileged}
CLTS
END CLTS;
PROCEDURE -GetFPU(adr: SYSTEM.ADDRESS);
CODE {SYSTEM.i386, SYSTEM.FPU}
POP EBX
FNSTENV [EBX] ; also masks all exceptions
FWAIT
END GetFPU;
PROCEDURE -CR2* (): SYSTEM.ADDRESS;
CODE {SYSTEM.i386, SYSTEM.Privileged}
MOV EAX, CR2
END CR2;
PROCEDURE GetExceptionState* (VAR int: State; VAR exc: ExceptionState);
VAR id: LONGINT; level0: BOOLEAN;
BEGIN
exc.halt := -int.INT; id := ID ();
IF int.INT = PF THEN exc.pf := CR2 () ELSE exc.pf := 0 END;
GetCR0to4(exc.CR);
GetDR0to7(exc.DR);
CLTS;
IF int.INT = MF THEN
GetFPU(SYSTEM.ADR(exc.FPU[0]));
int.PC := SYSTEM.VAL (SYSTEM.ADDRESS, exc.FPU[3]);
IF 2 IN exc.FPU[1] THEN exc.halt := -32
ELSIF 3 IN exc.FPU[1] THEN exc.halt := -33
ELSIF 0 IN exc.FPU[1] THEN exc.halt := -34
ELSIF 6 IN exc.FPU[1] THEN exc.halt := -35
ELSIF 1 IN exc.FPU[1] THEN exc.halt := -36
ELSIF 4 IN exc.FPU[1] THEN exc.halt := -37
ELSIF 5 IN exc.FPU[1] THEN exc.halt := -38
ELSE
END
ELSE
Fill32(SYSTEM.ADR(exc.FPU[0]), LEN(exc.FPU)*SYSTEM.SIZEOF(SET), 0)
END;
SetupFPU;
level0 := (int.CS MOD 4 = KernelLevel);
IF int.INT = BP THEN
IF level0 THEN
exc.halt := int.SP
ELSE
SYSTEM.GET (int.SP, exc.halt);
IF exc.halt >= MAX(INTEGER) THEN INC (int.SP, AddressSize) END
END;
IF exc.halt < MAX(INTEGER) THEN DEC (int.PC) END;
ELSIF int.INT = OVF THEN
DEC (int.PC)
ELSIF int.INT = PF THEN
IF int.PC = 0 THEN
IF level0 THEN int.PC := int.SP ELSE SYSTEM.GET (int.SP, int.PC) END
END
END;
GetSegments(exc.SS, exc.ES, exc.DS, exc.FS, exc.GS);
IF level0 THEN
exc.SP := SYSTEM.ADR(int.SP)
ELSE
exc.SP := int.SP; exc.SS := int.SS
END
END GetExceptionState;
PROCEDURE FieldInterrupt;
CODE {SYSTEM.i386} ; 3 bytes implicit code skipped: PUSH EBP; MOV EBP, ESP
entry:
PUSHAD ; save all registers (EBP = error code)
LEA EBP, [ESP+36] ; procedure link (for correct tracing of interrupt procedures)
MOV EBX, [ESP+32] ; EBX = int number
IMUL EBX, EBX, MaxNumHandlers
IMUL EBX, EBX, 12
LEA EAX, intHandler
ADD EAX, EBX ; address of intHandler[int, 0]
loop: ; call all handlers for the interrupt
MOV ECX, ESP
PUSH EAX ; save ptr for table
PUSH stateTag ; TAG(state)
PUSH ECX ; ADR(state)
MOV EBX, [EAX+8]
CMP EBX, 0
JE nodelegate
PUSH EBX ; object pointer for DELEGATE
nodelegate:
CALL DWORD [EAX+4] ; call handler
CLI ; handler may have re-enabled interrupts
POP EAX
ADD EAX, 12
MOV EBX, [EAX]
CMP EBX, 0
JNE loop
POPAD ; now EBP = error code
POP EBP ; now EBP = INT
POP EBP ; now EBP = caller EBP
IRETD
END FieldInterrupt;
PROCEDURE FieldIRQ;
CODE {SYSTEM.i386} ; 3 bytes implicit code skipped: PUSH EBP; MOV EBP, ESP
entry:
PUSHAD ; save all registers (EBP = error code)
LEA EBP, [ESP+36] ; procedure link (for correct tracing of interrupt procedures)
; PUSH [ESP+32] ; int number
; CALL traceInterruptIn
MOV EBX, [ESP+32] ; EBX = int number
IMUL EBX, EBX, MaxNumHandlers
IMUL EBX, EBX, 12
LEA EAX, intHandler
ADD EAX, EBX ; address of intHandler[int, 0]
loop: ; call all handlers for the interrupt
MOV ECX, ESP
PUSH EAX ; save ptr for linked list
PUSH stateTag ; TAG(state)
PUSH ECX ; ADR(state)
MOV EBX, [EAX+8]
CMP EBX, 0
JE nodelegate
PUSH EBX ; object pointer for DELEGATE
nodelegate:
CALL DWORD [EAX+4] ; call handler
CLI ; handler may have re-enabled interrupts
POP EAX
ADD EAX, 12
MOV EBX, [EAX]
CMP EBX, 0
JNE loop
; PUSH [ESP+32] ; int number
; CALL traceInterruptOut
; ack interrupt
MOV AL, 20H ; undoc PC ed. 2 p. 1018
CMP BYTE [ESP+32], IRQ8
JB irq0
OUT IntB0, AL ; 2nd controller
irq0:
OUT IntA0, AL ; 1st controller
POPAD ; now EBP = error code
POP EBP ; now EBP = INT
POP EBP ; now EBP = caller EBP
IRETD
END FieldIRQ;
PROCEDURE LoadIDT(base: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
CODE {SYSTEM.i386, SYSTEM.Privileged}
SHL DWORD [EBP+size], 16
MOV EBX, 2
LIDT [EBP+EBX+size]
END LoadIDT;
PROCEDURE InitInterrupts*;
VAR a: SYSTEM.ADDRESS; o, i: LONGINT; p: PROCEDURE; mask: SET;
BEGIN
stateTag := SYSTEM.TYPECODE(State);
Portout8 (IntA0, 11X); Portout8 (IntA1, CHR(IRQ0));
Portout8 (IntA1, 4X); Portout8 (IntA1, 1X); Portout8 (IntA1, 0FFX);
Portout8 (IntB0, 11X); Portout8 (IntB1, CHR(IRQ8));
Portout8 (IntB1, 2X); Portout8 (IntB1, 1X); Portout8 (IntB1, 0FFX);
Portin8(IntA1, SYSTEM.VAL (CHAR, mask));
EXCL(mask, IRQ2-IRQ0);
Portout8 (IntA1, SYSTEM.VAL (CHAR, mask));
default.valid := TRUE; default.handler := Unexpected;
FOR i := 0 TO IDTSize-1 DO
intHandler[i, 0] := default; o := 0;
glue[i][o] := 6AX; INC (o); glue[i][o] := 0X; INC (o);
glue[i][o] := 87X; INC (o); glue[i][o] := 2CX; INC (o); glue[i][o] := 24X; INC (o);
glue[i][o] := 6AX; INC (o); glue[i][o] := CHR(i); INC (o);
IF (i >= IRQ0) & (i <= IRQ15) THEN p := FieldIRQ ELSE p := FieldInterrupt END;
a := SYSTEM.VAL (SYSTEM.ADDRESS, p) + 3 - (SYSTEM.ADR(glue[i][o]) + 5);
glue[i][o] := 0E9X; INC (o);
SYSTEM.PUT32 (SYSTEM.ADR(glue[i][o]), a);
IF (i > 31) OR ~(i IN {8, 10..14, 17}) THEN a := SYSTEM.ADR(glue[i][0])
ELSE a := SYSTEM.ADR(glue[i][2])
END;
idt[i].offsetBits0to15 := SHORT(a MOD 10000H);
IF TRUE THEN
idt[i].selector := KernelCodeSel;
idt[i].gateType := SYSTEM.VAL (INTEGER, 0EE00H)
ELSE
idt[i].selector := UserCodeSel;
idt[i].gateType := SYSTEM.VAL (INTEGER, 08E00H)
END;
idt[i].offsetBits16to31 := SHORT(a DIV 10000H)
END
END InitInterrupts;
PROCEDURE Start*;
BEGIN
ASSERT(default.valid);
LoadIDT(SYSTEM.ADR(idt[0]), SYSTEM.SIZEOF(IDT)-1);
Sti
END Start;
PROCEDURE CurrentPC* (): SYSTEM.ADDRESS;
CODE {SYSTEM.i386}
MOV EAX, [EBP+4]
END CurrentPC;
PROCEDURE -CurrentBP* (): SYSTEM.ADDRESS;
CODE {SYSTEM.i386}
MOV EAX, EBP
END CurrentBP;
PROCEDURE -SetBP* (bp: SYSTEM.ADDRESS);
CODE {SYSTEM.i386}
POP EBP
END SetBP;
PROCEDURE -CurrentSP* (): SYSTEM.ADDRESS;
CODE {SYSTEM.i386}
MOV EAX, ESP
END CurrentSP;
PROCEDURE -SetSP* (sp: SYSTEM.ADDRESS);
CODE {SYSTEM.i386}
POP ESP
END SetSP;
PROCEDURE -FPUSaveMin* (VAR state: SSEState);
CODE {SYSTEM.i386, SYSTEM.FPU}
POP EAX
FNSTCW [EAX] ; control word is at state[0]
FWAIT
END FPUSaveMin;
PROCEDURE -FPURestoreMin* (VAR state: SSEState);
CODE {SYSTEM.i386, SYSTEM.FPU}
POP EAX
FLDCW [EAX] ; control word is at state[0]
END FPURestoreMin;
PROCEDURE -FPUSaveFull* (VAR state: SSEState);
CODE {SYSTEM.i386, SYSTEM.FPU}
POP EAX
FSAVE [EAX]
END FPUSaveFull;
PROCEDURE -FPURestoreFull* (VAR state: SSEState);
CODE {SYSTEM.i386, SYSTEM.FPU}
POP EAX
FRSTOR [EAX]
END FPURestoreFull;
PROCEDURE -SSESaveFull* (stateAdr: SYSTEM.ADDRESS);
CODE {SYSTEM.P2, SYSTEM.FPU, SYSTEM.SSE2}
POP EAX
FXSAVE [EAX]
FWAIT
FNINIT
END SSESaveFull;
PROCEDURE -SSERestoreFull* (stateAdr: SYSTEM.ADDRESS);
CODE {SYSTEM.P2, SYSTEM.FPU, SYSTEM.SSE2}
POP EAX
FXRSTOR [EAX]
END SSERestoreFull;
PROCEDURE -SSESaveMin* (stateAdr: SYSTEM.ADDRESS);
CODE {SYSTEM.i386, SYSTEM.FPU, SYSTEM.SSE2}
POP EAX
FNSTCW [EAX]
FWAIT
STMXCSR [EAX+24]
END SSESaveMin;
PROCEDURE -SSERestoreMin* (stateAdr: SYSTEM.ADDRESS);
CODE {SYSTEM.i386, SYSTEM.FPU, SYSTEM.SSE2}
POP EAX
FLDCW [EAX]
LDMXCSR [EAX+24]
END SSERestoreMin;
PROCEDURE -PushState* (CONST state: State);
CODE {SYSTEM.i386}
POP EAX ; ADR (state)
POP EBX ; TYPECODE (state), ignored
PUSH DWORD [EAX+48] ; FLAGS
PUSH DWORD [EAX+44] ; CS
PUSH DWORD [EAX+40] ; PC
PUSH DWORD [EAX+28] ; EAX
PUSH DWORD [EAX+24] ; ECX
PUSH DWORD [EAX+20] ; EDX
PUSH DWORD [EAX+16] ; EBX
PUSH DWORD 0 ; ignored
PUSH DWORD [EAX+36] ; BP
PUSH DWORD [EAX+4] ; ESI
PUSH DWORD [EAX+0] ; EDI
END PushState;
PROCEDURE -JumpState*;
CODE {SYSTEM.i386}
POPAD
IRETD
END JumpState;
PROCEDURE -CallLocalIPC*;
CODE {SYSTEM.i386}
INT MPIPCLocal
END CallLocalIPC;
PROCEDURE -HLT*;
CODE {SYSTEM.i386, SYSTEM.Privileged}
STI ;
HLT
END HLT;
PROCEDURE -GetEAX*(): LONGINT;
CODE{SYSTEM.i386}
END GetEAX;
PROCEDURE -GetECX*(): LONGINT;
CODE{SYSTEM.i386}
MOV EAX,ECX
END GetECX;
PROCEDURE -GetESI*(): LONGINT;
CODE{SYSTEM.i386}
MOV EAX,ESI
END GetESI;
PROCEDURE -GetEDI*(): LONGINT;
CODE{SYSTEM.i386}
MOV EAX,EDI
END GetEDI;
PROCEDURE -SetEAX*(n: LONGINT);
CODE{SYSTEM.i386} POP EAX
END SetEAX;
PROCEDURE -SetEBX*(n: LONGINT);
CODE{SYSTEM.i386}
POP EBX
END SetEBX;
PROCEDURE -SetECX*(n: LONGINT);
CODE{SYSTEM.i386}
POP ECX
END SetECX;
PROCEDURE -SetEDX*(n: LONGINT);
CODE{SYSTEM.i386}
POP EDX
END SetEDX;
PROCEDURE -SetESI*(n: LONGINT);
CODE{SYSTEM.i386}
POP ESI
END SetESI;
PROCEDURE -SetEDI*(n: LONGINT);
CODE{SYSTEM.i386}
POP EDI
END SetEDI;
PROCEDURE Portin8*(port: LONGINT; VAR val: CHAR);
CODE{SYSTEM.i386}
MOV EDX,[EBP+port]
IN AL, DX
MOV ECX, [EBP+val]
MOV [ECX], AL
END Portin8;
PROCEDURE Portin16*(port: LONGINT; VAR val: INTEGER);
CODE{SYSTEM.i386}
MOV EDX,[EBP+port]
IN AX, DX
MOV ECX, [EBP+val]
MOV [ECX], AX
END Portin16;
PROCEDURE Portin32*(port: LONGINT; VAR val: LONGINT);
CODE{SYSTEM.i386}
MOV EDX,[EBP+port]
IN EAX, DX
MOV ECX, [EBP+val]
MOV [ECX], EAX
END Portin32;
PROCEDURE Portout8*(port: LONGINT; val: CHAR);
CODE{SYSTEM.i386}
MOV AL,[EBP+val]
MOV EDX,[EBP+port]
OUT DX,AL
END Portout8;
PROCEDURE Portout16*(port: LONGINT; val: INTEGER);
CODE{SYSTEM.i386}
MOV AX,[EBP+val]
MOV EDX,[EBP+port]
OUT DX,AX
END Portout16;
PROCEDURE Portout32*(port: LONGINT; val: LONGINT);
CODE{SYSTEM.i386}
MOV EAX,[EBP+val]
MOV EDX,[EBP+port]
OUT DX,EAX
END Portout32;
PROCEDURE KernelCallHLT*;
CODE {SYSTEM.i386}
MOV EAX, 2
INT MPKC
END KernelCallHLT;
PROCEDURE CPUID1*(): LONGINT;
CODE {SYSTEM.i386, SYSTEM.Pentium}
MOV EAX, 1
CPUID
MOV EAX, EBX
END CPUID1;
PROCEDURE -AtomicInc*(VAR x: LONGINT);
CODE {SYSTEM.i386}
POP EAX
LOCK
INC DWORD [EAX]
END AtomicInc;
PROCEDURE -AtomicDec*(VAR x: LONGINT);
CODE {SYSTEM.i386}
POP EAX
LOCK
DEC DWORD [EAX]
END AtomicDec;
PROCEDURE AtomicExcl* (VAR s: SET; bit: LONGINT);
CODE {SYSTEM.i386}
MOV EAX, [EBP+bit]
MOV EBX, [EBP+s]
LOCK
BTR [EBX], EAX
END AtomicExcl;
PROCEDURE -AtomicAdd*(VAR x: LONGINT; y: LONGINT);
CODE {SYSTEM.i386}
POP EBX
POP EAX
LOCK
ADD DWORD [EAX], EBX
END AtomicAdd;
PROCEDURE -AtomicTestSet*(VAR x: BOOLEAN): BOOLEAN;
CODE {SYSTEM.i386}
POP EBX
MOV AL, 1
XCHG [EBX], AL
END AtomicTestSet;
PROCEDURE -AtomicCAS* (VAR x: LONGINT; old, new: LONGINT): LONGINT;
CODE {SYSTEM.i386}
POP EBX ; new
POP EAX ; old
POP ECX ; address of x
DB 0F0X, 00FX, 0B1X, 019X ; LOCK CMPXCHG [ECX], EBX; atomicly compare x with old and set it to new if equal
END AtomicCAS;
PROCEDURE CopyState* (CONST from: State; VAR to: State);
BEGIN
to.EDI := from.EDI; to.ESI := from.ESI;
to.EBX := from.EBX; to.EDX := from.EDX;
to.ECX := from.ECX; to.EAX := from.EAX;
to.BP := from.BP; to.PC := from.PC;
to.CS := from.CS; to.FLAGS := from.FLAGS;
to.SP := from.SP
END CopyState;
PROCEDURE NumberOfProcessors*( ): LONGINT;
BEGIN
RETURN numberOfProcessors
END NumberOfProcessors;
PROCEDURE SetNumberOfProcessors*(num: LONGINT);
BEGIN
numberOfProcessors := num;
END SetNumberOfProcessors;
PROCEDURE ChangeByteOrder* (n: LONGINT): LONGINT;
CODE { SYSTEM.Pentium }
MOV EAX, [EBP+n] ; load n in eax
BSWAP EAX ; swap byte order
END ChangeByteOrder;
PROCEDURE ApicPut(ofs: SYSTEM.SIZE; val: SET);
BEGIN
IF TraceApic THEN
Acquire(TraceOutput);
Trace.Hex(ofs, SYSTEM.SIZEOF(SYSTEM.SIZE)*2); Trace.String(" := "); Trace.Hex(SYSTEM.VAL (LONGINT, val), 9); Trace.Ln;
Release(TraceOutput);
END;
SYSTEM.PUT(localAPIC+ofs, SYSTEM.VAL (LONGINT, val))
END ApicPut;
PROCEDURE ApicGet(ofs: SYSTEM.SIZE): SET;
VAR val: SET;
BEGIN
SYSTEM.GET(localAPIC+ofs, SYSTEM.VAL (LONGINT, val));
IF TraceApic THEN
Acquire(TraceOutput);
Trace.String(" ("); Trace.Hex(ofs, SYSTEM.SIZEOF(SYSTEM.SIZE)*2); Trace.String(" = ");
Trace.Hex(SYSTEM.VAL(LONGINT, val), 9); Trace.StringLn (")");
Release(TraceOutput);
END;
RETURN val
END ApicGet;
PROCEDURE HandleIPC(VAR state: State);
VAR id: LONGINT;
BEGIN
id := ID();
IF ~TraceProcessor OR (id IN allProcessors) THEN
IF FrontBarrier IN ipcFlags THEN
AtomicExcl(ipcFrontBarrier, id);
WHILE ipcFrontBarrier # {} DO SpinHint END
END;
ipcHandler(id, state, ipcMessage);
IF BackBarrier IN ipcFlags THEN
AtomicExcl(ipcBackBarrier, id);
WHILE ipcBackBarrier # {} DO SpinHint END
END;
AtomicExcl(ipcBusy, id)
END;
IF state.INT = MPIPC THEN
ApicPut(0B0H, {})
END
END HandleIPC;
PROCEDURE HandleError(VAR state: State);
VAR esr: SET;
BEGIN
esr := ApicGet(280H);
ApicPut(0B0H, {});
HALT(2302)
END HandleError;
PROCEDURE LocalBroadcast(h: BroadcastHandler; msg: Message; flags: SET);
BEGIN
IF Self IN flags THEN ipcBusy := allProcessors
ELSE ipcBusy := allProcessors - {ID()}
END;
ipcFrontBarrier := ipcBusy; ipcBackBarrier := ipcBusy;
ipcHandler := h; ipcMessage := msg; ipcFlags := flags;
IF numProcessors > 1 THEN
ApicPut(300H, {18..19} + SYSTEM.VAL (SET, MPIPC));
END;
IF Self IN flags THEN CallLocalIPC END;
WHILE ipcBusy # {} DO SpinHint END;
ipcHandler := NIL; ipcMessage := NIL
END LocalBroadcast;
PROCEDURE Broadcast* (h: BroadcastHandler; msg: Message; flags: SET);
BEGIN
Acquire(Processors);
LocalBroadcast(h, msg, flags);
Release(Processors)
END Broadcast;
PROCEDURE StartAll*;
BEGIN
Acquire(Processors);
ASSERT(stopped & (ipcBusy = {}));
ipcBusy := allProcessors - {ID()};
stopped := FALSE;
WHILE ipcBusy # {} DO SpinHint END;
Release(Processors)
END StartAll;
PROCEDURE HandleFlushTLB(id: LONGINT; CONST state: State; msg: Message);
CODE {SYSTEM.i386, SYSTEM.Privileged}
MOV EAX, CR3
MOV CR3, EAX
END HandleFlushTLB;
PROCEDURE GlobalFlushTLB;
BEGIN
Acquire(Processors);
LocalBroadcast(HandleFlushTLB, NIL, {Self, FrontBarrier, BackBarrier});
Release(Processors)
END GlobalFlushTLB;
PROCEDURE HandleFlushCache(id: LONGINT; CONST state: State; msg: Message);
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
WBINVD ; write back and invalidate internal cache and initiate write back and invalidation of external caches
END HandleFlushCache;
PROCEDURE GlobalFlushCache;
BEGIN
Acquire(Processors);
LocalBroadcast(HandleFlushCache, NIL, {Self, FrontBarrier, BackBarrier});
Release(Processors)
END GlobalFlushCache;
PROCEDURE HandleKernelCall(VAR state: State);
BEGIN
IF IFBit IN state.FLAGS THEN
Sti
END;
CASE state.EAX OF
|2:
IF IFBit IN state.FLAGS THEN
HLT
END
END
END HandleKernelCall;
PROCEDURE HandleGetTimestamp(id: LONGINT; CONST state: State; msg: Message);
BEGIN
time[id] := GetTimer()
END HandleGetTimestamp;
PROCEDURE GlobalGetTimestamp;
VAR t: TimeArray; i: LONGINT; mean, var, n: HUGEINT;
BEGIN
Acquire(Processors);
LocalBroadcast(HandleGetTimestamp, NIL, {Self, FrontBarrier});
LocalBroadcast(HandleGetTimestamp, NIL, {Self, FrontBarrier});
t := time;
Release(Processors);
Acquire (TraceOutput);
FOR i := 0 TO numProcessors-1 DO Trace.HIntHex(t[i], 17) END;
IF numProcessors > 1 THEN
mean := 0;
n := numProcessors;
FOR i := 0 TO numProcessors-1 DO
INC (mean, t[i])
END;
mean := DivH(mean, n);
var := 0;
FOR i := 0 TO numProcessors-1 DO
n := t[i] - mean;
INC (var, MulH(n, n))
END;
var := DivH(var, numProcessors - 1);
Trace.String(" mean="); Trace.HIntHex(mean, 16);
Trace.String(" var="); Trace.HIntHex(var, 16);
Trace.String(" var="); Trace.Int(SHORT (var), 1);
Trace.String(" diff:");
FOR i := 0 TO numProcessors-1 DO
Trace.Int(SHORT (t[i] - mean), 1); Trace.Char(" ")
END
END;
Release (TraceOutput);
END GlobalGetTimestamp;
PROCEDURE ParseProcessor(adr: SYSTEM.ADDRESS);
VAR id, idx, signature, family, feat, ver, log: LONGINT; flags: SET; string : ARRAY 8 OF CHAR;
BEGIN
SYSTEM.GET(adr, SYSTEM.VAL (LONGINT, flags));
id := ASH(SYSTEM.VAL (LONGINT, flags * {8..15}), -8);
ver := ASH(SYSTEM.VAL (LONGINT, flags * {16..23}), -16);
SYSTEM.GET (adr+4, signature);
family := ASH(signature, -8) MOD 10H;
SYSTEM.GET (adr+8, feat);
idx := -1;
IF (family # 0) & (signature MOD 1000H # 0FFFH) & (24 IN flags) & (id < LEN(idMap)) & (idMap[id] = -1) THEN
IF 25 IN flags THEN idx := 0
ELSIF numProcessors < maxProcessors THEN idx := numProcessors; INC(numProcessors)
ELSE
END
END;
IF idx # -1 THEN apicVer[idx] := ver; idMap[id] := SHORT(SHORT(idx)) END;
Trace.String(" Processor "); Trace.Int(id, 1);
Trace.String(", APIC"); Trace.Hex(ver, -3);
Trace.String(", ver "); Trace.Int(family, 1);
Trace.Char("."); Trace.Int(ASH(signature, -4) MOD 10H, 1);
Trace.Char("."); Trace.Int(signature MOD 10H, 1);
Trace.String(", features "); Trace.Hex(feat, 9);
Trace.String(", ID "); Trace.Int(idx, 1);
IF (threadsPerCore > 1) THEN Trace.String(" ("); Trace.Int(threadsPerCore, 0); Trace.String(" threads)"); END;
Trace.Ln;
IF (threadsPerCore > 1) THEN
GetConfig("DisableHyperthreading", string);
IF (string = "1") THEN
Trace.String("Machine: Hyperthreading disabled."); Trace.Ln;
RETURN;
END;
log := (SYSTEM.LSH(CPUID1(), -16) MOD 256);
WHILE log > 1 DO
INC(id); DEC(log);
IF numProcessors < maxProcessors THEN
idx := numProcessors; INC(numProcessors);
apicVer[idx] := ver; idMap[id] := SHORT(SHORT(idx))
END
END
END
END ParseProcessor;
PROCEDURE ParseMPConfig;
VAR adr, x: SYSTEM.ADDRESS; i: LONGINT; entries: INTEGER; ch: CHAR; s: SET; str: ARRAY 8 OF CHAR;
BEGIN
localAPIC := 0; numProcessors := 1; allProcessors := {0};
FOR i := 0 TO LEN(idMap)-1 DO idMap[i] := -1 END;
FOR i := 0 TO MaxCPU-1 DO started[i] := FALSE END;
adr := configMP;
GetConfig("MaxProcs", str);
i := 0; maxProcessors := StrToInt(i, str);
IF maxProcessors = 0 THEN maxProcessors := MaxCPU END;
IF (maxProcessors > 0) & (adr # NilAdr) THEN
Trace.String("Machine: Intel MP Spec "); Trace.Int(ORD(revMP) DIV 10H + 1, 1);
Trace.Char("."); Trace.Int(ORD(revMP) MOD 10H, 1); Trace.Ln;
IF TraceVerbose THEN
IF ODD(ASH(ORD(featureMP[1]), -7)) THEN
Trace.StringLn (" PIC mode");
ELSE
Trace.StringLn (" Virtual wire mode");
END
END;
IF featureMP[0] # 0X THEN
Trace.String(" Default config "); Trace.Int(ORD(featureMP[0]), 1); Trace.Ln;
localAPIC := SHORT(0FEE00000H);
apicVer[0] := 0; apicVer[1] := 0
ELSE
MapPhysical(adr, 68*1024, adr);
SYSTEM.GET (adr, x); ASSERT(x = 504D4350H);
SYSTEM.GET (adr+4, x);
ASSERT(ChecksumMP(adr, x MOD 10000H) = 0);
IF TraceVerbose THEN
Trace.String(" ID: ");
FOR x := adr+8 TO adr+27 DO
SYSTEM.GET (x, ch); Trace.Char(ch);
IF x = adr+15 THEN Trace.Char(" ") END
END;
Trace.Ln
END;
localAPIC := 0; SYSTEM.GET(adr+36, SYSTEM.VAL (LONGINT, localAPIC));
IF TraceVerbose THEN Trace.String(" Local APIC:"); Trace.Address (localAPIC); Trace.Ln END;
SYSTEM.GET (adr+34, entries);
INC(adr, 44);
WHILE entries > 0 DO
SYSTEM.GET (adr, ch);
CASE ORD(ch) OF
0:
ParseProcessor(adr);
INC(adr, 20)
|1:
IF TraceVerbose THEN
SYSTEM.GET (adr+1, ch);
Trace.String(" Bus "); Trace.Int(ORD(ch), 1); Trace.String(": ");
FOR x := adr+2 TO adr+7 DO SYSTEM.GET (x, ch); Trace.Char(ch) END;
Trace.Ln
END;
INC(adr, 8)
|2:
IF TraceVerbose THEN
SYSTEM.GET (adr+1, ch); Trace.String(" IO APIC ID:"); Trace.Hex(ORD(ch), -3);
SYSTEM.GET (adr+2, ch); Trace.String(", version "); Trace.Int(ORD(ch), 1);
SYSTEM.GET(adr, SYSTEM.VAL (LONGINT, s)); IF ~(24 IN s) THEN Trace.String(" (disabled)") END;
Trace.Ln
END;
INC(adr, 8)
|3:
INC(adr, 8)
|4:
INC(adr, 8)
END;
DEC(entries)
END
END
END;
IF localAPIC = 0 THEN
Trace.StringLn ("Machine: Single-processor");
apicVer[0] := 0
END;
started[0] := TRUE;
FOR i := 0 TO MaxCPU-1 DO revIDmap[i] := -1 END;
FOR i := 0 TO LEN(idMap)-1 DO
x := idMap[i];
IF x # -1 THEN
ASSERT(revIDmap[x] = -1);
revIDmap[x] := SHORT(SHORT(i))
END
END;
GetConfig("TimerRate", str);
i := 0; timerRate := StrToInt(i, str);
IF timerRate = 0 THEN timerRate := 1000 END;
IF TraceProcessor THEN
GetConfig("TraceProc", str);
i := 0; traceProcessor := StrToInt(i, str) # 0
END
END ParseMPConfig;
PROCEDURE GetBusClockRate(): LONGINT;
VAR timer: LONGINT; t: LONGINT;
BEGIN
t := ticks;
REPEAT UNTIL ticks # t;
timer := ticks + ClockRateDelay;
ApicPut(380H, SYSTEM.VAL (SET, MAX(LONGINT)));
REPEAT UNTIL timer - ticks <= 0;
t := MAX(LONGINT) - SYSTEM.VAL (LONGINT, ApicGet(390H));
IF t <= MAX(LONGINT) DIV 1000 THEN
RETURN 1000 * t DIV ClockRateDelay
ELSE
RETURN t DIV ClockRateDelay * 1000
END
END GetBusClockRate;
PROCEDURE InitMPTimer;
VAR rate: LONGINT;
BEGIN
IF timerRate > 0 THEN
ApicPut(3E0H, {0,1,3});
ApicPut(320H, {16} + SYSTEM.VAL (SET, MPTMR));
rate := GetBusClockRate();
busHz0[ID()] := rate;
rate := (rate+500000) DIV 1000000 * 1000000;
busHz1[ID()] := rate;
ApicPut(320H, {17} + SYSTEM.VAL (SET, MPTMR));
ApicPut(380H, SYSTEM.VAL (SET, rate DIV timerRate))
END
END InitMPTimer;
PROCEDURE HandleMPTimer(VAR state: State);
BEGIN
timer(ID(), state);
ApicPut(0B0H, {});
Sti;
Timeslice(state)
END HandleMPTimer;
PROCEDURE HandleUPTimer(VAR state: State);
BEGIN
timer(0, state);
Sti;
Timeslice(state)
END HandleUPTimer;
PROCEDURE DummyEvent(id: LONGINT; CONST state: State);
END DummyEvent;
PROCEDURE InstallEventHandler* (h: EventHandler);
BEGIN
IF h # NIL THEN timer := h ELSE timer := DummyEvent END
END InstallEventHandler;
PROCEDURE InitAPIC;
BEGIN
ASSERT(MPSPU MOD 16 = 15);
ApicPut(0F0H, {8} + SYSTEM.VAL (SET, MPSPU));
ApicPut(370H, SYSTEM.VAL (SET, MPERR));
InitMPTimer
END InitAPIC;
PROCEDURE StartMP;
VAR id: LONGINT; state: State;
BEGIN
InitAPIC;
id := ID();
Acquire (TraceOutput);
Trace.String (" P"); Trace.Int(id, 1); Trace.StringLn (" running");
Release (TraceOutput);
IF TraceProcessor & traceProcessor & (id = numProcessors-1) THEN
DEC(numProcessors)
ELSE
INCL(allProcessors, id)
END;
started[id] := TRUE;
IF TraceProcessor & ~(id IN allProcessors) THEN
Acquire (TraceOutput);
Trace.String (" P"); Trace.Int(id, 1); Trace.StringLn (" tracing");
Release (TraceOutput);
LOOP
IF traceProcessorProc # NIL THEN traceProcessorProc(id, state) END;
SpinHint
END
END;
WHILE stopped DO SpinHint END;
AtomicExcl(ipcBusy, id);
Acquire (TraceOutput);
Trace.String (" P"); Trace.Int(id, 1); Trace.StringLn(" scheduling");
Release (TraceOutput);
ASSERT(id = ID());
start;
END StartMP;
PROCEDURE EnterMP;
BEGIN
InitProcessor;
InitMemory;
Start;
StartMP
END EnterMP;
PROCEDURE StartProcessor(phys: SYSTEM.ADDRESS; apicid: LONGINT; startup: BOOLEAN);
VAR j, k: LONGINT; s: SET; timer: LONGINT;
BEGIN
ApicPut(280H, {}); s := ApicGet(280H);
ApicPut(310H, SYSTEM.VAL (SET, ASH(apicid, 24)));
ApicPut(300H, {8, 10, 14, 15});
timer := ticks + 5;
REPEAT UNTIL timer - ticks <= 0;
ApicPut(310H, SYSTEM.VAL (SET, ASH(apicid, 24)));
ApicPut(300H, {8, 10, 15});
IF startup THEN
j := 0; k := 2;
WHILE j # k DO
ApicPut(280H, {});
ApicPut(310H, SYSTEM.VAL (SET, ASH(apicid, 24)));
ApicPut(300H, {9, 10} + SYSTEM.VAL (SET, phys DIV 4096 MOD 256));
timer := ticks + 10;
REPEAT UNTIL timer - ticks <= 0;
IF ~(12 IN ApicGet(300H)) THEN
IF ApicGet(280H) * {0..3, 5..7} = {} THEN k := j
ELSE INC(j)
END
ELSE INC(j)
END
END
END
END StartProcessor;
PROCEDURE BootMP;
VAR phys, page0Adr: SYSTEM.ADDRESS; i: LONGINT; timer: LONGINT;
BEGIN
stopped := TRUE; ipcBusy := {};
InitBootPage(EnterMP, phys);
MapPhysical(0, 4096, page0Adr);
Acquire(TraceOutput); Trace.String("Machine: Booting processors... "); Trace.Ln; Release(TraceOutput);
FOR i := 1 TO numProcessors-1 DO
SYSTEM.PUT (page0Adr + 467H, ASH(phys, 16-4));
PutNVByte(15, 0AX);
Acquire(TraceOutput); Trace.String(" P0 starting P"); Trace.Int(i, 1); Trace.Ln; Release(TraceOutput);
StartProcessor(phys, revIDmap[i], apicVer[i] >= 10H);
timer := ticks + 5000;
REPEAT SpinHint UNTIL started[i] OR (timer - ticks <= 0);
Acquire(TraceOutput);
IF started[i] THEN
Trace.String(" P0 recognized P"); Trace.Int(i, 1);
ELSE
Trace.String(" P0 timeout on P"); Trace.Int(i, 1);
END;
Trace.Ln;
Release(TraceOutput);
END;
SYSTEM.PUT (page0Adr + 467H, SYSTEM.VAL (LONGINT, 0));
UnmapPhysical(page0Adr, 4096);
PutNVByte(15, 0X)
END BootMP;
PROCEDURE TimerInterruptHandler(VAR state: State);
BEGIN
INC(ticks);
DEC(eventCount);
IF eventCount = 0 THEN
eventCount := eventMax; event(state)
END
END TimerInterruptHandler;
PROCEDURE Dummy(VAR state: State);
END Dummy;
PROCEDURE InitTicks;
CONST Div = (2*TimerClock + Second) DIV (2*Second);
BEGIN
eventCount := 0; eventMax := 0; event := Dummy;
ASSERT(Div <= 65535);
Portout8(43H, 34X); Wait;
Portout8(40H, CHR(Div MOD 100H)); Wait;
Portout8(40H, CHR(ASH(Div, -8)));
InstallHandler(TimerInterruptHandler, IRQ0)
END InitTicks;
PROCEDURE InstallTickHandler(handler: Handler; divisor: LONGINT);
BEGIN
eventMax := divisor; event := handler;
eventCount := eventMax
END InstallTickHandler;
PROCEDURE InitProcessors*;
BEGIN
traceProcessor := FALSE; traceProcessorProc := NIL;
ASSERT(Second = 1000);
InitTicks;
timer := DummyEvent;
ParseMPConfig;
InstallHandler(HandleIPC, MPIPCLocal);
IF localAPIC # 0 THEN
InitAPICArea(localAPIC, 4096);
InitAPICIDAdr(localAPIC+20H, idMap);
ASSERT(MPSPU MOD 16 = 15);
InstallHandler(HandleError, MPERR);
InstallHandler(HandleMPTimer, MPTMR);
InstallHandler(HandleIPC, MPIPC);
InitAPIC;
IF numProcessors > 1 THEN BootMP END
ELSE
IF timerRate > 0 THEN
InstallTickHandler(HandleUPTimer, Second DIV timerRate)
END
END;
InstallHandler(HandleKernelCall, MPKC);
END InitProcessors;
PROCEDURE TraceChar (c: CHAR);
VAR status: SHORTINT;
PROCEDURE Scroll;
VAR adr: SYSTEM.ADDRESS; off: SYSTEM.SIZE;
BEGIN
adr := traceBase + TraceLen;
SYSTEM.MOVE (adr, adr - TraceLen, TraceSize - TraceLen);
adr := traceBase + TraceSize - TraceLen;
FOR off := 0 TO TraceLen - SYSTEM.SIZEOF(INTEGER) BY SYSTEM.SIZEOF(INTEGER) DO SYSTEM.PUT16 (adr + off, 100H * 7H + 32) END
END Scroll;
BEGIN
IF TraceV24 IN traceMode THEN
REPEAT
Portin8 (SHORT(tracePort + 5), SYSTEM.VAL(CHAR,status))
UNTIL ODD (status DIV 20H);
Portout8 (SHORT(tracePort), c);
END;
IF TraceScreen IN traceMode THEN
IF c = 9X THEN c := 20X END;
IF c = 0DX THEN
DEC (tracePos, tracePos MOD TraceLen)
ELSIF c = 0AX THEN
IF tracePos < TraceSize THEN
INC (tracePos, TraceLen)
ELSE
Scroll
END
ELSE
IF tracePos >= TraceSize THEN
Scroll;
DEC (tracePos, TraceLen)
END;
SYSTEM.PUT16 (traceBase + tracePos, 100H * traceColor + ORD (c));
INC (tracePos, SYSTEM.SIZEOF(INTEGER))
END
END
END TraceChar;
PROCEDURE TraceColor (c: SHORTINT);
BEGIN traceColor := c;
END TraceColor;
PROCEDURE InitTrace;
CONST MaxPorts = 8;
VAR i, p, bps: LONGINT; off: SYSTEM.SIZE; s, name: ARRAY 32 OF CHAR;
baselist: ARRAY MaxPorts OF LONGINT;
BEGIN
GetConfig ("TraceMode", s);
p := 0; traceMode := SYSTEM.VAL (SET, StrToInt (p, s));
IF TraceScreen IN traceMode THEN
GetConfig ("TraceMem", s);
p := 0; traceBase := SYSTEM.VAL (SYSTEM.ADDRESS, StrToInt (p, s));
IF traceBase = 0 THEN traceBase := 0B8000H END;
FOR off := 0 TO TraceSize - SYSTEM.SIZEOF(INTEGER) BY SYSTEM.SIZEOF(INTEGER) DO SYSTEM.PUT16 (traceBase + off, 100H * 7H + 32) END;
tracePos := 0;
Portout8(3D4H, 0EX);
Portout8(3D5H, CHR((TraceWidth*TraceHeight) DIV 100H));
Portout8(3D4H, 0FX);
Portout8(3D5H, CHR((TraceWidth*TraceHeight) MOD 100H))
END;
IF TraceV24 IN traceMode THEN
FOR i := 0 TO MaxPorts - 1 DO
COPY ("COMx", name); name[3] := CHR (ORD ("1") + i);
GetConfig (name, s); p := 0; baselist[i] := StrToInt (p, s);
END;
IF baselist[0] = 0 THEN baselist[0] := 3F8H END;
IF baselist[1] = 0 THEN baselist[1] := 2F8H END;
GetConfig("TracePort", s); p := 0; p := StrToInt(p, s); DEC(p);
IF (p >= 0) & (p < MaxPorts) THEN tracePort := baselist[p] ELSE tracePort := baselist[0] END;
ASSERT(tracePort > 0);
GetConfig("TraceBPS", s); p := 0; bps := StrToInt(p, s);
IF bps <= 0 THEN bps := 38400 END;
Portout8 (SHORT(tracePort + 3), 80X);
bps := 115200 DIV bps;
Portout8 (SHORT(tracePort + 1), CHR (bps DIV 100H));
Portout8 (SHORT(tracePort), CHR (bps MOD 100H));
Portout8 (SHORT(tracePort + 3), 3X);
Portout8 (SHORT(tracePort + 4), 3X);
Portout8 (SHORT(tracePort + 1), 0X);
END;
traceColor := 7; Trace.Char := TraceChar; Trace.Color := TraceColor;
END InitTrace;
PROCEDURE InitHandlers;
VAR i,j: LONGINT;
BEGIN
FOR i := 0 TO IDTSize - 1 DO
FOR j := 0 TO MaxNumHandlers - 1 DO
intHandler[i, j].valid := FALSE;
intHandler[i, j].handler := NIL
END
END;
END InitHandlers;
PROCEDURE - PushBootValues;
CODE{SYSTEM.i386}
PUSH EDI
PUSH ESI
PUSH EAX
END PushBootValues;
PROCEDURE - GetBootValue(): LONGINT;
CODE{SYSTEM.i386}
POP EAX
END GetBootValue;
BEGIN
PushBootValues;
bootFlag := GetBootValue();
initRegs[0] := GetBootValue();
initRegs[1] := GetBootValue();
SYSTEM.PUT16(0472H, 01234H);
ReadBootTable(bootFlag);
InitTrace;
Trace.String("Machine: "); Trace.Blue;Trace.StringLn (Version); Trace.Default;
CheckMemory;
SearchMP;
AllocateDMA;
version := Version;
InitBoot;
InitProcessor;
InitLocks;
NmaxUserStacks := MaxUserStacks;
ASSERT(ASH(1, PSlog2) = PS);
Trace.String("Machine: Enabling MMU... ");
InitSegments;
InitPages;
InitMemory;
Trace.Green; Trace.StringLn("Ok"); Trace.Default;
InitHandlers;
default.valid := FALSE;
END Machine.
(*
03.03.1998 pjm First version
30.06.1999 pjm ProcessorID moved to AosProcessor
*)
(**
Notes
This module defines an interface to the boot environment of the system. The facilities provided here are only intended for the lowest levels of the system, and should never be directly imported by user modules (exceptions are noted below). They are highly specific to the system hardware and firmware architecture.
Typically a machine has some type of firmware that performs initial testing and setup of the system. The firmware initiates the operating system bootstrap loader, which loads the boot file. This module is the first module in the statically linked boot file that gets control.
There are two more-or-less general procedures in this module: GetConfig and StrToInt. GetConfig is used to query low-level system settings, e.g., the location of the boot file system. StrToInt is a utility procedure that parses numeric strings.
Config strings:
ExtMemSize Specifies size of extended memory (above 1MB) in MB. This value is not checked for validity. Setting it false may cause the system to fail, possible after running for some time. The memory size is usually detected automatically, but if the detection does not work for some reason, or if you want to limit the amount of memory detected, this string can be set. For example, if the machine has 64MB of memory, this value can be set as ExtMemSize="63".
*)