xor eax,eax mov al,byte[Buff+ecx]
mov edx,esi and edx,0000000Fh call DumpChar
Clear EAX to 0
Get a byte from the buffer into AL Copy total counter into EDX Mask out lowest 4 bits of char counter Call the char poke procedure
Bump the buffer pointer to the next character and see if buffer's done: inc esi ; Increment total chars processed counter inc ecx cmp ecx,ebp jb .modTest call LoadBuff cmp ebp,0 jbe Done
; Increment buffer pointer ; Compare with # of chars in buffer ; If we've processed all chars in buffer...
...go fill the buffer again
If ebp=0, sys_read reached EOF on stdin
If we got EOF, we're done
; See if we're at the end of a block of 16 and need to display a line:
Note that the label .modTest has a period in front of it. This period marks it as a local label. Local labels are local to the first nonlocal label (that is, the first label not prefixed by a period; we call these global) that precedes them in the code. In this particular case, the global label to which .modTest belongs is Scan. The block shown above is the portion of the main body of the program that scans the input file buffer, formats the input data into lines of 16 bytes, and displays those lines to the console.
In what way does a global label ''own'' a local label? It's a question of visibility within the source code: a local label cannot be referenced higher in the source code file than the global label that owns it, which, again, is the first global label above it in the file.
In this case, the local label .modTest cannot be referenced above the global label Scan. This means that there could conceivably be a second local label .modTest in the program, on the ''other side'' of Scan. As long as a global label exists between two local labels with the same name, NASM has no trouble distinguishing them.
Local labels may also exist within procedures. In another example from hexdump2.asm, there is a local label .poke in the clearLine procedure. It belongs to the clearLine label, and thus cannot be referenced from any other procedure elsewhere in the program or library. (Don't forget that procedure names are global labels.) This isolation within a single procedure isn't immediately obvious, but it's true, and stems from the fact that ''below'' a procedure in a program or library there is always either another procedure or the _start label at the beginning of the main program. It's obvious once you see it drawn out, as I've done in Figure 10-2.
Some notes on local labels:
■ Local labels within procedures are at least local to the procedures in which they are defined. (This is the point of Figure 10-2.) You may, of course, have global labels within procedures, which limits the visibility of local labels even further.
test esi,0000000Fh jnz Scan call PrintLine call ClearLine jmp Scan
Test 4 lowest bits in counter for 0 If counter is *not* modulo 16, loop back ...otherwise print the line Clear hex dump line to 0's Continue scanning the buffer
Local labels in a "zone" between two global labels belong to the label above them.
Figure 10-2: Local labels and the globals that own them
■ It's perfectly legal and often helpful to define global labels that are never referenced, simply to provide ownership of local labels. If you're writing a utility program that executes in straightforward fashion without a lot of jumping or long-distance looping back, you may go a long way without needing to insert a global label. I like to use global labels to set off major functional parts of a program, whether those labels are ever referenced or not. This enables me to use local labels freely within those major functional modules.
Local labels, unfortunately, are not accessible as breakpoints from the command-line interface of the Gdb debugger. I'm not entirely sure why this is so, but Gdb refuses to set a breakpoint on a local label from the command line. Of course, you can set a breakpoint on any line containing a machine instruction from the source code window of Insight, irrespective of labels. In general, use the command-line interface of Gdb only when you have to; it has peculiar limitations.
■ If you're writing dense code with a lot of intermixed global and local labels, be careful that you don't try to jmp to a local label on the other side of a global label. This is one reason not to have 15 local labels called .scan or .loopback within one part of a program—you can easily get them confused, and in trying to jump to one five instructions up, you may unknowingly be jumping to one seven instructions down. NASM won't warn you if there is a local label with the same name on your side of a global label and you try to jump to a local label on the other side of the global label. Bugs like this can be insanely difficult to find sometimes. Like any tool, local labels have to be used carefully to be of greatest benefit.
■ Here's a rule of thumb that I use: local labels and all jumps to them should occur within a single screen of code. In other words, you should be able to see both a local label and everything that refers to it without scrolling your program editor. This is just a guide to help you keep track of the sense in your programs, but I've found it very useful in my own work.
■ I also use a code style convention that makes the first character after the dot in a local label a lowercase character, and the first character of all global labels an uppercase character. Nothing in NASM requires this, but I find it helpful to distinguish global labels from local labels at a glance. Thus, .poke is easily identifiable as local (periods are tiny!) and Scan is easily identified as global.
Was this article helpful?