Initial (manual) import

This commit is contained in:
minexew committed 2017-01-22 20:26:44 +01:00
1 parent f39611c506
commit 895fe109de
661 files changed
+245862

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 R8 instruction needs a REX byte prefix to specify upper-8 reg.
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 2Gig away, you put the address into RAX, then CALL RAX.
9) CALL REL32 is significantly faster. See $LK,"::/Demo/Lectures/InterruptDemo.HC"$.
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.
+44
View File
@@ -0,0 +1,44 @@
//$WW,1$You may wonder why I use "&" instead of "%".
#define SAMPLE_SIZE 100000000
U0 TimeIns()
{
I64 start1,end1,start2,end2,overhead_time,test_time;
I64 reg i,reg tmp;
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++)
tmp=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++)
tmp=i&0x3FF;
end2=GetTSC;
test_time=end2-start2;
"Remainder Version #2 Cycles\t: %10.5f\n",
ToF64(test_time-overhead_time)/SAMPLE_SIZE;
}
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$*/
+96
View File
@@ -0,0 +1,96 @@
/*"Fixed point" means you use ints
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 is poor with floats.
I often use 64-bit ints with upper 32-bits
as int and lower 32-bits as fraction.
See $LK,"::/Demo/SubIntAccess.HC"$ for how
to access upper or lower 32-bits.
For a complete lst of nonstandard compiler
features, see $LK,"::/Doc/HolyC.DD"$.
*/
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;
"Int Sum\t\t:%.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\t:%.9f Time:%7.3fs\n",f_sum,tS-t0;
'\n';
}
U0 DoIt2()
{
I64 i=0x123456789ABCDEF0;
"i\t\t=%X\n",i;
"i&0xFFFFFFFF\t=%X\n",i&0xFFFFFFFF;
"i>>32\t\t=%X\n",i>>32;
/* Standard int 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.DD"$.
*/
"i.u32[0]\t=%X\n",i.u32[0];
"i.u32[1]\t=%X\n",i.u32[1];
}
CPURep;
Main;
DoIt2;
//See $LK,"::/Demo/Lectures/FixedPointAdvanced.HC"$
/*Program Output$HL,0$$WW+H,1$$FD,1$
$FG$$BG$16 Cores 3.500GHz
Int Sum :2000000199.768690240 Time: 0.803s
Float Sum :2000000225.656127040 Time: 3.615s
i =123456789ABCDEF0
i&0xFFFFFFFF =9ABCDEF0
i>>32 =12345678
i.u32[0] =9ABCDEF0
i.u32[1] =12345678
$HL,1$*/
@@ -0,0 +1,173 @@
/*"Fixed point" means you use ints
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 is poor with floats.
I often use 64-bit ints with upper 32-bits
as int and lower 32-bits as fraction.
See $LK,"::/Demo/SubIntAccess.HC"$ 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,tmp,reg d2,reg sum;
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--) {
tmp=d2*coordinates[i&65535];
sum+=tmp.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;
}
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$*/
@@ -0,0 +1,34 @@
//This is the fastest you can update the whole scrn.
//See $LK,"::/Demo/Lectures/MiniGrLib.HC"$ 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(VGAP_IDX,VGAR_MAP_MASK);
OutU8(VGAP_DATA,plane);
MemSetI64(text.vga_alias,p,640*480/64);
}
return tS-start;
}
U0 GrDemo()
{
I64 i;
F64 t;
CPURep;
for (i=0;i<16;i++) {
t=VGAPattern(0x5555555555555555);
"$$GREEN$$Rate:$$RED$$%5.1f FPS$$FG$$"
" $$GREEN$$Load@30Hz:$$RED$$%5.1f%%$$FG$$\n",1/t,100*t*30.0;
t=VGAPattern(0x3333333333333333);
"$$GREEN$$Rate:$$RED$$%5.1f FPS$$FG$$"
" $$GREEN$$Load@30Hz:$$RED$$%5.1f%%$$FG$$\n",1/t,100*t*30.0;
}
//<CTRL-ALT-v> will flush scrn VGA cache.
VGAFlush;
}
GrDemo;
+85
View File
@@ -0,0 +1,85 @@
//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;
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;
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$*/
+236
View File
@@ -0,0 +1,236 @@
/*$WW,1$
$TR-C,"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/CompilerA.HH,IC_END"$ at the parser stage. See $LK,"PrsExpression",A="MN:PrsExpression"$(), $LK,"PrsStmt",A="MN:PrsStmt"$().
The main compiler optimizes See $LK,"Intermediate Code Attributes",A="MN:intermediate_code_table"$, $LK,"Combining Consts",A="FF:::/Compiler/OptPass012.HC,case IC_MUL"$, $LK,"Choosing Reg Vars",A="FF:::/Compiler/OptPass3.HC,cmp.num_reg_vars"$. Use $LK,"PassTrace",A="MN:PassTrace"$() to see the optimization stages.
The main compiler makes machine code in the back end. See $LK,"IC Struct",A="MN:CIntermediateCode"$, $LK,"COCCompile",A="MN:COCCompile"$ and $LK,"OptPass789A",A="FF:::/Compiler/OptPass789A.HC,IC_MUL"$(), $LK,"BackEnd",A="FF:::/Compiler/BackA.HC,ICMul"$. Set $LK,"Trace",A="MN:Trace"$(ON) to see the output of the backend.
$ID,-2$
$TR-C,"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 inst 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 like this.
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.DD,IDIV"$ for details on asm insts.
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 ints, parentheses\n"
"and the operators +,-,* and /.\n";
src=GetStr;
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/KUtils.HC,_CALL"$::
//See also $LK,"CallInd",A="MN:CallInd"$(). See $LK,"_CALL_IND",A="FF:::/Kernel/KUtils.HC,_CALL_IND"$::
"$$LTBLUE$$Answer:%d$$FG$$\n",Call(code);
} catch {
"$$RED$$Error$$FG$$\n";
PutExcept;
}
Free(src);
} else {
Free(src);
break;
}
}
Free(code);
}
Main;
+132
View File
@@ -0,0 +1,132 @@
//See $LK,"::/Demo/Lectures/GraphicsCPULoad.HC"$
U8 rev[256], //The VGA bits are bwd
image[640*480/8]; //We need read-modify write.
//0xA0000 alias memory can't be read.
U0 MGInit()
{
I64 i,j;
MemSet(image,0,sizeof(image));
MemSet(rev,0,sizeof(rev));
for (i=0;i<256;i++)
for (j=0;j<8;j++)
if (Bt(&i,j))
Bts(&rev[i],7-j);
}
U0 MGUpdate()
{//Copy image to VGA memory
//For better performance we could only write what's changed.
//0xA0000 alias is slower than normal RAM.
OutU8(VGAP_IDX,VGAR_MAP_MASK);
OutU8(VGAP_DATA,0xF);//All color planes at once -- Black and White
MemCpy(text.vga_alias,image,sizeof(image)); //Alias of 0xA0000
}
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) SwapI64(&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|=rev[(0xFF00>>leading&(0x00FF<<trailing)>>8)];
else {
whole_bytes=(w-leading-trailing)>>3;
if (leading)
*ptr++|=rev[(0xFF00>>leading)&0xFF];
for (i=0;i<whole_bytes;i++)
*ptr++=0xFF;
if (trailing)
*ptr++|=rev[(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@(arg1+arg2)
//(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;
MGInit;
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;
Busy(1500000);
/*
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 scrn.
*/
//<CTRL-ALT-v> will flush scrn VGA cache.
VGAFlush;
}
MiniGrLibDemo;
//See $LK,"RawPutChar",A="MN:RawPutChar"$() for text.
//See $LK,"::/Demo/Lectures/ScrnMemory.HC"$ for color.
//See $LK,"::/Demo/Lectures/GraphicsCPULoad.HC"$.
+56
View File
@@ -0,0 +1,56 @@
class Person
{
U8 first [32];
U8 last [32];
U8 address1 [64];
U8 address2 [64];
U8 city [32];
U8 state_zip[32];
};
U0 OffsetDemo1()
{
Person *p=MAlloc(sizeof(Person));
StrCpy(p->first, "Terry");
StrCpy(p->last, "Davis");
StrCpy(p->address1, "8144 Sickle Lane");
StrCpy(p->address2, "");
StrCpy(p->city, "Las Vegas");
StrCpy(p->state_zip,"NV 89128");
}
U(&OffsetDemo1,34);
PressAKey;
/*
x86 has signed 8-bit displacements
and signed 32-bit displacements.
This example uses 8-bit negative displacements.
*/
class Person
{ $$=-128;
U8 first [32];
U8 last [32];
U8 address1 [64];
U8 address2 [64];
U8 city [32];
U8 state_zip[32];
};
U0 OffsetDemo2()
{
Person *p=MAlloc(sizeof(Person))(I64)+128;
StrCpy(p->first, "Terry");
StrCpy(p->last, "Davis");
StrCpy(p->address1, "8144 Sickle Lane");
StrCpy(p->address2, "");
StrCpy(p->city, "Las Vegas");
StrCpy(p->state_zip,"NV 89128");
}
U(&OffsetDemo2,34);
PressAKey;
+108
View File
@@ -0,0 +1,108 @@
/*The moral of this story is simple
inst level optimizations
don't matter much on a modern Intel CPU
because they convert complex insts
to a stream of RISC insts.
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 be bad, 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 //Inst has slow speed, but 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;
"Res:%d Time:%9.6f\n",i,tS-t0;
"Good Code #1\n";
t0=tS;
i=Loop2;
"Res:%d Time:%9.6f\n",i,tS-t0;
"Good Code #2\n";
t0=tS;
i=Loop3;
"Res:%d Time:%9.6f\n",i,tS-t0;
"Good Code #3\n";
t0=tS;
i=Loop4;
"Res:%d Time:%9.6f\n",i,tS-t0;
/* Program Output$HL,0$$WW+H,1$$FD,1$
8 Cores 2.660GHz
Bad Code
Res:3200000040000000 Time: 0.069966
Good Code #1
Res:3200000040000000 Time: 0.062567
Good Code #2
Res:3200000040000000 Time: 0.062907
Good Code #3
Res: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 IntMy40()
{
lock {progress1++;}
*(dev.uncached_alias+LAPIC_EOI)(U32 *)=0;
}
interrupt U0 IntMy41()
{
lock {progress2++;}
*(dev.uncached_alias+LAPIC_EOI)(U32 *)=0;
}
interrupt U0 IntMy42()
{
lock {progress3++;}
*(dev.uncached_alias+LAPIC_EOI)(U32 *)=0;
}
interrupt U0 IntMy43()
{
lock {progress4++;}
*(dev.uncached_alias+LAPIC_EOI)(U32 *)=0;
}
I64 vect[4]={&IntMy40,&IntMy41,&IntMy42,&IntMy43};
U0 Main()
{
I64 i;
U8 *da=dev.uncached_alias+IOAPIC_REG;
U32 *_d=dev.uncached_alias+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=dev.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=dev.mp_apic_ids[DEST_CPU]<<24;
*da=i;
*_d=0x14000+0x40+(i-IOREDTAB)/2;
}
ProgressBarsRst;
}
Main;
+75
View File
@@ -0,0 +1,75 @@
/*TempleOS runs exclusively in ring 0.
Ring 0 is part of the $LK,"Charter",A="FI:::/Doc/Charter.DD"$.
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 {
INT_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,INT_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;
+49
View File
@@ -0,0 +1,49 @@
//This just shows how scrn memory works.
//See $LK,"::/Demo/Lectures/MiniGrLib.HC"$
U0 PlotXY(I64 x,I64 y)
{
//Scrn bits are revd
LBts(text.vga_alias,y*GR_WIDTH+x^7);
}
U0 Main()
{
I64 i;
//This makes all 4 color planes active.
OutU8(VGAP_IDX,VGAR_MAP_MASK);
OutU8(VGAP_DATA,WHITE);
MemSet(text.vga_alias,0,GR_WIDTH*GR_HEIGHT/8);
OutU8(VGAP_IDX,VGAR_MAP_MASK);
OutU8(VGAP_DATA,RED);
for (i=0;i<200;i++)
PlotXY(i,i);
OutU8(VGAP_IDX,VGAR_MAP_MASK);
OutU8(VGAP_DATA,GREEN);
for (i=0;i<200;i++)
PlotXY(100,i);
OutU8(VGAP_IDX,VGAR_MAP_MASK);
OutU8(VGAP_DATA,BLUE);
for (i=0;i<200;i++)
PlotXY(200-i,i);
/*If you want a mixed color sel multiple planes
but you have to be sure the unseled planes
are zero, so sel them and make them zero.
You can't do reads on VGA memory, by the way.
That means no read-modify-writes, too.
*/
Busy(4000000);
//TempleOS has a 4 plane memory duplicate of the scrn, $LK,"gr.scrn_image",A="MN:CGrGlbls"$,
//and only writes actual changes. See $LK,"GrUpdateVGAGraphics",A="MN:GrUpdateVGAGraphics"$().
//<CTRL-ALT-v> will flush scrn VGA cache.
VGAFlush;
}
Main;
//See $LK,"::/Demo/Lectures/GraphicsCPULoad.HC"$.