Assertions

Assembly code can get confusing enough without having to debug it just to find that misaligned memory was the problem. Thus, a code development tool used within the source code for this book is the use of an assertion. Typically, the top of your file should include the following header file declaration:

 #include <assert.h> 

Sorry to bore those of you who religiously use these! An assertion is essentially a test for a "not true thus false" condition. That is, if the condition is false, then an assertion will occur whereby the instruction pointer is halted at the offending line of code. This is very similar to the following:

 if (!(3==a))                 // assert(3==a)  {     // System Break     DebugBreak();            // Win32, Xbox  } 

As mentioned, the condition must fail to halt the code. Here we wanted the variable a to be assigned the value of 3 to be successful; otherwise it would halt the system. There are many philosophies on the subject about assertions. In my case, I believe in four types of assertions:

  • Fatal Debug

  • Fatal Release

  • Non-fatal Debug

  • Non-fatal Release

A Fatal Debug is a programmer error. This is something that should never , ever , ever occur, such as passing a NULL, the address of a pointer, an obviously bad pointer, an out-of-range argument to a function, or a misaligned vector or matrix. It halts the debug version of the code and forces the programmer to fix it right away. This is the one assertion that should occur most of the time in your debug code while it is being developed, but never in the shipped code.

A Fatal Release is an unrecoverable error in a shipped version of the application. The computer is totally out of memory so there is not even enough memory to display an error message. Catastrophic error. This should preferably never be used. There are even ways in an application to stop the application logic, jettison memory, and put up your "Sorry, I am dead! Call your Customer Support Person" screen. You do not want your customers to ever get the Blue Screen of Death! (Windows users know what I am talking about!)

These last two Non-fatal Debug and Non-fatal Release are recoverable errors. That is, they are errors that can be worked around without killing the application. These errors are typically not able to load a resource from the media, missing or contaminated file, missing sound resources, etc. Your application can be crippled in such a way to allow it to progress. Resources such as art can use placeholders when are can't be loaded and allocated. Sounds that aren't loaded don't get played . The idea is to try to keep the application running. Even during development of the application you may want to alert the developer that table sizes do not match the number of defined indexes for that table. That sort of thing. The idea is to not kill the running application but to annoy the programmer enough to get him to fix the problem. I like that so much I am going to state that again!

Tip 

The idea of non-fatal assertions is to not kill the running application but to annoy the programmer enough to get him to fix the problem.

On a personal note: Please do not get annoyed and then turn off the assertions at the source instead of fixing the real problem. (I once worked with a programmer who did just that! It caused lots of Homer Simpson type errors to creep into the code that would have been caught immediately otherwise. DOH!) On the other hand, a non-technical supervisor once threatened to chop off my fingers because the assertions in the code to catch errors were doing just that and thus breaking the build! A better method is to spend a couple minutes now catching the errors early in the development cycle rather than a long time during crunch mode at the tail end of the project trying to track them down, or never knowing about them until after the product has shipped.

For purposes of this book, only a debug assertion will be utilized. In essence this means if an application were to be defined as a debug model, then the assertion would exist in the code, making the code run a little slower but considerably safer. In a release model, it is stubbed out, thus does not exist, and your code will magically run a bit faster. In the code for this book you will find the following assertions in use for the generic assertion ASSERT():

Listing 2-1: Assertion type definitions
image from book
 #ifdef USE_ASSERT  // Active assertions #define ASSERT(arg1)       assert((arg1)) #define ASSERT_PTR(arg1)   assert(NULL!=(arg1)) #define ASSERT_PTR4(arg1)  assert((NULL!=(arg1)) \                            && (0==(((long)(arg1))&3))) #define ASSERT_PTR8(arg1)  assert((NULL!=(arg1)) \                            && (0==(((long)(arg1))&7))) #define ASSERT_PTR16(arg1) assert((NULL!=(arg1)) \                            && (0==(((long)(arg1))&15))) #define ASSERT_FNEG(arg1)  assert(0.0f<=(arg1)); #define ASSERT_FZERO(arg1) assert(0.0f!=(arg1)); #define ASSERT_NEG(arg1)   assert(0<=(arg1)); #define ASSERT_ZERO(arg1)  assert(0!=(arg1));     #else         // Assertions stubbed to nothing               // (empty macro) #define ASSERT(arg1) #define ASSERT_PTR(arg1) #define ASSERT_PTR4(arg1) #define ASSERT_PTR8(arg1) #define ASSERT_PTR16(arg1) #define ASSERT_FNEG(arg1) #define ASSERT_FZERO(arg1) #define ASSERT_NEG(arg1) #define ASSERT_ZERO(arg1) #endif 
image from book
 

You would merely insert your custom assertion.

 uint nActor = 5; ASSERT(nActor < MAX_ACTOR); 

The pointer assertion ASSERT_PTR4() does two things: The first is to guarantee that the pointer is not assigned to NULL. The second is that memory pointers must or should be (depending on the processor and exception settings) referencing memory that is at least 4-byte aligned so that pointer alignment is checked for. Even if only referencing strings, it should be recognized that if they are at least 4-byte aligned, algorithms can be made more efficient when dealing with those string components in parallel.

 void *pApe; ASSERT_PTR4(pApe); 

This is a good way to ensure that integers and single-precision floating-point values are properly aligned.

The pointer assertion ASSERT_PTR16() is virtually the same, except that it guarantees that the pointer is not NULL and is referencing data on a 16-byte alignment, which is necessary for vector math processing. This is what this part of the book is all about!

 ASSERT_PTR16(pApe); ;       Assert if 32-bit pointer is n=NULL or not 16-byte aligned. ; ASSERT_PTR16    MACRO    arg1 ifdef USE_ASSERT         test    arg1,0FFFFFFFFh      ; Not NULL? then...         jnz     $+3                  ; ...Hop over interrupt             int     3                    ; Break (OOPS)             test    arg1,00000000Fh      ; Is zero aligned...         jz      $+3                  ; ...Hop over interrupt             int     3                    ; Break (OOPS) endif endm 

Different processors have different behaviors, which could become a camouflage problem. Asserting on a non-aligned 16-byte data reference finds these problems fast. These last two deal with assertions of values of zero or negative single-precision floating-point numbers .

 float f; ASSERT_FNEG(f);      // Assert if a negative  ASSERT_FZERO(f);     // Assert if absolute zero 

One last assertion topic would be the utilization of strings with variable arguments within your assertions as an additional type of assertion. The trick here though is that assertion macros do not like variable arguments, so you would need something in the assertion name to represent the number of string arguments, such as the following:

 ASSERT1(a == b, "So BOOM because %u", nDucks); 


32.64-Bit 80X86 Assembly Language Architecture
32/64-Bit 80x86 Assembly Language Architecture
ISBN: 1598220020
EAN: 2147483647
Year: 2003
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net