I cannot understand how the computer implement a binary operation.
First of all, an instruction is stored in the memory unit.
Then, control unit in CPU takes this instruction to store it in instruction register(IR) in itself.
After that, CU decodes operation code in the instruction code by using instruction decoder (ID).
Assume that opcode = sum
If it is so, we need two operands. Some books tell that there is only one address of operand in the address part of the instruction code.
Where is second operand ?
Where is address of second operand ?
Mostly of the time, the second operand is already stored in a register of the CPU, the ALU (Arithmetic Logical Unit).
The instruction sequence may look like this:
lda $1234 ' loads value from memory address $1234 into ALU
add #20 ' adds value 20 to the value stored in ALU and stores the result in the ALU again
sda $5678 ' stores the resulting value from the ALU in the memory at address $5678
Modern CPUs have a lot of registers, so the instruction sequence for a modern CPU may look like this:
load ax, $1234 ' loads value from memory address $1234 into AX
load bx, [si+2] ' load the value from the address stored in register SI+2
add ax, bx ' adds the value in register BX to the value in register AX and stores the result in AX
store di+5, ax ' stores the value of register in memory at address stored in DI+5
Hopefully this clears it up for you.
Related
I know that the 8086 addressing mode is a left shift of the segment register by four bits then add an offset. But will the information of significant bit not be lost after shifting the 16-bit register?
For example, the segment register stores 0x1000, will it not become 0x0000 after shifting four bits to the left?
Address calculation happens with 20-bit math. Or more on later CPUs. Not the same width as the inputs. The CPU doesn't shift inside the segment reg!
In CPUs that don't support other modes (like protected mode where the base comes from the GDT), I assume the selected segment register is just hard-wired to bits [20:4] of an adder in the AGU, so the "shift" is just built-in to the wiring. Not like when you run a shift instruction and the result goes back into the destination.
(Actual 8086 didn't have an AGU separate from its ALU, so presumably that's what happens when using that block of logic to do address math instead of 16-bit normal integer math. It also supports a mul instruction which produces a 32-bit product, that has to get split across DX:AX when it's done, but internally the ALU has to shift and add numbers across 16-bit boundaries for multiply to work.)
I am studying computer architecture from the Intel Manual. The thing that I understand is that the instructions that we give are logical addresses which consist of a segment selector and an offset.
It is basically CS register<<4 + offset. The Segment Selector maps to the GDT or LDT as given in the TI bit of the segment selector. GDT consists of Segment Descriptors which have BASE, LIMIT and RPL and the output is base address. This base address + offset provides the logical address.
What are the rules that decide which segment register (SS, DS, etc.) applies to different memory operations? e.g. what determines which segment is used for mov eax, [edi]?
Code fetch always uses CS.
Data addressing modes default to DS (or SS when EBP or ESP are the base register) in "normal" addressing modes. (e.g. mov eax, [edi] is equivalent to [ds:edi], mov eax, [ebp+edi*4] is equivalent to mov eax, [ss: ebp + edi*4]).
(Some disassemblers make the segment explicit even when it's the default, so you see a lot of DS: cluttering up the disassembly output. (You can use a segment override prefix to select which segment will apply to the memory operand in an instruction.) In NASM syntax, explicitly using a [ds:edi] addressing mode will result in a redundant ds prefix in the machine code.)
Some instructions with implicit memory operands have different defaults:
Some string instructions use ES:EDI implicitly. e.g. The movs instruction reads from [DS:ESI] and writes to [ES:EDI], making it easy to copy between segments without segment override prefixes.
Memory operands using esp or ebp as the base register default to SS, and so do the implicit accesses for stack instructions like push/pop/call/ret.
FS and GS are never the default, so they can be used for special purposes (like thread-local storage) in a flat memory model system like modern 32 and 64-bit OSes.
wikipedia explains the same thing here.
This is also documented officially in Intel's ISA manuals. e.g. in Volume 2 (the instruction-set ref), Table 2-1. 16-Bit Addressing Forms with the ModR/M Byte has a footnote saying:
The default segment register is SS for the effective addresses containing a BP index, DS for other effective addresses.
(note that SP isn't a valid base address for 16-bit addressing modes.
Also note that when they say "index", that means when BP is used at all, even for [bp + si] or [bp+di]. In 32 and 64-bit addressing modes, there is a clearer distinction between base and index, and [symbol + ebp*4] still implies DS as the segment because EBP is used as an index, not the base.)
There's no equivalent footnote for 32 or 64-bit addressing modes, so the details must be in another volume of the manual.
See also the x86 tag wiki for more links.
Consider the Code Segment Address to be FE00 and Instruction Pointer to be ABBE. Shifting the Code Segment by 4 bits and Adding the Instruction pointer leads to an additional carry. How do we represent the generated address?
Consider the Code Segment Address to be FE00 and Instruction Pointer to be ABBE. How do we represent the generated address?
Either you represent the address as
0FE00h:0ABBEh, its segmented form using two 16-bit numbers separated by a colon and always (segment - colon - offset)
00108BBEh, its linear form using one 32-bit number
No matter your choice it will always require 2 word sized registers on 8086.
"The instruction pointer is an offset in the 64KB memory segment that starts at the linear address obtained from multiplying the value in the CS code segment register by 16 (same as shifting left 4 times)."
Calculating the linear address could be done inefficiently (but easy to understand) like this:
mov ax, 0FE00h ; The code segment
mov dx, 16
mul dx ; "shifting the code segment by 4 bits"
add ax, 0ABBEh ; "adding the instruction pointer"
adc dx, 0 ; Taking care of the additional carry
This linear address 00108BBEh uses 2 registers AX and DX. The AX register will hold the least significant part 8BBEh and the DX register will hold the most significant part 0010h. If you need to refer to the whole pair of registers you do it like DX:AX. So highWord - colon - lowWord.
Unlike with seg:off notation, this is just one 32-bit number split over 2 registers, no overlapping place values. The low bit of the high half has place value 2^16 when we're talking about a flat 32-bit (or 20-bit) number, not a seg:off address.
I am reading about the architecture of intel's 8086 and can't figure out the following things about segmentation: I know that segment registers point to segments respectively and contain the base address of a 64kb long segment. But who calculates and in which point sets the physical address in the segment registers? Also, because one physical address can be accessed by multiple segment:offset pairs and segments can overlap, how you can be sure that you won't overwrite something? Where I can read more about this?
Generally speaking the Assembler will only use offset addresses to access a logical address. For example looking at this code:
start lea si,[hello] ; Load effective address of string
mov word [ds:si+10],0 ; Zero-terminate string after 10th letter
jmp $ ; Loop endlessly
; Fill rest of the segment with 0s
times 65536-($-$$) db 0x00
hello db "I'm just outside of the current segment. Hello!",0
The assembler will try to calculate the offset of 'hello' from the origin of the program. Since no origin is defined 0x0 will be assumed. However the offset of 'hello' would be 0x10000 in this case, which does not fit 16-bits. Therefor the Assembler will truncate the address to 0x0000. It will not change any of the Segment registers. However it will likely issue a warning, for example test.asm:1: warning: word data exceeds bounds. What actually happens when you run this program is that the jmp $ line is overwritten with zeroes, because the address of hello wrapped around and the CPU will start executing nothing but Zeroes, which was not what you intended to do.
That is of course only if the code-segment and data-segment are the same. Now who guarantees that is the case? Nobody really. Especially since I still don't know what platform you are coding for. It is entirely your resposibility to set up the segment registers with correct values. The easiest way to do so is:
push cs ; Push address of code segment to stack
pop ds ; Pop address back into data segment
push cs ; Same for extra data segment
pop es ;
This way you can be certain your you are accessing the offset in the correct-data segment.
Now regarding 'How do you make sure the code segment doesnt overlap the data segment', why shouldn't it? When your program with data is smaller than 64KB it is actually the easiest way to access data if your code and data segment are identical.
And how can you be sure that you don't overwrite anything important? Assembler can't help you with that, you have to check yourself if the segment:offset address you are writing to already contains data.
Im reading the book "Write great code: understanding the machine" by Randall Hyde, is a great and clear text but here im completely stuck with his explanation of, for example, the mov instruction.
He dissects the steps for the mov(srcReg,destMem) instruction as follows:
1. Fetch the instruction's opcode from memory.
2. Update the EIP register with the address of the byte following the opcode.
3. Decode the instruction's opcode to see what instruction it specifies.
4. Fetch the displacement associated with the memory operand from the memory location immediately
following the opcode.
5. Update EIP to point at the first byte beyond the operand that follows the opcode.
6. If the mov instruction uses a complex addressing mode (for example, the indexed addressing mode),compute the effective address of the destination memory location.
7. Fetch the data from srcReg.
8. Store the fetched value into the destination memory location.
Im lost in steps 4-6. My exact questions are:
Step 4: Why do I need this displacement, how Im gonna use it later and why?
Step5: I understand that in step 2, the EIP must "point" to the next byte where the next instruction to be executed is stored. But I dont understand why does EIP needs to be one byte beyond the operand address. I belived that EIP was concerned only with instructions/opcodes, not data.
Step6: What is exactly and effective address? Are there other types of address?
Step 4:
Some opcodes reference memory that's relative to the opcode's location. For example, a function might have a constant or static piece of data. If it does, the code may opt to place that right before the function starts (or right after it ends) and refer to it by saying "get the memory from 46 bytes earlier". That's the displacement -- it's an offset from the contents of a register (in this case, EIP), used for referencing data relative to the register's contents.
Step 5
The operands for opcodes are normally stored right after the opcode. So you might have some memory arranged like so: a b c. a is and opcode, b is the operand for a and c is the next opcode.
If you only move EIP to the end of a (so it references b), then in the next instruction cycle, the computer will assume that b is the next opcode to execute. b isn't supposed to be an opcode though; it's an operand. The computer can't tell the difference between an opcode and an operand though. It just assumes whatever EIP points to is an instruction and executes it. That's why EIP needs to be moved past the operand too.
Step 6
An "effective" address is just an absolute one (relative to the start of memory) while the "complex" address the book refers to is relative to something else (often the contents of a register).
Step 4 showed that an opcode might not refer to an absolute memory address. It could easily refer to a relative one. In fact, programs very frequently refer to addresses that are relative to some register. For example, if you wrote some_struct.data in C and compiled it for an x86 processor, it would load the address of some_struct into a register (say, EAX), then hard-code data's offset from the base of some_struct into the operand. So if there are 5 bytes of data between the start of the struct and the start of the data element, then the instruction might look like load [EAX + 5] -> EBX which means "take what's in EAX, add 5, fetch the data from that address and put it in EBX".
The thing is, the memory doesn't really understand relative addresses like this. It only understands absolute ones. So in order to access a relative address, the processor has to first add that 5 to whatever's in EAX to compute an absolute address. Then it can send that address to the memory controller and have it understood.
There are two basic types of relative addresses I've worked with (there are more I haven't).
Register relative: The processor takes the contents of a register and uses that as the address in memory. Depending on the opcode and processor support, it may also add an operand to the register as well. Step 4 was dealing with this kind of addressing, with EIP as the register the address was relative to.
Memory relative: Sometimes referred to as "indirect". The processor starts out with a register relative address, then automatically fetches the data at that address and treats it as the real address.
Wikipedia describes lots of other addressing modes on their addressing modes page.
Memory relative took me a while to understand. Say you did a memory relative load where the register contains 10 and the offset is 5. The processor will add them together (10 + 5 = 15). Then, it'll go to that address (15 in this case) and grab whatever's there. If address 15 happens to contain the value 60, then 60 will be treated as the actual address and the processor will load the contents of address 60. If you're familiar with a language with pointers (e.g. C), memory relative is like a pointer-to-a-pointer.