As indicated in Fig. 3, silicon has 14 orbiting electrons, germanium
has 32 electrons, gallium has 31 electrons, and arsenic has 33 orbiting
electrons.
For germanium and silicon there are four electrons in the outermost
shell, which are referred to as valence electrons.
Valence electrons are electrons in the outer shells that are not
filled. Because valence electrons have higher energy than electrons in
inner orbits, they are involved in the majority of chemical processes.
They assist us in determining the chemical properties of an element,
such as its valency or how it forms bonds with other elements.
Gallium has three valence electrons and arsenic has five valence
electrons. Atoms that have four valence electrons are called
tetravalent, those with three are called trivalent,
and those with five are called pentavalent.
The term valence is used to indicate that the potential
(ionization potential) required to remove any one of these electrons
from the atomic structure is significantly lower than that required for
any other electron in the structure.
Ionization energy, also called ionization potential, is the energy
necessary to remove an electron from the neutral atom. X + energy → X+ +
e− where X is any atom or molecule capable of being ionized, X + is that
atom or molecule with an electron removed (positive ion), and e − is the
removed electron.
In a pure silicon or germanium crystal the four valence electrons of
one atom form a bonding arrangement with four adjoining atoms, as shown
in Fig. 4.
This bonding of atoms, strengthened by the sharing of
electrons, is called covalent bonding.
Because GaAs is a compound semiconductor, there is sharing between
the two different atoms, as shown in Fig. 5. Each atom, gallium or
arsenic, is surrounded by atoms of the complementary type. There is
still a sharing of electrons similar in structure to that of Ge and Si,
but now five electrons are provided by the As atom and three by the Ga
atom.
Although the covalent bond will result in a stronger bond between the
valence electrons and their parent atom, it is still possible for the
valence electrons to absorb sufficient kinetic energy from external
natural causes to break the covalent bond and assume the “free” state.
The term free is applied to any electron that has separated
from the fixed lattice structure and is very sensitive to any applied
electric fields such as established by voltage sources or any difference
in potential. The external causes include effects such as light
energy in the form of photons and thermal energy (heat) from the
surrounding medium. At room temperature there are approximately
free carriers in
of intrinsic
silicon material, that is, 15,000,000,000 (15 billion) electrons in a
space smaller than a small sugar cube—an enormous number.
The central processing unit(CPU) always starts in real mode, and then
the main loader usually executes the code to explicitly switch it to
protected mode and then to the long mode.
These are the registers usable in real mode:
ip, flags;
ax, bx, cx, dx, sp, bp, si, di;
Segment registers: cs, ds, ss, es, (later also gs and fs).
As it was not straightforward to address more than 64 Kilobytes of
memory, engineers came up with a solution to use special
segement registers in the following way:
Each physical address consists of 20 bits(so, 5 hexadecimal
digits).
Each logical address consists of two components. One is taken
from a segment register and encodes the segment start. The other is an
offset inside this segment. The hardware calculates the physical address
from these components the following way:
physical address = segment base * 16 + offset
You can often see addresses written in form of segment:offset, for
example:
1
4a40:0002, ds:0001, 7bd3:ah
Note that strictly speaking, the segment register do not hold
segments' starting addresses but rather their parts(the four most
significant hexadecimal digits). By adding another zero digit to
multiply it by we get the
real segment starting address.
Each instruction referencing memory implicitly assumes usage of one
of segment registers. Documentation clarifies the default segment
registers for each instruction. However, common sense can help as well.
For instance, mov is used to manipulate data, so the address is relative
to the data segment.
1
mov al, [0004] ; === mov al, ds:04444
It is possible to redefine the segment explicitly:
1
mov al, cs:[0004]
When the program is loaded, the loader set ip, cs, ss, and sp
register to that cs:ip corresponds to the entry point, and ss:sp points
on top of the stack.
Real mode has numerous drawbacks
It makes multitasking very hard. The same address space is shared
between all programs, so they should be loaded at different addresses.
Their relative placement should usually be decided during compilation.
:joy: But maybe we can distributed these tasks by hand.
Programs can rewrite each other's code or even operating system as
they all live in the same address space. :dog: What about only one
user?
Any program can execute any instruction, including those used to set
up the processor's state. Some instructions should only be used by the
operating system(like those used to set up virtual memory, perform power
management, etc.) as their incorrect usage can crash the whole system.
:laughing: We do not have operating system!
Protected Mode
Intel 80386 was the first processor implementing protected 32-bit
mode.
It provides wider versions of registers(eax, ebx, ..., esi, edi) as
well as new protection mechanisms: protection rings, virtural memory,
and an improved segmentation.
Obtaining a segment starting address has changed.
Linear address = segment base(taken from system table) + offset
Each of segment registers cs, ds, ss, es, gs, and fs stores so-called
segment selector, containing an index in a special
segment descriptor table and a little additional information.
Two types of segment descriptor tables:
LDT(Local Descriptor Table)
GDT(Global Descriptor Table)
Index denotes descriptor position in either GDT or LDT. The T bit
select either LDT or GDT. As LDTs are no longer used, it will be zero in
all cases.
The table entries in GDT/LDT also store information about which
privilege level is assigned to the described segment. When a segment is
accessed through segement selector, a check of Request Privilege
Level(RPL) value(stored in selector = segment register) against
Descriptor Privilege Level(stored in descriptor table) is performed. If
RPL is not privileged enough to access a high privileged segment, an
error will occur. This way we could create numerous segments with
various permissions and use RPL values in segment selectors to define
which of them are accessible to us right now(given our privilege
level).
G-Granularity, e.g., size is in 0=bytes, 1=pages of size 4096 bytes
each.
D-Default operand size(0=16 bit, 1=32 bit).
L-Is it a 64-bit mode segment?
V-Available for use by system software.
P-Present in memory right now.
S-Is it data/code (1) or is it just some system information holder
(0).
X-Data (0) or code (1).
RW-For data segment, is writing allowed? (reading is always allowed);
for code segment, is reading allowed? (writing is always
prohibited).
DC-Growth direction: to lower or to higher addresses? (for data
segment); can it be executed from higher privilege levels? (if code
segment)
A-Was it accessed?
DPL-Descriptor Privilege Level (to which ring is it attached?)
mov eax, cr0 ; !! Privileged instruction or al, 1 ; this is the bit responsible for protected mode mov cr0, eax ; !! Privileged instruction jmp (0x1 << 3):start32 ; assign first seg selector to cs align 16 _gdtr: ; stores GDT's last entry index + GDT address dw 47 dq _gdt
align 16
_gdt: ; Null descriptor (should be present in any GDT) dd 0x00, 0x00 ; x32 code descriptor: db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00 ; differ by exec bit ; x32 data descriptor: db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00 ; execution off (0x92) ; size size base base base util util|size base
As you see, writing in 8-bit or 16-bit parts leaves the rest of bits
intact. Writing to 32-bit parts, however, fill the upper half of a wide
register with sign bit!
Explanation
Let's think about instruction decoding. The part of a CPU called
instruction decoder is constantly translating commands from an older
CISC system to a more convenient RISC one. Pipelines allow for a
simultaneous execution of up to six smaller instructions. To achieve
that, however, the notion of registers should be virtualized. During
microcode execution, the decoder choose an available register from a
large bank of physical registers. As soon as the bigger instruction
ends, the effects become visible to programmer: the value of some
physical registers may be copied to those, currently assigned to be,
let's say, rax.
The data interdependencies between instructions stall the pipeline,
decreasing performance. The worst cases occur when the same register is
read and modified by several consecutive instructions(think about
rflags!).
If modifying eax means keeping upper bits of rax intact, it
introduces an additional dependency between current instruction and
whatever instruction modified rax or its parts before. By discarding
upper 32 bits on each write to eax we eliminate this dependency, because
we do not care anymore about previous rax value or its parts.
This kind of a new behavior was introduced with the latest general
purpose registers' growth to 64 bits and does not affect operations with
their smaller parts for the sake of compatibility. Otherwise, most older
binaries would have stopped working because assigning to, for example,
bl, would have modified the entire ebx, which was not true back when
64-bit registers had not yet been introduced.