TempleOSTS_130315.ISO
This commit is contained in:
1 parent
e57428affb
commit
17b71f1a2d
958 files changed
+26043
-15907
No files matched your search
@@ -0,0 +1,94 @@
|
||||
$WW,1$$FG,5$$TX+CX,"64-Bit Assembly Quiz"$$FG$
|
||||
|
||||
1) In 64-bit mode, how many bytes are always pushed?
|
||||
|
||||
PUSH 12
|
||||
PUSH EAX
|
||||
|
||||
2) What happens to the upper 32-bits?
|
||||
|
||||
XOR EAX,EAX
|
||||
MOV EAX,0x12345678
|
||||
MOV EAX,0x80000000
|
||||
|
||||
3) How do you set FS or GS values?
|
||||
|
||||
4) If FS points to current task record, what's wrong with this instruction?
|
||||
|
||||
MOV RAX,U64 FS:[TSS_SOME_MEMBER]
|
||||
|
||||
5) Which instruction takes more bytes?
|
||||
|
||||
MOV RAX,U64 [R8]
|
||||
MOV RAX,U64 [R13]
|
||||
|
||||
6) Are these the same number of bytes?
|
||||
|
||||
MOV RAX,1234
|
||||
MOV R8,1234
|
||||
MOV EAX,1234
|
||||
|
||||
7) True or False
|
||||
|
||||
a) You can access the lowest byte of RAX.
|
||||
|
||||
b) You can access the lowest byte of ESI.
|
||||
|
||||
c) You can access the second-to-lowest byte of RAX.
|
||||
|
||||
d) You can access the second-to-lowest byte of ESI.
|
||||
|
||||
8) How do you call a subroutine at 0x10,0000,0000 from code at 0x00,0010,0000?
|
||||
|
||||
9) How much faster is a REL32 call instruction compared to a software interrupt or SYSCALL?
|
||||
|
||||
10) How long does an IN or OUT instruction take on a 1GHz machine and on a 3GHz machine?
|
||||
|
||||
11) How do you push all 16 registers?
|
||||
|
||||
12) Should you put the registers in a TSS?
|
||||
|
||||
13) You can have 4K or 4Meg pages in 32-bit mode. You can have 4K or what size pages in 64-bit mode?
|
||||
|
||||
14) On a fresh CPU with an empty TLB, how many memory accesses (page tables) does it take to access one virtual address?
|
||||
|
||||
15) Is there an instruction to PUSH or POP all registers?
|
||||
|
||||
----
|
||||
|
||||
TempleOS identity-maps everything, all the time, so the usual convention of upper memory being for kernel does not apply. It uses physical addresses, basically. It puts all code in the lowest 2-Gig memory range so that it can use the CALL REL32 instruction, the fastest. It never changes privilege levels or messes with page tables, once it is up-and-running.
|
||||
|
||||
----
|
||||
|
||||
ANSWERS:
|
||||
|
||||
1) All stack pushes and pops are 64-bits.
|
||||
|
||||
2) The upper 32-bits are set to zero.
|
||||
|
||||
3) To set FS or GS, you use WRMSR to write a model specific register. See $LK,"IA32_FS_BASE","MN:IA32_FS_BASE"$ and $LK,"SET_FS_BASE","MN:SET_FS_BASE"$.
|
||||
|
||||
4) Displacement addressing is now RIP relative, so RIP would be added to TSS_SOME_MEMBER. (Useless)
|
||||
|
||||
5) The R13 instruction takes one more byte because it is like $LK,"CREG_RBP","MN:CREG_RBP"$ in the ModR.
|
||||
|
||||
6) The RAX instruction has a REX byte prefix to specify 64-bit size. So does the R8 instruction, but in the REX is also a flag to specify upper-8 reg. For EAX, there is no need of REX.
|
||||
|
||||
7) You can access the lowest byte of any reg. You can access AH but not the second-to-lowest byte of ESI.
|
||||
|
||||
8) To call a subroutine farther than 2 Gig away, you put the address into RAX, then CALL RAX.
|
||||
|
||||
9) CALL REL32 is significantly faster. See $LK-A,"::/TempleOS/Demo/Lectures/InterruptDemo.CPP"$.
|
||||
|
||||
10) IN or OUT instructions happen at a fixed speed based on the original ISA bus clock.
|
||||
|
||||
11) PUSHAD is not available for 64-bit mode, so you do it by hand.
|
||||
|
||||
12) The TSS is no longer used to hold the task state because there are 16 regs and they are 64-bits, not 32-bits. I guess Intel decided doing it by hand was better than TSSes.
|
||||
|
||||
13) 64-bit mode has 4K or 2Meg page size.
|
||||
|
||||
14) For one access, there are 3-4 levels of page tables plus the location itself.
|
||||
|
||||
15) There is no instruction to PUSH or POP all registers.
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
|
||||
$WW,1$You may wonder why I use "&" instead of "%".
|
||||
$WW,0$
|
||||
*/
|
||||
|
||||
#define SAMPLE_SIZE 100000000
|
||||
|
||||
U0 TimeIns()
|
||||
{
|
||||
I64 start1,end1,start2,end2,overhead_time,test_time;
|
||||
I64 reg i,reg temp;
|
||||
Bool old_preempt=Preempt(OFF);
|
||||
|
||||
CPURep;
|
||||
|
||||
//Measure Loop Overhead
|
||||
start1=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++) {
|
||||
}
|
||||
end1=GetTimeStamp;
|
||||
overhead_time=end1-start1;
|
||||
|
||||
//Measure remainder...
|
||||
start2=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
temp=i%0x400;
|
||||
end2=GetTimeStamp;
|
||||
test_time=end2-start2;
|
||||
"Remainder Version #1 Cycles\t: %10.5f\n",
|
||||
ToF64(test_time-overhead_time)/SAMPLE_SIZE;
|
||||
|
||||
//Measure remainder...
|
||||
start2=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
temp=i&0x3FF;
|
||||
end2=GetTimeStamp;
|
||||
test_time=end2-start2;
|
||||
"Remainder Version #2 Cycles\t: %10.5f\n",
|
||||
ToF64(test_time-overhead_time)/SAMPLE_SIZE;
|
||||
|
||||
Preempt(old_preempt);
|
||||
}
|
||||
|
||||
TimeIns;
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
Remainder Version #1 Cycles : 26.85345
|
||||
Remainder Version #2 Cycles : -0.00800
|
||||
|
||||
$HL,1$*/
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
"Fixed point" means you use integers
|
||||
that are scaled by a value. A common
|
||||
example would be using number of pennies
|
||||
instead of dollars with a float.
|
||||
|
||||
Fixed-point used to be much $TX+HTML,"faster","http://en.wikipedia.org/wiki/X87"$,
|
||||
but modern processors do well with
|
||||
floats. It also depends on the compiler
|
||||
and my compiler used to do floats poorly.
|
||||
It's a little better.
|
||||
|
||||
I often use 64-bit ints with upper 32-bits
|
||||
as integer and lower 32-bits as fraction.
|
||||
|
||||
See $LK-A,"::/TempleOS/Demo/SubIntAccess.CPP"$ for how
|
||||
to access upper or lower 32-bits.
|
||||
|
||||
For a complete lst of nonstandard compiler
|
||||
features, see $LK-A,"::/TempleOS/Doc/Differences.TXT"$.
|
||||
|
||||
*/
|
||||
|
||||
U0 Main()
|
||||
{
|
||||
F64 t0,f_sum=0,f_val;
|
||||
I64 i ,i_sum=0,i_val;
|
||||
|
||||
i_val= 2.0000002 *0x100000000;
|
||||
t0=tT;
|
||||
for (i=1000000000;i;i--)
|
||||
i_sum+=i_val;
|
||||
"Integer Sum:%.9f Time:%7.3fs\n",i_sum/ToF64(0x100000000),tT-t0;
|
||||
|
||||
f_val= 2.0000002;
|
||||
t0=tT;
|
||||
for (i=1000000000;i;i--)
|
||||
f_sum+=f_val;
|
||||
"Float Sum:%.9f Time:%7.3fs\n",f_sum,tT-t0;
|
||||
NewLine;
|
||||
}
|
||||
|
||||
U0 DoIt2()
|
||||
{
|
||||
I64 i=0x123456789ABCDEF0;
|
||||
"i =%X\n",i;
|
||||
|
||||
"i&0xFFFFFFFF=%X\n",i&0xFFFFFFFF;
|
||||
"i>>32 =%X\n",i>>32;
|
||||
|
||||
/*
|
||||
Standard integer types are declared
|
||||
with a special compiler feature which
|
||||
allows a structure to be accessed as a
|
||||
whole. That's why the i variable can
|
||||
be accessed normally in addition to
|
||||
structure member access $LK,"I64","MN:I64"$. The actual
|
||||
intrinsic compiler type is U64i.
|
||||
|
||||
public U64i union I64
|
||||
{
|
||||
I8i i8[8];
|
||||
U8i u8[8];
|
||||
I16 i16[4];
|
||||
U16 u16[4];
|
||||
I32 i32[2];
|
||||
U32 u32[2];
|
||||
};
|
||||
|
||||
It's not quite as great as it seems
|
||||
because the compiler decides it cannot
|
||||
place i into a register, so there is a
|
||||
penalty.
|
||||
|
||||
For a complete lst of nonstandard compiler
|
||||
features, see $LK-A,"::/TempleOS/Doc/Differences.TXT"$.
|
||||
|
||||
*/
|
||||
|
||||
"i.u32[0] =%X\n",i.u32[0];
|
||||
"i.u32[1] =%X\n",i.u32[1];
|
||||
}
|
||||
|
||||
CPURep;
|
||||
Main;
|
||||
DoIt2;
|
||||
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/FixedPointAdvanced.CPP"$
|
||||
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
Integer Sum:2000000199.768689408 Time: 0.787s
|
||||
Float Sum:2000000225.656126208 Time: 3.918s
|
||||
|
||||
i =123456789ABCDEF0
|
||||
i&0xFFFFFFFF=9ABCDEF0
|
||||
i>>32 =12345678
|
||||
i.u32[0] =9ABCDEF0
|
||||
i.u32[1] =12345678
|
||||
|
||||
$HL,1$*/
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
"Fixed point" means you use integers
|
||||
that are scaled by a value. A common
|
||||
example would be using number of pennies
|
||||
instead of dollars with a float.
|
||||
|
||||
Fixed-point used to be much faster,
|
||||
but modern processors do well with
|
||||
floats. It also depends on the compiler
|
||||
and my compiler used to do floats poorly.
|
||||
|
||||
I often use 64-bit ints with upper 32-bits
|
||||
as integer and lower 32-bits as fraction.
|
||||
|
||||
See $LK-A,"::/TempleOS/Demo/SubIntAccess.CPP"$ for how
|
||||
to access upper or lower 32-bits.
|
||||
|
||||
*/
|
||||
#define SAMPLE_SIZE 10000000
|
||||
|
||||
I32 coordinates[65536];
|
||||
|
||||
asm {
|
||||
IMPORT coordinates;
|
||||
|
||||
_ASM_FIXED_POINT::
|
||||
ENTER 0
|
||||
PUSH RSI
|
||||
PUSH RDI
|
||||
MOV RSI,coordinates
|
||||
MOV RDI,ToI64(Sin(1.0)*0x100000000)
|
||||
XOR RBX,RBX //SUM
|
||||
MOV RCX,SAMPLE_SIZE-1
|
||||
@@05: XOR RDX,RDX
|
||||
MOV DX,CX
|
||||
MOVSXD RAX,U32 [RSI+RDX*4]
|
||||
IMUL RDI
|
||||
SAR RAX,32
|
||||
ADD RBX,RAX
|
||||
DEC RCX
|
||||
JGE @@05
|
||||
MOV RAX,RBX
|
||||
POP RDI
|
||||
POP RSI
|
||||
LEAVE
|
||||
RET
|
||||
|
||||
SINE_VAL: DU64 Sin(1.0);
|
||||
RET_VAL: DU64 0;
|
||||
|
||||
_ASM_FLOAT::
|
||||
ENTER 0
|
||||
PUSH RSI
|
||||
MOV RSI,coordinates
|
||||
FLD U64 [SINE_VAL]
|
||||
FLDZ
|
||||
MOV RCX,SAMPLE_SIZE-1
|
||||
@@05: XOR RDX,RDX
|
||||
MOV DX,CX
|
||||
FILD U32 [RSI+RDX*4]
|
||||
FMUL ST0,ST2
|
||||
FADDP ST1,ST0
|
||||
DEC RCX
|
||||
JGE @@05
|
||||
FISTP U64 [RET_VAL]
|
||||
MOV RAX,U64 [RET_VAL]
|
||||
FFREE ST0
|
||||
FINCSTP
|
||||
POP RSI
|
||||
LEAVE
|
||||
RET
|
||||
}
|
||||
|
||||
_extern _ASM_FIXED_POINT I64 AsmFixedPt();
|
||||
_extern _ASM_FLOAT I64 AsmFloat();
|
||||
|
||||
U0 Main()
|
||||
{
|
||||
I64 start,end,overhead_time,test_time;
|
||||
F64 d1,fsum;
|
||||
I64 reg i,temp,reg d2,reg sum;
|
||||
Bool old_preempt=Preempt(OFF);
|
||||
|
||||
CPURep;
|
||||
|
||||
//Set-up some sample coordinates
|
||||
for (i=0;i<65536;i++)
|
||||
coordinates[i]=RandU32;
|
||||
|
||||
//Measure Loop Overhead
|
||||
start=GetTimeStamp;
|
||||
for (i=SAMPLE_SIZE-1;i>=0;i--) {
|
||||
}
|
||||
end=GetTimeStamp;
|
||||
overhead_time=end-start;
|
||||
"$$FG,RED$$Overhead Cycles :%10.5f$$FG$$\n",
|
||||
ToF64(overhead_time)/SAMPLE_SIZE;
|
||||
|
||||
//Measure F64 arithmetic
|
||||
// (Some of this is due to crappy
|
||||
// compiler code.)
|
||||
d1=Sin(1.0);
|
||||
fsum=0;
|
||||
start=GetTimeStamp;
|
||||
for (i=SAMPLE_SIZE-1;i>=0;i--)
|
||||
fsum+=d1*coordinates[i&65535];
|
||||
end=GetTimeStamp;
|
||||
test_time=end-start;
|
||||
"Float Sum :%X\n",ToI64(fsum);
|
||||
"$$FG,RED$$Float Cycles :%10.5f$$FG$$\n",
|
||||
ToF64(test_time)/SAMPLE_SIZE;
|
||||
|
||||
|
||||
//Measure fixed point arithmetic
|
||||
d2=Sin(1.0)*0x100000000;
|
||||
sum=0;
|
||||
start=GetTimeStamp;
|
||||
for (i=SAMPLE_SIZE-1;i>=0;i--) {
|
||||
temp=d2*coordinates[i&65535];
|
||||
sum+=temp.i32[1];
|
||||
}
|
||||
end=GetTimeStamp;
|
||||
test_time=end-start;
|
||||
"Fixed Point Sum :%X\n",sum;
|
||||
"$$FG,RED$$Fixed Point Cycles :%10.5f$$FG$$\n",
|
||||
ToF64(test_time)/SAMPLE_SIZE;
|
||||
|
||||
//Measure fixed point arithmetic
|
||||
start=GetTimeStamp;
|
||||
sum=AsmFixedPt;
|
||||
end=GetTimeStamp;
|
||||
test_time=end-start;
|
||||
"Asm Fixed Point Sum :%X\n",sum;
|
||||
"$$FG,RED$$Asm Fixed Point Cycles:%10.5f$$FG$$\n",
|
||||
ToF64(test_time)/SAMPLE_SIZE;
|
||||
|
||||
//Measure float arithmetic
|
||||
start=GetTimeStamp;
|
||||
sum=AsmFloat;
|
||||
end=GetTimeStamp;
|
||||
test_time=end-start;
|
||||
"Asm Float Sum :%X\n",sum;
|
||||
"$$FG,RED$$Asm Float Cycles :%10.5f$$FG$$\n",
|
||||
ToF64(test_time)/SAMPLE_SIZE;
|
||||
|
||||
Preempt(old_preempt);
|
||||
}
|
||||
|
||||
Main;
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
$FG,4$Overhead Cycles : 2.00814$FG$
|
||||
Float Sum :FFFFE1D361BEED68
|
||||
$FG,4$Float Cycles : 10.16076$FG$
|
||||
Fixed Point Sum :FFFFE1D361729914
|
||||
$FG,4$Fixed Point Cycles : 5.29392$FG$
|
||||
Asm Fixed Point Sum :FFFFE1D361729914
|
||||
$FG,4$Asm Fixed Point Cycles: 4.20464$FG$
|
||||
Asm Float Sum :FFFFE1D361BEED56
|
||||
$FG,4$Asm Float Cycles : 3.04635$FG$
|
||||
|
||||
$HL,1$*/
|
||||
@@ -0,0 +1,76 @@
|
||||
//This is the fastest you can update the whole screen.
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/MiniGrLib.CPP"$ if this interests you.
|
||||
|
||||
F64 VGAPattern(I64 p)
|
||||
{//This returns the time to update in seconds.
|
||||
F64 start=tT;
|
||||
I64 i,plane,*ptr;
|
||||
for (plane=1;plane<0x10;plane<<=1) {
|
||||
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
|
||||
OutU8(VGA_SC_DATA,plane);
|
||||
ptr=0xA0000;
|
||||
for (i=0;i<640*480/64;i++)
|
||||
*ptr++=p;
|
||||
}
|
||||
return tT-start;
|
||||
}
|
||||
|
||||
|
||||
U0 GrDemo()
|
||||
{
|
||||
I64 i;
|
||||
F64 t;
|
||||
Bool old_preempt=Preempt(OFF);
|
||||
CPURep;
|
||||
for (i=0;i<16;i++) {
|
||||
t=VGAPattern(0x5555555555555555);
|
||||
"$$FG,GREEN$$Rate:$$FG,RED$$%5.1f FPS$$FG$$"
|
||||
" $$FG,GREEN$$Load@60Hz:$$FG,RED$$%5.1f%%$$FG$$\n",1/t,100*t*60.0;
|
||||
t=VGAPattern(0x3333333333333333);
|
||||
"$$FG,GREEN$$Rate:$$FG,RED$$%5.1f FPS$$FG$$"
|
||||
" $$FG,GREEN$$Load@60Hz:$$FG,RED$$%5.1f%%$$FG$$\n",1/t,100*t*60.0;
|
||||
}
|
||||
Preempt(old_preempt);
|
||||
//<CTRL-ALT-F> will flush screen VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
GrDemo;
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
$FG,2$Rate:$FG,4$369.7 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$370.3 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$361.2 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.6%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$370.2 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$360.6 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.6%$FG$
|
||||
$FG,2$Rate:$FG,4$370.4 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$353.1 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 17.0%$FG$
|
||||
$FG,2$Rate:$FG,4$370.3 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$357.0 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.8%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$370.4 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$353.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 17.0%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$370.2 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$370.4 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.7 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.9 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
$FG,2$Rate:$FG,4$369.8 FPS$FG$ $FG,2$Load@60Hz:$FG,4$ 16.2%$FG$
|
||||
|
||||
$HL,1$*/
|
||||
@@ -0,0 +1,90 @@
|
||||
//This is the software interrupt num
|
||||
//we will use. See $LK,"Software Int's","MN:I_WAKE"$ for
|
||||
//picking a num. Nums are subject to change.
|
||||
|
||||
#define I_F_UNARY_MINUS I_USER
|
||||
|
||||
asm {
|
||||
|
||||
//This changes the sign of the floating
|
||||
//point val in RAX
|
||||
F_UNARY_MINUS_INT::
|
||||
PUSH RAX
|
||||
FLD U64 [RSP]
|
||||
FCHS
|
||||
FSTP U64 [RSP]
|
||||
FWAIT
|
||||
POP RAX
|
||||
IRET
|
||||
|
||||
//This does the same thing, but not as
|
||||
//an interrupt.
|
||||
F_UNARY_MINUS_CALL::
|
||||
PUSH RAX
|
||||
FLD U64 [RSP]
|
||||
FCHS
|
||||
FSTP U64 [RSP]
|
||||
FWAIT
|
||||
POP RAX
|
||||
RET
|
||||
|
||||
//This invokes the interrupt version
|
||||
//with a C callable function.
|
||||
_F_UM_INT::
|
||||
ENTER 0
|
||||
MOV RAX,SF_ARG1[RBP]
|
||||
INT I_F_UNARY_MINUS
|
||||
LEAVE
|
||||
RET1 8
|
||||
|
||||
//This invokes the call version
|
||||
//with a C callable function.
|
||||
_F_UM_CALL::
|
||||
ENTER 0
|
||||
MOV RAX,SF_ARG1[RBP]
|
||||
CALL F_UNARY_MINUS_CALL
|
||||
LEAVE
|
||||
RET1 8
|
||||
}
|
||||
|
||||
_extern _F_UM_INT F64 UnaryMinusInt(F64 d);
|
||||
_extern _F_UM_CALL F64 UnaryMinusCall(F64 d);
|
||||
|
||||
|
||||
#define SAMPLE_SIZE 1000000
|
||||
U0 TimeIns()
|
||||
{
|
||||
I64 start,end;
|
||||
I64 i,old_irq;
|
||||
Bool old_preempt=Preempt(OFF);
|
||||
|
||||
CPURep;
|
||||
old_irq=IntEntrySet(I_F_UNARY_MINUS,F_UNARY_MINUS_INT,IDTET_TRAP);
|
||||
|
||||
//Measure interrupt time
|
||||
start=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
UnaryMinusInt(ã);
|
||||
end=GetTimeStamp;
|
||||
"Interrupt Cycles: %10.5f\n",ToF64(end-start)/SAMPLE_SIZE;
|
||||
|
||||
//Measure call time
|
||||
start=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
UnaryMinusCall(ã);
|
||||
end=GetTimeStamp;
|
||||
"Call Cycles: %10.5f\n",ToF64(end-start)/SAMPLE_SIZE;
|
||||
|
||||
Preempt(old_preempt);
|
||||
IntEntrySet(I_F_UNARY_MINUS,old_irq,IDTET_IRQ);
|
||||
}
|
||||
|
||||
TimeIns;
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
Interrupt Cycles: 492.36652
|
||||
Call Cycles: 22.40882
|
||||
|
||||
$HL,1$*/
|
||||
@@ -0,0 +1,238 @@
|
||||
/*$WW,1$
|
||||
$TR,"Main Compiler"$
|
||||
$ID,2$The mini compiler is like the main compiler, except the main compiler's lexical analyser removes comments and does preprocessing. See $LK,"Lex","MN:Lex"$(). Set $LK,"Echo","MN:Echo"$(ON) or $LK,"LTrace","MN:LTrace"$(ON), to see the output of the lexical analyser.
|
||||
|
||||
The main compiler generates $LK,"Intermediate Code","FF:::/TempleOS/Compiler/CmpDefs.HPP,IC_END"$ at the parser stage. See $LK,"PrsExpression","MN:PrsExpression"$(), $LK,"PrsStatement","MN:PrsStatement"$(). Set $LK,"PTrace","MN:PTrace"$(ON) to see the output of the parser.
|
||||
|
||||
The main compiler optimizes See $LK,"Intermediate Code Attributes","FF:D:/TempleOS/Compiler/CmpInit.CPP,ST_INTERMEDIATE_CODE"$, $LK,"Combining Constants","FF:::/TempleOS/Compiler/OptPass012.CPP,IC_MUL"$, $LK,"Choosing Reg Vars","FF:::/TempleOS/Compiler/OptPass3.CPP,num_reg_vars"$. Set $LK,"OTrace","MN:OTrace"$(ON) to see the optimization stages.
|
||||
|
||||
The main compiler makes machine code in the back end. See $LK,"IC Struct","MN:CIntermediateCode"$, $LK,"CBCompile","MN:CBCompile"$ and $LK,"OptPass789A","FF:::/TempleOS/Compiler/OptPass789A.CPP,IC_MUL"$(), $LK,"BackEnd","FF:::/TempleOS/Compiler/BackEnd.CPP,ICMul"$. Set $LK,"CTrace","MN:CTrace"$(ON) to see the output of the backend.
|
||||
$ID,-2$
|
||||
$TR,"Mini Compiler"$
|
||||
$ID,2$For this mini compiler, some things you should know about 64-bit asm:
|
||||
|
||||
* Putting a 0x48, known as the REX byte, in front of an instruction makes it 64-bit size.
|
||||
|
||||
* "PUSH EAX", "POP EAX" and "XOR EAX,EAX" will behave as 64-bit even without REX because the stk is always 64 bit and because the XOR clears the upper 32-bits.
|
||||
|
||||
It is okay in TempleOS to change RAX, RBX, RCX, RDX, R8 and R9 without restoring them to their original values.
|
||||
$ID,-2$$WW,0$*/
|
||||
|
||||
#define TK_EOF 0
|
||||
#define TK_NUM 1
|
||||
#define TK_OP 2
|
||||
#define TK_LEFT 3
|
||||
#define TK_RIGHT 4
|
||||
|
||||
#define OP_MUL 1
|
||||
#define OP_DIV 2
|
||||
#define OP_ADD 3
|
||||
#define OP_SUB 4
|
||||
|
||||
I64 Lex(U8 **_src,I64 *num)
|
||||
{ //See $LK,"Lex","MN:Lex"$().
|
||||
U8 *src=*_src;
|
||||
I64 i;
|
||||
while (TRUE) {
|
||||
switch (*src) {
|
||||
case 0:
|
||||
case ';':
|
||||
*_src=src;
|
||||
return TK_EOF;
|
||||
case CH_SPACE:
|
||||
case CH_CARRIAGE_RETURN:
|
||||
case '\n':
|
||||
src++;
|
||||
break;
|
||||
case '0'...'9':
|
||||
i=0;
|
||||
do {
|
||||
i=i*10+*src-'0';
|
||||
src++;
|
||||
} while ('0'<=*src<='9');
|
||||
*num=i;
|
||||
*_src=src;
|
||||
return TK_NUM;
|
||||
case '*':
|
||||
*num=OP_MUL;
|
||||
*_src=src+1;
|
||||
return TK_OP;
|
||||
case '/':
|
||||
*num=OP_DIV;
|
||||
*_src=src+1;
|
||||
return TK_OP;
|
||||
case '+':
|
||||
*num=OP_ADD;
|
||||
*_src=src+1;
|
||||
return TK_OP;
|
||||
case '-':
|
||||
*num=OP_SUB;
|
||||
*_src=src+1;
|
||||
return TK_OP;
|
||||
case '(':
|
||||
*_src=src+1;
|
||||
return TK_LEFT;
|
||||
case ')':
|
||||
*_src=src+1;
|
||||
return TK_RIGHT;
|
||||
default:
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define PREC_EOF 0
|
||||
#define PREC_TERM 1
|
||||
#define PREC_MUL 2
|
||||
#define PREC_ADD 3
|
||||
#define PREC_PAREN 4
|
||||
|
||||
extern I64 Parse(U8 **_src,U8 **_dst);
|
||||
|
||||
U0 PrsTerm(U8 **_src,U8 **_dst,I64 prec)
|
||||
{ //See $LK,"PrsExpression","MN:PrsExpression"$().
|
||||
I64 i;
|
||||
U8 *src2;
|
||||
U8 *dst2;
|
||||
if (Parse(_src,_dst)==PREC_TERM) {
|
||||
src2=*_src;
|
||||
dst2=*_dst;
|
||||
while (TRUE) {
|
||||
//This is inefficient. The main compiler doesn't back-up.
|
||||
i=Parse(&src2,&dst2);
|
||||
if (PREC_MUL<=i<prec) {
|
||||
*_src=src2;
|
||||
*_dst=dst2;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
} else
|
||||
throw;
|
||||
}
|
||||
|
||||
I64 Parse(U8 **_src,U8 **_dst)
|
||||
{
|
||||
//See $LK,"PrsExpression","MN:PrsExpression"$().
|
||||
//See $LK,"Opcode Fmts","FF:::/TempleOS/Compiler/OpCodes.TXT,IDIV"$ for details on asm instructions.
|
||||
I64 i;
|
||||
U8 *dst=*_dst;
|
||||
switch (Lex(_src,&i)) {
|
||||
case TK_EOF:
|
||||
*dst++=0x58; //POP RAX
|
||||
*dst++=0xC3; //RET
|
||||
*_dst=dst;
|
||||
return PREC_EOF;
|
||||
case TK_NUM:
|
||||
*dst++=0x48; //REX
|
||||
*dst++=0xB8; //MOV RAX,immediate num
|
||||
*dst(I64 *)++=i;
|
||||
|
||||
*dst++=0x50; //PUSH RAX
|
||||
*_dst=dst;
|
||||
return PREC_TERM;
|
||||
case TK_LEFT:
|
||||
PrsTerm(_src,_dst,PREC_PAREN);
|
||||
if (Parse(_src,_dst)!=PREC_PAREN)
|
||||
throw;
|
||||
return PREC_TERM;
|
||||
case TK_RIGHT:
|
||||
return PREC_PAREN;
|
||||
case TK_OP:
|
||||
switch (i) {
|
||||
case OP_MUL:
|
||||
PrsTerm(_src,&dst,PREC_MUL);
|
||||
*dst++=0x5A; //POP RDX
|
||||
*dst++=0x58; //POP RAX
|
||||
|
||||
*dst++=0x48; //REX
|
||||
*dst++=0x0F;
|
||||
*dst++=0xAF; //IMUL RAX,RDX
|
||||
*dst++=0xC2;
|
||||
|
||||
*dst++=0x50; //PUSH RAX
|
||||
*_dst=dst;
|
||||
return PREC_MUL;
|
||||
case OP_DIV:
|
||||
PrsTerm(_src,&dst,PREC_MUL);
|
||||
*dst++=0x5B; //POP RBX
|
||||
*dst++=0x58; //POP RAX
|
||||
|
||||
*dst++=0x33; //XOR RDX,RDX
|
||||
*dst++=0xD2;
|
||||
|
||||
*dst++=0x48; //REX
|
||||
*dst++=0xF7; //IDIV RBX
|
||||
*dst++=0xFB;
|
||||
|
||||
*dst++=0x50; //PUSH RAX
|
||||
*_dst=dst;
|
||||
return PREC_MUL;
|
||||
case OP_ADD:
|
||||
PrsTerm(_src,&dst,PREC_ADD);
|
||||
*dst++=0x5A; //POP RDX
|
||||
*dst++=0x58; //POP RAX
|
||||
|
||||
*dst++=0x48; //REX
|
||||
*dst++=0x03; //ADD RAX,RDX
|
||||
*dst++=0xC2;
|
||||
|
||||
*dst++=0x50; //PUSH RAX
|
||||
*_dst=dst;
|
||||
return PREC_ADD;
|
||||
case OP_SUB:
|
||||
PrsTerm(_src,&dst,PREC_ADD);
|
||||
*dst++=0x5A; //POP RDX
|
||||
*dst++=0x58; //POP RAX
|
||||
|
||||
*dst++=0x48; //REX
|
||||
*dst++=0x2B; //SUB RAX,RDX
|
||||
*dst++=0xC2;
|
||||
|
||||
*dst++=0x50; //PUSH RAX
|
||||
*_dst=dst;
|
||||
return PREC_ADD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
U0 Main()
|
||||
{
|
||||
U8 *src,*src2,*code,*dst;
|
||||
|
||||
//Fixed size, no buffer overrun check.
|
||||
//You can make it fancier if you like.
|
||||
code=MAlloc(512,Fs->code_heap);
|
||||
|
||||
while (TRUE) {
|
||||
"This will compile an expression\n"
|
||||
"consisting of integers, parentheses\n"
|
||||
"and the operators +,-,* and /.\n";
|
||||
src=MGetS;
|
||||
if (*src) {
|
||||
src2=src;
|
||||
dst=code;
|
||||
try {
|
||||
PrsTerm(&src2,&dst,PREC_PAREN);
|
||||
if (Parse(&src2,&dst)!=PREC_EOF)
|
||||
throw;
|
||||
"$$FG,RED$$This code is not efficient, but the compiler is simple.$$FG$$\n";
|
||||
Un(code,dst-code); //Unassemble the code we created.
|
||||
//$LK,"Call","MN:Call"$() is a function. See $LK,"_CALL","FF:::/TempleOS/Kernel/Utils1a.CPP,_CALL"$::
|
||||
//See also $LK,"CallInd","MN:CallInd"$(). See $LK,"_CALLIND","FF:::/TempleOS/Kernel/Utils1a.CPP,_CALLIND"$::
|
||||
"$$FG,LTBLUE$$Answer:%d$$FG$$\n",Call(code);
|
||||
} catch {
|
||||
"$$FG,RED$$Error$$FG$$\n";
|
||||
CatchAll;
|
||||
}
|
||||
Free(src);
|
||||
} else {
|
||||
Free(src);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Free(code);
|
||||
}
|
||||
|
||||
Main;
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/GraphicsCPULoad.CPP"$
|
||||
U8 reverse[256], //The VGA bits are backward
|
||||
|
||||
image[640*480/8]; //We need read-modify write.
|
||||
//0xA0000 memory can't be read.
|
||||
|
||||
U0 MGInit()
|
||||
{
|
||||
I64 i,j;
|
||||
MemSet(image,0,sizeof(image));
|
||||
MemSet(reverse,0,sizeof(reverse));
|
||||
for (i=0;i<256;i++)
|
||||
for (j=0;j<8;j++)
|
||||
if (Bt(&i,j))
|
||||
Bts(&reverse[i],7-j);
|
||||
}
|
||||
|
||||
U0 MGUpdate()
|
||||
{ //Copy image to VGA memory
|
||||
//For better performance we could only write what's changed.
|
||||
//0xA0000 is slower than normal RAM.
|
||||
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
|
||||
OutU8(VGA_SC_DATA,0xF);//All color planes at once -- Black and White
|
||||
MemCpy(0xA0000,image,sizeof(image));
|
||||
}
|
||||
|
||||
U0 MGPlot(I64 x,I64 y)
|
||||
{
|
||||
if (0<=x<640 && 0<=y<480)
|
||||
Bts(image,y*640+x^7);
|
||||
}
|
||||
|
||||
U0 MGHLine(I64 x1,I64 x2,I64 y)
|
||||
{ //Warning! No clipping
|
||||
//For performance, we do as many whole-bytes as possible.
|
||||
U8 *ptr;
|
||||
I64 i,w,leading,trailing,whole_bytes;
|
||||
if (x2<x1) SwapU64(&x1,&x2);
|
||||
ptr=image+y*640/8+x1>>3;
|
||||
w=x2-x1+1;
|
||||
leading =8-x1&7;
|
||||
trailing=(x2+1)&7;
|
||||
if (leading+trailing>w)
|
||||
*ptr|=reverse[(0xFF00>>leading&(0x00FF<<trailing)>>8)];
|
||||
else {
|
||||
whole_bytes=(w-leading-trailing)>>3;
|
||||
if (leading)
|
||||
*ptr++|=reverse[(0xFF00>>leading)&0xFF];
|
||||
for (i=0;i<whole_bytes;i++)
|
||||
*ptr++=0xFF;
|
||||
if (trailing)
|
||||
*ptr++|=reverse[(0x00FF<<trailing)>>8];
|
||||
}
|
||||
}
|
||||
|
||||
U0 MGLine(I64 x1,I64 y1,I64 x2,I64 y2)
|
||||
{ //Warning! No clipping
|
||||
I64 dx=x2-x1,dy=y2-y1;
|
||||
x1<<=32; x2<<=32;
|
||||
y1<<=32; y2<<=32;
|
||||
if (AbsI64(dx)>AbsI64(dy)) {
|
||||
dy=dy<<32/AbsI64(dx);
|
||||
dx=SignI64(dx)<<32;
|
||||
while (x1!=x2) {
|
||||
MGPlot(x1.i32[1],y1.i32[1]);
|
||||
x1+=dx; y1+=dy;
|
||||
}
|
||||
} else {
|
||||
dx=dx<<32/AbsI64(dy);
|
||||
dy=SignI64(dy)<<32;
|
||||
while (y1!=y2) {
|
||||
MGPlot(x1.i32[1],y1.i32[1]);
|
||||
x1+=dx; y1+=dy;
|
||||
}
|
||||
}
|
||||
MGPlot(x1.i32[1],y1.i32[1]);
|
||||
}
|
||||
|
||||
U0 MGCircle(I64 x,I64 y,F64 r)
|
||||
{
|
||||
F64 s,c,x1,y1,x2,y2;
|
||||
I64 len;
|
||||
if (r<0) return;
|
||||
x1=r; y1=0;
|
||||
c=Cos(1/r);
|
||||
s=Sin(1/r);
|
||||
len=2*r*ã;
|
||||
MGPlot(x+x1,y+y1);
|
||||
while (len-->=0) {
|
||||
|
||||
//m1@a1 * m2@a2 = m1*m2@(a1+a2)
|
||||
|
||||
//(x1+y1i)*(x2+y2i) = x1*x2+(x1*y1+x2*y2)i-y1*y2
|
||||
|
||||
// me$SY,-3$ti$SY,0$=mCos(t)+imSin(t)
|
||||
|
||||
x2=x1; y2=y1;
|
||||
x1=c*x2-s*y2;
|
||||
y1=s*x2+c*y2;
|
||||
MGPlot(x+x1,y+y1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
U0 MiniGrLibDemo()
|
||||
{
|
||||
I64 i;
|
||||
Bool old_preempt;
|
||||
MGInit;
|
||||
|
||||
old_preempt=Preempt(OFF);
|
||||
for (i=0;i<100;i++)
|
||||
MGHLine(200+i,400+i,300+i);
|
||||
for (i=0;i<500;i+=10)
|
||||
MGLine(i,0,0,480-i);
|
||||
for (i=0;i<300;i+=4)
|
||||
MGCircle(200,100+i,i);
|
||||
MGUpdate;
|
||||
BusyWait(1500000);
|
||||
Preempt(old_preempt);
|
||||
/*
|
||||
We are returning graphics to normal operations under TempleOS.
|
||||
It is not normal to by-pass the TempleOS graphcis routines.
|
||||
The TempleOS graphics don't know VGA has changed.
|
||||
This bit tells TempleOS to update whole screen.
|
||||
*/
|
||||
//<CTRL-ALT-F> will flush screen VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
MiniGrLibDemo;
|
||||
|
||||
//See $LK,"RawPutChar","MN:RawPutChar"$() for text.
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/ScreenMemory.CPP"$ for color.
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/GraphicsCPULoad.CPP"$.
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
The moral of this story is simple
|
||||
instruction level optimizations
|
||||
don't matter much on a modern Intel CPU
|
||||
because they convert complex instructions
|
||||
to a stream of RISC instructions.
|
||||
|
||||
I learned this the hard way when I thought
|
||||
I was greatly improving my compiler by
|
||||
cutting code by a third. No significant
|
||||
speed-up. Depressing.
|
||||
*/
|
||||
|
||||
#define SAMPLES (8*10000000+1)
|
||||
|
||||
asm {
|
||||
|
||||
LIMIT:: DU64 SAMPLES; //Memory reference should suck, right?
|
||||
|
||||
_BADLY_UNOPTIMIZED::
|
||||
MOV RAX,0
|
||||
MOV RCX,1
|
||||
@@05: MOV RDX,RCX
|
||||
INC RCX //if no dependencies, Free!
|
||||
ADD RAX,RDX
|
||||
MOV RDX,LIMIT-16 //added 16 displacement to make it worse
|
||||
CMP RCX,U64 16[RDX]
|
||||
JB @@05
|
||||
RET
|
||||
|
||||
|
||||
_WELL_OPTIMIZED1::
|
||||
XOR RAX,RAX
|
||||
MOV RCX,SAMPLES-1
|
||||
@@05: ADD RAX,RCX
|
||||
DEC RCX
|
||||
JNZ @@05
|
||||
RET
|
||||
|
||||
_WELL_OPTIMIZED2:: //Unrolled
|
||||
XOR RAX,RAX
|
||||
MOV RCX,SAMPLES-1
|
||||
@@05: ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
ADD RAX,RCX
|
||||
DEC RCX
|
||||
JNZ @@05
|
||||
RET
|
||||
|
||||
_WELL_OPTIMIZED3::
|
||||
XOR RAX,RAX
|
||||
MOV RCX,SAMPLES-1
|
||||
@@05: ADD RAX,RCX
|
||||
LOOP @@05 //This instruction sucks, speedwise, but it saves code size
|
||||
RET
|
||||
}
|
||||
|
||||
|
||||
|
||||
_extern _BADLY_UNOPTIMIZED I64 Loop1();
|
||||
_extern _WELL_OPTIMIZED1 I64 Loop2();
|
||||
_extern _WELL_OPTIMIZED2 I64 Loop3();
|
||||
_extern _WELL_OPTIMIZED3 I64 Loop4();
|
||||
|
||||
I64 i;
|
||||
F64 t0;
|
||||
|
||||
CPURep;
|
||||
|
||||
"Bad Code\n";
|
||||
t0=tT;
|
||||
i=Loop1;
|
||||
"Result:%d Time:%9.6f\n",i,tT-t0;
|
||||
|
||||
"Good Code #1\n";
|
||||
t0=tT;
|
||||
i=Loop2;
|
||||
"Result:%d Time:%9.6f\n",i,tT-t0;
|
||||
|
||||
"Good Code #2\n";
|
||||
t0=tT;
|
||||
i=Loop3;
|
||||
"Result:%d Time:%9.6f\n",i,tT-t0;
|
||||
|
||||
"Good Code #3\n";
|
||||
t0=tT;
|
||||
i=Loop4;
|
||||
"Result:%d Time:%9.6f\n",i,tT-t0;
|
||||
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
Bad Code
|
||||
Result:3200000040000000 Time: 0.069966
|
||||
Good Code #1
|
||||
Result:3200000040000000 Time: 0.062567
|
||||
Good Code #2
|
||||
Result:3200000040000000 Time: 0.062907
|
||||
Good Code #3
|
||||
Result:3200000040000000 Time: 0.156359
|
||||
|
||||
$HL,1$*/
|
||||
@@ -0,0 +1,73 @@
|
||||
//See $LK,"PCI","HI:PCI"$.
|
||||
|
||||
#if mp_cnt>1
|
||||
#define DEST_CPU 1
|
||||
#else
|
||||
#define DEST_CPU 0
|
||||
#endif
|
||||
|
||||
#assert I_USER==0x40
|
||||
|
||||
interrupt U0 MyVect40()
|
||||
{
|
||||
lock {progress1++;}
|
||||
IntEOI;
|
||||
}
|
||||
|
||||
interrupt U0 MyVect41()
|
||||
{
|
||||
lock {progress2++;}
|
||||
IntEOI;
|
||||
}
|
||||
|
||||
interrupt U0 MyVect42()
|
||||
{
|
||||
lock {progress3++;}
|
||||
IntEOI;
|
||||
}
|
||||
|
||||
interrupt U0 MyVect43()
|
||||
{
|
||||
lock {progress4++;}
|
||||
IntEOI;
|
||||
}
|
||||
|
||||
I64 vect[4]={&MyVect40,&MyVect41,&MyVect42,&MyVect43};
|
||||
|
||||
U0 Main()
|
||||
{
|
||||
I64 i;
|
||||
U8 *da=IOAPIC_REG;
|
||||
U32 *_d=IOAPIC_DATA;
|
||||
|
||||
ProgressBarsRst;
|
||||
for (i=I_USER;i<I_USER+4;i++)
|
||||
IntEntrySet(i,vect[i&3],IDTET_IRQ);
|
||||
|
||||
//There are like 4*6 vects. Bus zero
|
||||
//has the first four vects.
|
||||
for (i=IOREDTAB;i<IOREDTAB+4*2;i+=2) {
|
||||
*da=i+1;
|
||||
*_d=mp_apic_ids[DEST_CPU]<<24;
|
||||
*da=i;
|
||||
*_d=0x4000+0x40+(i-IOREDTAB)/2;
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
"The keyboard and timer seem to be active.\n"
|
||||
"IntA:%d IntB:%d IntC:%d IntD:%d\n\n"
|
||||
"IntB is my keybaord and registers a single KEY-UP.\n"
|
||||
"Rerun and press a few keys.\n",
|
||||
progress1,progress2,progress3,progress4;
|
||||
|
||||
//Disable vects
|
||||
for (i=IOREDTAB;i<IOREDTAB+4*2;i+=2) {
|
||||
*da=i+1;
|
||||
*_d=mp_apic_ids[DEST_CPU]<<24;
|
||||
*da=i;
|
||||
*_d=0x14000+0x40+(i-IOREDTAB)/2;
|
||||
}
|
||||
ProgressBarsRst;
|
||||
}
|
||||
|
||||
Main;
|
||||
@@ -0,0 +1,48 @@
|
||||
//What's funner than page tables for programmers to play with!!
|
||||
|
||||
//This changes a page table entry for VGA graphics memory.
|
||||
//TempleOS keeps a cached copy of VGA memory in
|
||||
//$LK,"gr_screen_image","MN:gr_screen_image"$. See $LK,"GrUpdateVGAGraphics","MN:GrUpdateVGAGraphics"$.
|
||||
|
||||
//TempleOS used 4096 size pages for memory below addr 2 Meg
|
||||
//and 2 Meg pages above two Meg. See $LK,"MemPageSize","MN:MemPageSize"$() and $LK,"INIT_MEMORY","FF:::/TempleOS/Kernel/Memory.CPP,SYS_INIT_MEM"$.
|
||||
|
||||
|
||||
U0 PageTableDemo1()
|
||||
{
|
||||
I64 *pte=MemPtAtPageTableEntry(VGA_GRAPHIC_MEM_BASE),p0,p1;
|
||||
"Page table entries for VGA screen memory\n";
|
||||
DocD(pte);
|
||||
PressAKey;
|
||||
|
||||
p0=pte[0];
|
||||
p1=pte[1];
|
||||
pte[0]=p1;
|
||||
pte[1]=p0;
|
||||
InvlPg(VGA_GRAPHIC_MEM_BASE); //Invalidate translation look aside buffer (TLB).
|
||||
InvlPg(VGA_GRAPHIC_MEM_BASE+0x1000);
|
||||
|
||||
//Use this if you wish to change
|
||||
//the page table register.
|
||||
|
||||
// SetRAX(0x100000+0x1000*(NUM_PML1+NUM_PML2+NUM_PML3));
|
||||
// asm {MOV_CR3_EAX}
|
||||
|
||||
//<CTRL-ALT-F> will flush screen VGA cache.
|
||||
VGAFlush;
|
||||
PressAKey;
|
||||
|
||||
pte[0]=p0;
|
||||
pte[1]=p1;
|
||||
InvlPg(VGA_GRAPHIC_MEM_BASE);
|
||||
InvlPg(VGA_GRAPHIC_MEM_BASE+0x1000);
|
||||
|
||||
//<CTRL-ALT-F> will flush screen VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
PageTableDemo1;
|
||||
|
||||
//See $LK,"MemBlks2MegAlloc","MN:MemBlks2MegAlloc"$(), $LK,"MemBlks2MegFree","MN:MemBlks2MegFree"$() for page aligned allocations.
|
||||
//See $LK,"CBlkPool","MN:CBlkPool"$, $LK,"CHeapCtrl","MN:CHeapCtrl"$, $LK,"sys_code_bp","MN:sys_code_bp"$, $LK,"sys_data_bp","MN:sys_data_bp"$.
|
||||
//See $LK,"Memory","HI:Memory"$
|
||||
@@ -0,0 +1,46 @@
|
||||
I64 *addr=VGA_GRAPHIC_MEM_BASE,
|
||||
*pte_addr=MemPtAtPageTableEntry(addr),
|
||||
err_code=0,
|
||||
*reported_fault_addr;
|
||||
|
||||
|
||||
/*
|
||||
0[RBP]=old base ptr
|
||||
8[RBP]=err code
|
||||
16[RBP]=RIP
|
||||
24[RBP]=RIP_SELECTOR
|
||||
32[RBP]=RFLAGS
|
||||
40[RBP]=RSP
|
||||
48[RBP]=STK_SELECTOR
|
||||
*/
|
||||
|
||||
interrupt U0 PageFault()
|
||||
{
|
||||
I64 reg *rbp=GetBasePtr,reg *rsp=GetStkPtr,reg *src=rbp,reg *dst=rbp+1;
|
||||
*pte_addr|=1;
|
||||
InvlPg(addr);
|
||||
err_code=rbp[1];
|
||||
asm { MOV_RAX_CR2}
|
||||
reported_fault_addr=GetRAX;
|
||||
//Remove err_code from stk
|
||||
//so IRET works right.
|
||||
while (src>=rsp)
|
||||
*dst--=*src--;
|
||||
SetBasePtr(rbp+1);
|
||||
SetStkPtr(rsp+1);
|
||||
}
|
||||
|
||||
|
||||
U0 PageTableDemo2()
|
||||
{
|
||||
U8 *old_idte=IntEntrySet(I_PAGE_FAULT,&PageFault,IDTET_IRQ);
|
||||
err_code=0;
|
||||
*pte_addr &=~1; //Not present
|
||||
InvlPg(addr);
|
||||
WinMgrSync; //wait for VGA update
|
||||
WinMgrSync;
|
||||
IntEntrySet(I_PAGE_FAULT,old_idte,IDTET_IRQ);
|
||||
"Err Code:%X Addr:%X\n",err_code,reported_fault_addr;
|
||||
}
|
||||
|
||||
PageTableDemo2;
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
TempleOS runs exclusively in ring 0.
|
||||
Ring 0 is part of the $LK,"Charter","FI:::/TempleOS/Doc/Charter.TXT"$.
|
||||
This demo is for you to play around
|
||||
with ring 3. TempleOS is for
|
||||
recreational programming, after all.
|
||||
|
||||
This redirects the general protection
|
||||
fault, switches to ring 3, and generates
|
||||
a fault to switch back.
|
||||
*/
|
||||
|
||||
U8 *old_stk,*new_rip;
|
||||
|
||||
asm {
|
||||
IMPORT SYS_PROGRESS1,old_stk,new_rip;
|
||||
TO_RING0:: //Set to handle general protection 0xD fault temporarily.
|
||||
INC U64 [SYS_PROGRESS1]
|
||||
PUSH U32 GDT_ZERO_DS_SEL //STKSEG
|
||||
MOV RAX,U64 [&old_stk]
|
||||
PUSH RAX
|
||||
PUSH U32 0 //FLAGS--interrupts off
|
||||
PUSH U32 GDT_CS64_SEL
|
||||
MOV RAX,U64 [&new_rip]
|
||||
PUSH RAX
|
||||
IRET
|
||||
}
|
||||
|
||||
|
||||
|
||||
U0 Ring3Demo()
|
||||
{
|
||||
U8 *old_pf;
|
||||
"Progress1 Before:%X\n",progress1;
|
||||
Cli;
|
||||
old_pf=IntEntrySet(0x0D,TO_RING0,IDTET_TRAP,0);
|
||||
|
||||
TSSBusy(Gs->tss->tr_ring3,OFF);
|
||||
SetRAX(Gs->tss->tr_ring3+3);
|
||||
asm { LTR AX}
|
||||
|
||||
|
||||
asm {
|
||||
IMPORT SYS_PROGRESS1,old_stk,new_rip;
|
||||
|
||||
MOV U64 [&old_stk],RSP
|
||||
|
||||
LEA RAX,[R3_CALLBACK]
|
||||
MOV U64 [&new_rip],RAX
|
||||
|
||||
MOV AX,GDT_ZERO_DS_RING3_SEL+3
|
||||
MOV DS,AX
|
||||
MOV ES,AX
|
||||
|
||||
PUSH U32 GDT_ZERO_DS_RING3_SEL+3 //STKSEG
|
||||
PUSH U64 [&old_stk]
|
||||
PUSH U32 0 //FLAGS--interrupts off
|
||||
PUSH U32 GDT_CS64_RING3_SEL+3
|
||||
LEA RAX,[R3_START]
|
||||
PUSH RAX
|
||||
IRET
|
||||
|
||||
R3_START:
|
||||
INC U64 [SYS_PROGRESS1]
|
||||
CLI //This causes general protection fault #13
|
||||
|
||||
R3_CALLBACK:
|
||||
MOV AX,GDT_ZERO_DS_SEL
|
||||
MOV DS,AX
|
||||
MOV ES,AX
|
||||
}
|
||||
|
||||
TSSBusy(Gs->tss->tr,OFF);
|
||||
SetRAX(Gs->tss->tr);
|
||||
asm { LTR AX}
|
||||
|
||||
IntEntrySet(0x0D,old_pf,IDTET_IRQ,0);
|
||||
Sti;
|
||||
"Progress1 After :%X\n",progress1;
|
||||
}
|
||||
|
||||
|
||||
Ring3Demo;
|
||||
@@ -0,0 +1,55 @@
|
||||
//This just shows how screen memory works.
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/MiniGrLib.CPP"$
|
||||
|
||||
U0 PlotXY(I64 x,I64 y)
|
||||
{
|
||||
//Screen bits are reversed
|
||||
LBts(VGA_GRAPHIC_MEM_BASE,y*GR_WIDTH+x^7);
|
||||
}
|
||||
|
||||
|
||||
|
||||
U0 Main()
|
||||
{
|
||||
I64 i;
|
||||
Bool old_preempt=Preempt(OFF);
|
||||
|
||||
//This makes all 4 color planes active.
|
||||
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
|
||||
OutU8(VGA_SC_DATA,WHITE);
|
||||
MemSet(VGA_GRAPHIC_MEM_BASE,0,GR_WIDTH*GR_HEIGHT/8);
|
||||
|
||||
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
|
||||
OutU8(VGA_SC_DATA,RED);
|
||||
for (i=0;i<200;i++)
|
||||
PlotXY(i,i);
|
||||
|
||||
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
|
||||
OutU8(VGA_SC_DATA,GREEN);
|
||||
for (i=0;i<200;i++)
|
||||
PlotXY(100,i);
|
||||
|
||||
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
|
||||
OutU8(VGA_SC_DATA,BLUE);
|
||||
for (i=0;i<200;i++)
|
||||
PlotXY(200-i,i);
|
||||
/*
|
||||
If you want a mixed color select multiple planes
|
||||
but you have to be sure the unselected planes
|
||||
are zero, so select them and make them zero.
|
||||
You can't do reads on VGA memory, by the way.
|
||||
That means no read-modify-writes, too.
|
||||
*/
|
||||
BusyWait(4000000);
|
||||
|
||||
Preempt(old_preempt);
|
||||
|
||||
//TempleOS has a 4 plane memory duplicate of the screen, $LK,"gr_screen_image","MN:gr_screen_image"$,
|
||||
//and only writes actual changes. See $LK,"GrUpdateVGAGraphics","MN:GrUpdateVGAGraphics"$().
|
||||
//<CTRL-ALT-F> will flush screen VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
Main;
|
||||
|
||||
//See $LK-A,"::/TempleOS/Demo/Lectures/GraphicsCPULoad.CPP"$.
|
||||
@@ -0,0 +1,109 @@
|
||||
//This task lets you hear how busy the CPU is.
|
||||
U0 LoadTask(I64)
|
||||
{
|
||||
I64 i,j=100;
|
||||
//Preemption is ctrlled on a per task basis.
|
||||
//Preemption is OFF by default on new tasks.
|
||||
//It is turned on for user terminal tasks.
|
||||
Preempt(ON);
|
||||
while (TRUE) {
|
||||
for (i=0;i<10000;i++)
|
||||
InU8(0x21); //Roughly 1 microsecond on all PC's
|
||||
Snd(j);
|
||||
j+=20;
|
||||
if (j>1000) j=100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
U0 SpinningPreempt()
|
||||
{
|
||||
I64 i;
|
||||
"Spinning with preemption....\n";
|
||||
for (i=0;i<3000000;i++)
|
||||
InU8(0x21); //This is a sample hardware stat read
|
||||
}
|
||||
|
||||
U0 SpinningNoPreempt()
|
||||
{
|
||||
I64 i;
|
||||
Bool old_preempt;
|
||||
"Spinning with no preemption...\n";
|
||||
|
||||
//Put on screen before it starts. Text lags by one refresh
|
||||
//and we might catch it in the middle of an update.
|
||||
WinMgrSync;
|
||||
WinMgrSync;
|
||||
WinMgrSync;
|
||||
|
||||
old_preempt=Preempt(OFF);
|
||||
for (i=0;i<3000000;i++)
|
||||
InU8(0x21);
|
||||
Preempt(old_preempt);
|
||||
}
|
||||
|
||||
U0 SpinningNoInterrupts()
|
||||
{
|
||||
I64 i;
|
||||
//Interrupts are ctrlled on a per task basis.
|
||||
"Spinning with no interrupts...\n";
|
||||
WinMgrSync;
|
||||
WinMgrSync;
|
||||
WinMgrSync;
|
||||
Cli;
|
||||
for (i=0;i<3000000;i++)
|
||||
InU8(0x21);
|
||||
Sti;
|
||||
}
|
||||
|
||||
|
||||
U0 CooperativeMultitaskingPreempt()
|
||||
{
|
||||
F64 timeout=tT+3.0;
|
||||
|
||||
"Cooperative multitasking with preemption...\n";
|
||||
while (tT<timeout) {
|
||||
InU8(0x21);
|
||||
Yield;
|
||||
}
|
||||
}
|
||||
|
||||
U0 CooperativeMultitaskingNoPreempt()
|
||||
{
|
||||
F64 timeout=tT+3.0;
|
||||
Bool old_preempt=Preempt(OFF);
|
||||
"Cooperative multitasking with no preemption...\n";
|
||||
while (tT<timeout) {
|
||||
InU8(0x21);
|
||||
Yield;
|
||||
}
|
||||
Preempt(old_preempt);
|
||||
}
|
||||
|
||||
U0 CooperativeMultitaskingNoInterrupts()
|
||||
{
|
||||
F64 timeout=tT+3.0;
|
||||
|
||||
Cli;
|
||||
"Cooperative multitasking with no interrupts...\n";
|
||||
while (tT<timeout) {
|
||||
InU8(0x21);
|
||||
Yield;
|
||||
}
|
||||
Sti;
|
||||
}
|
||||
|
||||
U0 SpinningDemo()
|
||||
{
|
||||
CTask *task=Spawn(&LoadTask);
|
||||
SpinningPreempt;
|
||||
SpinningNoPreempt;
|
||||
SpinningNoInterrupts;
|
||||
CooperativeMultitaskingPreempt;
|
||||
CooperativeMultitaskingNoPreempt;
|
||||
CooperativeMultitaskingNoInterrupts;
|
||||
Kill(task);
|
||||
Snd(0);
|
||||
}
|
||||
|
||||
SpinningDemo;
|
||||
@@ -0,0 +1,53 @@
|
||||
#define SAMPLE_SIZE 100000
|
||||
|
||||
U0 TimeIns()
|
||||
{
|
||||
I64 i,start,end;
|
||||
I64 *cached_I64=MAlloc(sizeof(I64)),
|
||||
shared_blks,*uncached_I64;
|
||||
CBlkPool *bp;
|
||||
CHeapCtrl *hc;
|
||||
Bool old_preempt;
|
||||
|
||||
CPURep;
|
||||
|
||||
shared_blks=1;
|
||||
bp=MemBlksUncachedAlloc(&shared_blks);
|
||||
hc=HeapCtrlIndependentInit(bp,shared_blks<<12);
|
||||
uncached_I64=MAlloc(sizeof(I64),hc);
|
||||
|
||||
old_preempt=Preempt(OFF);
|
||||
|
||||
//Measure cached time
|
||||
start=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
*cached_I64=i;
|
||||
|
||||
end=GetTimeStamp;
|
||||
" Cached Cycles: %10,d\n",end-start;
|
||||
|
||||
//Measure uncached time
|
||||
start=GetTimeStamp;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
*uncached_I64=i;
|
||||
end=GetTimeStamp;
|
||||
"Uncached Cycles: %10,d\n",end-start;
|
||||
|
||||
"$$FG,GREEN$$The difference is even greater\n"
|
||||
"because the loop overhead was not subtracted.\n\n";
|
||||
Free(cached_I64);
|
||||
Free(uncached_I64);
|
||||
MemBlks2MegFree(bp,shared_blks);
|
||||
Preempt(old_preempt);
|
||||
}
|
||||
|
||||
TimeIns;
|
||||
|
||||
/* Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
Cached Cycles: 202,220
|
||||
Uncached Cycles: 14,357,016
|
||||
$FG,2$The difference is even greater
|
||||
because the loop overhead was not subtracted.
|
||||
$HL,1$*/
|
||||
Reference in new issue
Block a user