TempleOSCDV4.12.ISO
This commit is contained in:
1 parent
93ca216483
commit
b84bbff6db
381 files changed
+18356
-18798
No files matched your search
@@ -74,7 +74,7 @@ ANSWERS:
|
||||
|
||||
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.
|
||||
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"$.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
U0 TimeIns()
|
||||
{
|
||||
I64 start1,end1,start2,end2,overhead_time,test_time;
|
||||
I64 reg i,reg temp;
|
||||
I64 reg i,reg tmp;
|
||||
|
||||
CPURep;
|
||||
|
||||
@@ -19,7 +19,7 @@ U0 TimeIns()
|
||||
//Measure remainder...
|
||||
start2=GetTSC;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
temp=i%0x400;
|
||||
tmp=i%0x400;
|
||||
end2=GetTSC;
|
||||
test_time=end2-start2;
|
||||
"Remainder Version #1 Cycles\t: %10.5f\n",
|
||||
@@ -28,7 +28,7 @@ U0 TimeIns()
|
||||
//Measure remainder...
|
||||
start2=GetTSC;
|
||||
for (i=0;i<SAMPLE_SIZE;i++)
|
||||
temp=i&0x3FF;
|
||||
tmp=i&0x3FF;
|
||||
end2=GetTSC;
|
||||
test_time=end2-start2;
|
||||
"Remainder Version #2 Cycles\t: %10.5f\n",
|
||||
|
||||
+16
-18
@@ -6,8 +6,7 @@ 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.
|
||||
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.
|
||||
@@ -17,7 +16,6 @@ to access upper or lower 32-bits.
|
||||
|
||||
For a complete lst of nonstandard compiler
|
||||
features, see $LK,"::/Doc/HolyC.DD"$.
|
||||
|
||||
*/
|
||||
|
||||
U0 Main()
|
||||
@@ -29,23 +27,23 @@ U0 Main()
|
||||
t0=tS;
|
||||
for (i=1000000000;i;i--)
|
||||
i_sum+=i_val;
|
||||
"Int Sum:%.9f Time:%7.3fs\n",i_sum/ToF64(0x100000000),tS-t0;
|
||||
"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:%.9f Time:%7.3fs\n",f_sum,tS-t0;
|
||||
"Float Sum\t:%.9f Time:%7.3fs\n",f_sum,tS-t0;
|
||||
'\n';
|
||||
}
|
||||
|
||||
U0 DoIt2()
|
||||
{
|
||||
I64 i=0x123456789ABCDEF0;
|
||||
"i =%X\n",i;
|
||||
"i\t\t=%X\n",i;
|
||||
|
||||
"i&0xFFFFFFFF=%X\n",i&0xFFFFFFFF;
|
||||
"i>>32 =%X\n",i>>32;
|
||||
"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
|
||||
@@ -74,8 +72,8 @@ For a complete lst of nonstandard compiler
|
||||
features, see $LK,"::/Doc/HolyC.DD"$.
|
||||
*/
|
||||
|
||||
"i.u32[0] =%X\n",i.u32[0];
|
||||
"i.u32[1] =%X\n",i.u32[1];
|
||||
"i.u32[0]\t=%X\n",i.u32[0];
|
||||
"i.u32[1]\t=%X\n",i.u32[1];
|
||||
}
|
||||
|
||||
CPURep;
|
||||
@@ -86,13 +84,13 @@ DoIt2;
|
||||
|
||||
/*Program Output$HL,0$$WW+H,1$$FD,1$
|
||||
|
||||
8 Cores 2.660GHz
|
||||
Int Sum:2000000199.768689408 Time: 0.787s
|
||||
Float Sum:2000000225.656126208 Time: 3.918s
|
||||
$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
|
||||
i =123456789ABCDEF0
|
||||
i&0xFFFFFFFF =9ABCDEF0
|
||||
i>>32 =12345678
|
||||
i.u32[0] =9ABCDEF0
|
||||
i.u32[1] =12345678
|
||||
$HL,1$*/
|
||||
@@ -3,18 +3,18 @@ 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,
|
||||
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.
|
||||
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];
|
||||
@@ -77,7 +77,7 @@ U0 Main()
|
||||
{
|
||||
I64 start,end,overhead_time,test_time;
|
||||
F64 d1,fsum;
|
||||
I64 reg i,temp,reg d2,reg sum;
|
||||
I64 reg i,tmp,reg d2,reg sum;
|
||||
|
||||
CPURep;
|
||||
|
||||
@@ -113,8 +113,8 @@ U0 Main()
|
||||
sum=0;
|
||||
start=GetTSC;
|
||||
for (i=SAMPLE_SIZE-1;i>=0;i--) {
|
||||
temp=d2*coordinates[i&65535];
|
||||
sum+=temp.i32[1];
|
||||
tmp=d2*coordinates[i&65535];
|
||||
sum+=tmp.i32[1];
|
||||
}
|
||||
end=GetTSC;
|
||||
test_time=end-start;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//This is the fastest you can update the whole screen.
|
||||
//This is the fastest you can update the whole scrn.
|
||||
//See $LK,"::/Demo/Lectures/MiniGrLib.HC"$ if this interests you.
|
||||
|
||||
F64 VGAPattern(I64 p)
|
||||
@@ -27,7 +27,7 @@ U0 GrDemo()
|
||||
"$$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 screen VGA cache.
|
||||
//<CTRL-ALT-v> will flush scrn VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
//This file is required by $LK,"::/Demo/Lectures/Mem/Uncached.HC"$.
|
||||
|
||||
U8 *Mem2MegAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
|
||||
{/*Alloc 2Meg pages from BlkPool. Don't link to task.
|
||||
(Linking to a task means they will be freed when the task dies.)
|
||||
It might give you more than you asked for
|
||||
so a ptr to a page count is passed.
|
||||
|
||||
Return: NULL if out of memory.
|
||||
*/
|
||||
I64 i,j,*pte,num=*_pages2Meg;
|
||||
CMemBlk *res=NULL,*m,*m1;
|
||||
|
||||
if (!bp) bp=sys_code_bp;
|
||||
PUSHFD
|
||||
CLI
|
||||
while (LBts(&bp->locked_flags,BPlf_LOCKED))
|
||||
PAUSE
|
||||
num<<=21-MEM_PAGE_BITS;
|
||||
|
||||
m=&bp->mem_free_2meg_lst;
|
||||
while (TRUE) {
|
||||
if (!(res=m->next))
|
||||
break;
|
||||
if (res->pages<num)
|
||||
m=res;
|
||||
else {
|
||||
if (res->pages==num) {
|
||||
m->next=res->next;
|
||||
goto am_done;
|
||||
} else {
|
||||
res->pages-=num;
|
||||
res(U8 *)+=res->pages<<MEM_PAGE_BITS;
|
||||
res->pages=num;
|
||||
goto am_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m=&bp->mem_free_lst;
|
||||
while (TRUE) {
|
||||
if (!(res=m->next)) {
|
||||
num=0;
|
||||
res=NULL; //Out of memory
|
||||
goto am_done;
|
||||
}
|
||||
if (res->pages<num)
|
||||
m=res;
|
||||
else {
|
||||
if (res->pages==num) {
|
||||
if (res(U8 *)&0x1FFFFF)
|
||||
m=res;
|
||||
else {
|
||||
m->next=res->next;
|
||||
goto am_done;
|
||||
}
|
||||
} else {
|
||||
if (i=(res(U8 *)&0x1FFFFF)>>MEM_PAGE_BITS) {
|
||||
j=1<<(21-MEM_PAGE_BITS)-i;
|
||||
if (res->pages<num+j)
|
||||
m=res;
|
||||
else if (res->pages==num+j) {
|
||||
res->pages-=num;
|
||||
res(U8 *)+=res->pages<<MEM_PAGE_BITS;
|
||||
res->pages=num;
|
||||
goto am_done;
|
||||
} else {
|
||||
m1=res;
|
||||
res(U8 *)+=j<<MEM_PAGE_BITS;
|
||||
res->pages=num;
|
||||
m=res(U8 *)+num<<MEM_PAGE_BITS;
|
||||
m->pages=m1->pages-num-j;
|
||||
m1->pages=j;
|
||||
m->next=m1->next;
|
||||
m1->next=m;
|
||||
m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
|
||||
goto am_done;
|
||||
}
|
||||
} else {
|
||||
m=m->next=res(U8 *)+num<<MEM_PAGE_BITS;
|
||||
m->next=res->next;
|
||||
m->pages=res->pages-num;
|
||||
m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
|
||||
res->pages=num;
|
||||
goto am_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
am_done:
|
||||
i=num<<MEM_PAGE_BITS;
|
||||
bp->used_u8s+=i;
|
||||
num>>=21-MEM_PAGE_BITS;
|
||||
*_pages2Meg=num;
|
||||
m=res;
|
||||
m1=m(U8 *)+i;
|
||||
while (m<m1) {
|
||||
pte=MemPageTable(m);
|
||||
*pte &= ~0x18;
|
||||
InvlPg(m);
|
||||
m(U8 *)+=0x200000;
|
||||
}
|
||||
LBtr(&bp->locked_flags,BPlf_LOCKED);
|
||||
POPFD
|
||||
return res;
|
||||
}
|
||||
|
||||
U8 *Mem2MegUncachedAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
|
||||
{/*Alloc 2Meg pages from BlkPool. Don't link to task.
|
||||
(Linking to a task means they will be freed when the task dies.)
|
||||
It will be marked uncached. It might give you more than you asked for
|
||||
so a ptr to a page count is passed.
|
||||
|
||||
Return: NULL if out of memory.
|
||||
*/
|
||||
CMemBlk *res,*m,*m1;
|
||||
I64 num=*_pages2Meg,*pte;
|
||||
if (res=Mem2MegAlloc(_pages2Meg,bp)) {
|
||||
num=*_pages2Meg;
|
||||
m=res;
|
||||
m1=m(U8 *)+num<<21;
|
||||
while (m<m1) {
|
||||
pte=MemPageTable(m);
|
||||
*pte= *pte& ~0x18 |0x10;
|
||||
InvlPg(m);
|
||||
m(U8 *)+=0x200000;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
U8 *Mem2MegWriteThruAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
|
||||
{/*Alloc 2Meg pages from BlkPool. Don't link to task.
|
||||
(Linking to a task means they will be freed when the task dies.)
|
||||
It will be marked write-through. It might give you more than you asked for
|
||||
so a ptr to a page count is passed.
|
||||
|
||||
Return: NULL if out of memory.
|
||||
*/
|
||||
CMemBlk *res,*m,*m1;
|
||||
I64 num=*_pages2Meg,*pte;
|
||||
if (res=Mem2MegAlloc(_pages2Meg,bp)) {
|
||||
num=*_pages2Meg;
|
||||
m=res;
|
||||
m1=m(U8 *)+num<<21;
|
||||
while (m<m1) {
|
||||
pte=MemPageTable(m);
|
||||
*pte= *pte& ~0x18 |8;
|
||||
InvlPg(m);
|
||||
m(U8 *)+=0x200000;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
U0 Mem2MegFree(CMemBlk *m,I64 pages2Meg,CBlkPool *bp=NULL)
|
||||
{//Return non-task 2Meg pages to BlkPool.
|
||||
I64 *pte;
|
||||
CMemBlk *m1,*m2;
|
||||
if (m) {
|
||||
if (!bp) bp=sys_code_bp;
|
||||
m2=m;
|
||||
m1=m(U8 *)+pages2Meg<<21;
|
||||
while (m2<m1) {
|
||||
pte=MemPageTable(m2);
|
||||
*pte=*pte & ~0x18;
|
||||
InvlPg(m2);
|
||||
m2(U8 *)+=0x200000;
|
||||
}
|
||||
PUSHFD
|
||||
CLI
|
||||
while (LBts(&bp->locked_flags,BPlf_LOCKED))
|
||||
PAUSE
|
||||
m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
|
||||
m->pages=pages2Meg<<(21-MEM_PAGE_BITS);
|
||||
bp->used_u8s-=pages2Meg<<21;
|
||||
m->next=bp->mem_free_2meg_lst;
|
||||
bp->mem_free_2meg_lst=m;
|
||||
LBtr(&bp->locked_flags,BPlf_LOCKED);
|
||||
POPFD
|
||||
}
|
||||
}
|
||||
|
||||
CHeapCtrl *HeapCtrlBPInit(CBlkPool *bp,I64 pages512)
|
||||
{//Make mem chunk into HeapCtrl and BlkPool.
|
||||
I64 num;
|
||||
CMemBlk *m;
|
||||
CHeapCtrl *hc;
|
||||
MemSet(bp,0,sizeof(CBlkPool)+sizeof(CHeapCtrl));
|
||||
hc=HeapCtrlInit(bp(U8 *)+sizeof(CBlkPool),,bp);
|
||||
m=(bp(U8 *)+sizeof(CBlkPool)+sizeof(CHeapCtrl)+MEM_PAGE_SIZE-1)&
|
||||
~(MEM_PAGE_SIZE-1);
|
||||
num=(bp(U8 *)+pages512<<MEM_PAGE_BITS-m(U8 *))>>MEM_PAGE_BITS;
|
||||
bp->alloced_u8s=(pages512-num)<<MEM_PAGE_BITS;
|
||||
BlkPoolAdd(bp,m,num);
|
||||
return hc;
|
||||
}
|
||||
|
||||
/* PCI hardware devices, when they require uncached memory buffers, need
|
||||
this code. TempleOS, however, has a law requiring only IN/OUT port hardware
|
||||
access except VGA, handled here: $LK,"::/Kernel/Mem/PageTables.HC"$.
|
||||
*/
|
||||
@@ -1,37 +0,0 @@
|
||||
I64 *addr=text.vga_alias,
|
||||
*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 IntPageFault()
|
||||
{
|
||||
I64 reg *rbp=GetRBP;
|
||||
*pte_addr|=1;
|
||||
InvlPg(addr);
|
||||
err_code=rbp[1];
|
||||
MOV_RAX_CR2
|
||||
reported_fault_addr=GetRAX;
|
||||
}
|
||||
|
||||
U0 PageTableDemo()
|
||||
{
|
||||
U8 *old_idte=IntEntrySet(I_PAGE_FAULT,&IntPageFault,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;
|
||||
}
|
||||
|
||||
PageTableDemo;
|
||||
@@ -1,46 +0,0 @@
|
||||
// Requires $LK,"::/Demo/Lectures/Mem/Mem2Meg.HC"$.
|
||||
|
||||
Cd(__DIR__);;
|
||||
#include "Mem2Meg"
|
||||
|
||||
#define SAMPLE_SIZE 100000
|
||||
U0 TimeIns()
|
||||
{
|
||||
I64 i,start,end;
|
||||
I64 *cached_I64=MAlloc(sizeof(I64)),
|
||||
shared_blks,*uncached_I64;
|
||||
CBlkPool *bp;
|
||||
CHeapCtrl *hc;
|
||||
|
||||
"$$RED$$Now, this demo seems broken, perhaps,\n"
|
||||
"because I run in a virtual machine.$$FG$$\n\n";
|
||||
CPURep;
|
||||
|
||||
shared_blks=1;
|
||||
bp=Mem2MegUncachedAlloc(&shared_blks);
|
||||
hc=HeapCtrlBPInit(bp,shared_blks<<12);
|
||||
uncached_I64=MAlloc(sizeof(I64),hc);
|
||||
|
||||
//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);
|
||||
Mem2MegFree(bp,shared_blks);
|
||||
}
|
||||
|
||||
TimeIns;
|
||||
@@ -8,7 +8,7 @@ The main compiler optimizes See $LK,"Intermediate Code Attributes",A="MN:inter
|
||||
|
||||
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,"Mini Compiler"$
|
||||
$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.
|
||||
@@ -98,7 +98,7 @@ U0 PrsTerm(U8 **_src,U8 **_dst,I64 prec)
|
||||
src2=*_src;
|
||||
dst2=*_dst;
|
||||
while (TRUE) {
|
||||
//This is inefficient. The main compiler doesn't back-up.
|
||||
//This is inefficient. The main compiler doesn't back-up like this.
|
||||
i=Parse(&src2,&dst2);
|
||||
if (PREC_MUL<=i<prec) {
|
||||
*_src=src2;
|
||||
|
||||
@@ -119,14 +119,14 @@ U0 MiniGrLibDemo()
|
||||
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.
|
||||
This bit tells TempleOS to update whole scrn.
|
||||
*/
|
||||
//<CTRL-ALT-v> will flush screen VGA cache.
|
||||
//<CTRL-ALT-v> will flush scrn VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
MiniGrLibDemo;
|
||||
|
||||
//See $LK,"RawPutChar",A="MN:RawPutChar"$() for text.
|
||||
//See $LK,"::/Demo/Lectures/ScreenMemory.HC"$ for color.
|
||||
//See $LK,"::/Demo/Lectures/ScrnMemory.HC"$ for color.
|
||||
//See $LK,"::/Demo/Lectures/GraphicsCPULoad.HC"$.
|
||||
@@ -1,9 +1,9 @@
|
||||
//This just shows how screen memory works.
|
||||
//This just shows how scrn memory works.
|
||||
//See $LK,"::/Demo/Lectures/MiniGrLib.HC"$
|
||||
|
||||
U0 PlotXY(I64 x,I64 y)
|
||||
{
|
||||
//Screen bits are revd
|
||||
//Scrn bits are revd
|
||||
LBts(text.vga_alias,y*GR_WIDTH+x^7);
|
||||
}
|
||||
|
||||
@@ -37,9 +37,9 @@ That means no read-modify-writes, too.
|
||||
*/
|
||||
BusyWait(4000000);
|
||||
|
||||
//TempleOS has a 4 plane memory duplicate of the screen, $LK,"gr.screen_image",A="MN:CGrGlbls"$,
|
||||
//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 screen VGA cache.
|
||||
//<CTRL-ALT-v> will flush scrn VGA cache.
|
||||
VGAFlush;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user