I like how casually descriptive you are about everything in your videos. It’s really nice that you’re explaining exactly what things do without using formal overly complicated terms that would only make sense to someone who already knows how to use asm. It’s not often that people make programming videos that are both correct and easy to understand.
Wow Chris, thank you for keeping up these ASM tutorials. I'm working thru your old ones too (at MMX) and I appreciate you also keeping them up, but these new ones are great. Python is fantastic as well, but the real gold of this channel is the ASM because it is EXTREMELY DIFFICULT to find good, modern video training on this. This is why I'm a patron of yours on Patreon.
Just found this tutorial series yesterday. I am a PHP developer so this is a big jump in difficulty but you're making it easier than anybody else could have! Thanks for making these videos!
mov eax, eax doesn't work as a multi-byte nop because it zeroes the top 32 bits of rax! By nop you usually mean that the instruction has no side effects, and zeroing the top half of the register is a side effect.
I remember LEA as being mostly useful for arrays and quick math: LEA EBX, [Array + ECX*4] would calculate the address of your array location, where the array is of 32-bit ints and ECX has the index.
If you wonder: why XOR RAX, RAX instead of MOV RAX, 0? It's because the former is a much shorter instruction, since the latter has to include the whole 8-byte immediate value. Shorter code fits in instruction caches better.
My original AES series is finished. I never did 192, 256 or decryption. I find encryption depressing because it reminds me that we humans don't and can't trust each other. I would like to do some vids on the AES instruction set, if I find time, we will look at 192 and 256 in those vids. Cheers mate, thanks for watching!
Computer hardware can multiply the values in two registers and give a double register product, using either signed or unsigned multiply instructions. I have looked for this capability in many high level languages but found none. Hence, large integer arithmetic must have some assembler code to achieve this efficiency. IBM 370, DEC PDP-11, and x86 processors all can do this ... because I did. I will compute large integer numbers on my 64 bit Windows desktop machine using C++ calling assembler code, and your videos.
So the LEA instruction is quite simply just loading a REFERENCE of a value to the desired location and then to derefrence you could do something like MOV DI, byte ptr [SI] assuming that SI has a valid address.
What is the difference between: mov rax, [foo] and lea rax, foo ? Also, isn't a register of a given name always at the same address? Because there is only one "rax". So: mov byte ptr[ rax ], 7 is just a type safe way of saying: move rax, 7 ?
MOV moves a value, LEA loads an address. So after the "MOV", RAX would have the same value as whatever is at the address foo. So if "fo" was a variable with the value 10, then RAX would contain 10. After "LEA", RAX would contain the pointer foo itself, a memory address, like 0x0010DFA010, or whatever. It would contain the the address in memory of the foo variable; RAX would be a pointer pointing to foo. Registers don't have addresses. You're right, there's only one RAX exposed in ASM. On a low level, the CPU renames registers, and switches instructions around, to execute as much as it can at once. So the internal register the CPU uses when we say "RAX" is up to the CPU. It will only switch instructions and rename registers in such a way that it computes the correct answer. But it might be good to know that there really is no RAX; that's just a label we use in ASM. The actual register set is much larger; and it's in control of the CPU. It's not important if you're just starting ASM, but it's good to keep in the back of your mind. "MOV byte ptr [rax], 7" will move 7 into whatever RAX points to. So we if say LEA rax and foo, like we did before; then RAX points to foo. If we then say "MOV byte ptr [rax], 7", it will move 7 into foo. "MOV rax, 7", will move 7 into RAX. There's no type safety in ASM, other than operands being the correct size, there's no type safety. You can move a float into an DWORD, or add floating point doubles using the insteger ADD instruction, etc. The CPU doesn't care at all. Sounds silly, but that's half the fun of ASM :) Hope that was helpful. Cheers for watching :)
I'm still confused that we can load immediate value into register using MOV, address is nothing more than a number, so THEORETICALLY we can load address of known place just as MOV with immediate value. I would argue MOV RAX, MyBte would do exactly that, while MOV RAX, [MyBte] would load value pointed by MyBte, but they did it the same and made it almost impossible to use MOV with immediate value for this purpose.
@@xrafter No, I would like "MOV RAX, MyBte" to load address of MyBte into RAX, while "MOV al, [MyBte]" would load value from that address. It is perfectly possible in machine codes because address becomes known to translator or maybe linker. And putting some immediate value into register is perfectly OK with MOV. But somehow both these lines would mean the same...
Assembler called "nasm" does that. When you do "mov rax, myByte" it will put adress of myByte into rax. When you use "mov rax, [myByte]" it will put value at adress myByte into rax. But for some reason the assembler "masm" he uses sees it as same. That is why I use nasm.
It used to be recommended for optimization that we align tight loops to 16 bytes. I see no gain on my hardware, but it might be worth testing. It's also theoretically possible to control which execution units execute our instructions by cleverly inserting NOP. Whilst it's theoretically possible, I have not been able to manage it. Great question
Hello, i just wanted to ask, is there a way to move parts of 64 bit register into 2 32 bit registers ? Or work with selected bytes of that 64 bit register ?
Yeah mate! If you have 64 bits in RDX, then you can move the low 32 into eax with "MOV EAX, EDX". Or you could move the upper 32 into EBX with SHRD or SHLD instructions. Can't remember the specifics of those instructions, but I'm pretty sure they'd do the trick! Otherwise you could just go "MOV RBX, RDX; SHR RBX, 32", move the data in 64 bits, then move it. You can also work with the smaller registers for RDX directly, without moving the data, but you have to be careful because a lot of instructions tht operate on 32 bit versions of the registers, i.e. EDX, will clear the top 32 bits of RDX! Well, hope this helps mate. I did a vid on registers somewhere. That might help too? Cheers for watching. Feel free to ask if you have any questions :)
I think MOV is generally faster, tho it's probably hardware specific, and certainly worth testing if it's crucial. Agner Fog's instruction manual lists MOV as 0.5, and LEA as 1 for Skylake, so that's a pretty good hint (reciprocal throughput). Cheers for watching mate :)
MOV is generally faster. Instruction speeds are hardware specific. On almost all hardware, "MOV reg, reg" is usually one of the fastest instructions of all. For memory reads, complex addressing, etc. the speeds of these instructions are all over the place; some MOV's are faster, some LEA's are faster. Anywho, cheers for watching :)
00:46 MOV 04:15 LEA This table of content was created using "Smart Bookmarks for TH-cam" chrome extension. You can import and edit them using this extension. You can install it from the official Chrome Store Page (shortened link): smb.page.link/store
It depends on which assembler you are using. I am using MASM, which uses the Intel syntax, "mov dest, src". But some assemblers (like GAS in linux programming), use the AT&T syntax, which is "mov src, dest".
It’s also different between the x86 family and Motorola 680x0 family. A Motorola 680x0 instruction such as MOVE.L (A6), D0 will move the long word (32-bits) at the address pointed to by address register 6 to data register 0. I kind of lament the abandonment of the MC680x0 family. Such a more elegant design than the kludgy x86-64 line of products.
I like how casually descriptive you are about everything in your videos. It’s really nice that you’re explaining exactly what things do without using formal overly complicated terms that would only make sense to someone who already knows how to use asm.
It’s not often that people make programming videos that are both correct and easy to understand.
Wow Chris, thank you for keeping up these ASM tutorials. I'm working thru your old ones too (at MMX) and I appreciate you also keeping them up, but these new ones are great. Python is fantastic as well, but the real gold of this channel is the ASM because it is EXTREMELY DIFFICULT to find good, modern video training on this. This is why I'm a patron of yours on Patreon.
Cheers mate!
Brilliant mate! The old vids will defo stay up. Thank you for your support, you're a legend!
Just found this tutorial series yesterday. I am a PHP developer so this is a big jump in difficulty but you're making it easier than anybody else could have! Thanks for making these videos!
mov eax, eax doesn't work as a multi-byte nop because it zeroes the top 32 bits of rax! By nop you usually mean that the instruction has no side effects, and zeroing the top half of the register is a side effect.
So true!! Did I say that? Whoops :) I wish YT still had annotations...
Thank you! Your assembly tutorial is so clear. Now Assembly is not as dark magic as it was for me 😅
Thanks man, you have a good way of explaining
I remember LEA as being mostly useful for arrays and quick math: LEA EBX, [Array + ECX*4] would calculate the address of your array location, where the array is of 32-bit ints and ECX has the index.
Thank you for your vids! Easy to follow, clear, precise. This is really good assembly stuff. I look forward to seeing more on this series soon!
Yeah, imma university student, that just want to know a lil' bit more
And I want to say thank you! Your videos are kinda legendary!
These videos are simply outstanding
If you wonder: why XOR RAX, RAX instead of MOV RAX, 0? It's because the former is a much shorter instruction, since the latter has to include the whole 8-byte immediate value. Shorter code fits in instruction caches better.
Thanks
Man! you did wonders here ...
Thanks for your videos! They're helping me a lot!
Thank you Creel.
Are you going to continue the AES series? I'd like to know how AddRoundKey works for 192- and 256-bit keys
My original AES series is finished. I never did 192, 256 or decryption. I find encryption depressing because it reminds me that we humans don't and can't trust each other. I would like to do some vids on the AES instruction set, if I find time, we will look at 192 and 256 in those vids. Cheers mate, thanks for watching!
Computer hardware can multiply the values in two registers and give a double register product, using either signed or unsigned multiply instructions. I have looked for this capability in many high level languages but found none. Hence, large integer arithmetic must have some assembler code to achieve this efficiency. IBM 370, DEC PDP-11, and x86 processors all can do this ... because I did. I will compute large integer numbers on my 64 bit Windows desktop machine using C++ calling assembler code, and your videos.
now i know what lea does, thanks
So the LEA instruction is quite simply just loading a REFERENCE of a value to the desired location and then to derefrence you could do something like MOV DI, byte ptr [SI] assuming that SI has a valid address.
wait nevermind the byte ptr [REGISTER] is the derefrence my bad.
Wow this was awesome
What is the difference between: mov rax, [foo] and lea rax, foo ?
Also, isn't a register of a given name always at the same address?
Because there is only one "rax".
So: mov byte ptr[ rax ], 7 is just a type safe way of saying: move rax, 7 ?
MOV moves a value, LEA loads an address. So after the "MOV", RAX would have the same value as whatever is at the address foo. So if "fo" was a variable with the value 10, then RAX would contain 10. After "LEA", RAX would contain the pointer foo itself, a memory address, like 0x0010DFA010, or whatever. It would contain the the address in memory of the foo variable; RAX would be a pointer pointing to foo.
Registers don't have addresses. You're right, there's only one RAX exposed in ASM. On a low level, the CPU renames registers, and switches instructions around, to execute as much as it can at once. So the internal register the CPU uses when we say "RAX" is up to the CPU. It will only switch instructions and rename registers in such a way that it computes the correct answer. But it might be good to know that there really is no RAX; that's just a label we use in ASM. The actual register set is much larger; and it's in control of the CPU. It's not important if you're just starting ASM, but it's good to keep in the back of your mind.
"MOV byte ptr [rax], 7" will move 7 into whatever RAX points to. So we if say LEA rax and foo, like we did before; then RAX points to foo. If we then say "MOV byte ptr [rax], 7", it will move 7 into foo. "MOV rax, 7", will move 7 into RAX. There's no type safety in ASM, other than operands being the correct size, there's no type safety. You can move a float into an DWORD, or add floating point doubles using the insteger ADD instruction, etc. The CPU doesn't care at all. Sounds silly, but that's half the fun of ASM :)
Hope that was helpful. Cheers for watching :)
Love it, really clean.
Good stuff :)
No worries mate! Cheers for watching :)
I have never before heard move called mauve.
Thank you very much!!! LIFESAVER
I'm still confused that we can load immediate value into register using MOV, address is nothing more than a number, so THEORETICALLY we can load address of known place just as MOV with immediate value. I would argue MOV RAX, MyBte would do exactly that, while MOV RAX, [MyBte] would load value pointed by MyBte, but they did it the same and made it almost impossible to use MOV with immediate value for this purpose.
So you want to get a value from address 56?
Segmention fault (core dumped)
@@xrafter No, I would like "MOV RAX, MyBte" to load address of MyBte into RAX, while "MOV al, [MyBte]" would load value from that address. It is perfectly possible in machine codes because address becomes known to translator or maybe linker. And putting some immediate value into register is perfectly OK with MOV. But somehow both these lines would mean the same...
@@allmycircuits8850
Will yes it will work but you need to give it a size for the assembler
Assembler called "nasm" does that. When you do "mov rax, myByte" it will put adress of myByte into rax. When you use "mov rax, [myByte]" it will put value at adress myByte into rax. But for some reason the assembler "masm" he uses sees it as same. That is why I use nasm.
excellent
5:20 can't you use offset ?
mov al, OFFSET qwaral
3:07 shouldn't "mov al, bittyye" be "mov al, [bittyye]' ? ... bittyye should have brackets?
I think it should, maybe visual studio is casting it?
Is there any reason for padding code except for patching purpoes?
It used to be recommended for optimization that we align tight loops to 16 bytes. I see no gain on my hardware, but it might be worth testing. It's also theoretically possible to control which execution units execute our instructions by cleverly inserting NOP. Whilst it's theoretically possible, I have not been able to manage it. Great question
That sounds interesting. I definitely have to do some testing, when I have some time :)
For performance reasons, data often needs to be word-aligned. This might not be as much of an issue with systems today having 64-bit data buses.
Hello, i just wanted to ask, is there a way to move parts of 64 bit register into 2 32 bit registers ? Or work with selected bytes of that 64 bit register ?
Yeah mate! If you have 64 bits in RDX, then you can move the low 32 into eax with "MOV EAX, EDX". Or you could move the upper 32 into EBX with SHRD or SHLD instructions. Can't remember the specifics of those instructions, but I'm pretty sure they'd do the trick! Otherwise you could just go "MOV RBX, RDX; SHR RBX, 32", move the data in 64 bits, then move it. You can also work with the smaller registers for RDX directly, without moving the data, but you have to be careful because a lot of instructions tht operate on 32 bit versions of the registers, i.e. EDX, will clear the top 32 bits of RDX!
Well, hope this helps mate. I did a vid on registers somewhere. That might help too? Cheers for watching. Feel free to ask if you have any questions :)
@@WhatsACreel Thanks mate :) u are a grade saver :)
@@honzabart12 Welcome, good luck :)
good vid :)
thankies
(1)mov eax,dword ptr [p] (2) lea eax,[p]. which one is faster?? why? Thank you
I think MOV is generally faster, tho it's probably hardware specific, and certainly worth testing if it's crucial. Agner Fog's instruction manual lists MOV as 0.5, and LEA as 1 for Skylake, so that's a pretty good hint (reciprocal throughput). Cheers for watching mate :)
what is a creel?
It's creel
Google?
Thanks
mybyte: db 75
lea rax, mybyte ; DONT WORK
lea.asm:11: error: invalid combination of opcode and operands
you got extra ":" in the .data where mybyte is
are you from Australia?
5:42 qwral is better
I love this man.
No homo tho.
tysm
Which of them is faster?
MOV is generally faster. Instruction speeds are hardware specific. On almost all hardware, "MOV reg, reg" is usually one of the fastest instructions of all. For memory reads, complex addressing, etc. the speeds of these instructions are all over the place; some MOV's are faster, some LEA's are faster. Anywho, cheers for watching :)
Playlist: th-cam.com/play/PLKK11Ligqitg9MOX3-0tFT1Rmh3uJp7kA.html
00:46 MOV
04:15 LEA
This table of content was created using "Smart Bookmarks for TH-cam" chrome extension. You can import and edit them using this extension. You can install it from the official Chrome Store Page (shortened link): smb.page.link/store
Why they choosen so wordy syntax "byte ptr [smthg]" ? C-language syntax would be more convenient (byte*)rax, or byte*[rax].
I learned in my class that MOV is written as source destination, and not destination source...
It depends on which assembler you are using. I am using MASM, which uses the Intel syntax, "mov dest, src". But some assemblers (like GAS in linux programming), use the AT&T syntax, which is "mov src, dest".
Understood, thank you :)
It’s also different between the x86 family and Motorola 680x0 family. A Motorola 680x0 instruction such as MOVE.L (A6), D0 will move the long word (32-bits) at the address pointed to by address register 6 to data register 0. I kind of lament the abandonment of the MC680x0 family. Such a more elegant design than the kludgy x86-64 line of products.
NOP DWORD ptr [AX + AX*1 + 00000000H] :)
Oh great! Is this a multi-byte NOP? Which Assembler?
AX ist not a valid address register.
wtf brain burning
Sorry, maybe Scully can help you, Fox