Команда move из eax в регистр es дает ошибку
Я не могу определить способ перемещения кода из одного места в другое в памяти
поэтому я помещаю что-то вроде этого, но не работает
extern _transfer_code_segment
extern _kernel_segment
extern _kernel_reloc
extern _kernel_reloc_segment
extern _kernel_para_size
section .text16
global transfer_to_kernel
transfer_to_kernel:
;cld
;
; Turn off interrupts -- the stack gets destroyed during this routine.
; kernel must set up its own stack.
;
;cli
; stack for only for this function
push ebp
mov ebp, esp
mov eax, _kernel_segment ; source segment
mov ebx, _kernel_reloc_segment ; dest segment
mov ecx, _kernel_para_size
.loop:
; XXX: Will changing the segment registers this many times have
; acceptable performance?
mov ds, eax ;this the place where the error
mov es, ebx ; this to
xor esi, esi
xor edi, edi
movsd
movsd
movsd
movsd
inc eax
inc ebx
dec ecx
jnz .loop
leave
ret
есть ли другой способ сделать это или как я могу решить эту проблему
Ответы
Ответ 1
У этого будет ужасная производительность. Agner Fog говорит, что mov sr, r
имеет один на 13 циклов пропускной способности на Nehalem, и я бы догадался, что если что-нибудь хуже на более последних процессорах с тех пор сегментация устарела. Agner прекратил тестирование mov в/из производительности регистра сегментов после Nehalem.
Вы делаете это, чтобы скопировать больше, чем 64kiB? Если это так, по крайней мере скопируйте полный 64kiB перед изменением регистров сегментов.
Я думаю, вы можете использовать 32-битные режимы адресации, чтобы избежать беспорядка с сегментами, но сегменты, которые вы установили в 16-битном режиме, неявно имеют "предел" 64k. (т.е. mov eax, [esi]
кодируется в 16-битном режиме с префиксом размера операнда и размера адреса. Но со значением в esi более 0xFFFF я думаю, что это было бы ошибкой для нарушения предела сегмента ds
). Ссылка osdev ниже для более.
Как говорит Коди, используйте rep movsd
, чтобы процессор использовал оптимизированный микрокод memcpy
. (или rep movsb
, но только на процессорах с функцией ERMSB.
На практике большинство процессоров, поддерживающих ERMSB, дают одинаковое преимущество производительности для rep movsd
тоже, поэтому, возможно, проще всего использовать только rep movsd
. Но IvyBridge не может.) Это намного быстрее, чем отдельные инструкции movsd
(которые медленнее, чем отдельные mov
загрузки/хранилища). Цикл с векторными нагрузками/хранилищами SSE 16B может идти почти так же быстро, как rep movsd
на некоторых процессорах, но вы не можете использовать AVX для векторов 32B в 16-разрядном режиме.
Другой вариант для больших копий: огромный нереальный режим
В 32-битном защищенном режиме значения, которые вы помещаете в сегменты, являются дескрипторами, а не самой основной базой сегмента. mov es, ax
запускает CPU для использования значения в качестве индекса в GDT или LDT и получает от него базу/лимит сегмента.
Если вы делаете это в 32-битном режиме, а затем переключаетесь обратно в 16-битный режим, вы находитесь в огромном нереальном режиме с сегментами, которые могут быть больше 64k. База сегментов/лимит/разрешения остается кешированной до тех пор, пока что-то не записывает сегментный регистр в 16-битном режиме и возвращает его в обычный 16*seg
с пределом 64k. (Если я правильно описываю это). Подробнее см. http://wiki.osdev.org/Unreal_Mode.
Тогда вы можете использовать rep movsd
в 16-битном режиме с префиксами размера операнда и размера адреса, чтобы вы могли копировать более 64kiB за один раз.
Это хорошо работает для ds
и es
, но прерывания установят cs:ip
, поэтому это не удобно для большого адресного пространства с плоским кодом, просто данные.
Ответ 2
Регистры сегментов имеют размер 16 бит. Сравните это с регистрами e?x
, размер которых составляет 32 бита. Очевидно, что эти две вещи не имеют одинакового размера, что побуждает ассемблер генерировать ошибку "несоответствие размера операнда" - размеры двух операндов не совпадают.
Предположительно, вы хотите инициализировать регистр сегмента с младшими 16 битами регистра, так что вы бы сделали что-то вроде:
mov ds, ax
mov es, bx
Кроме того, нет, вам фактически не нужно инициализировать регистры сегментов на каждой итерации цикла. Теперь вы увеличиваете сегмент и заставляете смещение 0, а затем копируете 4 DWORD. То, что вы должны делать, это оставить только сегмент и просто увеличивать смещение (которое команда MOVSD
делает неявно).
mov eax, _kernel_segment ; TODO: see why these segment values are not
mov ebx, _kernel_reloc_segment ; already stored as 16 bit values
mov ecx, _kernel_para_size
mov ds, ax
mov es, bx
xor esi, esi
xor edi, edi
.loop:
movsd
movsd
movsd
movsd
dec ecx
jnz .loop
Но обратите внимание, что добавление REP
prefix в инструкцию MOVSD
позволит вам сделать это еще эффективнее. В основном это MOVSD
всего ECX
раз. Например:
mov ds, ax
mov es, bx
xor esi, esi
xor edi, edi
shl ecx, 2 ; adjust size since we're doing 1 MOVSD for each ECX, rather than 4
rep movsd
Несколько интуитивно, если ваш процессор реализует оптимизацию ERMSB (мост Intel Ivy и позже), REP MOVSB
может быть быстрее, чем REP MOVSD
, поэтому вы можете сделать:
mov ds, ax
mov es, bx
xor esi, esi
xor edi, edi
shl ecx, 4
rep movsb
Наконец, хотя вы прокомментировали инструкцию CLD
в своем коде, вам нужно это сделать, чтобы убедиться, что ходы происходят в соответствии с планом. Вы не можете полагаться на флаг направления, имеющий определенное значение; вам нужно инициализировать его самостоятельно до требуемого значения.
(Другой альтернативой может быть потоковая передача инструкций SIMD или даже магазинов с плавающей запятой, ни одна из которых не будет заботиться о флаге направления. Это имеет преимущество увеличения пропускной способности памяти, поскольку вы будете делать 64-разрядные 128-битные, или больших копий одновременно, но вводит другие недостатки. В ядре я придерживаюсь MOVSD
/MOVSB
, если вы не сможете доказать, что это не существенное узкое место и/или вы хотите иметь оптимизированные пути для разных процессоры.)