View Full Version: setint problem?

dex >>DexOS >>setint problem?


roboman- 04-27-2008
setint problem?
I think there might be a problem with the setint function. In DexOS 2, I was going to add a clock function, basicly write a program that would write the clock program to high memory, set the int then close the program leaving the clock code in place. Then work started on DexOS 3 that would include drivers at boot... so I dropped it until today. When I loaded up the test program I had writen it no longer workes in DexOS 3. Wrote a quick program that just loops the clock and it worked, so I'm guessing it's the setint function. ;=========================================================; ; DexTime ; ;---------------------------------------------------------; ; By Roboman ; ;=========================================================; use32 ORG 0x400000 ; where our program is loaded to jmp start ; jump to the start of program. db 'DEX1' ; We check for this, to make shore it a valid Dex4u file. ;----------------------------------------------------; ; Start of program. ; ;----------------------------------------------------; start: mov ax,18h ; set ax to nonlinear base mov ds,ax ; add them to ds mov es,ax ; and es. ;----------------------------------------------------; ; Get calltable address. ; ;----------------------------------------------------; mov edi,Functions ; this is the interrupt mov al,0 ; we use to load the DexFunction.inc mov ah,0x0a ; with the address to dex4u functions. int 40h ;----------------------------------------------------; ; Init stuff. ; ;----------------------------------------------------; mov cx,1 ; set timer to 1 second call [InterruptTimer] mov eax,2 ; set up clock mov edi,clock call [SetInt] mov [old],edi end_program: call [WaitForKeyPress] mov eax,2 ; Give clock back to system mov edi,[old] call [SetInt] mov cx,0 ; Restore int timer call [InterruptTimer] ret ; Return to the CLI/GUI clock: push eax push ebx mov ax,0147h call [SetCursorPos] call [GetTime] call [WriteHex8] shr eax,8 mov ebx,eax mov ax,0144h call [SetCursorPos] mov al,bl call [WriteHex8] mov ax,0141h call [SetCursorPos] mov al,bh call [WriteHex8] pop ebx pop eax ret ;----------------------------------------------------; ; Data. ; ;----------------------------------------------------; old rd 1 include 'Dex.inc' ; Here is where we includ our "DexFunctions.inc"

Dex- 04-27-2008

Hi roboman, I have made the command better, by making it like Dos, now you can get or set any int. This is how it is written for uses with the latest ver ;=========================================================; ; DexTime ; ;---------------------------------------------------------; ; By Roboman ; ;=========================================================; use32 ORG 0x400000 ; where our program is loaded to jmp start ; jump to the start of program. db 'DEX2' ; We check for this, to make shore it a valid Dex4u file. ;----------------------------------------------------; ; Start of program. ; ;----------------------------------------------------; start: mov ax,18h ; set ax to nonlinear base mov ds,ax ; add them to ds mov es,ax ; and es. ;----------------------------------------------------; ; Get calltable address. ; ;----------------------------------------------------; mov edi,Functions ; this is the interrupt mov al,0 ; we use to load the DexFunction.inc mov ah,0x0a ; with the address to dex4u functions. int 40h ;----------------------------------------------------; ; Init stuff. ; ;----------------------------------------------------; mov eax,20h ; put interrupt number in AL call [GetIntVector] ; New function to get int vector mov [old],edx ; The offset is returned in EDX, CX = selector mov edx,clock ; EDX = interrupt handler address mov eax,20h ; put interrupt number in AL call [SetIntVector] ; New function to set int vector end_program: call [WaitForKeyPress] mov eax,20h ; put interrupt number in AL mov edx,[old] call [SetIntVector] ; New function to set int vector call [Clstext] ; This will set the cursor xy back to normal ret ; Return to the CLI/GUI ; In here you could test the seconds and not reprint if it the same as old seconds. clock: push eax push ebx mov ax,0147h call [SetCursorPos] call [GetTime] call [WriteHex8] shr eax,8 mov ebx,eax mov ax,0144h call [SetCursorPos] mov al,bl call [WriteHex8] mov ax,0141h call [SetCursorPos] mov al,bh call [WriteHex8] pop ebx pop eax jmp dword[old] ; We need to call old handler, as timer may be used for delay etc. jmp $ ; It will not return here, but just incase. ;----------------------------------------------------; ; Data. ; ;----------------------------------------------------; old rd 1 include 'Dex.inc' ; Here is where we includ our "DexFunctions.inc" The function used are now called SetIntVector rd 1 ; 78. Hooks in the interrupt vector, AL = interrupt number, EDX = interrupt handler address (Note: selector, is not updated, but you can unhighlight it in the kernel32) GetIntVector rd 1 ; 79. Gets the the interrupt vector address, Entry: AL = interrupt number, Return EDX = offset, CX = selector (Note: only offset need updating, on returning old address or setting new) Hope this helps, if you have any more ? just ask. Regards Dex.

roboman- 04-27-2008

Ah, nice! Your ver of the program works great and I like the added functionality. I did have to change SetIntVectror to SetInt GetIntVector to CloseInt Since the Dex.inc with the latest release still calls them by the old name. Thanks for the help

Dex- 04-28-2008

Cool glad it works, not sure why i have two differant names for the same functions, i will look into that, thanks for the info. Regards Dex.

roboman- 05-03-2008

Well I cleaned the program up a bit. Hadn't ever looked at the coff formast or the example program. Does any one have a good refference to read up on the format, as it would apply here? Any ways the basic code I wanted to put there is : ;=========================================================; ; DexTime ; ;---------------------------------------------------------; ; By Roboman ; ;=========================================================; use32 ORG 0x400000 ; where our program is loaded to jmp start ; jump to the start of program. db 'DEX2' ; Mark it a DexOS ver3 file. ;--------------------; ; Set up DexOS stuff ; ;--------------------; start: mov ax,18h ; set ax to nonlinear base mov ds,ax ; set ds to nonlinear base mov es,ax ; set es to nonlinear base. mov edi,Functions ; address of jump table (in dex.inc) mov ax,0x0a00 ; load table wih DexOS function addresses int 40h ;----------------------------------------------------; ; Init stuff. ; ;----------------------------------------------------; mov eax,20h ; timer interrupt number call [GetIntVector] ; get address of where it goes now mov [old],edx ; save that address mov edx,clock ; address of function we are adding call [SetIntVector] ; set int to our function end_program: call [WaitForKeyPress] ; don't need as a driver, just here so clock doesn't go away before we can see it mov eax,20h ; put interrupt number in AL mov edx,[old] ; the old address the int called call [SetIntVector] ; restore the old address, guess this could be a problem if things close out of order call [Clstext] ; This will set the cursor xy back to normal, not needed ret ; all done clock: push eax ; don't want to trash a & b several times a sec push bx call [GetTime] ; ask the computer what time it is shr eax,8 ; Blow off seconds cmp ax,word[old_time] ; has the time changed je done ; no need to reprint the same time mov bx,ax ; store the time in ebx mov ah,01 ; set color shr al,4 ; blow off the low digit of the min add al,48 ; change it to ascii mov [0xb7b08],ax ; write to screen mov al,00001111b ; mask to dump other part of min and al,bl ; put other digit of min in al add al,48 ; change to ascii mov [0xb7b0a],ax ; write to screen mov al,bh ; get the hour from bx shr al,4 ; blow off low digit add al,48 ; change to acii mov [0xb7b02],ax ; put high digit of hour on screen mov al,00001111b ; mask to dump high didit of hour and al,bh ; put low didit of hour in al add al,48 ; make ascii mov [0xb7b04],ax ; put it on screen mov al,':' ; gotta have a colen mov [0xb7b06],ax ; on the screen done: pop bx pop eax jmp dword[old] ; We need to call old handler, as timer may be used for delay etc. jmp $ ; It will not return here, but just incase. ;----------------------------------------------------; ; Data. ; ;----------------------------------------------------; old_time rw 1 old rd 1 include 'Dex.inc' ; Here is where we includ our "DexFunctions.inc"

roboman- 05-03-2008

This is what I have, but I'm sure there is a lot wrong with it. and was wondering how I would call things in the driver, like a service or unload. ;=========================================================; ; Time module 25/08/07 ; ;---------------------------------------------------------; ; ; ; Assemble like this: ; ; c:\fasm Time.asm Time.obj ; ;=========================================================; format COFF jmp ModRun ; jump to the start of program. Signature db 'MOD1' ; We check it's a valid Module file. ModID db 'DEX4UMOD' ; This the ID of the module. SizeOfModule dd ModEnd ; This put at end of module. ModLoadPtr dd ModLoad ; This is to run code on first load of module. ModUnLoadPtr dd ModUnLoad ; This is to run code on unload of module. ModNumberPtr dd 2 ; This points to the function list. ModRun: pushad ; ************ [STARTUP CODE HERE] ************ push ds push es mov ax,18h ; set ax to nonlinear base mov ds,ax ; set ds to nonlinear base mov es,ax ; set es to nonlinear base. mov edi,Functions ; address of jump table (in dex.inc) mov ax,0x0a00 ; load table wih DexOS function addresses int 40h ;----------------------------------------------------; ; Init stuff. ; ;----------------------------------------------------; mov eax,20h ; timer interrupt number call [GetIntVector] ; get address of where it goes now mov [old],edx ; save that address mov edx,clock ; address of function we are adding call [SetIntVector] ; set int to our function mov esi,MsgModLoadOK call [PrintString] pop es pop ds popad ret ; This returns to the CLI/GUI ModLoad: ; ************ [HERE IS CODE TO CALL FUNCTIONS] ************ push ds push es push eax mov ax,18h mov ds,ax mov es,ax pop eax cmp ebx,[ModNumberPtr] ja ModError shl ebx,2 add ebx,ModFunctions call dword[ebx] jc ModError ModOK: pop es pop ds clc ret ModError: pop es pop ds stc ret ModUnLoad: ; ************ [UNLOAD MOD CODE HERE] ************ push eax push edx mov eax,20h ; timer interrupt number mov edx,[old] ; address of old call call [SetIntVector] ; reset timer address pop edx pop eax ret ServiceUnUsed: ; Unused function. ret clock: push eax ; don't want to trash a & b several times a sec push bx call [GetTime] ; ask the computer what time it is shr eax,8 ; Blow off seconds cmp ax,word[old_time] ; has the time changed je done ; no need to reprint the same time mov bx,ax ; store the time in ebx mov ah,01 ; set color shr al,4 ; blow off the low digit of the min add al,48 ; change it to ascii mov [0xb7b08],ax ; write to screen mov al,00001111b ; mask to dump other part of min and al,bl ; put other digit of min in al add al,48 ; change to ascii mov [0xb7b0a],ax ; write to screen mov al,bh ; get the hour from bx shr al,4 ; blow off low digit add al,48 ; change to acii mov [0xb7b02],ax ; put high digit of hour on screen mov al,00001111b ; mask to dump high didit of hour and al,bh ; put low didit of hour in al add al,48 ; make ascii mov [0xb7b04],ax ; put it on screen mov al,':' ; gotta have a colen mov [0xb7b06],ax ; on the screen done: pop bx pop eax jmp dword[old] ; We need to call old handler, as timer may be used for delay etc. jmp $ ; It will not return here, but just incase. Service2: ; ************ [SECOND FUNCTON CODE HERE] ************ ret ;----------------------------------------------------; ; Start of code in module. ; ;----------------------------------------------------; ModFunctions: dd ServiceUnUsed ; Reserve the first one. dd clock ; Points to first function.n. dd Service2 ; Points to second function. ;----------------------------------------------------; ; Data. ; ;----------------------------------------------------; MsgModLoadOK db 'Time module loaded',13,13,0 old_time rw 1 old rd 1 ;----------------------------------------------------; ; BSS goes here. ; ;----------------------------------------------------; align 4 ;-----+ Cut db '2CUT' ; These must be here ;-----+ include 'Dex.inc' ; Dex inc file ModEnd:

Dex- 05-03-2008

Sure, here is the prosess if you want to make a driver or loadable module, if i am wrong let me know. To turn your code into a driver (in coff format) you would rewrite it as follows ;=========================================================; ; DexTime ; ;---------------------------------------------------------; ; By Roboman ; ;=========================================================; format COFF jmp ModRun ; jump to the start of program. Signature db 'MOD1' ; We check it's a valid Module file. ModID db 'DEX TIME' ; This the ID of the module. SizeOfModule dd ModEnd ; This put at end of module. ModLoadPtr dd ModLoad ; This is to run code on first load of module. ModUnLoadPtr dd ModUnLoad ; This is to run code on unload of module. ModNumberPtr dd 1 ; This points to the function list. ModRun: pushad ; ************ [STARTUP CODE HERE] ************ push ds push es mov ax,18h ; set ax to nonlinear base mov ds,ax ; set ds to nonlinear base mov es,ax ; set es to nonlinear base. mov edi,Functions ; address of jump table (in dex.inc) mov ax,0x0a00 ; load table wih DexOS function addresses int 40h ;----------------------------------------------------; ; Init stuff. ; ;----------------------------------------------------; mov eax,20h ; timer interrupt number call [GetIntVector] ; get address of where it goes now mov [old],edx ; save that address mov edx,clock ; address of function we are adding call [SetIntVector] ; set int to our function ;----------------------------------------------------; ; Module setup OK, exit here. ; ;----------------------------------------------------; pop es pop ds popad call [Clstext] ; This will set the cursor xy back to normal, not needed ret ; This returns to the CLI/GUI clock: push eax ; don't want to trash a & b several times a sec push bx call [GetTime] ; ask the computer what time it is shr eax,8 ; Blow off seconds cmp ax,word[old_time] ; has the time changed je done ; no need to reprint the same time mov bx,ax ; store the time in ebx mov ah,01 ; set color shr al,4 ; blow off the low digit of the min add al,48 ; change it to ascii mov [0xb7b08],ax ; write to screen mov al,00001111b ; mask to dump other part of min and al,bl ; put other digit of min in al add al,48 ; change to ascii mov [0xb7b0a],ax ; write to screen mov al,bh ; get the hour from bx shr al,4 ; blow off low digit add al,48 ; change to acii mov [0xb7b02],ax ; put high digit of hour on screen mov al,00001111b ; mask to dump high didit of hour and al,bh ; put low didit of hour in al add al,48 ; make ascii mov [0xb7b04],ax ; put it on screen mov al,':' ; gotta have a colen mov [0xb7b06],ax ; on the screen done: pop bx pop eax jmp dword[old] ; We need to call old handler, as timer may be used for delay etc. jmp $ ; It will not return here, but just incase. ModLoad: ; ************ [HERE IS CODE TO CALL FUNCTIONS] ************ push ds push es push eax mov ax,18h mov ds,ax mov es,ax pop eax cmp ebx,[ModNumberPtr] ja ModError shl ebx,2 add ebx,ModFunctions call dword[ebx] jc ModError ModOK: pop es pop ds clc ret ModError: pop es pop ds stc ret ModUnLoad: ; ************ [UNLOAD MOD CODE HERE] ************ ret ServiceUnUsed: ; Unused function. ret Service1: ; ************ [FIST FUNCTON CODE HERE] ************ pushad mov eax,20h ; put interrupt number in AL mov edx,[old] ; the old address the int called call [SetIntVector] ; restore the old address, guess this could be a problem if things close out of order call [Clstext] ; This will set the cursor xy back to normal, not needed popad ret ;----------------------------------------------------; ; Start of code in module. ; ;----------------------------------------------------; ModFunctions: dd ServiceUnUsed ; Reserve the first one. dd Service1 ; Points to first function. ;----------------------------------------------------; ; String Data. ; ;----------------------------------------------------; MsgModLoadOK db 'DexTime module loaded',13,13,0 ;----------------------------------------------------; ; BSS goes here. ; ;----------------------------------------------------; align 4 ;-----+ Cut db '2CUT' ; These must be here ;-----+ old_time rw 1 old rd 1 align 4 include 'Dex.inc' ; Dex inc file ModEnd: As you can see i have converted it to DexOS driver interface layout, to simple use the coff format you would only need to do this format COFF jmp start start: ; some code here But DexOS user a mod coff file, so it can ge the relcation info it needs to load the file to any address. Anyway if you take the driver ver of your code, assemble it with fasm like this fasm DexTime.asm DexTime.obj <enter> You will end up with a DexTime.obj , this file in many case will be very big (not so in this case). Next use Coff2Dex as follows Coff2Dex DexTime.obj DexTime.dri DexTime.rel <enter> You should now have two more files DexTime.dri and DexTime.rel these are the files DexOS will use. If you want to run them at startup add this line to the startup.bat load DexTime.dri Make sure it on its own line and if it the last driver to load put end On the next line, also add DexTime.dri and DexTime.rel to the drivers folder, thats it on booting DexOS up your time driver should load and run, and should keep running even if you run other programs. Now just incase you want to stop the DexTime i written a simple program to do that Here it is ;----------------------------------------------------; ; Simple program to unhook DexTime module. ; ;----------------------------------------------------; use32 ORG 0x400000 ; where our program is loaded to start1: jmp start ; jump to the start of program. db 'DEX2' ; We check for this, to make shore it a valid Dex4u file. ;----------------------------------------------------; ; Start of program. ; ;----------------------------------------------------; start: mov ax,18h mov ds,ax mov es,ax ;----------------------------------------------------; ; Get calltable address. ; ;----------------------------------------------------; mov edi,Functions ; this is the interrupt mov al,0 ; we use to load the DexFunction.inc mov ah,0x0a ; with the address to dex4u functions. int 40h ;----------------------------------------------------; ; Unhook time. ; ;----------------------------------------------------; mov esi,ModID ; point to id to the DexTime module call [ModuleFunction] ; call function jc ModError ; any errors ? mov [TimeModAddress],eax ; Save the address mov ebx,1 ; call function 1 (unhook time) call [TimeModAddress] ;----------------------------------------------------; ; Exit OK . ; ;----------------------------------------------------; mov esi,ModOKMess ; no print OK message call [PrintString] LetsExit: ret ; Exit. ;----------------------------------------------------; ; Error exit. ; ;----------------------------------------------------; ModError: mov esi,ModErrorMess ; yes print error message. call [PrintString] ret ; Exit. ModID: db 'DEX TIME',0 ; This is the id to the DexTime module ModOKMess: db 'Time unhooked',13,13,0 ModErrorMess: db 'Timer unhook error',13,13,0 TimeModAddress dd 0 include 'Dex.inc' ; Dex inc file Now there is one thing you need to know, that is, the way it is at the moment it will only load the same driver once (that so you do not load the same driver twice). I going to add unload driver code, but up to now i have not got round to it, so if your testing and you load the driver and then close it with the timeoff program, then want to load it again you will need to reboot, as it will say driver already loaded. Also note: you can load it at anytime ( if it as not already been loaded) by having both DexTime.dri and DexTime.rel in the same dir as the load program and typing load DexTime.dri <enter> Hope this helps any ?, just ask. Regards Dex. PS: The comment in the Dex.inc are not upto date :( , for the ModuleFunction, you first call ModuleFunction along with ID (must be caps and 8 letters or spaced to 8 letters) If no error (eg: module not loaded) it will return the call address in EAX. To call a function put the number in EBX and call the address returned in EAX. See above demo, for more details.

roboman- 05-03-2008

Thank you very much. That answers about every thing. I think I have a grasp on it now. I'll do up some services to turn the display off and on, as well as restoring the int. Do a program to access the driver and then do a bit of a tutorial on the basics of writing and using a driver in DexOS. Thanks for the great toy and for letting us play with it :)

Dex- 05-03-2008

Sounds cool and a tut would be very helpfull thanks. What we need is to agree on the different driver names, so when we make drivers for the same device it has the same ID, eg: "DEXSOUND" for sound cards ID and then everyone that writes a sound card driver will use that ID and than we can agree what function a basic sound driver needs and what the function numbers are. This way when you need your program to use the sound (for example), you just need to test the "DEXSOUND" ID for module loaded, than print a error if no sound driver found or use the differant function to play the sound 8) . I would also like to thank you and coders like you, for making the great program that make the difference to the way DexOS works, thanks. Regards Dex. PS: I have a screenshot program that i will make into a loadable module, as it would be nice to take screenshot to show others our work. PPS: Here is a skeleton driver that may help ;=========================================================; ; Test module 25/08/07 ; ;---------------------------------------------------------; ; By Dex. ; ; ; ; Here is a simple Module demo. ; ; Assemble like this: ; ; c:\fasm Test.asm Test.obj ; ; ; ; Then use: ; ; Coff2Dex test.obj test.dri test.rel <enter> ; ;=========================================================; format COFF jmp ModRun ; jump to the start of program. Signature db 'MOD1' ; We check it's a valid Module file. ModID db 'DEXOSMOD' ; This the ID of the module. SizeOfModule dd ModEnd ; This put at end of module. ModLoadPtr dd ModLoad ; This is to run code on first load of module. ModUnLoadPtr dd ModUnLoad ; This is to run code on unload of module. ModNumberPtr dd 4 ; This points to the function list. ModRun: pushad ; ************ [STARTUP CODE HERE] ************ push ds push es mov ax,18h mov ds,ax mov es,ax mov edi,Functions ; this is the interrupt mov al,0 ; we use to load the DexFunction.inc mov ah,0x0a ; with the address to dex4u functions. int 40h mov esi,MsgModLoadOK call [PrintString] pop es pop ds popad ret ; This returns to the CLI/GUI ModLoad: ; ************ [HERE IS CODE TO CALL FUNCTIONS] ************ push ds push es push eax mov ax,18h mov ds,ax mov es,ax pop eax cmp ebx,[ModNumberPtr] ja ModError shl ebx,2 add ebx,ModFunctions call dword[ebx] jc ModError ModOK: pop es pop ds clc ret ModError: pop es pop ds stc ret ModUnLoad: ; ************ [UNLOAD MOD CODE HERE] ************ ret ServiceUnUsed: ; Unused function. ret Service1: ; ************ [FIRST FUNCTON CODE HERE] ************ pushad mov esi,msgService1 call [PrintString] popad ret Service2: ; ************ [SECOND FUNCTON CODE HERE] ************ pushad mov esi,msgService2 call [PrintString] popad ret Service3: ; ************ [THIRD FUNCTON CODE HERE] ************ pushad mov esi,msgService3 call [PrintString] popad ret Service4: ; ************ [FOURTH FUNCTON CODE HERE] ************ pushad mov esi,msgService4 call [PrintString] popad ret ;----------------------------------------------------; ; Start of code in module. ; ;----------------------------------------------------; ModFunctions: dd ServiceUnUsed ; Reserve the first one. dd Service1 ; Points to first function. dd Service2 ; Points to second function. dd Service3 ; Points to first function. dd Service4 ; Points to second function. ;----------------------------------------------------; ; Data. ; ;----------------------------------------------------; MsgModLoadOK db 'Test module loaded',13,13,0 msgService1 db 'Hello from Service1!',13,0 msgService2 db 'Hello from Service2!',13,0 msgService3 db 'Hello from Service3!',13,0 msgService4 db 'Hello from Service4!',13,0 ;----------------------------------------------------; ; BSS goes here. ; ;----------------------------------------------------; align 4 ;-----+ Cut db '2CUT' ; These must be here ;-----+ include 'Dex.inc' ; Dex inc file ModEnd: And to test them use32 ORG 0x400000 ; where our program is loaded to start1: jmp start ; jump to the start of program. db 'DEX2' ; We check for this, to make shore it a valid Dex4u file. ;----------------------------------------------------; ; Start of program. ; ;----------------------------------------------------; start: mov ax,18h mov ds,ax mov es,ax ;----------------------------------------------------; ; Get calltable address. ; ;----------------------------------------------------; mov edi,Functions ; this is the interrupt mov al,0 ; we use to load the DexFunction.inc mov ah,0x0a ; with the address to dex4u functions. int 40h mov esi,ModID1 call [ModuleFunction] jc LetsExitErr mov [mod1],eax mov ebx,1 call [mod1] mov ebx,2 call [mod1] mov ebx,3 call [mod1] mov ebx,4 call [mod1] LetsExit: ret ; Exit. LetsExitErr: mov esi,ErrMes call [PrintString] ret ;----------------------------------------------------; ; calltable include goes here. ; ;----------------------------------------------------; ErrMes: db "Error, module not loaded ",13,13,0 mod1 dd 0 ModID1 db 'DEXOSMOD' ; This the ID of the module. include 'Dex.inc' ; Dex inc file

roboman- 05-03-2008

Thought you might want to take a look before I go posting wrong info around the internet: http://home.comcast.net/~dexos/time.zip I liked your last example, working examples are always great. As you pointed out several times, drivers are a large part of the problem with hobby os's. It's half the problem with Linux and half the clout of Microsoft. Windows is about the only thing that makes all that odd hardware work. An outline of a naming convention in the coff2dex zip and a sticky on the forum might be good (is there a driver section?

Dex- 05-04-2008

Great work roboman, the only thing i would change is add a .txt doc. But you have got the idea behind and uses of the driver interface just right. Thanks again for your work, which will add greatly to make DexOS more useably. Regards Dex.

Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.