программирование

Подписчиков: 195     Сообщений: 502     Рейтинг постов: 5,369.8

программирование geek OSDev операционная система разработка песочница 

Урок ОСдева №4: работа с RAM, адресация в 16-битном режиме, регистры процессора.

Поздравим себя. В прошлый раз мы добавили блок параметров BIOS, после чего винда перестала

ругаться на дискету. Пора начинать писать загрузчик. Но перед этим надо подробнее

разобраться в специфике программирования на ассемблере. Всё-таки он сильно отличается от языков

высокого уровня. Давайте вспомним, как выглядела программа в конце прошлого поста.


.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG


begin:            jmp short execute                   ;Точка входа. Перейти к исполняемой части.

                     nop                                       ;Пустой оператор. Заполняет 3-й байт перед BPB.


;БЛОК ПАРАМЕТРОВ BIOS===================================================================;

;=======================================;

;Блок параметров BIOS, 33 байта.

;Здесь хранятся характеристики

;носителя. Должен быть в 3-х байтах

;от начала загрузочного сектора.

;=======================================;

BPB_OEMname    db 'BOOTDISK'    ;0-7. Имя производителя. Может быть любым.

BPB_bytespersec  dw 512              ;8-9. Размер сектора в байтаx.

BPB_secperclust   db 1                  ;10. Количество секторов в кластере.

BPB_reserved      dw 1                  ;11-12. Число зарезервированныx секторов (1, загрузочный).

BPB_numFATs     db 2                   ;13. Число FAT.

BPB_RDentries    dw 224               ;14-15. Число записей Корневой Директории.

BPB_sectotal       dw 2880             ;16-17. Всего секторов на носителе.

BPB_mediatype   db 0F0h              ;18. Тип носителя. 0F0 - 3,5-дюймовая дискета с 18 секторами в дорожке.

BPB_FATsize       dw 9                   ;19-20. Размер FAT в сектораx.

BPB_secpertrack dw 18                  ;21-22. Число секторов в дорожке.

BPB_numheads   dw 2                   ;23-24. Число головок (поверxностей).

BPB_hiddensec    dd 0                  ;25-28. Число скрытыx секторов перед загрузочным.

BPB_sectotal32    dd 0                  ;29-32. Число секторов, если иx больше 65535.


;===============================================;

;Расширенный блок параметров BIOS, 26 байт.

;Этот раздел используется в DOS 4.0.

;===============================================;

EBPB_drivenum   db 0                       ;0. Номер привода.

EBPB_NTflags      db 0                      ;1. Флаги в Windows NT. Бит 0 - флаг необxодимости проверки диска.

EBPB_extsign      db 29h                   ;2. Признак расшренного BPB по версии DOS 4.0.

EBPB_volID         dd 0                      ;3-6. "Серийный номер". Любое случайное число или ноль, без разницы.

EBPB_vollabel     db 'BOOTLOADER '  ;7-17. Название диска. Устарело.

EBPB_filesys       db 'FAT12   '           ;18-25. Имя файловой системы.




;ИСПОЛНЯЕМЫЙ БЛОК=====================================================================;

execute:                     cli

                                 hlt


           org 1FEh                              ;Заполняет память нулями до 511-го байта.

           dw 0AA55h                           ;Байты 511 и 512. Признак загрузочного сектора.

CSEG ends

end begin


Я снабдил всё подробными комментариями. Надеюсь, они помогут вам освежить память. Вкратце -

после запуска программы процессор выполняет переход к метке execute - и после этого останавливается

командами cli и hlt. Давайте добавим следующий код после execute, а потом разберём его.


execute:             mov ax,07C0h

                         mov ds,ax

                         mov es,ax

                         mov fs,ax

                         mov gs,ax


                         cli

                         mov ss,ax

                         mov sp,0FFFFh

                         sti


                         push ax

                         mov ax,offset stop

                         and ax,03FFh

                         push ax

                         retf


stop:                  cli

                         hlt


           org 1FEh                                     ;Заполняет память нулями до 511-го байта.

           dw 0AA55h                                  ;Байты 511 и 512. Признак загрузочного сектора.


Целая куча новых команд. Для того, чтобы их понять, придётся освоиться с новыми понятиями.


Регистр - ячейка памяти процессора, которая может выполнять какую-то конкретную задачу

или иметь общее назначение. Программируя на ассемблере, вы постоянно будете оперировать

регистрами: помещать в них данные, извлекать, модифицировать и т.д. В 16-битном режиме

процессор использует следующий набор регистров: AX, BX, CX, DX, SI, DI, BP, SP, flags, CS, DS, ES,

FS, GS, SS. С функциями каждого из них будем разбираться по мере надобности.


Сегмент:смещение - устаревшая система адресации, применявшаяся в эпоху 16-битных процессоров.

Тем не менее, для нас она важна, так как ради обратной совместимости именно в этом

режиме BIOS оставляет систему перед запуском загрузчика.


Постараюсь объяснить. 16-битная разрядность процессора подразумевает, что за раз он может обработать

16 бит данных. Максимальное значение, которое можно передать 16 битами - 65535. Это ограничение

касается и адресации памяти. Выходит, процессору доступно всего (65536/1024) 64 килобайта RAM. Чтобы

обойти это ограничение, была придумана модель адресации segment:offset. Сегмент в ней - это базовый адрес,

от которого считается смещение. Регистры процессора CS, DS, ES, FS, GS и SS - сегментные. Они используются

для указания адреса в памяти, от какого отсчитывается смещение. Например, DS:0050h означает байт 0050h

от значения, помещённого в DS. Вернее, от значения в DS*16. Это называется гранулярностью. Единица,

помещённая в регистр DS, устанавливает основание сегмента не в 1-й байт, а в 16-й. За счёт этого нам

становится доступен целый мегабайт оперативной памяти! (или даже больше с некоторыми ухищрениями,

но рассказывать о них я большого смысла не вижу, т.к. мы всё равно скоро покинем 16-битное царство)


Стек - область памяти, через которую можно передавать параметры процедурам в си-подобных языках

или сохранять состояние регистров при вызове прерывания. В случае ассемблера в стеке можно хранить

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

используется некоторыми командами процессора.


Команда mov op1,op2 используется для того, чтобы переместить значение op2 в op1. В качестве

операнда op1 может выступать адрес ячейки памяти или регистр. В качестве op2 может быть ячейка

памяти, регистр или конкретное значение. Есть два ограничения: операнды должны совпадать по

разрядности (нельзя поместить содержимое 16-битного регистра в 32-битный, например) и в качестве

обоих операндов не могут быть адреса в памяти. Так что делает этот код?


mov ax,07C0h

mov ds,ax

mov es,ax

mov fs,ax

mov gs,ax


Правильно, он помещает значение 07С0h в регистр AX,  потом копирует AX в сегментные регистры

DS, ES, FS и GS. Зачем? Затем, что BIOS копирует загрузочный сектор в 07С0h:0000h. Так как

наш загрузчик находится по этому адресу, будет правильным установить значения сегментных регистров

так, чтобы они указывали туда же. По какой-то причине (ей-богу не помню!) присваивать значения

сегментным регистрам напрямую нельзя, но можно через другой регистр - поэтому сначала мы загружаем

его в AX, а уже AX копируем в сегментные регистры. Вы наверое уже обратили внимание, что сегментные

регистры здесь не все. Для модификации оставшихся надо немного поплясать с бубном.


cli

mov ss,ax

mov sp,0FFFFh

sti


Что происходит здесь? Пара команд cli и sti запрещает и разрешает прерывания. Прерывания - то, при помощи

чего разные устройства в компьютере общаются с процессором. Они могут поступать от таймеров, дисковых

контроллеров и из множества других источников. Позже мы ещё поговорим о них подробно, а сейчас достаточно

знать, что команда cli вешает на процессор знак "не беспокоить". sti, соответственно, его снимает.

Дело в том, что SS - это сегментный регистр стека. При манипуляциях с ним лучше убедиться, что в

неподходящий момент не произойдёт переключение задачи. Обратите внимание: сегмент стека у нас там же,

где и загрузчик. Получается, помещая данные в стек, мы затрём часть собственного кода? Нет. Позиция стека

передаётся парой регистров SS:SP. SS - сегмент, а SP - смещение. mov sp, 0FFFFh устанавливает начало

стека в конец сегмента. Получается, ему некуда расти? Тоже нет. Стек растёт в обратном направлении.

Если мы командой push отправим в стек 16-битное слово, то указатель изменит значение на 0FFFDh. Таким

образом, загрузчик и стек находятся в разных концах 64-килобайтного сегмента, и расстояние между ними

вполне приличное.


push ax

mov ax,offset stop

and ax,03FFh

push ax

retf


Соберитесь, последний на сегодня кусок кода. Здесь мы модифицируем сегментный регистр кода, CS. К нему

тоже нужен особый подход. Кстати, самое время поговорить о том, как процессор узнаёт, какую команду выполнять

следующей. Как и в случае стека, существует указатель в виде пары регистров CS:IP. Каждый раз после

считывания из памяти инструкции IP увеличивается на её размер в байтах. Все модели BIOS помещают загрузчик

в 07C0h:0000h, но вот состояние CS:IP может быть разным: например, 07C0h:0000h и 0000h:7C00h указывают на

один и тот же байт в памяти, но во втором случае у нас могут быть проблемы. В каком именно состоянии

оказались регистры CS:IP при старте загрузчика, мы не знаем, поэтому лучше перестраховаться и установить

своё значение.


Как установить значение CS:IP? Например, при помощи инструкции дальнего возврата retf. Обычно она

используется для возврата из процедур, но подойдёт и нам, так кк делает именно то, что нужно: меняет

значения CS:IP. Сегмент и смещение для возврата должны быть в стеке. В AX у нас значение сегмента, 07C0h,

так что командой push отправляем его в стек. А вот с IP придётся повозиться. Щас объясню. CS в данный момент

может быть установлен либо в 07C0h, либо в 0000h. Значит, любое считанное нами смещение относительно его

начала будет равно или X или X+7C00h. Нам нужно однозначно привести его к X. Как это сделать? Команда

mov AX,offset stop помещает в AX смещение метки stop (то есть, конечно, команды cli, сами метки

в исполняемом файле физически не присутствуют и места не занимают). 7С00h, если его перевести в

двоичный вид, будет равно 111110000000000b. Соответственно, искомый X помещается в восьми нулях в начале

значения.  обнуление старших пяти единиц будет эквивалентно уменьшению значения на 7С00h, что нам и нужно.

Про логические операции поговорим позже, но пока знайте, что команда and AX,03FFh делает как раз это:

обнуляет все старшие разряды AX, начиная с первой единицы в 111110000000000b. 03FFh, кстати, в

двоичном виде будет выглядеть так: 1111111111b. Заметили связь? В общем, если кто-то не разбирается в

логических операциях, то ДЗ на сегодня - просветиться по этой теме.


Фух, чёрт возьми, на сегодня всё! Теперь наш загрузчик будет работать в предсказуемой среде, что сэкономит

нам море усилий.


Развернуть

программирование geek ассемблер операционная система разработка OSDev песочница 

Уроки ОСдева №3: блок параметров BIOS

В прошлый раз мы разобрались с физической геометрией дискеты и расположением данных. Кроме того, мы узнали, как записать программу-загрузчик в нулевой сектор носителя. Если кто-то из вас действительно пытался повторить мои действия и потом использовать дискету как загрузочную, то наверняка обнаружил два неприятных момента:


1. При попытке открыть образ дискеты Wndows говорит, что она не отформатирована.
2. BIOS не воспринимает дискету как загрузочную и пишет что-нибудь вроде "no bootable device found".

Давайте разбираться. Если помните, в конце прошлого поста наша будущая программа-загрузчик выглядела так:

.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG

begin:                    cli

                             hlt

CSEG ends

end begin


По сути это просто заглушка, которая при запуске должна останавливать процессор. Причина ругани Виндоус в том, что в нулевом секторе на отформатированном носителе хранится важная структура данных - блок параметров BIOS (BPB). Записав туда же нашу программу, мы его пот+ёрли. Для того, чтобы этого избежать, нам придётся воссоздать BPB в тексте программы. Для этого нужно знать геометрию носителя. К счастью, в наше время сохранился только один тип дискет.


Непроверенный метод: возможно, если с помощью утилиты debug записать программу не в начало сектора, а со смещением, достаточным, чтобы пропустить оригинальный BPB, то он не пострадает, но я не проверял. По-моему, debug всё равно забьёт остатки сектора каким-то мусором.


Во-первых, измените программу вот так:


.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG

begin:                     jmp short execute

                             nop

execute:                 cli

                             hlt

CSEG ends

end begin


У нас появились две новые инструкции: jmp short и nop. Последняя - просто пустой оператор. Процессор пропускает его, не выполняя никаких действий. Занимает 1 байт. jmp - инструкция перехода. jmp short - переход в пределах 127 байт от текущего положения. Исполняется гораздо быстрее jmp, так что везде где возможно - используйте его. Занимает 2 байта. execute - название метки, на которую указывает инструкция jmp short.


Зачем всё это и зачем nop? BPB должен располагаться строго в трёх байтах от начала нулевого сектора. Эти три байта и занимают инструкции jmp short execute и nop. Таким образом, когда программа начнёт исполняться, первой инструкцией, которую выполнит процессор, будет пропустить BPB. В противном случае он бы попытался исполнить его как код, что привело бы к катастрофе.

Теперь давайте вставим сам блок параметров BIOS между nop и меткой execute.


.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG

begin:                     jmp short execute

                             nop


                BPB_OEMname          db    'BOOTDISK'

                BPB_bytespersec       dw    512

                BPB_secperclust        db     1

                BPB_reserved            dw    1

                BPB_numFATs           db     2

                BPB_RDentries          dw    224

                BPB_sectotal             dw    2880

                BPB_mediatype         db     0F0h

                BPB_FATsize             dw     9

                BPB_secpertrack        dw     18

                BPB_numheads         dw     2

                BPB_hiddensec          dd     0

                BPB_sectotal32          dd     0

                EBPB_drivenum         db     0

                EBPB_NTflags            db     0

                EBPB_extsign            db     29h

                EBPB_volID               dd     0

                EBPB_vollabel            db     'BOOTLOADER '

                EBPB_filesys              db     'FAT12   '


execute:                 cli

                             hlt


CSEG ends

end begin


BPB - это блок данных, и здесь мы впервые объявляем переменные. В TASM это выглядит так: BPB_OEMname (имя) -пробел- db, dw, dd или dq -пробел- 'BOOTDISK' (значение). Имени может и не быть, но тогда к переменной нужно будет обращаться по смещению, это не очень удобно. DB, DW, DD и DQ - сокращение от define byte (word, double word или quad word) - обозначают размер переменной. Соответственно, 1, 2, 4 или 8 байт. Инстркция этого типа позволяют объявлять целые серии значений через запятую: myvalue dw 2, 5, 165, 776. С помощью инструкции db можно объявлять строки: mytext db 'Allo, Yoba!' Обратите внимание, что в плоском бинарнике переменные при компиляции не выносятся в какую-то специальную область данных. В исполняемом файле они будут именно там, где вы их объявили в тексте программы. Ещё важный момент: имена переменных только для вашего личного пользования, в исполняемый файл они не попадут, так что вы не обязаны копировать названия у меня. Теперь давайте посмотрим, что за информация хранится в BPB.


BPB_OEMname - 8 байт: по идее здесь должно быть название производителя, но по факту вы можете писать что угодно, никто на это значение не смотрит.

BPB_bytespersec - 2 байта: размер сектора в байтах, для дискет как правило 512.

BPB_secperclust - 1 байт: число секторов в кластере. Про кластеры мы поговорим позже, но в случае с дискетами секторы и кластеры соответствуют друг другу.

BPB_reserved - 2 байта: число зарезервированных секторов, недоступных файловой системе. В нашем случае такой один, это наш загрузочный сектор.

BPB_numFATs - 1 байт: количество FAT (file allocation table), таблиц распределения файлов. Так как носители информации (особенно дискеты) подвержены порче, а FAT - очень важная часть файловой системы, для неё часто делается резервная копия.

BPB_RDentries - 2 байта: количество записей в корневой директории (Root Directory). Про корневую директорию тоже будем говорить в другой раз, но пока можете представить её как список файлов с указанием их физического расположения на носителе.

BPB_sectotal - 2 байта: число секторов на диске, если их не больше 65535. Если больше, здесь должен быть 0.

BPB_mediatype - 1 байт: тип носителя. F0 - код для 3,5-дюймовой дискеты с 18 секторами в дорожке.

BPB_FATsize - 2 байта: размер одной FAT в секторах.

BPB_secpertrack - 2 байта: число секторов в дорожке.

BPB_numheads - 2 байта: число головок.

BPB_hiddensec - 4 байта: количество скрытых секторов перед загрузочным, в нашем случае 0.

BPB_sectotal32 - 4 байта: число секторов, если их больше 65535. Если меньше, здесь должен быть 0.


Здесь стандартный BIOS Parameter Block заканчивается и начинается расширенный, который появился в поздних версиях DOS.


EBPB_drivenum - 1 байт: бесполезная переменная, хранящая номер привода, в который был вставлен носитель при форматировании.

EBPB_NTflags - 1 байт: флаги Вин НТ. Если установлен бит 0, необходимо проверить носитель на битые секторы. Значения других флагов не знаю.

EBPB_extsign - 1 байт: признак расширенного BPB. Для нашей версии должно быть 29h.

EBPB_volID - 4 байта: случайный номер, который присваивается при форматировании. В общем бесполезен.

EBPB_vollabel - 11 байт: имя носителя.

EBPB_filesys - 8 байт: имя файловой системы.


Если вы теперь заново скомпилируете программу и запишите на дискету, то она отлично откроется в Windows. Первая проблема решена, но осталась вторая: дискета всё ещё не опознаётся как загрузочная. Вспоминаем: для этого последние 2 байта загрузочного сектора должны иметь значения AAh и 55h. Добавим ещё две строчки в нашу программу:


.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG

begin:                     jmp short execute

                             nop


                BPB_OEMname          db    'BOOTDISK'

                BPB_bytespersec       dw    512

                BPB_secperclust        db     1

                BPB_reserved            dw    1

                BPB_numFATs           db     2

                BPB_RDentries          dw    224

                BPB_sectotal             dw    2880

                BPB_mediatype         db     0F0h

                BPB_FATsize             dw     9

                BPB_secpertrack        dw     18

                BPB_numheads         dw     2

                BPB_hiddensec          dd     0

                BPB_sectotal32          dd     0


                EBPB_drivenum         db     0

                EBPB_NTflags            db     0

                EBPB_extsign            db     29h

                EBPB_volID               dd     0

                EBPB_vollabel            db     'BOOTLOADER '

                EBPB_filesys              db     'FAT12   '


execute:                 cli

                             hlt


               org 510

                dw 0AA55h


CSEG ends

end begin


Команда org 510 заполнит нулями место от текущей позиции до 510 байта, а в последние два мы поместили метку загрузочного сектора. Вуаля, проблема 2 решена.


Выражалось мнение, что всё это ебучее легаси и современные пацаны предпочитают UEFI, но UEFI не даст вам того интимного, я бы сказал, понимания железа, на котором работает ваша ось, так что основная серия будет продолжена по старинке.


Нужны ли туторы по UEFI?
Да
84 (69.4%)
Нет
7 (5.8%)
Я не знаю, заебал свою гиковскую xуйню сюда постить, вали на гитxаб!
30 (24.8%)
Развернуть

it-юмор geek программирование 

World: *Exists* New Programmers:,it-юмор,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор,программирование
Развернуть

geek OSDev операционная система разработка программирование ассемблер песочница 

Уроки ОСдева №2: схемы адресации и твоя первая программа на ассемблере

Если кто-то решил следовать этим туторам, самое время обзавестись необходимыми инструментами. Скачайте turbo assembler (TASM) или любой другой привычный вам. Установите виртуальную машину с Windows XP. Работать с большинством используемых программ проще в ней. Я бы посоветовал Oracle VirtualBox.


CHS


В прошлый раз мы выяснили несколько важных вещей:

- для того, чтобы загрузить ОС с дискеты, нам нужна программа-загрузчик.

- программа-загрузчик должна занимать ровно 512 байт, причём последние два байта должны содержать метку загрузчика (AA55h).

- программа-загрузчик должна располагаться в первом физическом секторе носителя.


Эта информация в свою очередь ставит перед нами новый вопрос: как поместить загрузчик на полагающееся ему место? Придётся разбираться в схемах адресации данных на цифровых носителях. Тема не очень простая, но необходимая, так что крепитесь.


geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор,OSDev,операционная система,разработка,программирование,ассемблер,песочница


На картине выше - структура дискового носителя. Это может быть жёсткий диск, дискета или CD, суть не поменяется. Головка, она же лицо, - это одна сторона поверхности носителя (пластины). У жёсткого диска на рисунке 4 двусторонних пластины, то есть 8 головок. Головка в свою очередь разбита на концентрические дорожки (они же цилиндры), а те - на одинакового размера (как правило 512 байт) секторы. Независимо от радиуса цилиндра в каждом из них одинаковое число секторов, что позволяет для доступа к любому конкретному сектору использовать трёхкомпонентную координату Цилиндр-Головка-Сектор (Cylinder-Head-Sector, CHS). Важно помнить, что в системе CHS цилиндры и головки нумеруются с 0, а секторы - с 1. То есть для доступа к первому физическому сектору носителя наши координаты будут иметь значения: 0(цилиндр), 0(головка), 1(сектор).


Проблема в том, что привычные нам файловые системы скрывают физическую геометрию носителей за набором абстракций вроде файлов и папок. К счастью, есть альтернативы. Например, в Windows есть команда debug. Выглядит так:


debug boot.bin(имя файла, загружается в память по адресу 100h)

-w(запись) 100(адрес загруженного файла) 0(номер привода) 0(номер первого сектора) 1(число секторов для загрузки)

-q(выход)


Я выше писал, что секторы нумеруются с 1, но debug закон не писан. Всё остальное время правило в силе.


Само собой, для этого дискета должна быть вставлена в первый флоппи-привод. Так как большинство из вас скорее всего флоппи-привода и набора дискет не имеет, я предлагаю использовать привод вашей виртуальной машины и чистый образ дискеты.


LBA


Информацию из этого раздела мы пока использовать не будем, но позже она пригодится. Формат CHS на сегодняшний день устарел. Практически все устройства нативно поддерживают схему адресации под названием LBA (Linear Block Addressing), в которой к секторам обращаются просто по их порядковому номеру, без учёта геометрии носителя. Тем не менее, CHS поддерживается на уровне эмуляции и для работы с дискетами пользоваться мы будем именно ей.


boot.asm


Итак, мы разобрались, как поместить нашу программу в нужное место на дискете. Самое время приступать к написанию кода! Мы, конечно, не будем в этот раз писать полный загрузчик. Это будет просто небольшая вводная в турбо ассемблер. Она предназначена для людей без опыта в ассемблере и подразумевает, что вы будете пользоваться TASM. Если вы уже знаете x86 ассемблер и намерены пользоваться каким-то другим - смело пропускайте конец статьи.


Скачали TASM? Отлично, инсталлируйте его в какое-нибудь легкодоступное место на диске и в папке BIN/ создайте папки OS/boot/stage1/.


Теперь в stage1/ создайте пустой текстовый файл, переименуйте его в boot.asm и откройте текстовым редактором. В файле создайте следующий текст:


.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG

begin:


CSEG ends

end begin


Сделали? Сохраните и давайте разбираться. Сразу скажу, что собственно программы здесь пока нет. Всё, что вы видите - набор инструкций для компилятора, в исполняемый файл они не попадут.


.386p - указание компилятору сверять инструкции с набором для 386 процессора в защищённом (protected, отсюда p) режиме. Соответственно, если компилятор встретит инструкцию, которую данный процессор не поддерживает, он выдаст ошибку.


CSEG - название сегмента, в котором будет содержаться код нашей программы.


Следующую строчку будет сложно объяснить, т.к. мы пока не касались устройства процессора, так что просто примите как есть: это указание компилятору считать, что все сегментные регистры процессора указывают на сегмент CSEG.


begin - метка начала программы. Отсюда будет начинаться собственно код.


CSEG ends - конец сегмента CSEG.


end begin - метка конца программы.


Скомпилировать программу в таком виде не выйдет, так как в ней нет ни одной инструкции. Давайте добавим парочку.


.386p

CSEG segment use16

ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG

begin:                    cli

                             hlt

CSEG ends

end begin

Мы добавили 2 команды: cli и hlt. Первая запрещает прерывания, то есть не даёт устройствам отправить сигнал процессору, а вторая останавливает сам процессор. Таким образом, сейчас наша программа при запуске просто "вешает" компьютер. Тем не менее, её уже можно превратить в исполняемый файл.


Откройте командную строку, перейдите в папку BIN/ и введите: tasm OS/boot/stage1/boot.asm. После нажатия enter в папке BIN/ появится файл BOOT.OBJ. Это ещё не конец. Файл .obj - это инструкция для линкера, так что пишите: tlink boot.obj. Теперь у вас добавились два новых файла, BOOT.MAP и BOOT.EXE, последний из которых - и есть исполняемый файл, который нам нужен! Незадача в том, что мы пишем не приложение под DOS, а операционную систему, которая должна будет работать на голом железе. exe-формат содержит таблицы релокации и всякие прочие данные, которые нам будут мешать. В нашем исполняемом файле должны быть только инструкции программы и больше ничего. К счастью, и тут есть готовое решение. Пишите в командной строке exe2bin boot.exe и жмите enter. Появился файл boot.bin. Проверьте его размер, он должен занимать ровно 2 байта, по одному байту на инструкции cli и hlt. Успех!


В качестве ДЗ предлагаю всем желающим самостоятельно загнать файл в первый сектор дискеты, вся нужная информация в тексте статьи есть.

Развернуть

coub программирование geek 

Развернуть

программирование geek Collapse OS 

Создана ОС для самодельных компьютеров на случай апокалипсиса

Операционная система Collapse OS, разработанная Вирджилом Дупрасом, способна работать на микроконтроллерах Z80. Их широкое распространение за счет использования в самой разной технике, по мнению автора проекта, позволит людям, пережившим апокалипсис (коллапс, как его называет Дупрас), собирать ПК на их основе, и работать на них посредством Collapse OS. Система наделена базовыми функциями, в том числе работой с внешними накопителями и редактированием текстовых файлов, и Дупрас призывает сторонних разработчиков присоединиться к ее дальнейшему развитию.


НИМИ11 НИМИ й„;.¡1« ч«««!2*. ■\ - И»; Шg ^ Ш • I 1 ■Щг ЕЯ «Ик\ Ч7Т Й1 1 53Г^ ч • |Ч47 ■л,программирование,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор,Collapse OS


Постапокалиптическая ОС


Разработчик программного обеспечения Вирджил Дупрас (Virgil Dupras) создал операционную систему Collapse OS, способную, по его мнению, пережить апокалипсис (сам Дупрас называет возможное будущее человечества «коллапсом», collapse). Он утверждает, что его ОС сможет работать на компьютерах, которые люди будут собирать из компонентов, найденных ими на свалках.

Для распространения Collapse OS Дупрас создал сайт collapseos.org, на котором привел объяснение, зачем такая ОС вообще нужна. По мнению разработчика, в обозримом будущем, примерно к 2030 г., мировые цепочки поставок себя исчерпают и перестанут быть эффективными, а то и вовсе разорвутся. Это приведет сперва к резкому сокращению, а затем и вовсе к прекращению производства микроэлектроники, и в итоге людям придется искать компоненты для новых ПК в буквальном смысле в горах мусора.

Системные требования ОС на 10 октября 2019 г. опубликованы не были. Разработчик также не уточнил, на каком ядре базируется Collapse OS.



Главное преимущество ОС


Вирджил Дупрас подчеркнул, что главной проблемой постапокалиптических пользователей его ОС может стать поиск микроконтроллеров. Да, их можно раздобыть на материнских платах компьютеров и других устройств, но перепрограммировать их под новые нужды уже не получится. «Через несколько десятилетий компьютеры придут в такое состояние, что уже не будут подлежать ремонту. Люди больше не смогут программировать микроконтроллеры», - отметил Дупрас на посвященном его ОС сайте.

В этом, по словам разработчика, и заключается главная особенность Collapse OS, которая позволит ей пережить апокалипсис. Система может работать даже на восьмибитных микропроцессорах Z80, использующихся в сравнительно простых с точки зрения электроники устройствах. Их можно обнаружить в кассовых аппаратах и калькуляторах.

Дупрас специально готовил Collapse OS к работе на этих микропроцессорах – он уверен, что именно они, ввиду широкого их распространения, будут чаще всего попадаться «копателям», в отличие от 16- и 32-битных микросхем.



Что умеет Collapse OS


Проект Collapse OS еще находится в разработке, но на момент публикации материала Вирджил Дупрас интегрировал в нее массу базовых функций. За 10 лет до предполагаемой Дупрасом даты апокалипсиса его система научилась редактировать текстовые файлы, читать данные с внешнего накопителя и копировать информацию на носители, воспроизводить себя и заодно компилировать исходные файлы на языке ассемблера для целого спектра центральных процессоров и микроконтроллеров. Также система поддерживает ряд интерфейсов и привычное устройство ввода – клавиатуру. Разработчиком заявлена поддержка карт памяти стандарта SD.

Работоспособность системы

Collapse OS находится на одной из ранних стадий разработки, но, по заверениям Дупраса, она вполне может запускаться на самосборных ПК самого базового уровня. Актуальную версию своей СО он запустил на компьютере домашней сборки на базе микроконтроллеров Z80 под названием RC2014.

Девелопер отметил, что в теории Collapse OS должна запускаться даже на игровых приставках. В качестве примера он привел классическую консоль четвертого поколения Sega Genesis, выпущенную в 1988 г. и известную в России под названием Mega Drive. Управлять системой, запущенной на этой приставке, можно через комплектный джойстик или клавиатуру со специальным переходником.



Дальнейшие планы и поиск единомышленников


Базовыми функциями Дупрас ограничиваться не станет, так как его цель заключается в создании полноценной многогранной ОС, которой должно быть удобно пользоваться. Он разместил открытый исходный код на портале Github (принадлежит Microsoft) и призвал сторонних разработчиков присоединиться к работе над его проектом.

«Я думаю, что смогу закончить разработку сам, но мне кажется, что было бы веселее работать с парой других специалистов. Участие в проекте требует очень специфического набора склонностей (вера в апокалипсис) и навыков (электроника и сборка z80). Я думаю, на свете есть не очень много людей, соответствующих этим требованиям, но если таковые все же есть, я бы хотел их найти», – сказал Вирджил Дупрас.

Согласно «дорожной карте» разработки, в планы Дупраса на ближайшее будущее входит запуск Collapse OS на программируемых графических калькуляторах TI-83+ (выпущен в 1999 г.) TI-84+ (2004 г.), разработанных компанией Texas Instruments. Следующим шагом станет запуск системы на настольном микрокомпьютере TRS-80 model 1 образца начала 80 годов XX века. Также в систему будет добавлена поддержка широкого спектра LCD-дисплеев и панелей на электронных чернилах (Е Ink) и различных дискет, включая 3,5-дюймовые.


Развернуть

программирование geek it-юмор 

Программист неудачник Bug Error 404 Bug Error Bug Успешный программист,программирование,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор,it-юмор
Развернуть

программирование geek python помощь песочница пидоры помогите 

Помогите чайнику

Дана задача с такими условиями
Посчитать сумму чисел от 1 до N. Значение N по модулю не превышает 10^{9} .
Входные данные содержат одно целое число — N.
Нужно вывести целое число — сумму чисел от 1 до N.

Мой "код" на Python выглядит так:

N=int(input())
d=1
rez=((2+d*(N-1))*N)/2
print(int(rez))

Из 8 вариантов переменной N, c 5 получается правильный результат, а два выдают неправильный. В чём может быть проблема?
Развернуть

программирование geek 

программирование,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор
Развернуть

it-юмор geek программирование профессиональный юмор много букв перевел сам 

//Простые вопросы из разных собеседований:


/******************/
/* СТРАННОСТИ КОДА */
/******************/


//Задача: Найдите все нечётные числа в промежутке от 0 до 100
for(int i = 1; i >=1 100; i++) {
i = i%1;
int result;
if (result = 1) { int output = result; }
if (result = 0) { i++; }
}
//Я заставил претендента внимательно пройтись по коду... дважды. Это не опечатки, ни "i%1", ни одиночные "=" в последних двух строчках. Это претендент у которого 8 лет опыта.


//Задача: Найдите все нечётные числа в промежутке от 0 до 100
let number = 100;
for(let i = 0; i < number; i++) {
console.log(number[i]);
}
//Претендент сказал, что находить только нечётные числа слишком сложно и таким способом мы находим ВСЕ числа, так что это должно сработать!


//Задача: Найдите все нечётные числа в промежутке от 0 до 100
⟨html⟩
1,3,5...99
⟨/html⟩
//Я сказал: "Это работает, но что если нам понадобится найти все нечётные числа между 0 и 1 миллионом?"
//Претендент, немного подумав: "Тогда писать придётся очень долго."


//Задача: Найдите все нечётные числа в промежутке от 0 до 100
int a = [10];
for(i = 0; i < 10; i++) {
if( (a[i]<0) || (a[i] > 100) ){
exit(0);
}
}
//Думайте сами, что это должно значить. Я и понятия не имею, что это должно делать.


//Задача: Найдите все нечётные числа в промежутке от 0 до 100
function (0,100)
echo odd
//Я спросил претендента, что код вообще должен был делать, на что он ответил: "Я не хочу отвечать как-то помимо этого."
//Я ещё раз попробовал. Тот же претендент, следующая задача: Найдите сумму всех целых чисел в промежутке от 0 до 100
echo sum(0,100)
//Я: "Ладно, и если мы хотим сделать это на Javascript?"
echo sum(0,100)
sum++
//Это был самый опытный претендент, с 19 годами профессионального стажа. Девятнадцать, это не опечатка.
Этот претендент программировал профессионально до того как многие из вас ещё родились.


//Задача: Предположим у вас два целых числа, x и y, y больше чем x. Сложите все числа от x до y.
Пример: Если x равно 1, и y равно 5, тогда сумма будет 1+2+3+4+5.

int num1 = x;
int num2 = y;
for( int i = num1; i>= num2; i++) {
int result = i + (i+1);
i++;
}
//Что тут происходит? Ваша догадка будет хороша настолько же насколько и моя. Претендент и понятия не имелчто этот код должен делать.


//Задача: Сосчитайте от 700 до 200 с помощью уменьшения на 13.
for(int x = 700; x < 700; x--) {
int start = 700;
int increment = 13;
double answer;
answer = start - increment;
if(answer < 0) {
break;
}
}
//Из любопытства я спросил: "А почему ответ это число двойной точности?"
//Претендент: "Потому что надо как-то содержать значение взятое из _двух_ переменных."


//Сделайте стандартную колоду карт. Перемешайте колоду и вытащите случайно две карты. Покажите две карты.
let cards = [
['1','2','3','4','5','6','7','8','9','10','11','12','13'],
['a1','a2','a3','a4','a5','a6','a7','a8','a9','a10','a11','a12','a13']
['b1','b2','b3','b4','b5','b6','b7','b8','b9','b10','b11','b12','b13']
['c1','c2','c3','c4','c5','c6','c7','c8','c9','c10','c11','c12','c13']
];
for(let count = 0; count <=1 ; count ++ ) {
let firstLoop = Math.random(0,3);
let secondLoop = Math. random(0,12);
console.log(cards[firstLoop] [secondLoop]);
}
//Это было медленно и вымученно написано прямо на моих глазах в codeshare. Я хотел спросить почему цикл был использовандля второй части, в которой было всего лишь две карты, в то время как ни одного цикла не было использованодля 52 карт... но я так и не решился.


//Задача: Найдите все нечётные числа между x и y, предпологая что y больше чем x.
var arr = [1, 3, 5, 7, 9];
var numLength = x.length;
var x = x.substr(numLength -1, 1);
var parsed = JSON.parse(x);

for (var i = 0; i < arr.length; i++) {
if(arr[i] === parsed) {
console.log('Starting with an odd number');
for (var i = x; i <= y; i+=2) {
console.log(i)
}
} else {
console.log('Starting with and even number');
var bb = x +1;
for (var i = x; i <= y; i+=2) {
console.log(i)
}
}
};
//Если это вас озадачило, то позвольте объяснить. Претендент берёт последнюю цифру числа x, и проверяет
равна ли она 1/3/5/7/9. Если это так, то к нему добавляется 2, пока вы не достигните числа y. Иначе добавляется 1, чтобы сделать число нечётным и уж потом добавляются 2 до тех пор пока не достигнет y.
//Несмотря на мелкие ошибки в коде, логика была вполне функциональной, но неэффективной до ужаса.


//Сделайте стандартную колоду карт. Перемешайте колоду и вытащите случайно две карты. Покажите две карты.
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'];
var arr1 = [];
var arr2 = [];
var arr3 = [];
var arr4 = [];

var one = 'D';
var two = 'H';
var three = 'C';
var four = 'S';

var position = 1;

for (var i = 0; i < arr.length; i++) {
if (position === 1) {
x = one;
} else if (position === 2) {
x = two;
} else if (position === 3) {
x = three;
} else if (position === 4) {
x = four;
}
arr[position].push(arr[i] * x);
position += 1;
};

var fr = math.random();
var finalArr = [];
var newArr = [];
var eeArr = [];

for (var 1 = 0; i < finalArr.length; i++) {
var fr = math.random();
newArr.push({name: finalArr[i], value: fr});
eeArr.push(fr);
};

eeArr.sort();
var yyArr = [];

for (var i = 0; i < newArr.length; i++) {
var index = eeArr.indexOf(newArr[i].value);
newArr.newPos = index;
};
//Узрите этот шедевр! Читайте его медленно и вкушайте каждую строчку, такая красота приходит только раз в жизни.


/*********************/
/* ИНТЕРЕСНЫЕ ЛИЧНОСТИ */
/*********************/


//Задача: Напишите код, который проверяет является ли число нечётным.
//Претендент: Ладно, дайте мне минутку. *звуки печатания*
//Претендент: Мой ответ *голос заметно меняется с говорящего на читающий*

if((i%2)==1) cout⟨⟨i;
//Я *подозреваю, что претендент просто нагуглил это*: Ладно, а можете ли вы объяснить что эта строчка обозначает?
//Претендент: Это означает, что мы находим процент от i в 2.
//Я: Процент? Зачем мы находим "процент"?
//Претендент: Ээээ... Я хотел... эмм.... уууу....
//Я *с приятным голоском, но злобными намерениями*: Я понимаю, что это сложно. Вы когда-нибудь использовали этот
"процентный" оператор раньше?
//Претендент *с заметным облегчением*: Нет, никогда! Это мой первый раз когда я его использовал!
//Я: ...
//Претендент *сообразил что он только что сказал*: Я имел в виду... имел в виду...


//Задача: Найдите все нечётные числа в промежутке от 0 до 100.
//Претендент: Я прошу прощения, но я забыл точную формулу для нахождения нечётных чисел.
//Я: Нечётные числа это 1, 3, 5, и так далее.
//Претендент: Я понял, но я не могу вспомнить формулу, соответственно я не смогу найти эти числа.


//Вопрос: Можете рассказать мне об истории своей профессии?
//Претендент: Я выкладываюсь по полной, чтобы быть уверенным, что в моём коде не так много ошибок, перед тем как он заработает.


//Задача: Сосчитайте от 700 до 200 с помощью уменьшения на 13.
*совсем непонятный код*
//Я: Вы думаете, что оно заработает если я это запущу?
//Претендент: Честно говоря, нет. Я не думаю, что это вообще заработает.
//Я: Почему же?
//Претендент: Там куча синтаксических ошибок.
//Я: Ну а что если мы их исправим, тогда оно заработает?
//Претендент: Все равно не.
//Я: С чего же это?
//Претендент: Там ещё и логические ошибки.


//Претендент: Я программировал на протяжение 10 лет. 5 лет в качестве хобби и 5 профессионально. Я знаю PHP, Ruby,
JS (включая node и angular) , MongoDB, MySQL, PostgreSQL, и много чего ещё.
//Я: Чудесно. Ради чего вы используете свои навыки?
//Претендент: В течение 5 лет, в те, что я работал профессионально, я работал в 4 компаниях в качестве вебдева.
У меня также множество сайтов, созданных с нуля. Некоторые разрабатывал в одиночку, а некоторые находясь в команде
Я занимался одновременно фронт-энд и бэк-энд разработкой. Я готов встретиться с любыми трудностями!
//Я: Ладно. Используя язык на ваш выбор, сможете ли вы найти все нечётные числа между 0 и 100?
//Претендент: *пытается где-то 3 минуты*
//Претендент: Я не могу сделать этого. Мои навыки в математике не столь сильны.


//Эта ситуация произошла с _двумя_ разными претендентами. Их ответы звучали по разному, но посыл был один и тот же.
//Я: Ладно, я хочу дать вам парочку задач по программированию. У вас есть с собой бумага и ручка?
//Претендент: Конечно.
//Я: Хорошо. Используя предпочитаемый вами язык программирования, найдите все нечётные числа между 0 и 100.
//Претендент: Что, прям на бумаге?
//Я: Да. Напишите ваш код сюда и когда закончите зачитайте его мне и тогда мы перейдём дальше.
//Претендент: Но это же невозможно! Как я могу написать код не используя компьютер?


//Задача: Найдите все нечётные числа между 0 и 100. Заметка: это было собеседование по телефону.
//Претендент: А гуглить можно?
//Я: Я пытаюсь оценить ваши умения по программированию, а не по использованию поиска в Google.
//Претендент: Хах, верно! Но если я использую Google, то как вы узнаете?
//Я: Я дам вам похожую задачу, когда наше собеседование будет происходить с глазу на глаз.
//Претендент: Ну тогда я не могу.


//Худшее из худших собеседований, что у меня было и которое действительно заставило меня почувствовать себя плохо, было именно этим. Я пытался запечатлить _настрой_ того, как это проходило, так как мне не довелось увидеть хоть какой-нибудь код.
Некоторые из действий, которые я описываю, это то, что предположительно происходило на другом конце телефона, основываясь на том, что я услышал.
//Я: Здравствуйте! Благодарю, что позволили мне провести собеседование с вами. Вы подготовились? Готовы?
//Претендент: Я готов.
//Я: Отлично! Давайте начнём. *вступление и немного расслабляющей беседы, претендент очень клёвый* Ладно, так
ваше резюме перечисляет около 5 разных языков программирования. Вы хороши во всех из них?
//Претендент: Я хорош в Javascript и PHP, и неплох в Java.
//Я: Хорошо, какой ваш любимый язык?
//Претендент: Конечно же Javascript! Я много проработал на Javascript!
//Я: Это хорошо. Могу ли я дать вам пару задач по Javascript?
//Претендент: *молчит 10 секунд*
//Я: Алло? Вы всё ещё на линии?
//Претендент: Да, да. Я всё ещё на линии!
//Я: Так не возражаете если я дам парочку задач по Javascript?
//Претендент *с нервной дрожью в голосу*: Да, я думаю, я могу попробовать приложить все усилия, чтобы ответить.
//Я *вспоминая себя, не пытаюсь уничижить претендента*: Хорошо, так вы знаете что такое нечётные числа, верно? Ну 1, 3, 5 и так далее?
//Претендент *голос слегка подуспокоился*: Да! Да, я знаю что такое нечётные числа!
//Я: Ладно, моя задача такова. Используя ваш любимый язык, Javascript, сможете ли вы найти все нечётные числа в промежутке от 0 до 100?
//Претендент: *слышимые тяжёлые вздохи, но никаких слов на протяжении 10 секунд*
//Я: Алло?
//Претендент *говорит с большим усилием, голос в сейчас-точно-запаникует режиме*: Я э... Я... нахожу... а... нечётные...
Я... нечётные числа... ухх...
//Я *стараюсь немного проконтроллировать ситуацию*: Всё хорошо. Почему бы вам не потратить несколько минут, чтобы
записать ответ, и уж тогда зачтёте мне, когда закончите и будете готовы?
//Претендент *весь взволнованный и бездыханный*: ДА! Да, да! Я сначала запишу. Ладно! Ладно, я сейчас записываю это!
//На этом моменте, я убедился, что претендент действительно отложил трубку и начал писать. Я мог слышать лёгкие поскрябывания около двух минут, затем более жёсткие поскрябывания в течение минуты, а затем *чёёёёёёрк* *чёёёёёрк* звуки, будто бы кто-то расчерчивает кучу линий на бумаге. Затем наступила минута очень тяжёлого, очень быстрого дыхания, но никаких звуков записывания.
//Претендент *голос совершенно сломлен*: Я думаю я... я... нечётные числа... ааа... Я думаю... эммм...
//Я *совершаю критическую ошибку в суждении вопросом*: Там всё в порядке?
//Претендент: Нет. Нет! НЕТ! Я не могу! Это... не! Как это... нечётные... нечётные числа? Я... Я не могу делать это.
Я не могу это БОЛЬШЕ делать! Простите меня, мне надо идти. Надо идти, сейчас же! Прямо сейчас! Мне НУЖНО идти!
//Я: Ладно, без проблем. Может мы сможем продолжить это попозже.
//Претендент: Да, может завтра. *пииип*
//Претендент *в спокойном, вежливом электронном сообщении, гораздо позже в тот же день*: В этот момент я сожалею, что мне придётся отказаться от собеседования. Тем не менее может найдётся подходящее время когда-нибудь в будущем.

Развернуть
В этом разделе мы собираем самые смешные приколы (комиксы и картинки) по теме программирование (+502 картинки, рейтинг 5,369.8 - программирование)