2016-11-30 6 views
1

В настоящее время я работаю над небольшой игрой, которая может работать из загрузочного сектора жесткого диска, просто для чего-то интересного. Это означает, что моя программа работает в 16-битном реальном режиме, и у меня есть мои флаги компилятора, настроенные для испускания чистого кода i386. Я пишу игру на C++, но мне нужно много встроенной сборки, чтобы поговорить с BIOS через вызовы прерываний. Некоторые из этих вызовов возвращают 32-разрядное целое число, но хранятся в двух 16-разрядных регистрах. В настоящее время я делаю следующее, чтобы получить мой номер из сборки:Как получить части высокого и низкого порядка из двух регистров в встроенной сборке?

auto getTicks = [](){ 
    uint16_t ticksL{ 0 }, ticksH{ 0 }; 
    asm volatile("int $0x1a" : "=c"(ticksH), "=d"(ticksL) : "a"(0x0)); 
    return static_cast<uint32_t>((ticksH << 16) | ticksL); 
}; 

Это функция лямбды я использую для вызова this interrupt function который возвращает количество тиков. Я знаю, что есть лучшие методы для получения временных данных, и что я не выполнил проверку для AL, чтобы узнать, прошла ли полночь, но это еще одна тема.

Как вы можете видеть, мне нужно использовать два 16-битных значения, получить значения регистра отдельно, а затем объединить их в 32-разрядное число, как вы видите в инструкции return.

Есть ли способ получить эти данные в одном 32-битном номере в моем коде сразу же, избегая сдвига и поразрядного или? Я знаю, что эти 16-битные регистры, к которым я обращаюсь, на самом деле представляют собой только более высокие и младшие 16-разрядные 32-битные регистры в реальности, но я понятия не имею, как получить доступ к исходному 32-битовому регистру в целом.

+3

'CX' и' DX' - это низкие 16 бит разных регистров, чтобы объединить их в 32-битный регистр, вам нужно будет немного свернуть, например shift и 'or'. Поскольку вы использовали код C, просто дайте компилятору понять, как лучше создать код для этого. – Jester

+0

О, это разные регистры, я вижу. Я думал, что они были выше и ниже 16-бит одного регистра, поэтому я подумал, что могу заставить это работать более эффективно. Если на самом деле это разные регистры, я боюсь, что не могу избежать этих операций. – adam10603

+0

Лучшие 16 битов недоступны напрямую. – Jester

ответ

4

Я знаю, что эти 16-разрядные регистры, к которым я обращаюсь, на самом деле представляют собой только более высокие и младшие 16-битные 32-разрядные регистры в реальности, но я понятия не имею, как получить доступ к исходным 32-битным регистрироваться в целом.

Как уже указывал Jester, это фактически два отдельных регистра, поэтому нет способа получить «исходный 32-разрядный регистр».

Еще одна точка: это прерывание изменяет регистр топора (возвращает флаг «прошлой полуночи»), однако ваш asm не сообщает gcc о том, что вы меняете топор. Я мог бы предложить что-то вроде этого:

asm volatile("int $0x1a" : "=c"(ticksH), "=d"(ticksL), "=a"(midnight) : "a"(0x0)); 

Заметьте, что midnight также uint16_t.

+0

Вы можете использовать ограничение '' + a "(полуночи)' для ввода/вывода и просто установить переменную полуночи в 0 перед шаблоном ассемблера. –

0

Вы ищете ограничение 'A', которое относится к паре регистра dx: ax как значение шириной в два раза. Вы можете увидеть полный набор определенных ограничений для x86 in the gcc documentation. К сожалению, для каких-либо других пар регистров нет ограничений, поэтому вы должны получить их как два значения и собрать их со сдвигом и, как вы описали.

+0

Мои данные находятся в 'CX' и' DX', а не те, к которым относится ограничение A, поэтому да, похоже, мне нужно собрать их во время выполнения. – adam10603

2

Как и другие ответы, вы не можете загрузить DX и CX прямо в 32-разрядный регистр. Вы должны объединить их, как вы предлагаете.


В этом случае есть альтернатива. Вместо использования INT 1Ah/AH=0h вы можете прочитать BIOS Data Area (BDA) в малой памяти для 32-разрядного значения DWORD и загрузить его в 32-разрядный регистр. Это разрешено в реальном режиме на процессорах i386. Два адреса памяти интереса:

40:6C dword Daily timer counter, equal to zero at midnight; 
       incremented by INT 8; read/set by INT 1A 
40:70 byte Clock rollover flag, set when 40:6C exceeds 24hrs 

Эти два адреса памяти в формате segment:offset, но было бы эквивалентно физическому адресу 0x0046C и 0x00470.

Все, что вам придется сделать, это временно установить DS регистра в 0 (сохранение предыдущего значения), отключить прерывания с CLI извлекать значения из нижней памяти с помощью C/C++ указатели, включите прерывает с STI и восстанавливает DS к ранее сохраненному значению. Это, конечно же, добавляет накладные расходы в загрузочном секторе по сравнению с использованием INT 1Ah/AH = 0h, но позволит вам получить прямой доступ к адресам памяти, которые BIOS читает/записывает от вашего имени.

Примечание: Если DS установлен на ноль, то уже не нужно сохранять/устанавливать/восстанавливать его. Поскольку мы не видим код, который устанавливает среду перед вызовом кода C++, я не знаю, каковы ваши значения сегмента по умолчанию. Если вам не нужно извлекать как значения опрокидывания, так и таймера, и только хотите получить их индивидуально, вы можете исключить CLI/STI.

+0

Уверены ли вы в этом? Я читал [здесь] (https://books.google.com/books?id=WKaekAHjQ1EC&pg=PA345&lpg=PA345&dq=read+time+from+bios+data+area+cli&source=bl&ots=A2RR15gn4f&sig=wznVw6ygHSTr8O_LPln6wQ6B4bc&hl=ru&sa=X&ved = 0ahUKEwiR-Ozi6NTQAhVS_mMKHenRCsoQ6AEILzAD # v = onepage & q = read% 20time% 20from% 20bios% 20data% 20area% 20cli & f = false) (прокрутите вниз до абзаца «ЧАСЫ РЕАЛЬНОГО ВРЕМЕНИ»), которые вам нужно было наблюдать за обновлениями в байте состояния A, чтобы узнать, когда было безопасно читать. Предположительно, использование cli может замедлить работу часов? –

+0

@DavidWohlferd: Этот метод фактически не запрашивает аппаратные часы. 2 значения, о которых я упоминал, фактически обновлены обработчиком BIOS Int 8h (IRQ0) по умолчанию. Обработчик прерываний IRQ0 может получить доступ к аппаратным средствам реального времени, хотя BIOS делает эти 2 значения доступными после обработки. Если вы будете запрашивать RTC через порты данных, тогда вам нужно беспокоиться о сроках. –