diff --git a/src/bin/shell/commands.c b/src/bin/shell/commands.c index a7c24d9..44bdef6 100644 --- a/src/bin/shell/commands.c +++ b/src/bin/shell/commands.c @@ -68,8 +68,10 @@ } else { printf("Childs Pid: [%i]\n",cPid); + /* while (pidStatus(cPid) > 0) sched_yield(); + */ } } } diff --git a/src/sys/include/vmm/paging.h b/src/sys/include/vmm/paging.h index 1bfecfa..ac632a7 100644 --- a/src/sys/include/vmm/paging.h +++ b/src/sys/include/vmm/paging.h @@ -58,7 +58,9 @@ void *vmmGetPhysicalAddr(uInt32); void *vmmCreateVirtualSpace(pidType); void *vmmGetFreeVirtualPage(pidType,int); -void vmm_pageFault(uInt32,uInt32); + +void *vmm_getFreeMallocPage(uInt16 count); +void vmm_pageFault(uInt32,uInt32); void _vmm_pageFault(); extern uInt32 *kernelPageDirectory; @@ -67,6 +69,9 @@ /*** $Log$ + Revision 1.5 2004/07/24 23:04:44 reddawg + Changes... mark let me know if you fault at pid 185 when you type stress + Revision 1.4 2004/07/24 20:00:51 reddawg Lots of changes to the vmm subsystem.... Page faults have been adjust to now be blocking on a per thread basis not system wide. This has resulted in no more deadlocks.. also the addition of per thread locking has removed segfaults as a result of COW in which two tasks fault the same COW page and try to modify it. diff --git a/src/sys/isa/atkbd.c b/src/sys/isa/atkbd.c index a97402d..454b2d3 100644 --- a/src/sys/isa/atkbd.c +++ b/src/sys/isa/atkbd.c @@ -149,7 +149,7 @@ int atkbd_init() { /* Insert the IDT vector for the keyboard handler */ - setVector(&keyboardISR, mVec+1, dPresent + dInt + dDpl0); + setVector(&keyboardISR, mVec+1, dPresent + dInt + dDpl3); /* Set the LEDS to their defaults */ setLED(); @@ -171,16 +171,15 @@ asm( ".globl keyboardISR \n" "keyboardISR: \n" + " sti \n" " pusha \n" /* Save all registers */ " mov $0x60,%dx \n" " inb %dx,%al \n" " push %eax \n" - - /* " sti \n" */ + " mov $0x20,%dx \n" /* The Following Sends Our EOI To The MPIC */ + " mov $0x20,%ax \n" + " outb %al,%dx \n" " call keyboardHandler \n" - " mov $0x20,%dx \n" /* The Following Sends Our EOI To The MPIC */ - " mov $0x20,%ax \n" - " outb %al,%dx \n" " pop %eax \n" " popa \n" " iret \n" /* Exit interrupt */ @@ -290,6 +289,9 @@ /*** $Log$ + Revision 1.14 2004/07/25 05:32:58 reddawg + fixed + Revision 1.13 2004/07/25 05:24:39 reddawg atkbd: removed sti... does it still miss keys diff --git a/src/sys/lib/kmalloc.c b/src/sys/lib/kmalloc.c index 17ba935..a83ec87 100644 --- a/src/sys/lib/kmalloc.c +++ b/src/sys/lib/kmalloc.c @@ -44,7 +44,7 @@ emptyKernDesc - The empty descriptor table (descriptors with out a memory backing) */ -static struct memDescriptor *kernDesc = 0x0; +static struct memDescriptor *usedKernDesc = 0x0; static struct memDescriptor *freeKernDesc = 0x0; static struct memDescriptor *emptyKernDesc = 0x0; @@ -54,27 +54,6 @@ static spinLock_t mallocSpinLock = SPIN_LOCK_INITIALIZER; static spinLock_t emptyDescSpinLock = SPIN_LOCK_INITIALIZER; -static void initMalloc() { - int i = 0x0; - - /* allocate out 4 pages of memory for our descriptor table this gives us 170 Entries */ - if ((emptyKernDesc = (struct memDescriptor *)vmmGetFreeKernelPage(sysID,4)) == 0x0) - kpanic("Error: vmmGetFreeKernelPage returned NULL\n"); - - /* zero out the memory so we know there is no garbage */ - memset(emptyKernDesc,0x0,0x4000); - - emptyKernDesc[0].next = &emptyKernDesc[1]; - - for (i = 0x1;i < ((0x4000/sizeof(struct memDescriptor)));i++) { - emptyKernDesc[i].next = &emptyKernDesc[i+1]; - emptyKernDesc[i].prev = &emptyKernDesc[i-1]; - } - - //Return - return; - } - /************************************************************************ Function: void *getEmptyDesc() @@ -102,7 +81,7 @@ return(tmpDesc); } kprintf("out of descriptors\n"); - if ((emptyKernDesc = (struct memDescriptor *)vmmGetFreeKernelPage(sysID,4)) == 0x0) + if ((emptyKernDesc = (struct memDescriptor *)vmm_getFreeMallocPage(4)) == 0x0) kpanic("Error: vmmGetFreeKernelPage returned NULL\n"); /* zero out the memory so we know there is no garbage */ @@ -138,7 +117,7 @@ ************************************************************************/ static int insertFreeDesc(struct memDescriptor *freeDesc) { - struct memDescriptor *tmpDesc; + struct memDescriptor *tmpDesc = 0x0; assert(freeDesc); @@ -146,27 +125,35 @@ kpanic("Inserting Descriptor with no limit\n"); if (freeKernDesc != 0x0) { - for (tmpDesc=freeKernDesc;tmpDesc;tmpDesc=tmpDesc->next) { - if (freeDesc->limit >= tmpDesc->limit) { - freeDesc->next = tmpDesc->next; - tmpDesc->next = freeDesc; - if (freeDesc->next != 0x0) - freeDesc->next->prev = freeDesc; - return(0x0); - } - else if (freeDesc->limit <= tmpDesc->limit) { + /* + */ + freeDesc->next = freeKernDesc; + freeDesc->prev = 0x0; + freeKernDesc->prev = freeDesc; + freeKernDesc = freeDesc; + /* + */ + /* + for (tmpDesc = freeKernDesc;tmpDesc != 0x0;tmpDesc = tmpDesc->next) { + if (freeDesc->limit <= tmpDesc->limit) { freeDesc->prev = tmpDesc->prev; - freeDesc->next = tmpDesc; tmpDesc->prev = freeDesc; - if (freeKernDesc == tmpDesc) + freeDesc->next = tmpDesc; + + if (tmpDesc == freeKernDesc) freeKernDesc = freeDesc; return(0x0); } - else { - - kpanic("unhandled insert? [%i:%i]\n",freeDesc->limit,tmpDesc->limit); - } + if (tmpDesc->next == 0x0) { + tmpDesc->next = freeDesc; + freeDesc->prev = tmpDesc; + freeDesc->next = 0x0; + return(0x0); + } } + kpanic("didnt Insert\n"); + */ + return(0x0); } else { freeDesc->prev = 0x0; @@ -174,7 +161,7 @@ freeKernDesc = freeDesc; return(0x0); } - //sysErr("Error With Freeing Blocks"); + return(0x1); } @@ -270,15 +257,15 @@ tmpDesc1->prev->next = tmpDesc1->next; if (tmpDesc1->next != 0x0) tmpDesc1->next->prev = tmpDesc1->prev; + if (tmpDesc1 == freeKernDesc) freeKernDesc = tmpDesc1->next; - if (freeKernDesc == 0x0) - kprintf("hmm no more kern desc\n"); + tmpDesc1->prev = 0x0; - tmpDesc1->next = kernDesc; - kernDesc->prev = tmpDesc1; - kernDesc = tmpDesc1; - if (tmpDesc1->limit > (len + 16)) { + tmpDesc1->next = usedKernDesc; + usedKernDesc->prev = tmpDesc1; + usedKernDesc = tmpDesc1; + if (tmpDesc1->limit > (len + 32)) { tmpDesc2 = getEmptyDesc(); assert(tmpDesc2); tmpDesc2->limit = tmpDesc1->limit - len; @@ -287,7 +274,7 @@ tmpDesc2->next = 0x0; tmpDesc2->prev = 0x0; if (tmpDesc2->limit <= 0x0) - kprintf("kmalloc-1 tmpDesc2: [%i:%i:%i]\n",tmpDesc2->limit,tmpDesc1->limit,len); + //kprintf("kmalloc-1 tmpDesc2: [%i:%i:%i]\n",tmpDesc2->limit,tmpDesc1->limit,len); insertFreeDesc(tmpDesc2); } buf = (char *)tmpDesc1->baseAddr; @@ -295,17 +282,19 @@ (char)buf[i] = (char)0x0; } spinUnlock(&mallocSpinLock); + //kprintf("baseAddr1[0x%X:0x%X]",tmpDesc1,tmpDesc1->baseAddr); return(tmpDesc1->baseAddr); } } tmpDesc1 = getEmptyDesc(); + //kprintf("no empty desc\n"); if (tmpDesc1 != 0x0) { - tmpDesc1->baseAddr = (struct memDescriptor *)vmmGetFreeKernelPage(sysID,((len + 4095)/4096)); + tmpDesc1->baseAddr = (struct memDescriptor *)vmm_getFreeMallocPage((len + 4095)/4096); tmpDesc1->limit = len; - tmpDesc1->next = kernDesc; + tmpDesc1->next = usedKernDesc; tmpDesc1->prev = 0x0; - kernDesc->prev = tmpDesc1; - kernDesc = tmpDesc1; + usedKernDesc->prev = tmpDesc1; + usedKernDesc = tmpDesc1; if ((len%0x1000) > 0) { tmpDesc2 = getEmptyDesc(); @@ -323,10 +312,12 @@ (char)buf[i] = (char)0x0; } spinUnlock(&mallocSpinLock); + //kprintf("baseAddr2[0x%X:0x%X]",tmpDesc1,tmpDesc1->baseAddr); return(tmpDesc1->baseAddr); } //Return Null If Unable To Malloc spinUnlock(&mallocSpinLock); + //kprintf("baseAddr3[0x0]"); return(0x0); } @@ -341,35 +332,29 @@ ************************************************************************/ void kfree(void *baseAddr) { - uInt32 *data = 0x0; - long i = 0x0; struct memDescriptor *tmpDesc1 = 0x0; - struct memDescriptor *tmpDesc2 = 0x0; spinLock(&mallocSpinLock); - for (tmpDesc1=kernDesc;tmpDesc1;tmpDesc1=tmpDesc1->next) { + for (tmpDesc1 = usedKernDesc;tmpDesc1 != 0x0;tmpDesc1 = tmpDesc1->next) { if (tmpDesc1->baseAddr == baseAddr) { - if (tmpDesc1->prev != 0x0) { - tmpDesc2 = tmpDesc1->prev; - tmpDesc2->next = tmpDesc1->next; - } - if (tmpDesc1->next != 0x0) { - tmpDesc2 = tmpDesc1->next; - tmpDesc2->prev = tmpDesc1->prev; - } - if (kernDesc == tmpDesc1) { - kernDesc = tmpDesc1->next; - } + + + if (tmpDesc1->prev != 0x0) + tmpDesc1->prev->next = tmpDesc1->next; + + if (tmpDesc1->next != 0x0) + tmpDesc1->next->prev = tmpDesc1->prev; + + if (usedKernDesc == tmpDesc1) + usedKernDesc = tmpDesc1->next; + tmpDesc1->next = 0x0; tmpDesc1->prev = 0x0; if (tmpDesc1->limit <= 0x0) kprintf("kfree tmpDesc1: [%i]\n",tmpDesc1->limit); insertFreeDesc(tmpDesc1); - data = (uInt32 *)baseAddr; - for (i=0;i < (tmpDesc1->limit/4);i++) { - data[i] = 0x0; - } + //mergeMemBlocks(); spinUnlock(&mallocSpinLock); return; @@ -382,6 +367,9 @@ /*** $Log$ + Revision 1.18 2004/07/26 16:52:45 reddawg + here we go + Revision 1.17 2004/07/24 23:04:44 reddawg Changes... mark let me know if you fault at pid 185 when you type stress diff --git a/src/sys/ubixfs/ubixfs.c b/src/sys/ubixfs/ubixfs.c index de57b37..483a5e7 100644 --- a/src/sys/ubixfs/ubixfs.c +++ b/src/sys/ubixfs/ubixfs.c @@ -70,29 +70,37 @@ /* UBU + */ do { + /* */ cacheNode = ubixfs_cacheFind(fsInfo->dirCache, file); if (cacheNode == NULL) return 0; /* UBU + */ if (cacheNode->present == 1) break; + /* */ /* one level of the cache wasn't loaded */ /* UBU + */ if (cacheNode->size != 0 && cacheNode->info == NULL) { kprintf("allocating space for %s\n", cacheNode->name); cacheNode->info = kmalloc(UBIXFS_ALIGN(cacheNode->size)); - fd->size = cacheNode->size; */ /* UBU what is the best way to keep these in sync? */ + fd->size = cacheNode->size; /* */ /* UBU what is the best way to keep these in sync? */ /* UBU + */ ubixfs_loadData(fd,cacheNode->info,cacheNode->size,cacheNode->startCluster); cacheNode->present = 1; - } */ /* if */ + } /* if */ /* UBU + */ } while (1); + /* */ assert(cacheNode); if (cacheNode == NULL) return 0; @@ -102,11 +110,13 @@ fd->size = cacheNode->size; fd->perms = cacheNode->permissions; fd->dirBlock = 0x0; /* Directory Start Sector */ + /* if (cacheNode->size != 0x0 && cacheNode->info == NULL) { cacheNode->info = kmalloc(UBIXFS_ALIGN(cacheNode->size)); ubixfs_loadData(fd,cacheNode->info,cacheNode->size,cacheNode->startCluster); cacheNode->present = 0x1; } + */ return(0x1); } else @@ -526,6 +536,9 @@ /*** $Log$ + Revision 1.34 2004/07/24 23:04:44 reddawg + Changes... mark let me know if you fault at pid 185 when you type stress + Revision 1.33 2004/07/23 09:10:06 reddawg ubixfs: cleaned up some functions played with the caching a bit vfs: renamed a bunch of functions diff --git a/src/sys/vmm/copyvirtualspace.c b/src/sys/vmm/copyvirtualspace.c index 29a7b0a..023f9e4 100644 --- a/src/sys/vmm/copyvirtualspace.c +++ b/src/sys/vmm/copyvirtualspace.c @@ -29,6 +29,9 @@ #include #include +#include + +static spinLock_t cvsSpinLock = SPIN_LOCK_INITIALIZER; /************************************************************************ @@ -53,10 +56,12 @@ uInt32 *parentStackPage = 0x0, *newStackPage = 0x0; uInt16 x = 0, i = 0, s = 0; + spinLock(&cvsSpinLock); + /* Set Address Of Parent Page Directory */ parentPageDirectory = (uInt32 *) parentPageDirAddr; /* Allocate A New Page For The New Page Directory */ - newPageDirectory = (uInt32 *) vmmGetFreePage(pid); + newPageDirectory = (uInt32 *) vmmGetFreeKernelPage(pid,1); /* Set newPageDirectoryAddress To The Newly Created Page Directories Page */ newPageDirectoryAddress = vmmGetPhysicalAddr((uInt32) newPageDirectory); /* First Set Up A Flushed Page Directory */ @@ -78,7 +83,7 @@ /* Set Parent To Propper Page Table */ parentPageTable = (uInt32 *) (tablesBaseAddress + (4096 * x)); /* Allocate A New Page Table */ - newPageTable = (uInt32 *) vmmGetFreePage(pid); + newPageTable = (uInt32 *) vmmGetFreeKernelPage(pid,1); /* Set Parent And New Pages To COW */ for (i = 0; i < pageEntries; i++) { /* If Page Is Mapped */ @@ -86,7 +91,7 @@ /* Check To See If Its A Stack Page */ if (((uInt32) parentPageTable[i] & pageStack) == pageStack) { /* Alloc A New Page For This Stack Page */ - newStackPage = (uInt32 *) vmmGetFreePage(pid); + newStackPage = (uInt32 *) vmmGetFreeKernelPage(pid,1); /* Set Pointer To Parents Stack Page */ parentStackPage = (uInt32 *) (((1024 * 4096) * x) + (4096 * i)); /* Copy The Tack Byte For Byte (I Should Find A Faster Way) */ @@ -124,7 +129,7 @@ * Allocate A New Page For The The First Page Table Where We Will Map The * Lower Region */ - newPageTable = (uInt32 *) vmmGetFreePage(pid); + newPageTable = (uInt32 *) vmmGetFreeKernelPage(pid,1); /* Flush The Page From Garbage In Memory */ for (x = 0; x < pageEntries; x++) { newPageTable[x] = (uInt32) 0x0; @@ -165,7 +170,7 @@ /* First Lets Unmap The Previously Allocated Page Table */ vmmUnmapPage((uInt32) newPageTable, 1); /* Allocate A New Page Table */ - newPageTable = (uInt32 *) vmmGetFreePage(pid); + newPageTable = (uInt32 *) vmmGetFreeKernelPage(pid,1); /* First Set Our Page Directory To Contain This */ newPageDirectory[767] = (uInt32) vmmGetPhysicalAddr((uInt32) newPageTable) | pageDefault; /* Now Lets Build The Page Table */ @@ -176,12 +181,18 @@ vmmUnmapPage((uInt32) newPageTable, 1); /* Now We Are Done With The Page Directory So Lets Unmap That Too */ vmmUnmapPage((uInt32) newPageDirectory, 1); + + spinUnlock(&cvsSpinLock); + /* Return Physical Address Of Page Directory */ return (newPageDirectoryAddress); } /*** $Log$ + Revision 1.5 2004/07/25 06:04:00 reddawg + Last of my fixes for the morning + Revision 1.4 2004/07/20 22:29:55 reddawg assert: remade assert diff --git a/src/sys/vmm/getphysicaladdr.c b/src/sys/vmm/getphysicaladdr.c index eba83f3..b2c4393 100644 --- a/src/sys/vmm/getphysicaladdr.c +++ b/src/sys/vmm/getphysicaladdr.c @@ -36,21 +36,26 @@ ************************************************************************/ void *vmmGetPhysicalAddr(uInt32 pageAddr) { - int pageDirectoryIndex = 0, pageTableIndex = 0; + int pageDirectoryIndex = 0x0, pageTableIndex = 0x0; uInt32 *pageTable = 0x0; - /* Get The Index To The Page Directory */ - pageDirectoryIndex = (pageAddr / (1024 * 4096)); - /* Get The Index To The Page Table */ - pageTableIndex = ((pageAddr - (pageDirectoryIndex * (1024 * 4096))) / 4096); + //Calculate The Page Directory Index + pageDirectoryIndex = (pageAddr >> 22); + + //Calculate The Page Table Index + pageTableIndex = ((pageAddr >> 12) & 0x3FF); + /* Set pageTable To The Virtual Address Of Table */ - pageTable = (uInt32 *) (tablesBaseAddress + (4096 * pageDirectoryIndex)); + pageTable = (uInt32 *) (tablesBaseAddress + (0x1000 * pageDirectoryIndex)); /* Return The Physical Address Of The Page */ return ((void *)(pageTable[pageTableIndex] & 0xFFFFF000)); } /*** $Log$ + Revision 1.2 2004/06/16 12:52:51 reddawg + Fixed bug with & + Revision 1.1.1.1 2004/04/15 12:06:51 reddawg UbixOS v1.0 diff --git a/src/sys/vmm/memory.c b/src/sys/vmm/memory.c index adc36e5..e308909 100644 --- a/src/sys/vmm/memory.c +++ b/src/sys/vmm/memory.c @@ -266,10 +266,6 @@ /* Adjust COW Counter */ vmmMemoryMap[vmmMemoryMapIndex].cowCounter += adjustment; - /* - * kprintf("(%i[%i]%x)\n",vmmMemoryMapIndex,vmmMemoryMap[vmmMemoryMapIndex].cowCounte - * r,baseAddr); - */ if (vmmMemoryMap[vmmMemoryMapIndex].cowCounter == 0) { vmmMemoryMap[vmmMemoryMapIndex].status = memAvail; vmmMemoryMap[vmmMemoryMapIndex].cowCounter = 0x0; @@ -319,7 +315,7 @@ if (vmmMemoryMap[i].cowCounter == 0) { vmmMemoryMap[i].status = memAvail; vmmMemoryMap[i].cowCounter = 0x0; - vmmMemoryMap[i].pid = -3; + vmmMemoryMap[i].pid = vmmID; freePages++; systemVitals->freePages = freePages; } @@ -335,6 +331,9 @@ /*** $Log$ + Revision 1.9 2004/07/24 23:04:44 reddawg + Changes... mark let me know if you fault at pid 185 when you type stress + Revision 1.8 2004/07/24 17:47:28 reddawg vmm_pageFault: deadlock resolved thanks to a propper solution suggested by geist diff --git a/src/sys/vmm/page_fault.S b/src/sys/vmm/page_fault.S index b0ab6af..24ccddf 100644 --- a/src/sys/vmm/page_fault.S +++ b/src/sys/vmm/page_fault.S @@ -41,7 +41,7 @@ push %eax movl %cr2,%eax pushl %eax - sti + sti call vmm_pageFault addl $0x8,%esp pop %fs @@ -55,6 +55,9 @@ /*** $Log$ + Revision 1.1 2004/07/24 23:13:21 reddawg + Oh yeah try this one + END ***/ diff --git a/src/sys/vmm/pagefault.c b/src/sys/vmm/pagefault.c index c026d97..29785c5 100644 --- a/src/sys/vmm/pagefault.c +++ b/src/sys/vmm/pagefault.c @@ -75,7 +75,7 @@ //Set Src To Base Address Of Page To Copy src = memAddr & 0xFFFFF000; //Allocate A Free Page For Destination - dst = (uInt32 *) vmmGetFreePage(_current->id); + dst = (uInt32 *) vmmGetFreeVirtualPage(_current->id,1); //Copy Memory for (i=0;i 1) { + for (c = 0; c < count; c++) { + if (y + c >= 1024) + kpanic("hmm?"); + if ((uInt32) pageTableSrc[y + c] != (uInt32) 0x0) { + c = -1; + break; + } + } + if (c != -1) { + for (c = 0; c < count; c++) { + vmmRemapPage((uInt32) vmmFindFreePage(sysID), ((x * 0x400000) + ((y + c) * 0x1000))); + vmmClearVirtualPage((uInt32) ((x * 0x400000) + ((y + c) * 0x1000))); + } + spinUnlock(&fkpSpinLock); + return ((void *)((x * 0x400000) + (y * 0x1000))); + } + } else { + /* Map A Physical Page To The Virtual Page */ + vmmRemapPage((uInt32) vmmFindFreePage(sysID), ((x * 0x400000) + (y * 0x1000))); + /* Clear This Page So No Garbage Is There */ + vmmClearVirtualPage((uInt32) ((x * 0x400000) + (y * 0x1000))); + /* Return The Address Of The Newly Allocate Page */ + spinUnlock(&fkpSpinLock); + return ((void *)((x * 0x400000) + (y * 0x1000))); + } + } + } + } + /* If No Free Page Was Found Return NULL */ + spinUnlock(&fkpSpinLock); + return (0x0); +} + /*** $Log$ + Revision 1.9 2004/07/24 23:13:21 reddawg + Oh yeah try this one + Revision 1.8 2004/07/24 20:00:51 reddawg Lots of changes to the vmm subsystem.... Page faults have been adjust to now be blocking on a per thread basis not system wide. This has resulted in no more deadlocks.. also the addition of per thread locking has removed segfaults as a result of COW in which two tasks fault the same COW page and try to modify it. diff --git a/src/sys/vmm/unmappage.c b/src/sys/vmm/unmappage.c index 63d4333..41dc7b3 100644 --- a/src/sys/vmm/unmappage.c +++ b/src/sys/vmm/unmappage.c @@ -53,11 +53,13 @@ uInt32 *pageTable = 0x0; /* Get The Index To The Page Directory */ - pageDirectoryIndex = (pageAddr / (1024 * 4096)); - /* Get The Index To The Page Table */ - pageTableIndex = ((pageAddr - (pageDirectoryIndex * (1024 * 4096))) / 4096); + pageDirectoryIndex = (pageAddr >> 22); + + //Calculate The Page Table Index + pageTableIndex = ((pageAddr >> 12) & 0x3FF); + /* Set pageTable To The Virtual Address Of Table */ - pageTable = (uInt32 *) (tablesBaseAddress + (4096 * pageDirectoryIndex)); + pageTable = (uInt32 *) (tablesBaseAddress + (0x1000 * pageDirectoryIndex)); /* Free The Physical Page If Flags Is 0 */ if (flags == 0) { @@ -113,6 +115,9 @@ /*** $Log$ + Revision 1.3 2004/06/15 12:35:05 reddawg + Cleaned Up + Revision 1.2 2004/06/10 22:23:56 reddawg Volatiles