TempleOSTS_140317.ISO

This commit is contained in:
Terry A. Davis authored and minexew committed 2018-09-08 21:46:02 +02:00
1 parent 2d2a65d572
commit 7ef616a20e
1416 files changed
+223873 -53917

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
$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 regs?
12) Should you put the regs 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?
----
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 reg. See $LK,"IA32_FS_BASE",A="MN:IA32_FS_BASE"$ and $LK,"SET_FS_BASE",A="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,"REG_RBP",A="MN:REG_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,"::/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.
+53
View File
@@ -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=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++) {
}
end1=GetTSC;
overhead_time=end1-start1;
//Measure remainder...
start2=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++)
temp=i%0x400;
end2=GetTSC;
test_time=end2-start2;
"Remainder Version #1 Cycles\t: %10.5f\n",
ToF64(test_time-overhead_time)/SAMPLE_SIZE;
//Measure remainder...
start2=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++)
temp=i&0x3FF;
end2=GetTSC;
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$*/
+103
View File
@@ -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,"faster",HTML="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,"::/Demo/SubIntAccess.CPP"$ for how
to access upper or lower 32-bits.
For a complete lst of nonstandard compiler
features, see $LK,"::/Doc/HolyC.TXT"$.
*/
U0 Main()
{
F64 t0,f_sum=0,f_val;
I64 i ,i_sum=0,i_val;
i_val= 2.0000002 *0x100000000;
t0=tS;
for (i=1000000000;i;i--)
i_sum+=i_val;
"Integer Sum:%.9f Time:%7.3fs\n",i_sum/ToF64(0x100000000),tS-t0;
f_val= 2.0000002;
t0=tS;
for (i=1000000000;i;i--)
f_sum+=f_val;
"Float Sum:%.9f Time:%7.3fs\n",f_sum,tS-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",A="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 reg, so there is a
penalty.
For a complete lst of nonstandard compiler
features, see $LK,"::/Doc/HolyC.TXT"$.
*/
"i.u32[0] =%X\n",i.u32[0];
"i.u32[1] =%X\n",i.u32[1];
}
CPURep;
Main;
DoIt2;
//See $LK,"::/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$*/
+177
View File
@@ -0,0 +1,177 @@
/*
"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,"::/Demo/SubIntAccess.CPP"$ for how
to access upper or lower 32-bits.
*/
#define SAMPLE_SIZE 10000000
I32 coordinates[65536];
asm {
_ASM_FIXED_POINT::
PUSH RBP
MOV RBP,RSP
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
POP RBP
RET
SINE_VAL: DU64 Sin(1.0);
RET_VAL: DU64 0;
_ASM_FLOAT::
PUSH RBP
MOV RBP,RSP
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
POP RBP
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=GetTSC;
for (i=SAMPLE_SIZE-1;i>=0;i--) {
}
end=GetTSC;
overhead_time=end-start;
"$$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=GetTSC;
for (i=SAMPLE_SIZE-1;i>=0;i--)
fsum+=d1*coordinates[i&65535];
end=GetTSC;
test_time=end-start;
"Float Sum :%X\n",ToI64(fsum);
"$$RED$$Float Cycles :%10.5f$$FG$$\n",
ToF64(test_time)/SAMPLE_SIZE;
//Measure fixed point arithmetic
d2=Sin(1.0)*0x100000000;
sum=0;
start=GetTSC;
for (i=SAMPLE_SIZE-1;i>=0;i--) {
temp=d2*coordinates[i&65535];
sum+=temp.i32[1];
}
end=GetTSC;
test_time=end-start;
"Fixed-Point Sum :%X\n",sum;
"$$RED$$Fixed-Point Cycles :%10.5f$$FG$$\n",
ToF64(test_time)/SAMPLE_SIZE;
//Measure fixed point arithmetic
start=GetTSC;
sum=AsmFixedPt;
end=GetTSC;
test_time=end-start;
"Asm Fixed-Point Sum :%X\n",sum;
"$$RED$$Asm Fixed-Point Cycles:%10.5f$$FG$$\n",
ToF64(test_time)/SAMPLE_SIZE;
//Measure float arithmetic
start=GetTSC;
sum=AsmFloat;
end=GetTSC;
test_time=end-start;
"Asm Float Sum :%X\n",sum;
"$$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$
Machine 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$
Machine 2:
8 Cores 3.395GHz
$FG,4$Overhead Cycles : 4.87040$FG$
Float Sum :D20A01DB177
$FG,4$Float Cycles : 10.11558$FG$
Fixed-Point Sum :D209FD18CC7
$FG,4$Fixed-Point Cycles : 4.50618$FG$
Asm Fixed-Point Sum :D209FD18CC7
$FG,4$Asm Fixed-Point Cycles: 3.02426$FG$
Asm Float Sum :D20A01DB17B
$FG,4$Asm Float Cycles : 3.21070$FG$
$HL,1$*/
+36
View File
@@ -0,0 +1,36 @@
//This is the fastest you can update the whole screen.
//See $LK,"::/Demo/Lectures/MiniGrLib.CPP"$ if this interests you.
F64 VGAPattern(I64 p)
{//This returns the time to update in seconds.
F64 start=tS;
I64 plane;
for (plane=1;plane<0x10;plane<<=1) {
OutU8(VGA_SC_INDEX,VGA_MAP_MASK);
OutU8(VGA_SC_DATA,plane);
MemSetU64(0xA0000,p,640*480/64);
}
return tS-start;
}
U0 GrDemo()
{
I64 i;
F64 t;
Bool old_preempt=Preempt(OFF);
CPURep;
for (i=0;i<16;i++) {
t=VGAPattern(0x5555555555555555);
"$$GREEN$$Rate:$$RED$$%5.1f FPS$$FG$$"
" $$GREEN$$Load@60Hz:$$RED$$%5.1f%%$$FG$$\n",1/t,100*t*60.0;
t=VGAPattern(0x3333333333333333);
"$$GREEN$$Rate:$$RED$$%5.1f FPS$$FG$$"
" $$GREEN$$Load@60Hz:$$RED$$%5.1f%%$$FG$$\n",1/t,100*t*60.0;
}
Preempt(old_preempt);
//<CTRL-ALT-F> will flush screen VGA cache.
VGAFlush;
}
GrDemo;
+90
View File
@@ -0,0 +1,90 @@
//This is the software interrupt num
//we will use. See $LK,"Software Int's",A="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]
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]
POP RAX
RET
//This invokes the interrupt version
//with a C callable function.
_F_UM_INT::
PUSH RBP
MOV RBP,RSP
MOV RAX,SF_ARG1[RBP]
INT I_F_UNARY_MINUS
POP RBP
RET1 8
//This invokes the call version
//with a C callable function.
_F_UM_CALL::
PUSH RBP
MOV RBP,RSP
MOV RAX,SF_ARG1[RBP]
CALL F_UNARY_MINUS_CALL
POP RBP
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=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++)
UnaryMinusInt(ã);
end=GetTSC;
"Interrupt Cycles: %10.5f\n",ToF64(end-start)/SAMPLE_SIZE;
//Measure call time
start=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++)
UnaryMinusCall(ã);
end=GetTSC;
"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$
$PT$$FG$$BG$6 Cores 3.395GHz
Interrupt Cycles: 573.98543
Call Cycles: 9.74349
$HL,1$*/
+238
View File
@@ -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. $LK,"Lex",A="MN:Lex"$(),$LK,"Echo",A="MN:Echo"$(ON).
The main compiler generates $LK,"Intermediate Code",A="FF:::/Compiler/CmpDefs.HPP,IC_END"$ at the parser stage. See $LK,"PrsExpression",A="MN:PrsExpression"$(), $LK,"PrsStatement",A="MN:PrsStatement"$().
The main compiler optimizes See $LK,"Intermediate Code Attributes",A="FF:D:/Compiler/CmpInit.CPP,ST_INTERMEDIATE_CODE"$, $LK,"Combining Consts",A="FF:::/Compiler/OptPass012.CPP,case IC_MUL"$, $LK,"Choosing Reg Vars",A="FF:::/Compiler/OptPass3.CPP,num_reg_vars"$. Set $LK,"OTrace",A="MN:OTrace"$(ON) to see the optimization stages.
The main compiler makes machine code in the back end. See $LK,"IC Struct",A="MN:CIntermediateCode"$, $LK,"LCCCompile",A="MN:LCCCompile"$ and $LK,"OptPass789",A="FF:::/Compiler/OptPass789.CPP,IC_MUL"$(), $LK,"BackEnd",A="FF:::/Compiler/Back1a.CPP,ICMul"$. Set $LK,"CTrace",A="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",A="MN:Lex"$().
U8 *src=*_src;
I64 i;
while (TRUE) {
switch (*src) {
case 0:
case ';':
*_src=src;
return TK_EOF;
case CH_SPACE:
case '\r':
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",A="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",A="MN:PrsExpression"$().
//See $LK,"Opcode Fmts",A="FF:::/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;
"$$RED$$This code is not efficient, but the compiler is simple.$$FG$$\n";
Un(code,dst-code); //Unassemble the code we created.
//$LK,"Call",A="MN:Call"$() is a function. See $LK,"_CALL",A="FF:::/Kernel/Utils1a.CPP,_CALL"$::
//See also $LK,"CallInd",A="MN:CallInd"$(). See $LK,"_CALLIND",A="FF:::/Kernel/Utils1a.CPP,_CALLIND"$::
"$$LTBLUE$$Answer:%d$$FG$$\n",Call(code);
} catch {
"$$RED$$Error$$FG$$\n";
PutExcept;
}
Free(src);
} else {
Free(src);
break;
}
}
Free(code);
}
Main;
+135
View File
@@ -0,0 +1,135 @@
//See $LK,"::/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",A="MN:RawPutChar"$() for text.
//See $LK,"::/Demo/Lectures/ScreenMemory.CPP"$ for color.
//See $LK,"::/Demo/Lectures/GraphicsCPULoad.CPP"$.
+115
View File
@@ -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=tS;
i=Loop1;
"Result:%d Time:%9.6f\n",i,tS-t0;
"Good Code #1\n";
t0=tS;
i=Loop2;
"Result:%d Time:%9.6f\n",i,tS-t0;
"Good Code #2\n";
t0=tS;
i=Loop3;
"Result:%d Time:%9.6f\n",i,tS-t0;
"Good Code #3\n";
t0=tS;
i=Loop4;
"Result:%d Time:%9.6f\n",i,tS-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$*/
+73
View File
@@ -0,0 +1,73 @@
//See $LK,"PCI",A="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++;}
*LAPIC_EOI(U32 *)=0;
}
interrupt U0 MyVect41()
{
lock {progress2++;}
*LAPIC_EOI(U32 *)=0;
}
interrupt U0 MyVect42()
{
lock {progress3++;}
*LAPIC_EOI(U32 *)=0;
}
interrupt U0 MyVect43()
{
lock {progress4++;}
*LAPIC_EOI(U32 *)=0;
}
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 regs 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;
+48
View File
@@ -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",A="MN:gr_screen_image"$. See $LK,"GrUpdateVGAGraphics",A="MN:GrUpdateVGAGraphics"$.
//TempleOS used 4096 size pages for memory below addr 2 Meg
//and 2 Meg pages above two Meg. See $LK,"MemPageSize",A="MN:MemPageSize"$() and $LK,"INIT_MEMORY",A="FF:::/Kernel/Mem1a.CPP,SYS_INIT_MEM"$.
U0 PageTableDemo1()
{
I64 *pte=MemPageTable(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 reg.
// SetRAX(0x100000+0x1000*(MEM_NUM_PML1+MEM_NUM_PML2+MEM_NUM_PML3));
// 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",A="MN:MemBlks2MegAlloc"$(), $LK,"MemBlks2MegFree",A="MN:MemBlks2MegFree"$() for page aligned allocations.
//See $LK,"CBlkPool",A="MN:CBlkPool"$, $LK,"CHeapCtrl",A="MN:CHeapCtrl"$, $LK,"sys_code_bp",A="MN:sys_code_bp"$, $LK,"sys_data_bp",A="MN:sys_data_bp"$.
//See $LK,"Memory",A="HI:Memory"$
+40
View File
@@ -0,0 +1,40 @@
I64 *addr=VGA_GRAPHIC_MEM_BASE,
*pte_addr=MemPageTable(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 haserrcode U0 PageFault()
{
I64 reg *rbp=GetRBP;
*pte_addr|=1;
InvlPg(addr);
err_code=rbp[1];
MOV_RAX_CR2
reported_fault_addr=GetRAX;
}
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;
+80
View File
@@ -0,0 +1,80 @@
/*
TempleOS runs exclusively in ring 0.
Ring 0 is part of the $LK,"Charter",A="FI:::/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 {
TO_RING0:: //Set to handle general protection 0xD fault temporarily.
INC U64 [SYS_PROGRESS1]
PUSH U32 CGDT.ds //STKSEG
MOV RAX,U64 [&old_stk]
PUSH RAX
PUSH U32 0 //FLAGS--interrupts off
PUSH U32 CGDT.cs64
MOV RAX,U64 [&new_rip]
PUSH RAX
IRET
}
U0 Ring3Demo()
{
U8 *old_vect;
"Progress1 Before:%X\n",progress1;
CLI
old_vect=IntEntrySet(0x0D,TO_RING0,IDTET_TRAP,0);
TSSBusy(Gs->tss->tr_ring3,OFF);
SetRAX(Gs->tss->tr_ring3+3);
LTR AX
asm {
MOV U64 [&old_stk],RSP
LEA RAX,[R3_CALLBACK]
MOV U64 [&new_rip],RAX
MOV AX,CGDT.ds_ring3+3
MOV DS,AX
MOV ES,AX
PUSH U32 CGDT.ds_ring3+3 //STKSEG
PUSH U64 [&old_stk]
PUSH U32 0 //FLAGS--interrupts off
PUSH U32 CGDT.cs64_ring3+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,CGDT.ds
MOV DS,AX
MOV ES,AX
}
TSSBusy(Gs->tss->tr,OFF);
SetRAX(Gs->tss->tr);
LTR AX
IntEntrySet(0x0D,old_vect,IDTET_IRQ,0);
STI
"Progress1 After :%X\n",progress1;
}
Ring3Demo;
+55
View File
@@ -0,0 +1,55 @@
//This just shows how screen memory works.
//See $LK,"::/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",A="MN:gr_screen_image"$,
//and only writes actual changes. See $LK,"GrUpdateVGAGraphics",A="MN:GrUpdateVGAGraphics"$().
//<CTRL-ALT-F> will flush screen VGA cache.
VGAFlush;
}
Main;
//See $LK,"::/Demo/Lectures/GraphicsCPULoad.CPP"$.
+109
View File
@@ -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=tS+3.0;
"Cooperative multitasking with preemption...\n";
while (tS<timeout) {
InU8(0x21);
Yield;
}
}
U0 CooperativeMultitaskingNoPreempt()
{
F64 timeout=tS+3.0;
Bool old_preempt=Preempt(OFF);
"Cooperative multitasking with no preemption...\n";
while (tS<timeout) {
InU8(0x21);
Yield;
}
Preempt(old_preempt);
}
U0 CooperativeMultitaskingNoInterrupts()
{
F64 timeout=tS+3.0;
CLI
"Cooperative multitasking with no interrupts...\n";
while (tS<timeout) {
InU8(0x21);
Yield;
}
STI
}
U0 SpinningDemo()
{
CTask *task=Spawn(&LoadTask);
SpinningPreempt;
SpinningNoPreempt;
SpinningNoInterrupts;
CooperativeMultitaskingPreempt;
CooperativeMultitaskingNoPreempt;
CooperativeMultitaskingNoInterrupts;
Kill(task);
Snd(0);
}
SpinningDemo;
+55
View File
@@ -0,0 +1,55 @@
#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;
"$$RED$$Now, this demo seems broken, perhaps,\n"
"because I run in a virtual machine.$$FG$$\n\n";
CPURep;
shared_blks=1;
bp=MemBlksUncachedAlloc(&shared_blks);
hc=HeapCtrlBPInit(bp,shared_blks<<12);
uncached_I64=MAlloc(sizeof(I64),hc);
old_preempt=Preempt(OFF);
//Measure cached time
start=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++)
*cached_I64=i;
end=GetTSC;
" Cached Cycles: %10,d\n",end-start;
//Measure uncached time
start=GetTSC;
for (i=0;i<SAMPLE_SIZE;i++)
*uncached_I64=i;
end=GetTSC;
"Uncached Cycles: %10,d\n",end-start;
"$$GREEN$$The difference is even greater\n"
"because the loop overhead was not subtracted.$$FG$$\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$*/