В начале 8086 был расширением 8-битного процессора 8085. 8085 мог адресовать только 65536 байтов с его 16-битной адресной шиной.Когда Intel разработала 8086, они хотели, чтобы программное обеспечение было максимально совместимо со старыми 8-битными процессорами, поэтому они представили концепцию сегментной адресации памяти.Это позволило запустить 8-битное программное обеспечение, чтобы жить в большем диапазоне адресов, не замечая этого.8086 имел 20-битную адресную шину и, таким образом, мог обрабатывать до 1 МБ памяти (2 ^ 20).К сожалению, он не мог обратиться к этой памяти напрямую, для этого ему пришлось использовать регистры сегментов.Реальный адрес был рассчитан путем сложения 16-битного значения сегмента, сдвинутого влево на 4, к 16-битному смещению.
Example:
Segment 0x1234 Offset 0x5678 will give the real address
0x 1234
+0x 5678
---------
=0x 179B8
Как вы заметили, эта операция не является биективной, то есть вы можете генерировать реальный адрес с другими комбинациями сегмента и смещения.
0x 1264 0x 1111
+0x 5378 +0x 68A8
--------- --------- etc.
=0x 179B8 =0x 179B8
На самом деле существует 4096возможны различные комбинации из-за 3 перекрывающихся кусков (3*4 = 12
бит, 2^12 = 4096
).Нормализованная комбинация является единственной из 4096 возможных значений, которые будут иметь 3 старших куска смещения к нулю.В нашем примере это будет:
0x 179B
+0x 0008
---------
=0x 179B8
Разница между far
и huge
указателем не в нормализации, вы можете иметь ненормализованный указатель huge
, это абсолютно допустимо.Разница заключается в коде, генерируемом при выполнении арифметики с указателями.При использовании дальних указателей при увеличении или добавлении значений к указателю обработка переполнения не будет, и вы сможете обрабатывать только 64 КБ памяти.
char far *p = (char far *)0x1000FFFF;
p++;
printf("p=%p\n");
напечатает 1000:0000
Для больших указателей компилятор сгенерирует код, необходимый для обработки переноса.
char huge *p = (char huge *)0x1000FFFF;
p++;
printf("p=%p\n");
выведет 2000:0000
Это означает, что вы должны быть осторожны при использовании дальних или огромных указателей, так как стоимость арифметики с ними различна.
Не следует также забывать, что большинство 16-битных компиляторов имели библиотеки, которые не обрабатывали эти случаи правильнодавая иногда глючный софт.Компилятор реального режима Microsoft не обрабатывал огромные указатели на все свои строковые функции.Borland был еще хуже, так как даже функции mem (memcpy
, memset
и т. Д.) Не обрабатывали переполнения смещения.По этой причине было полезно использовать нормализованные указатели с этими библиотечными функциями, вероятность их переполнения была ниже.