|
From: randyhyde on 26 May 2005 20:42 Hi All, This is really for Paul Panks, but the programming technique is sufficiently general and interesting that I will post it here. Paul needs the ability to put status information on the screen in a fixed location. This is difficult to do under Windows and Linux in a portable fashion, so I am offering an alternate solution: multiple windows. Rather than attempting to put the status message at the same point on a screen that is scrolling by (and whose buffer is at an unknown point under OSes such as Linux), a cleaner solution is to open up two windows - one for the normal game (that scrolls) and one for the status window (that does not scroll, and upon which the game writes status information to specific spots on the screen). Because the status window never scrolls, it is easy to control where stuff is written to it (in a portable fashion) and still provide the usual user interface in the main game window. The scheme I am providing uses "memory-mapped files" under Windows and Linux to create a "shared memory" window through which the program can exchange data. The idea is as follows: 1. you run a "statusConsole" application whose sole job is to read data from the shared memory area and write that data to the status window. 2. the main game program writes data to the shared memory block that it wants the statusConsole application to display in the status window. This is all fairly straight-forward. The only catch is that the user has to run both applications (in different console windows) for this process to work properly. Well, actually, there is one other *big* catch. Because the two processes have different address spaces, you cannot pass pointers between the two processes. And HLA strings *are* pointers, so it's important that you pass the actual string (character) data between the processes rather than pointers to this character data. But other than this issue, using the two systems is very straight-forward. The following is a sample application that demonstrates this: ----- statusConsole.hhf ------ Header File ------ #if( !@defined( statusConsole_hhf )) ?statusConsole_hhf := true; const sharedEBX :text := "(type sharedData_t [ebx])"; // The following macro is used to declare string objects // in the shared data area. This macro reserves storage // for the maxlength, length, zero-terminator, and // character data fields needed by the HLA string format. // It also reserves some extra storage so we can guarantee // that the string data is dword-aligned. #macro sharedStr( _maxLen_ ); char[ (_maxLen_+20) div 4 * 4 ]; #endmacro // The following data structure is the template for // the shared memory area. Declare any variables you want // to pass between the two processes in this record. // // Note: no strings and no pointer objects. For string // objects, use the sharedStr type above and initialize // these strings in your two processes (see the example // code for details). type sharedData_t: record // The current example shares two variables // between the two processes, a string whose // length is a maximum of 256 characters and // an integer value: statusString :sharedStr(256); currentScore :int32; // Put any other shared variables here: endrecord; pSharedData_t :pointer to sharedData_t; #endif --- statusConsole.hla ---- status program source file ---- program statusConsole; #include( "stdlib.hhf" ) #include( "console.hhf" ) #include( "w.hhf" ) #include( "statusConsole.hhf" ) static statusStr :string; shared :pSharedData_t; mmFile :mmap; begin statusConsole; // The statusConsole program, which must be run first, // is responsbile for creating the memory-mapped file // in the file system: mmFile.create(); mmFile.openNew ( "advGameSharedData.dat", @max( @size( sharedData_t ), 4096) ); mov( mmFile.filePtr, eax ); mov( eax, shared ); // Zero out the shared memory area: mov( eax, edi ); xor( eax, eax ); mov( @size( sharedData_t ), ecx ); rep.stosb(); // Initialize the string pointers into the shared data area. // The call to str.init initializes the string you pass // as the first parameter to the length specified by the // second parameter. This function returns a pointer to // the string data that you should store into a local // STRING variable to use to manipulate that string. mov( shared, ebx ); str.init( sharedEBX.statusString, 256 ); mov( eax, statusStr ); // Sit in a loop, forever updating the console info forever mov( shared, ebx ); // Clearing the screen causes flicker, so you // may want to do something else here: console.cls(); // Position the cursor and display the status // string: console.gotoxy( 0, 0 ); stdout.put( statusStr ); // Position the cursor and display the current score: console.gotoxy( 0, 10 ); stdout.put( "Current Score: ", sharedEBX.currentScore ); // Update the display once every 500 ms: w.Sleep( 500 ); endfor; end statusConsole; ---- Main Game Program source file ----------- program main; #include( "stdlib.hhf" ) #include( "console.hhf" ) #include( "statusConsole.hhf" ) static shared :pSharedData_t; mmFile :mmap; statusStr :string; inputLine :str.strvar( 256 ); statusLine :str.strvar( 300 ); begin main; // The following code assumes that you've run the // statusConsole application in a separate window // *before* executing this program: mmFile.create(); mmFile.open( "advGameSharedData.dat", fileio.rw ); mov( mmFile.filePtr, eax ); mov( eax, shared ); // Initialize the status line string. Same as // for the statusConsole app except we also // initialize the string data with a default // string. mov( eax, ebx ); str.init( sharedEBX.statusString, 256 ); mov( eax, statusStr ); str.cpy( "(no status)", statusStr ); // "Play" the game: forever stdout.put( "Enter a command (ctrl-C to quit): " ); stdin.gets( inputLine ); str.put( statusLine, "You entered: '", inputLine, "'" nl ); mov( shared, ebx ); str.cpy( statusLine, statusStr ); inc( sharedEBX.currentScore ); endfor; end main; To try out this code, compile both modules, open two console windows, run statusConsole in one window *and then* run the main program in the other window. When you type lines in the main window, the text should appear in the status window. If you type enough lines, the main window will begin to scroll, but the status window will be unaffected. Of course, there is a lot of clean-up to do here. It would be nice to automatically run the statusConsole program from the main program (a quick call to os.system could do that, though getting it to run in a new window in an OS-independent fashion might be a chore). Also, rather than simply refreshing the status window every 500 ms, you might also try passing an "update available" variable to the status window that it can use to determine when it should update the window. Clearly, the code could be a bit more robust. I've not tried to run this code under Linux. It *should* work, but no guarantees). Cheers, Randy Hyde
From: Frank Kotler on 26 May 2005 22:31 randyhyde(a)earthlink.net wrote: .... > I've not tried to run this code under > Linux. It *should* work, but no guarantees). Nope. Gas complains about "junk '@4+0' after expression". The offending line... call dword ptr [__imp__Sleep@4+0] /* Sleep */ ....isn't going to work in Linux, I don't think, even if I get Gas to swallow it... I don't *see* a portable "sleep" function in the library (I *thought* that "w" looked suspicious). We've got "linux.nanosleep", so something can probably be worked out... I may give a shot at producing a "nanosleep" version of this, 'cause I don't quite get how it's supposed to work... When you say run it in "two windows", do you mean like F1 and F3 (F2 is busy running the X server)? Or are we talking about two "xterms" - intended to be on the screen at the same time? I don't think I like either much, as a solution to Paul's problem, but as an example of "interprocess communication using mmapped files", I think I can learn from it! So, once again, "Thanks, Randy!" Best, Frank
From: Frank Kotler on 27 May 2005 03:16 Frank Kotler wrote: > I may give a shot at producing a "nanosleep" version of > this, 'cause I don't quite get how it's supposed to work... Oh, my! I cobbed together a "nanosleep", which may not be right, but at least it assembles. Running the result gives me "Bus Error". That's a new one on me (*told* ya I had a lot to learn about this stuff!). I don't *think* it's my homemade "nanosleep"... Seems to be happening before the "cls". Strace shows "old_mmap" returning what looks like a reasonable value, and SIGBUS right after that... Perhaps in the... yes, ALD says it's happening in the "rep stosb"... The "main" part of the program assembles without complaint. Running it gives Memory Access Violation (HLA exception) - not unexpected - the other thing's supposed to be running first. If I type some arbitrary stuff into the zero-length file left by "statusconsole", it runs - repeatedly prompting for input - so I think that part's okay. Bus Error... Bus Error... This cat's sitting on a bench in Washington Square Park, 'bout three o'clock in the AM, you dig? Lady come up to him and says, "Does the crosstown bus run all night?" And he say, "... doo-dah, doo-dah..." Later, Frank
From: randyhyde on 27 May 2005 10:11 Frank Kotler wrote: > randyhyde(a)earthlink.net wrote: > > ... > > I've not tried to run this code under > > Linux. It *should* work, but no guarantees). > > Nope. Gas complains about "junk '@4+0' after expression". > The offending line... > > call dword ptr [__imp__Sleep@4+0] /* Sleep */ > > ...isn't going to work in Linux, I don't think, even if I > get Gas to swallow it... I don't *see* a portable "sleep" > function in the library (I *thought* that "w" looked > suspicious). We've got "linux.nanosleep", so something can > probably be worked out... Duh.... Maybe the #include( "w.hhf") should have been my first clue... Okay, just swap the w.Sleep call for a linux.sleep call and specify 1 (second) rather than 500 (ms). Of course, include linux.hhf rather than w.hhf, too. Cheers, Randy Hyde
From: T.M. Sommers on 27 May 2005 11:39 Frank Kotler wrote: > > Oh, my! I cobbed together a "nanosleep", which may not be > right, but at least it assembles. Running the result gives > me "Bus Error". That's a new one on me (*told* ya I had a > lot to learn about this stuff!). Bus errors usually mean that the program has tried to access an invalid memory location, as opposed to a valid memory location that you do not have permission to access, which gives a segmentation fault. The most common cause is trying to dereference a null pointer, but it could also be caused by a misaligned access. -- Thomas M. Sommers -- tms(a)nj.net -- AB2SB
|
Next
|
Last
Pages: 1 2 3 4 5 6 Prev: Assembly to C converter? Next: Here's a program that crashes RosAsm |