Archived

This topic is now archived and is closed to further replies.

Lorek

Assembly Language Question

Recommended Posts

Ok, I went out and got a book finally. "Assembly Language Step-By-Step" Second Edition, Programing with Dos and Linux by Jeff Duntemann. And I''m almost all the way through the book but I keep getting stuck on bit wise operators. Both with the AND NOT XOR OR, and with the ROL ROR SHR SHL commands. I''ve been trying to puzzle it out for about 2 days now and its just not working. So, is there anyone out there that could try and explain that to me. Also on a second note I''ve been having a hard time figuring out exactly what a specific line of code does. (It is working code) From a program my brother made. add ax, ds:[bx+si] ; not exactly sure how that works I know it has to do with putting a value and not a address into ax but that''s about it. inc ax ; ax++ and [bx], dx ; [bx] == dx ?? jbe 001Dh ; jump if below or equal address 001Dh push dx ; put dx on stack inc dx ; dx++ mov ax, es ; *ax = *es add ax, 0010h ; *ax = *ax+16 push cs ; put cs on stack pop ds ; take cs off stack, ds = cs mov word ptr [0004], ax ; this is what completely threw me because I thought you cant have a immediate data value in the destination spot. So I though could it be a software interrupt? Thanks everyone for the help.

Share this post


Link to post
Share on other sites
Ouch. 16bit real-mode code, and writing directly to the IDT. Nice.

quote:

add ax, ds:[bx+si] ; not exactly sure how that works


It takes to content (a word) of the memory in the data segment pointed by bx+si, and adds that value to ax.

quote:

mov word ptr [0004], ax ;

this is what completely threw me because I thought you cant have a immediate data value in the destination spot. So I though could it be a software interrupt?


Well, yeah, it is actually writing to the IDT, the interrupt descriptor table. I don''t remember what Int01 was for (single step IRQ I think ?), but it''s definitely not something you want to do as a beginner. Not even to mention, that it will bluescreen any modern OS (or call an exception), if you''re not on Ring 0. And BTW, directly accessing the IDT is pretty useless, unless you want to write your own OS, device driver, or a virus... ahem.

OK, if you''re trying to learn ASM, then forget that code as fast as you can. Try to stick to 32bit ASM instead, preferably something not writing into reserved areas and vector tables...

quote:

And I''m almost all the way through the book but I keep getting stuck on bit wise operators. Both with the AND NOT XOR OR, and with the ROL ROR SHR SHL commands. I''ve been trying to puzzle it out for about 2 days now and its just not working. So, is there anyone out there that could try and explain that to me.


So, what''s your exact problem with those opcodes ?

Share this post


Link to post
Share on other sites
Oh i just couldn't rationalize how those command's worked. My only problem now is fully understanding Bit-Wise Operator's, my brother threw that code at me thinking I wouldn't figure out what it did.

[edited by - Lorek on March 18, 2003 12:37:35 AM]

Share this post


Link to post
Share on other sites
quote:

My only problem now is fully understanding Bit-Wise Operator''s


They just apply a binary bitwise operation to the operand. The C equivalents are as follows:

AND: &
OR: |
XOR: ^
NOT: ~

SHL/SAL: <<
SHR/SAR: >>

The ROL, ROR, RCL, and RCR opcodes do not have a C equivalent. THey work similar to the SHL, etc, with the difference that the shifted bits are not lost, but rotated back into the register at the opposite side. Either directly (ROL, ROR), or through the carry flag, the acts as an additional bit (RCL, RCR).

Share this post


Link to post
Share on other sites
AND:

10010101b
11011110b
---------
10010100b

If both bits are set in both operands, the resulting bit is 1. Otherwise, the resulting bit is zero. i.e. bit n must be set in both operand a and b for bit n in a resulting operand, c, to be set.


OR:

10010101b
11011110b
---------
11011111b

If bit n of either operand a or b is set, then the resulting operand''s nth bit will also be set.

XOR:

10010101b
11011110b
---------
01001011b

If bit n of either operand a or b is set (but NOT both), then the resulting operand''s nth bit will also be set.

NOT:

10010101b
---------
01101010b

Simply flips bits 0-n of an n-bit operand.


SHL:

(assume 4 bit shift)

10010101b
---------
01010000b

Shifts the bits themselves to the left. Zero bits are padded to the right.

SHR:

as above, but in the other direction.

ROL:

(again, 4 bit shift)

10010101b
---------
01011001b

As per SHL, but bits are moved from the left-most to the right-most side of the operand. i.e. bit 0 becomes bit n, bit n becomes bit n-1, bit n-1 becomes bit n-2 and so on.

ROR:

As above, but from the right-most to the left-most.

Hope this helps a bit. Assembly''s a bitch

Tom L

Share this post


Link to post
Share on other sites
In C++ I have no problem understanding those. But in Assembly its like . . . Ok, its clear in C++ what it does exactly.

if ( j == 1; && i == 1)
perform(); // if true do perform();

with assembly everytime I see those I get all confused with what several different places have said about them. They go into bit numbering. with 0 being at the least significant bit and binary numbers, and the true / false being put into the first operand.
Theres a ton of tables and I totally understand that tables I just dont understand the concept their trying to get at and how it will be used in the future. The only part about XOR that I got was that its the easiest way to reset a register to 0 by XOR ing itself and its more generalized. I dont know how I can put it to use though.

I totally agree with your assessment of the language. But If I want to start writing graphics code for games I have to go with the speed.

[edited by - Lorek on March 19, 2003 1:16:18 AM]

Share this post


Link to post
Share on other sites
quote:
if ( j == 1; && i == 1) ...


Now hang on a moment! && is NOT the same as &. Not at all.

&& is known as the "logical AND operator" and & is the "bitwise AND operator."

&& is not an operation, it will compile down to condition-setting tests (CMP, TEST) and conditional (JNE, JE, JGE, SETNE, etc.)

The AND, OR, XOR, and NOT instructions operate on numbers. They don''t imply decision-making logic.

Someone here posted tables of how they work. For each bit:


AND: 1 & 1 = 1
1 & 0 = 0
0 & 1 = 0


Why is it called AND? Because when the first bit AND the second bit are 1, the result is 1.


OR: 1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0


Why is it called OR? Because when one OR the other bit is 1, the result is 1. This is also known as INCLUSIVE OR.


XOR: 1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0


Why is it called eXclusive OR? Because only 1 bit may be 1 for the result to be 1. If more (2) or less (0) than 1 bit is set to 1, the result is 0.

NOT simply inverts all bits (0->1 and 1->0.) It is equivalent to XORing a given value with a value containing all 1s.


AND is useful for masking off portions of a value. ANDing something with 0x3FF will preserve the low 10 bits, for example. ANDing a 32-bit value with 0x800000001 will preserve the lowest and the highest bits only.

OR is useful for turning bits on, no matter what their current status is. x | 1 will set the lowest bit. x | 2 will set the next bit, etc.

XOR is useful for selectively flipping bits: x ^ 1 will flip the lowest bit. For a 32-bit value, x ^ 0x80000000 will flip the highest bit. Look at the table I posted. XORing a number itself will always yield 0. Why? Because for bits that are 1, 1 ^ 1 = 0, and for bits that are 0, 0^0=1. Cool, huh?

NOT flips ALL bits.

NOT is equivalent to the ~ operator in C. ! is the logical NOT operator (try to investigate the difference yourself by experimenting and outputting results with printf().)

Shifts and rotations do what they imply.

What may help you is to write code to print things out in binary, and then perform the various operations you''re having trouble with and output the results before and after each operation.

Once you "get" binary, it is crucial you have an understanding of hexadecimal and how it relates to binary. You must understand how numbers are represented in integers (bits, etc.) There are plenty of resources that explain the connection between binary and hex (and octal for that matter, but it''s not used often.)


----
Bart

Share this post


Link to post
Share on other sites
quote:

But If I want to start writing graphics code for games I have to go with the speed.


For PC work, this is very misguided. You are more likely to write SLOWER code if you''re not a experienced assembly coder. Why? Because of the superscalar nature of the processor together with having multiple pipelines and memory caches.
What does this mean?
Well, in this code:
mov eax,100
add eax,ebp
the second instruction will stall the processor, i.e. the pipeline, because it can''t use eax (i.e. add to it) until the previous instruction has been retired (i.e. completed). Modern processors don''t process one instruction at a time, they are processing lots of instructions simultaneously - each instruction is broken down into micro-ops (u-op) internal to the cpu and each u-op requires several processes. The Pentiums can do a lot of instruction re-ordering to eliminate potential stalls, but the rules that govern this are very complex.
Also, on the Pentiums, there are two integer pipelines (U and V) and it is possible to dispatch and complete two instructions simultaneously, but again, there are lots of complex rules as to when two instructions can be dispatched.
Then there''s the cache. You can improve the speed considerably by sorting out memory accesses (unfortunately there''s no prefetch instruction ) so that cache misses are minimised (cache misses are very expensive).
The compiler (especially Intel''s, if you can afford it) know how the processor works and can generate code that doesn''t have these problems (that''s one of the main concepts behind the IA-64).
Also, when writing optimised assembler code you are writing for a specific processor (386, 486, Pentium I, II, etc) as each processor has a different core. This means optimising for a 486 can run slower on other machines, etc.
You''re biggest bottleneck is likely to be sending data to the video card, whatever platform you''re writing for, and not the main CPU.
In all modern games, I doubt that there is any hand coded assembler anywhere (except, of course, PS2 vector unit code).

The general optimisation method is:
1 Write code
2 Analyse
3 Find bottlenecks / stalls / slow portions
4 Eliminate above by reordering processes / rearranging memory / using different algorithms
5 Go to 2
Again, if you can afford it, Intel''s VTune is an invaluable tool for program analysis, otherwise you''ll have to either write your own or use whatever profiling tools are available.

So to sum up: you''d be a lot better off learning how to analyse your code when it''s running and finding those bottlenecks. Learn how the cache works (sometimes not inlining functions can improve speed since it keeps the code small and therefore in the cache - the cache affects both code and data!). Learn lots of alternative algorithms and the pros and cons of each (i.e. qsort is fast compared to a bubble sort when the data is random but slow when the data is already nearly sorted). Learn how the language you''re using works (especially if you''re using c++ - temporary variable creation can kill performance and can be avoided).

Sure, learn assembler, it’s a useful skill. It’s useful sometimes to look at what the compiler has generated. But for making code go faster – it really is a final, desparate tool to pull out of the box.

Skizz

Share this post


Link to post
Share on other sites