Команда 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, если вы не сможете доказать, что это не существенное узкое место и/или вы хотите иметь оптимизированные пути для разных процессоры.)