Результаты поиска по запросу

Дополнительные фильтры
Теги:
новый тег
Автор поста
Рейтинг поста:
-∞050100200300400+
Найдено: 61
Сортировка:

Урок ОСдева №6: минидрайвер флоппи-привода.

В предыдущем посте мы вычислили значения, нужные для работы с FAT12. Пора писать драйвер!
Начнём с постановки задачи. Что должен уметь драйвер FAT12 для первичного загрузчика?
Очень просто: загружать файлы. Больше ничего.

Для этого мы будем использовать прерывание BIOS. Кстати, про прерывания мы ещё не
говорили. Пока давайте считать, что это функции, предоставляемые для нашего удобства
BIOS. Подробно об этом говорить будем позднее, так как тема очень большая. Кроме того,
позже мы напишем полноценный драйвер, который будет работать с флоппи-приводом напрямую,
без посредства BIOS. Сделать это прямо сейчас мы не можем из-за ограничения по размеру
программы: первичный загрузчик должен занимать не больше 512 байт - полновесный драйвер
флоппи-привода в такой объём не влезет.

Прерывания вызываются командой int, после которой идёт номер. Стандартное
прерывание BIOS для работы с дисками - 13h. Соответственно, команда выглядит так: int 13h.
Как правило прерывания требуют передачи параметров через определённые регистры. Так,
int 13h нужен номер функции в AH. Прерывание 13h - это целый набор функций для работы
с различными видами съёмных и постоянных носителей. Нас интересует функция 2, чтение
секторов с диска. Она в свою очередь требует указать количество считываемых секторов в AL,
номер цилиндра в CH, номер начального сектора в CL, головку в DH, привод в DL и адрес
в памяти, куда будут считаны данные, в ES:BX.

Итак, ещё раз: в нашем случае int 13h вызывается со следующими параметрами:
AH = 2 (номер функции)
AL = число секторов
CH = номер цилиндра
CL = номер начального сектора
DH = номер головки
DL = 0 (номер привода)
ES:BX = сегмент:смещение области для загрузки

Прерывание int 13h у нас будет вызываться в процедуре read_sectors. Этой последней нужно
будет передать три параметра: LBA в AX, число секторов в CX и адрес для загрузки в ES:BX.
Что такое LBA мы уже знаем: это более современная линейная схема адресации секторов. К сожалению,
прерывание 13h работает с устаревшим форматом CHS, так что придётся делать конверсию
внутри процедуры.

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

(имя процедуры) proc
     (тело процедуры)
(имя процедуры) endp

Обе эти инструкции нужны только компилятору и в исполняемый файл не попадают. В случае
"плоского" бинарного файла процедура окажется именно там, где она расположена в тексте
программы - никакой отдельной области памяти для неё создаваться не будет. Процедура
вызывается инструкцией call (имя процедуры). Предварительно, конечно, надо поместить
нужные параметры в нужные регистры.

Теперь немного о внутреннем устройстве FAT12. Для того, чтобы загрузить файл в память,
нам нужно проделать следующие вещи:

1. Загрузить в память Корневую Директорию диска.
2. Найти в КД запись, соответствующую файлу.
3. Считать из записи номер первого кластера файла в FAT.
4. Загрузить в память FAT.
5. Загрузить в память цепочку кластеров, которую занимает файл.

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

.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_OEMnamedb 'BOOTDISK';0-7. Имя производителя. Может быть любым.
     BPB_bytespersecdw 512;8-9. Размер сектора в байтаx.
     BPB_secperclustdb 1;10. Количество секторов в кластере.
     BPB_reserveddw 1;11-12. Число зарезервированныx секторов (1, загрузочный).
     BPB_numFATsdb 2;13. Число FAT.
     BPB_RDentriesdw 224;14-15. Число записей Корневой Директории.
     BPB_sectotaldw 2880;16-17. Всего секторов на носителе.
     BPB_mediatypedb 0F0h;18. Тип носителя. 0F0 - 3,5-дюймовая дискета с 18 секторами в дорожке.
     BPB_FATsizedw 9;19-20. Размер FAT в сектораx.
     BPB_secpertrackdw 18;21-22. Число секторов в дорожке.
     BPB_numheadsdw 2;23-24. Число головок (поверxностей).
     BPB_hiddensecdd 0;25-28. Число скрытыx секторов перед загрузочным.
     BPB_sectotal32dd 0;29-32. Число секторов, если иx больше 65535.

     ;===============================================;
     ;Расширенный блок параметров BIOS, 26 байт.                                         ;
     ;Этот раздел используется в DOS 4.0.                                                       ;
     ;===============================================;
     EBPB_drivenumdb 0;0. Номер привода.
     EBPB_NTflagsdb 0;1. Флаги в Windows NT. Бит 0 - флаг необxодимости проверки диска. Бит 1 - флаг необходимости диагностики             поверхности.
     EBPB_extsigndb 29h;2. Признак расшренного BPB по версии DOS 4.0.
     EBPB_volIDdd 0;3-6. "Серийный номер". Любое случайное число или ноль, без разницы.
     EBPB_vollabeldb 'BOOTLOADER ';7-17. Название диска. Устарело.
     EBPB_filesysdb 'FAT12   ';18-25. Имя файловой системы.



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

;Шаг 1. Исправить значения сегментных регистров.
execute:
                    ;DS, ES, FS, GS.
                              mov ax,07C0h;Сегмент загрузчика.
                              mov ds,ax;Поместить это значение во все сегментные регистры.
                              mov es,ax
                              mov fs,ax
                              mov gs,ax

                    ;СЕГМЕНТ СТЕКА.
                              cli;Запретить прерывания перед переносом стека.
                              mov ss,ax;Поместить в SS адрес сегмента загрузчика.
                              mov sp,0FFFFh;Указатель стека - на конец сегмента.
                              sti;Разрешить прерывания.

                    ;СЕГМЕНТ КОДА.
                              push ax;Поместить в стек сегмент.
                              mov ax,offset jump;Указатель на инструкцию после retf.
                              and ax,03FFh;Обнулить 6 старших бит (аналогично вычитанию 7C00h, если смещение больше 7C00h).
                              push ax;Поместить в стек смещение.
                              retf;Дальний возврат для смены CS.

jump:                     mov byte ptr EBPB_drivenum,dl;BIOS должен вернуть номер загрузочного устройства в DL. Сохранить его в BPB.

                             mov ax,BPB_RDentries;Число записей КД
                             shl ax,5;*32 (размер записи в байтах) = размер КД в байтах.
                             div BPB_bytespersec;AX/размер сектора в байтах = размер КД в секторах.
                             mov cx,ax;Поместить его в CX (будет счетчиком для загрузки КД).
                             xor ax,ax;Обнулить AX.
                             mov al,byte ptr BPB_numFATs;Число FAT
                             mul BPB_FATsize;*размер FAT в секторах = общий размер всех FAT в секторах.
                             mov total_FATs_size,ax;Сохранить результат в переменной.
                             add ax,BPB_reserved;AX+число зарезервированных секторов = стартовый сектор КД.
                             mov datasector,ax;Стартовый сектор КД + размер КД в секторах =
                             add datasector,cx;= стартовый сектор области данных. Сохранить его в переменной.

                             cli
                             hlt

;ПЕРЕМЕННЫЕ==================================================================;
     total_FATs_size dw ?;Переменная для хранения общего размера FAT в секторах.
     datasector dw ?;Переменная для хранения номера стартового сектора области данных.

     org 1FEh;Заполняет память нулями до 511-го байта.
     dw 0AA55h;Байты 511 и 512. Признак загрузочного сектора.

CSEG ends
end begin

Добавим следующий код между add datasector,cx и cli:

                              mov bx,0200h
                              call read_sectors

Теперь загрузчик перед тем, как остановить процессор, вызывает процедуру read_sectors. Но передали ли
мы все нужные параметры? Напоминаю, в AX должен быть LBA первого загружаемого сектора. И в AX у нас
как раз номер первого сектора КД! CX должен содержать число загружаемых секторов. И, большая удача,
именно оно в CX и есть. Сегмент в ES у нас уже установлен, а смещение в BX мы явно задали перед
вызовом процедуры. Всё отлично! Осталась самая малость: написать саму процедуру.

Где-нибудь между hlt и переменными сделайте шаблон:

read_sectors proc
                              ;ПУСТО
read_sectors endp

Алгоритм работы в общих чертах представляется нам как-то так: перевести LBA в CHS, установить
значения регистров для int 13h, вызвать прерывание... Профит! Не будем медлить. Пишите:

read_sectors proc
                              div BPB_secpertrack;Разделить LBA в AX на число секторов в дорожке.
                              inc dl;Остаток + 1 = номер сектора, т.к. нумерация с 1.
                              mov cl,dl;Поместить номер сектора в CL для int 13h
                              xor dx,dx;Обнулить перед делением.
                              div BPB_numheads;Разделить результат на число головок.
                              shl dx,8;Остаток = номер головки, сдвинуть его в DH.
                              mov ch,al;Частное = номер дорожки, его поместить в CH для int 13h.
                              mov dl,0;DL = 0, флоппи-диск А.
                              mov ax,0201h;Функция 2 int 13h, загрузка секторов с диска. В AL - число секторов.
                              int 13h
                              ret
read_sectors endp

Поздравим себя, на сегодня дело сделано. Шутка. Включаем голову. Во-первых, носители - а особенно
флоппи-диски! - имеют свойство не читаться с первого раза. На этот случай int 13h возвращает
статус операции в CF: если флаг обнулён - всё хорошо, если установлен - была ошибка чтения.
Во-вторых, даже в случае успеха мы загрузили только один сектор: значение в CX до сих пор не
используется. Начнём со второй проблемы:

read_sectors proc
                              mov bp,BPB_bytespersec;Размер сектора, понадобится внутри цикла.

main:                      pusha;Сохранить регистры общего назначения.
                              div BPB_secpertrack;Разделить LBA в AX на число секторов в дорожке.
                              inc dl;Остаток + 1 = номер сектора, т.к. нумерация с 1.
                              mov cl,dl;Поместить номер сектора в CL для int 13h
                              xor dx,dx;Обнулить перед делением.
                              div BPB_numheads;Разделить результат на число головок.
                              shl dx,8;Остаток = номер головки, сдвинуть его в DH.
                              mov ch,al;Частное = номер дорожки, его поместить в CH для int 13h.
                              mov dl,0;DL = 0, флоппи-диск А.
                              mov ax,0201h;Функция 2 int 13h, загрузка секторов с диска. В AL - число секторов.
                              int 13h
                              popa;Восстановить сохраненные регистры.

                              inc ax;Увеличить LBA.
                              add bx,bp;Сместить указатель загрузки на длину сектора.
                              loop main;Продолжить цикл.
                              ret;Завершить процедуру.
read_sectors endp

Процедура теперь организована в виде цикла со счётчиком в CX. Команда loop возвращает
указатель инструкции к указанной метке при условии, что CX не равен 0. CX при этом
уменьшается на 1. Обратите внимание, что в начале процедуры мы помещаем в BP
размер сектора в байтах, а блок кода из прошлой версии теперь обрамляется инструкциями
pusha и popa. Последнее нужно для того, чтобы после выполнения шага цикла вернуть вводные
значения в соответствующие регистры. Перед началом следующего шага LBA в AX увеличивается
на 1, а смещение области загрузки увеличивается на размер сектора. Время разобраться
с возможными ошибками чтения.

read_sectors proc
                              mov bp,BPB_bytespersec;Размер сектора, понадобится внутри цикла.
main:                      mov di,5;Число попыток чтения в случае ошибки.

load_sector:            pusha;Сохранить регистры общего назначения.
                              div BPB_secpertrack;Разделить LBA в AX на число секторов в дорожке.
                             inc dl;Остаток + 1 = номер сектора, т.к. нумерация с 1.
                             mov cl,dl;Поместить номер сектора в CL для int 13h
                             xor dx,dx;Обнулить перед делением.
                             div BPB_numheads;Разделить результат на число головок.
                             shl dx,8;Остаток = номер головки, сдвинуть его в DH.
                             mov ch,al;Частное = номер дорожки, его поместить в CH для int 13h.
                             mov dl,0;DL = 0, флоппи-диск А.
                             mov ax,0201h;Функция 2 int 13h, загрузка секторов с диска. В AL - число секторов.
                             int 13h
                             jnc sector_loaded;Если CF не установлен, сектор загружен успешно.

                             xor ax,ax;Функция 0 int 13h, сброс головок.
                             xor dl,dl;DL = номер привода, флоппи-диск А.
                             int 13h
                             popa;Восстановить сохраненные регистры.
                             sub di,1;Уменьшить счетчик попыток.
                             jnz load_sector;Если счетчик не обнулился, перейти к загрузке сектора.
                             ret

sector_loaded:        popa;Восстановить сохраненные регистры.
                             inc ax;Увеличить LBA.
                             add bx,bp;Сместить указатель загрузки на длину сектора.
                             loop main;Продолжить цикл.
                             ret;Завершить процедуру.
read_sectors endp

После вызова прерывания у нас теперь стоит jnc sector_loaded. Эта инструкция делает переход к
указанной метке, но только если флаг CF не установлен. Таким образом, к инициализации переменных
для следующего шага цикла мы попадаем только если предыдущий завершился успешно. Если же CF
установлен, начинается обработка ошибки. Функция 0 int 13h возвращает читающие головки привода к
0 сектору 0 дорожки, это должно уменьшить вероятность ошибки при следующем чтении. После
этого мы уменьшаем счётчик попыток на 1 и, если он не обнулился (инструкция jnz), делаем повторную
попытку. Теперь процедура почти готова. Остались финальные штрихи. Во-первых, сброс головок
тоже может пойти с ошибкой, и для пущей уверенности операцию стоит повторить несколько раз.
Во-вторых, было бы неплохо, если бы процедура обрабатывала ситуацию, когда все попытки чтения
завершились неудачей. Сейчас она просто завершается, как и в случае успеха. Начнём опять со второй задачи.

read_sectors proc
                             mov bp,BPB_bytespersec;Размер сектора, понадобится внутри цикла.
main:                     mov di,5;Число попыток чтения в случае ошибки.

load_sector:            pusha;Сохранить регистры общего назначения.
                             div BPB_secpertrack;Разделить LBA в AX на число секторов в дорожке.
                             inc dl;Остаток + 1 = номер сектора, т.к. нумерация с 1.
                             mov cl,dl;Поместить номер сектора в CL для int 13h
                             xor dx,dx;Обнулить перед делением.
                             div BPB_numheads;Разделить результат на число головок.
                             shl dx,8;Остаток = номер головки, сдвинуть его в DH.
                             test ah,ah;Проверить AH. Если больше нуля, что-то пошло не так.
                             jnz error;Т.к. на диске не может быть больше 255 дорожек, завершить с ошибкой.
                             mov ch,al;Частное = номер дорожки, его поместить в CH для int 13h.
                             mov dl,0;DL = 0, флоппи-диск А.
                             mov ax,0201h;Функция 2 int 13h, загрузка секторов с диска. В AL - число секторов.
                             int 13h
                             jnc sector_loaded;Если CF не установлен, сектор загружен успешно.

                             xor ax,ax;Функция 0 int 13h, сброс головок.
                             xor dl,dl;DL = номер привода, флоппи-диск А.
                             int 13h
                             popa;Восстановить сохраненные регистры.
                             sub di,1;Уменьшить счетчик попыток.
                             jnz load_sector;Если счетчик не обнулился, перейти к загрузке сектора.
                             ret

sector_loaded:        popa;Восстановить сохраненные регистры.
                             inc ax;Увеличить LBA.
                             add bx,bp;Сместить указатель загрузки на длину сектора.
                             loop main;Продолжить цикл.
                             ret;Завершить процедуру.

error:                     popa;Попытки кончились. Восстановить сохраненные регистры.
                             mov ax,07c0h;Сегмент загрузчика
                             mov es,ax;поместить в ES для int 10h.
                             mov ah,03h;Функция 3 прерывания 10h, получить позицию курсора в DH, DL.
                             xor bh,bh;BH = номер видеостраницы.
                             int 10h;DH = строка, DL = столбец.

                             mov ax,1300h;Функция 19 прерывания 10h, вывод строки. AL = режим вывода.
                             mov bx,0007h;BH = страница, BL = атрибуты символа.
                             mov cx,0010h;CX = длина строки.
                             mov bp,offset msg_DRE;ES:BP = указатель на строку.
                             int 10h;Вывести строку в DH,DL без обновления курсора.
                             cli;Запретить прерывания
                             hlt;и остановить процессор.
read_sectors endp

В первом сегменте кода после shl dx,8 у нас появилась проверка на ошибочность результата. Она
явно избыточна, но пусть будет. Если в результате деления у нас получился номер дорожки больше
255, то что-то пошло не так. Программа переходит к метке error, после которой происходит
следующее: первый сегмент кода опустошает стек, а потом с помощью прерывания 10h
(прерывание для работы с дисплеем) считывает положение курсора на экране, а второй выводит сообщение
об ошибке и останавливает процессор. Строку с сообщением можно хранить рядом состальными переменными,
выглядит она так:

msg_DRE db 'Disk read error.' ;Сообщение об ошибке чтения с диска.

Осталась самая малость. Обработать повторные попытки сброса головок. Финальная версия прецедуры
будет выглядеть так:

read_sectors proc
                             mov bp,BPB_bytespersec;Размер сектора, понадобится внутри цикла.
main:                     mov di,5;Число попыток чтения в случае ошибки.

load_sector:            pusha;Сохранить регистры общего назначения.
                             div BPB_secpertrack;Разделить LBA в AX на число секторов в дорожке.
                             inc dl;Остаток + 1 = номер сектора, т.к. нумерация с 1.
                             mov cl,dl;Поместить номер сектора в CL для int 13h
                             xor dx,dx;Обнулить перед делением.
                             div BPB_numheads;Разделить результат на число головок.
                             shl dx,8;Остаток = номер головки, сдвинуть его в DH.
                             test ah,ah;Проверить AH. Если больше нуля, что-то пошло не так.
                             jnz error;Т.к. на диске не может быть больше 255 дорожек, завершить с ошибкой.
                             mov ch,al;Частное = номер дорожки, его поместить в CH для int 13h.
                             mov dl,0;DL = 0, флоппи-диск А.
                             mov ax,0201h;Функция 2 int 13h, загрузка секторов с диска. В AL - число секторов.
                             int 13h
                             jnc sector_loaded;Если CF не установлен, сектор загружен успешно.

                             mov cx,0003h;Счетчик попыток сброса головок.
reset:                     xor ax,ax;Функция 0 int 13h, сброс головок.
                             xor dl,dl;DL = номер привода, флоппи-диск А.
                             int 13h
                             jnc reload;Если не было ошибки - повторить попытку чтения сектора.
                             loop reset;Попробовать сбросить головки еще раз, если CX не обнулился.
error:                     popa;Попытки кончились. Восстановить сохраненные регистры.
                             jmp short disk_read_error;Сообщить об ошибке и завершить программу.

reload:                   popa;Восстановить сохраненные регистры.
                             sub di,1;Уменьшить счетчик попыток.
                             jnz load_sector;Если счетчик не обнулился, перейти к загрузке сектора.
                             jmp short disk_read_error;Сообщить об ошибке и завершить программу.

sector_loaded:        popa;Восстановить сохраненные регистры.
                             inc ax;Увеличить LBA.
                             add bx,bp;Сместить указатель загрузки на длину сектора.
                             loop main;Продолжить цикл.
                             ret;Завершить процедуру.

disk_read_error:      mov ax,07c0h;Сегмент загрузчика
                             mov es,ax;поместить в ES для int 10h.
                             mov ah,03h;Функция 3 прерывания 10h, получить позицию курсора в DH, DL.
                             xor bh,bh;BH = номер видеостраницы.
                             int 10h;DH = строка, DL = столбец.

                             mov ax,1300h;Функция 19 прерывания 10h, вывод строки. AL = режим вывода.
                             mov bx,0007h;BH = страница, BL = атрибуты символа.
                             mov cx,0010h;CX = длина строки.
                             mov bp,offset msg_DRE;ES:BP = указатель на строку.
                             int 10h;Вывести строку в DH,DL без обновления курсора.
                             cli;Запретить прерывания
                             hlt;и остановить процессор.
read_sectors endp

Поздравим себя! Теперь у нас есть процедура, считывающая данные с диска. Добавленный нами
вызов read_sectors в конце программы помещает КД диска в память сразу после самого
загрузчика. Следующий пост будет последним на тему первичного загрузчика. В нём мы научимся
пользоваться КД и FAT и загружать файлы.

История видеоигр, часть 30: Tank Trap (1977)

For the ATARI 400/800
An Exciting Graphic Animation Game By Don Ursem
QS
QUTJLny
SOFTW7IR6™,старые игры,atari
Случается, что витающая в воздухе идея попадает совсем не в ту голову. Так, игра, которую мог придумать только сходящий с ума от скуки рядовой российской армии, вдруг материализовалась в уме американца Дона Арсема. Судите сами: в самоволку ушёл танк (вероятно, с танкистами), и начальство поручает вам поймать мерзавцев, используя только списанные кирпичи и цемент.
TANK	TRAP
BY OOM	URSEM
CCJ 1980 Quality Software
DIFFICULTY LEVEL
1,старые игры,atari
Процесс ловли подарит вам пару часов весёлого времяпрепровождения. Выглядит так: по одноэкранному игровому полю беспорядочно носится танк; игрок контролирует человечка, который, хотя и бегает медленнее танка, зато оставляет за собой бетонную стену. Задача игрока - запереть танк в небольшом замкнутом пространстве. Потыкавшись в стены и не найдя выхода, тот рано или поздно сдастся. Игрок терпит поражение, если его давит танк.
,старые игры,atari
Победа, танк заперт в левом верхнем квадрате. На скриншоте его не видно, потому что
танк перестаёт отображаться после вывода сообщения.

На самом деле условие победы звучит так: "столкнувшись со стеной, танк сделает девять случайных разворотов; если при этом он не найдёт выхода, то сдастся". Это значит, что теоретически не обязательно окружать танк стеной. Достаточно нескольких блоков и везения. Кстати, построенный мной в этой партии квадрат - не оптимальное решение. Кривые стены с выступами и нишами поставят танк в тупик гораздо быстрее.
,старые игры,atari
У левой границы экрана мы видим два танка. Сначала стирать, потом перерисовывать!
Игра имеет четыре уровня сложности. На первом поле обнесено стеной по периметру и танк не может выехать за пределы экрана. На втором ограждающей стены нет, и танк, выехав с одной стороны поля, появляется с противоположной. Кроме того, часть оставленных игроком блоков состоит из долго застывающего бетона, который танк может протаранить. Впрочем, есть вероятность, что при ударе бетон затвердеет и задержит танк.
,старые игры,atari
Синие участки стены - ненадёжные. Если танк натолкнётся на них, то
они или превратятся в нормальные или исчезнут.

На третьем уровне на поле появляются гражданские. Их, по идее, надо защищать, но, как я ни старался узнать, что будет, если танк переедет одного из них, ничего не вышло; водитель машины наотрез отказывался давить людей. Насколько я понимаю, гибель гражданского - не конец игры, но очков с финального счёта спишут.
,старые игры,atari
На четвёртом уровне танк иногда стреляет из пушки и ломает построенные игроком стены, но только если игрок отдаляется от танка на четыре или более клетки.
,старые игры,atari
Tank Trap не может похвастаться детальной графикой, но звуковое сопровождение не раздражает, а коротенькие мелодии, звучащие в начале и конце раунда, вполне приятны на слух. Управлению несколько недостаёт чувствительности. Часто игрок гибнет под гусеницами танка просто потому что персонаж не отреагировал на нажатие кнопки направления.
YOU TRAPPED THE TANK IN 285 MOOES AND USED ONLY 180 TRAPS.
FOR THIS, YOU'VE EARNED..
THE RANK OF
★ ★ ★ ★
FULL GENERAL,старые игры,atari
По окончании партии игрок получает звание в соответствии со своей
эффективностью.

Найти в интернете информацию об авторе у меня не вышло. Tank Trap была выпущена компанией SPD Software. Отсутствие у этой фирмы каких-либо других игровых проектов заставляет меня думать, что SPD Software -- и есть Дон Арсем, зарегистрировавшийся как компания для распространения Tank Trap. В 1981 году он ещё раз появится на сцене со Starbase Hyperion, после чего навсегда покинет игровую индустрию.

Олдфажного геймдева пост номер 5

Corridor
A dark, empty corridor with high ceiling You see test visuals.
You hear test sound
Use arrow keys to move around the map.
Character
Inventory
Listen
Search
Take
health low^starvingBexhausted
PosX: 7 PosY: 1 PosZ: 0 Time: 608
and crude stone walls.,gamedev,Игры,игрострой,дневники

Выкладывал уже посты по Citadel, но как-то сумбурно. В этот раз напишу подробнее и с цифрами. Итак, что такое Citadel? Одскульная текстовая RPG/roguelike. И, говоря "олдскульная", я имею в виду рили олд. Вспоминайте не эти ваши новомодные Wizardry I, а Eamon и всякое такое из второй половины семидесятых. При этом я, конечно, не ставлю себе цели мучить игрока и стараюсь сделать интерфейс и управление максимально простыми и понятными. Нужные подсказки и требующая внимания информация всегда отображаются в нижней части экрана, так что Citadel можно просто запустить и начать играть, не вникая в многостраничный мануал, как это бывало со старыми играми.

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

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

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

- система персонажа есть частично. Опыта и уровней  привычном понимании не будет, вместо них - атрибуты и навыки, растущие от применения. Список атрибутов уже закончен, список навыков ещё будет пополняться. Помимо боевых будут навыки, направленные на выживание, ремонт/апгрейд снаряжения, скрытность и коммуникацию с обитателями подземелья. Для выживания игроку надо будет искать баланс между тремя параметрами персонажа: голодом, усталостью и боевым духом. Голод будет стимулировать исследовать территорию, а усталость и мораль - ограничивать.

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

- магия. Пока нет. Магические навыки нужно будет отыскивать в подземелье в виде свитков или книг, уже изученные будут прокачиваться при использовании. Будут полезны как в бою, так и для "улучшения качества жизни" в целом.

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

- снаряжение. Примерно такая же ситуация. Готов базовый набор оружия и защиты, но не хватает ещё очень многого. Система в целом ещё не совсем работает. В финале тяжёлая броня будет замедлять героя в бою; дубинки будут оглушать врагов, топоры разрушать броню, а копья и стрелковое оружие позволит доставать летающих и держащих дистанцию врагов. Кроме того, некоторые экземпляры будут иметь дополнительные магические свойства.

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

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

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

- враги. Есть небольшой стартовый набор существ. Бестиарий Citadel будет до краёв наполнен разной потусторонней нечистью со своими особенностями и умениями.

- боевая система. Есть, но далека от завершения.

- сюжет и основной квест. Прописан на бумаге, но в игре пока нет.

Guard post
An abandoned guard room with a sturdy-looking You see dart trap.
You hear nothing but your own footsteps.
Entrance to this room is trapped.
Character
Disarm
Go back
Inventory
Proceed and trigger the trap
PosX: 0 PosY: 4 PosZ: 0 Time: 176
table and several chairs.,gamedev,Игры


Как и в прошлый раз, предлагаю желающим скачать и пощупать игру. Напоминаю, что для запуска нужен будет эмулятор bochs. И игра и эмулятор лежат по ссылкам выше. За инструкциями  по запуску обращайтесь в личку или в комментаx. 

Beneath the Citadel v.0.17

rchive
lie of the Citadel's archives. In older times it was a treasure trove of nowedge, filled with books, ancient parchments, and arcane scrolls. Now lmost everything is destroyed by time, mold, and neglect ion.
ou see nothing of interest.
ou hear nothing but your own footsteps.
ou have
Никто не просил, но я всё-таки сделал это. Новая версия моего роуглайка/упражнения в низкоуровневом программировании/оммажа старым играм вроде Eamon выглядит намного более играбельной. Кто уже имел дело - представляет себе, что это такое. Для тех, кто не знаком: BtC - олдскульный текстовый roguelike про приключения одинокого героя в катакомбах под проклятой цитаделью. В меню - пошаговый режим, хардкор, смерти от голода, ожогов, отравления, ужаса, кровотечения, множественных переломов, мозговых паразитов, обморожений, отравлений, болезней, и многого другого! (некоторые фичи ещё не реализованы) Игра рассчитана на многочисленные неудачные забеги, выигрышная партия должна занимать 1-1,5 часа. На текущий момент запланировано 8 концовок.
ROUND RESULTS
servant husk throws a pot of flaming oil at you the pot hits you and breaks, you catch fire
Vou hit an empty space,gamedev,Игры,Roguelike,screenshotsaturday
Главные фичи версии 0.17:
- наконец-то работает позиционирование в бою. Это ещё не финальный набор действий, но уже кое-что. Игрок и существа могут перемещаться относительно друг-друга, разные действия работают на разной дистанции, у существ есть предпочтения относительно дистанции до игрока. Кроме того, наличие у существ разных наборов действий для разных дистанций добавляет в бои толику тактики: одни враги опаснее вблизи, другие - на расстоянии.
- некоторые умения существ теперь имеют расходуемый боезапас. Например, servant husk ходит с комплектом из трёх зажигательных бомб.
- появился навык survival, благодаря которому игрок сможет находить подножный корм и, соответственно, меньше зависеть от очень редко выпадающих пищевых рационов. В связи с этим скиллом я запланировал несколько интересных механик, но это пока дело будущего.
- доступна одна из восьми концовок.
Полный список изменений как водится приложу в комментариях.
^ Bochs for Windows - Display
USER Copy
si %
Reset suspeoo Power-
Û Ô Ф
=========R0UND RESULTS======
servant husk kicks
you take 14 points of physical damage
You take a mighty suing at servant husk servant husk takes 31 points of physical damage servant husk dies
CTRL + 3rd button enables
Игра написана под самодельную ОС, так что для запуска вам будет нужен эмулятор bochs и образ дискеты с операционкой и игрой. bochs экстремально прост, но если что, я всегда готов помочь, пишите в личку.
Ссыль на bochs:
Ссыль на дискету:

Beneath the Citadel v.0.18 (WIP)

Продолжаю пилить свой роуглайк. Главной фичей новой версии будут события. Что это такое? Если по-простому, - миниатюрные текстовые квесты, которые активируются при первом посещении комнаты игроком. Для меня это в первую очередь нарративный инструмент, потому что через них можно вводить элементы лора, которые иначе пришлось бы распихивать по случайным бумажкам, забытым дневникам и секретным документам, что на мой взгляд звенящая пошлость. Кроме того, это возможность разнообразить геймплейную рутину и задействовать те навыки и параметры персонажа, которые сейчас недостаточно нагружены.
^ Bochs for Windows - Display
Reset susPEno Rower-
Û *
ù ф
Dogs
You see a group of stray dogs tearing something on the floor apart. A moment later the dogs notice your presense and turn to you. Another moment and you realise that these are no ordinary stray dogs - their eye sockets are empty,
^te snapshot » a Reset suspeno Power-
i Ш M û ù Ф
Collapsing stonework
As you enter this room, several pieces of stonework fall from above. Evade (Dexterity)
iq? Bochs for Windows - Display
CTRL + 3rd button enables mouse IPS: 88,380M A: NUM CAPS SCRL,gamedev,Игры,Roguelike,screenshotsaturday

История видеоигр, часть 9: Star Trek (1971)

Порт оригинальной игры на javascript можно увидеть и потрогать здесь.

 Я помню, как в отрочестве, когда у нас ещё не было интернета, листал каналы на телевизоре и попадал случайно на серии Star Trek: The Next Generation. Сериал казался мне очень крутым, но не настолько, чтобы запоминать, когда и на каком канале он шёл. Позже я снова попробовал втянуться во вселенную Star Trek. Я скачал весь оригинальный сериал и начал смотреть. Меня хватило на один сезон - проблемы нервной взрослой жизни занимали намного сильнее, чем приключения команды "Энтерпрайза". В общем, настоящим trekkie я так и не стал. А вот авторы игры Star Trek, как мне кажется, этого звания достойны.

Бравые бойцы Звёздного Флота.

Майк Мэйфилд и его друзья-студенты решили увековечить свою любовь к завершившемуся в 1969 году сериалу в компьютерной игре. Последовали долгие дискуссии о том, каким должно быть их будущее творение. В конце концов консенсус был достигнут, и Майк засел за разработку. Платформой был выбран университетский компьютер SDS Sigma 7, но планы изменились после того, как компания HP предложила Мэйфилду рабочее время на аппарате HP 2000 при условии, что тот портирует на него игру. Такие были времена.

Позднее Дэвид Аль заметил игру, переписал на BASIC и включил в свою книгу 101 BASIC Games. Из этой версии выросла более дружелюбная к игроку версия Боба Лидома Super Star Trek для Data Digital Nova. Я буду рассматривать Java-порт именно этого варианта.

Кровожадные клингоны.

Star Trek - пошаговая стратегическая игра, где игроку в роли капитана корабля "Энтерпрайз" предстоит нелёгкая миссия по защите галактики от нашествия воинственных клингонов (см. выше). За отведённое время (около сорока ходов) нужно найти и уничтожить определённое количество (что-то между 10 и 20) клингонских кораблей. Задача выдаётся в коротком брифинге перед началом партии и слегка отличается для каждой новой игры.



Как только инструктаж закончен - игрок сам по себе. Для современного геймера Star Trek выглядит устрашающе. Ничего, сейчас во всём разберёмся! Прежде всего, в левом верхнем углу под надписью short range scan у нас находится карта квадранта, в котором находится Enterprise. Квадрант разделён на 64 сектора (8х8), каждый из которых может быть занят игроком "<E>", звездой "*", станцией ">!<" или клингонским кораблём "+++". Как видите, в этом квадранте ничего интересного нет. Об этом же говорит и надпись ниже: Condition: GREEN. Придётся искать приключений где-нибудь в другом месте. Сверху справа, под вывеской long range scan, у нас есть миникарта, которая показывает содержимое текущего (в центре) и соседних (вокруг) квадрантов. Трехзначное число - это кодировка, в которой единицы означают число звёзд, десятки - станций, а сотни - клингонов в квадранте. Видно, что в соседних квадрантах нас поджидают 4 клингона, а вот станций для ремонта и подзарядки корабля поблизости нет. Что же, будем осторожны и изучим управление перед тем, как кидаться в бой. Доступные команды у нас перечислены снизу (Commands).

nav - меню навигации. Все перемещения корабля осуществляются через него.


Для того, чтобы куда-то полететь, нужно ввести направление. На скриншоте выше видно, что 8 основных направлений пронумерованы. Кроме того, можно вводить дробные числа, например, 5,25 - чтобы пролететь между направлениями "5" и "6". После этого игроку предложат ввести "warp factor" - фактически, число клеток, на которое нужно переместиться. Выход за границы квадранта переносит игрока в соседний с ним в соответствующем направлении. Внимание: если на пути корабля в нынешнем квадранте есть звезда, то прыжка не получится, и Enterprise затормозит прямо перед ней. Перемещение расходует энергию и время, поэтому беспорядочные прыжки через всю галактику - верный путь к проигрышу. Как спланировать свои передвижения? Об этом чуть позже!

pha - управление фазерами. Если кто не знаком со Стар Треком, это такое энергетическое оружие будущего. Фазер позволяет наносить урон кораблям противника, не заботясь о прокладке курса или наличии препятствий на пути. Единственное, что требуется от игрока - указать, какое количество энергии он хочет вложить в залп (отняв, разумеется, от общего резерва своего корабля). Действие фазеров ослабляется с расстоянием, но запас энергии энтерпрайза (3000) настолько превышает силу щитов клингонов (300-400), что игрок без труда может разделать двоих-троих без подзарядки.

tor - фотонные торпеды, также известные всем поклонникам сериала. В отличие от фазеров, они разносят клингонов в труху с одного попадания  и не тратят энергетический резерв. Максимальный боезапас - 10 торпед - вполне достаточен, чтобы перебить половину захватчиков. Единственный их серьёзный недостаток в том, что на пути или даже рядом с пролетающей торпедой не должно быть звёзд, иначе гравитационное воздействие собьёт её с курса.

she - управление щитами. Для того, чтобы не взорваться от первого же вражеского залпа, игроку стоит выделить некоторую долю энергии (500, например) на поддержание щита.

com - это подменю компьютера с кучей собственных важных функций.


rec - очень нужная штука. Полная карта галактики, где можно посмотреть положение станций, недобитых клингонов и непосещённые квадранты. Выглядит примерно так:


Положение станций отмечено на карте с самого начала, но чтобы узнать, есть ли в квадранте клингоны, нужно его посетить (или просканировать при помощи long range scan). Области, которые я ещё не открыл, показывают нули (000). Карта помогает игроку спланировать маршрут патрулирования так, чтобы успеть облететь всю галактику с периодическими посещениями космических станций для ремонта и заправки. Кстати говоря, в какой-то более поздней вариации игры (Apple Trek, может быть?) станции исчезают после посещения, здесь же они "неисчерпаемые".

sta - отчёт о состоянии корабля и ходе миссии.


Только зайдя сюда, я узнал, что у моего корабля, оказывается, есть ещё подсистемы, которые могут быть повреждены. Чувство уверенности сразу как-то улетучилось. На самом деле повреждения важных систем чинятся быстро, а случаются редко и, по-моему, без связи с полученным уроном. Ни разу у меня ничего не вышло из строя в бою или сразу после, зато несколько раз ломались сканеры и двигатель просто во время полёта.

bas и nav - вспомогательные инструменты, облегчающие жизнь игроку. bas показывает курс до станции (при наличии её в текущем квадранте), а nav - прокладывает курс от нынешнего квадранта к указанному.

col и com - управляют цветом и контрастом экрана и нам неинтересны.

xxx - перезапускает игру, как и написано.

Теперь, когда мы знаем, что делать, - время показать мерзавцам, где раки зимуют! И вот тут выясняется, что клингоны - так себе противники, даже если их двое-трое в квадранте. Если не забывать включать щиты и вовремя подзаряжаться, то проиграть им невозможно. Основная опасность в игре - не уложиться в отведённое время, поэтому - планирование, планирование и ещё раз планирование! Партия проходит очень быстро - минут за двадцать, не больше. Со второго-третьего раза все непонятные моменты встают на свои места, а игра начинает приносить удовольствие. Даже сейчас она была бы неплохим развлечением во время поездки или на скучном семинаре. У игры есть преданное комьюнити, которое портирует её на новые платформы и даже делает графически оболочки для тех, кому тяжело воспринимать ASCII-псевдографику. 

История видеоигр, часть 1: Cathode-Ray Tube Amusement Device

Небольшое предисловие: текст ниже взят из моего блога Silicon Darwin. Зачем я перепощиваю его здесь вместо того, чтобы просто кинуть ссыль? Начиная блог, я xотел делать, что называется, качественный контент. Жизнь внесла свои коррективы, и как-то так вышло, что статьи писались в спешке, на коленке, с головой, занятой другими вещами. Недавно я пробежался по блогу и решил, что он мне не нравится. Я xочу переписать всю сотню статей, что там накопилась. Спокойно, без спешки, добавить исторический контекст, рассказы об автораx, всякое такое. По мере обновления буду выкладывать сюда. Тем не менее, для желающиx вот ссыль.

Управляющий
электрод
вертикально отклоняющие пластины
Горизонтально отклоняющие пластины,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,старые игры,обзор,песочница
Если вы остановите на улице случайного человека и спросите, с чего началась история видеоигр, то скорее всего он пошлёт вас подальше скажет: с Super Mario Bros, Tetris или стрип-покера на компе его папы. Особенно продвинутые назовут Space War! или Pong. Я бы тоже мог удовлетвориться одним из этих ответов, но научная любознательность заставляет углубиться в дебри истории.

Первая задокументированная попытка человека создать интерактивное развлекательное приспособление, снабжённое электронно-лучевой трубкой, произошла аж в 1947 году. Тогда двое сотрудников производителя телевизионного оборудования DuMont Laboratories, Томас Голдсмит младший и Эстл Рей Манн, собрали своё Увеселительное Устройство с Электронно-Лучевой Трубкой (далее для удобства УУЭЛТ). Неизвестно, с какой именно целью было создано устройство, но до массового производства дело не дошло: авторы продемонстрировали его руководству DuMont Laboratories, получили патент, а затем поставили в шкаф и забыли. В развитии игровой индустрии оба в дальнейшем не участвовали. Томас Голдсмит стал профессором физики в Университете Фурмана, о судьбе Манна информации я не нашёл. Известно, что Голдсмит, уходя из DuMont Laboratories, забрал УУЭЛТ с собой и демонстрировал коллегам на новом месте.
,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,старые игры,обзор,песочница
Спорный вопрос, можно ли считать УУЭЛТ первой видеоигрой. Технически оно удовлетворяет не всем критериям: не имеет цифрового компьютера и не использует программного кода. Тем не менее, я считаю правильным начать именно с него. К сожалению, изображений УУЭЛТ не сохранилось, читателю придётся довольствоваться моим путаным объяснением. ЭЛТ использовалась для вывода движущейся точки на экран осциллографа (напоминавший экраны радаров, над которым Голдсмит работал во время войны). Вращая одну из двух управляющих рукояток, игрок мог изменять высоту дуги, по которой точка перемещалась из одного конца экрана в другой. Эта точка символизировала летящий артиллерийский снаряд. В определённый момент, наступление которого можно было регулировать другой рукояткой, точка расплывалась, что обозначало взрыв. Поверх экрана были наклеены полупрозрачные силуэты разной военной техники, которые использовались в качестве целей: механизмов для вывода сложной графики тогда не было. Разумеется, игра не могла сама вести счёт, рядом с игроком должен был сидеть беспристрастный судья, фиксирующий его успехи.
Появление УУЭЛТ нельзя назвать отправной точкой в формировании игровой индустрии. Время и обстоятельства к этому не располагали: DuMont Laboratories не стали запускать устройство в продукцию, и до недавнего времени широкие массы вообще о нём не знали. УУЭЛТ осталось просто историческим курьёзом, а его создатели занялись другими, более важными и интересными вещами. Тем не менее, как и в живой природе, эволюция не идёт по прямой. Эволюция это пробы, ошибки и миллионы тупиковых путей, которые, хотя никуда и не ведут, интересны сами по себе.

Урок ОСдева №8: основной загрузчик, вводная.

Дисклеймер: эта серия постов не про UEFI. Это не значит, что я не знаю о существовании UEFI.
Про UEFI будет отдельная серия постов. Почему я не пишу про UEFI прямо сейчас? Потому что
UEFI - это уровень абстракции над железом, а мне интересно именно железо и работа с ним.

Итак, мы закончили писать первичный загрузчик. Что дальше? Дальше - немного пугающая
свобода. Если в случае ПЗ ограничения на объём памяти ставят разработчика в жёсткие рамки
и ограничивают полёт фантазии, то теперь вы можете строить архитектуру программы
по своему усмотрению и добавлять фичи сколько душа пожелает. Те решения, которые принял
я, могут быть не самыми разумными и правильными. Не буду вам их навязывать. Считаете,
что сможете сделать лучше - пробуйте. В любом случае стоит держать в голове минимальный
набор задач, который должен будет выполнять ваш загрузчик:

- Переключать процессор из 16-битного в 32-битный или 64-битный режим.
- Включать доступ к расширенной памяти.
- Составлять карту RAM и передавать её ОС.
- Минимально взаимодействовать с устройствами ввода-вывода, чтобы показать сообщение об ошибке
или принять ввод пользователя.
- Находить, считывать и обрабатывать файл конфигурации (опционально, делает загрузчик более гибким).
- Находить на диске и загружать в указанную область памяти ядро ОС.
- Находить на диске и загружать в указанные области памяти дополнительные файлы (опционально).
- Предоставлять ОС базовую GDT (об этом позже).

Важный момент: рано или поздно перед вами встанет вопрос совмещения в одной программе
16-битного и 32-битного (или 64-битного) кода. Лично я предпочёл с этим не заморачиваться и
разбил загрузчик на отдельные файлы. Тем более, что это соответствует любимой мной аккуратной
модульной структуре. Если вы всё-таки хотите запихать весь код в один файл, стоит заранее
выбрать ассемблер, который это поддерживает.

Ещё один важный момент: достоверное определение аппаратных ресурсов может быть (было до
появления UEFI - вставят тут апологеты) сложной задачей. Там, где это можно сделать
простым способом - я буду использовать его. Там, где для этого нужно будет полагаться
на технологии, рассказ о которых выйдет слишком объёмным для одного-двух постов (ACPI, UEFI) -
пока я буду заявлять наличие устройства как обязательное. Например, для работы ОС на этом
этапе будет обязательно наличие VGA-совместимых видеокарты и дисплея.

На этом вступление закончено и можно начинать писать. 16-битный модуль загрузчика будет
в некоторых местах повторять первичный. Такие участки кода я буду давать без объяснений.
Ну и - этот пост можно назвать вводным в новый этап программы, так что кода будет
немного. Поехали.Если вы вместе со мной писали ПЗ, то сейчас, запустив машину, получите вот такое
сообщение:

Plex86/Bochs UGABios (PCI) 0.7b 03 Jan 2020 This UGA/UBE Bios is released under the GNU LGPL
Please visit :
. http://bochs.sourceforge.net . http //www.nongnu.org/vgab ios
Bochs UBE Display Adapter enabled
Bochs 2.6.10.svn BIOS - build: 01/05/20
^Revision: 13752 $ $Date: 2019-12-30 14:16:18

Создайте в папке boot папку stage2, а в ней - текстовый файл loader.asm. Заголовок и конец
модуля выглядят почти идентично ПЗ:

.386p
CSEG segment use16
ASSUME cs:CSEG, ds:CSEG, es:CSEG, fs:CSEG, gs:CSEG, ss:CSEG
LOCALS @@

begin:

CSEG ends
end begin

Добавилась только директива LOCALS @@. В TASM эта команда разрешает использование локальных
меток. Начинающаяся с символов @@ метка или переменная будет работать только в границах
процедуры или модуля, в котором прописана. Это позволит нам создавать в разных местах
переменные и указатели с одинаковыми именами. Вы оцените полезность фичи, когда начнёте писать
большие программы. Идём дальше. Первое, что нам нужно сделать - это установить значения
сегментных регистров. Совсем как в ПЗ, изменилось только значение. Если помните, loader.bin
у нас был загружен в 0050h:0000h. Выглядит так:

begin:
     ;DS, ES, FS, GS.
          mov ax,0050h     ;Сегмент загрузчика.
          mov ds,ax     ;Поместить это значение во все сегментные регистры.
          mov es,ax
          mov fs,ax
          mov gs,ax

     ;СЕГМЕНТ СТЕКА.
          cli     ;Запретить прерывания перед переносом стека.
          mov ss,ax     ;Поместить в SS адрес сегмента загрузчика.
          mov sp,0FFFFh     ;Указатель стека - на конец сегмента.
          sti     ;Разрешить прерывания.

          cli
          hlt

Регистр CS мы не трогаем, так как его значение было корректно установлено ПЗ. Для комфортной
работы нам понадобится информация из блока параметров BIOS. Мы знаем, что он был загружен в
память вместе с ПЗ, поэтому обращаться к диску нужды нет. Собственно, я мог бы просто
скопировать всю структуру вместе со значениями из него, но это не кажется мне правильным.
Будет лучше считать структуру из RAM. Для этого нам понадобится неинициализированный
дубликат BPB и процедура, которая его заполнит. Добавьте эти переменные в конец модуля:

;=======================================;
;Блок параметров BIOS, 33 байта.;
;Здесь хранятся характеристики;
;носителя.;
;=======================================;
BPB:BPB_OEMnamedb ?,?,?,?,?,?,?,?     ;0-7. Имя производителя. Может быть любым.
BPB_bytespersecdw ?     ;8-9. Размер сектора в байтаx.
BPB_secperclustdb ?     ;10. Количество секторов в кластере.
BPB_reserveddw ?     ;11-12. Число зарезервированныx секторов (1, загрузочный).
BPB_numFATsdb ?     ;13. Число FAT.
BPB_RDentriesdw ?     ;14-15. Число записей Корневой Директории.
BPB_sectotaldw ?     ;16-17. Всего секторов на носителе.
BPB_mediatypedb ?     ;18. Тип носителя. 0F0 - 3,5-дюймовая дискета с 18 секторами в дорожке.
BPB_FATsizedw ?     ;19-20. Размер FAT в сектораx.
BPB_secpertrackdw ?     ;21-22. Число секторов в дорожке.
BPB_numheadsdw ?     ;23-24. Число головок (поверxностей).
BPB_hiddensecdd ?     ;25-28. Число скрытыx секторов перед загрузочным.
BPB_sectotal32dd ?     ;29-32. Число секторов, если иx больше 65535.

;===============================================;
;Расширенный блок параметров BIOS, 26 байт.;
;Этот раздел используется в DOS 4.0.;
;===============================================;
EBPB_drivenumdb ?     ;0. Номер привода.
EBPB_NTflagsdb ?     ;1. Флаги в Windows NT. Бит 0 - флаг необxодимости проверки диска. Бит 1 - флаг необходимости диагностики ;поверхности.
EBPB_extsigndb ?     ;2. Признак расшренного BPB по версии DOS 4.0.
EBPB_volIDdd ?     ;3-6. "Серийный номер". Любое случайное число или ноль, без разницы.
EBPB_vollabeldb ?,?,?,?,?,?,?,?,?,?,?     ;7-17. Название диска. Устарело.
EBPB_filesysdb ?,?,?,?,?,?,?,?     ;18-25. Имя файловой системы.
db ?     ;Еще один байт для того, чтобы структура занимала 15 32-битных слов.

Как видите, в этом варианте переменным не присвоены значения. Кроме того, в конце
зарезервирован ещё один байт. Он нужен для того, чтобы размер таблицы делился на 4
и её можно было загрузить как последовательность 32-битных слов, не перетерев
ничего лишнего. Теперь нужна процедура.

read_BPB proc
     push cx     ;Сохранить регистры.
     push di
     push si
     push ds

     push 07C0h
     pop ds     ;DS=07C0h, сегмент первичного загрузчика.
     mov si,3     ;SI=смещение BPB в ПЗ.
     mov di,offset BPB     ;DI=смещение BPB в loader.bin
     mov cx,15     ;CX=счётчик для копирования.
     rep movsd     ;Скопировать 15 32-битных слов, размер BPB+1 байт.

     pop ds     ;Восстановить регистры.
     pop si
     pop di
     pop cx
     ret     ;Завершить процедуру.
read_BPB endp

Тут в общем всё просто. Устанавливаем пару регистров DS:SI на начало BPB в первичном загрузчике,
а ES:DI - на нашу неинициализированную структуру. Потом командой rep movsd копируем всю
конструкцию + 1 байт. Вызовите процедуру инструкцией call после установки сегмента стека -
и готово. Кстати, вопрос знатокам: что быстрее выполнит 32-битный процессор в 16-битном
режиме - копирование 15 32-битных слов или 30 16-битных?

На этом пока всё. Сделайте новый батч файл с примерно таким текстом, чтобы не возиться
с инструкциями вручную:

tasm project\boot\stage2\loader.asm
tlink loader.obj
exe2bin loader.exe

Слинкуйте файл, закиньте на дискету - и готово. Сообщение о том, что loader.bin
не найден, должно пропасть. В следующий раз - установка видеорежима и методы вывода
текста.

Ссылка на полный полный текст модуля: https://drive.google.com/file/d/1rFc3OcEDmf7Rs_hEJ6iX98-1SimN_dx_/view?usp=sharing

ОСдев №10: основной загрузчик, часть 3. GDT.

Подготовительный этап закончен, можно приступать к интересному. Кроме собственно загрузки файлов наша программа должна подготовить для ОС рабочую среду. Это значит: переключить процессор в 32/64-битный режим, настроить таблицу прерываний и создать базовую GDT. Сегодня разберёмся с последним пунктом.
GDT - сокращение от Global Descriptor Table, глобальной таблицы дескрипторов. Что это такое? По сути - набор записей одинакового формата, описывающих области памяти и разрешения, которые они имеют. Упрощённо это выглядит так:
//область 0
адрес области
размер области
параметры доступа
флаги
//область 1
адрес области
размер области
параметры доступа
флаги
...
В 32/64-битном режиме дескрипторные таблицы используются вместо старой схемы адресации сегмент:смещение. Зачем? Сегмент:смещение - небезопасная технология, которая позволяет переписать любой участок памяти. Надо ли говорить, что при неосторожном обращении это легко может закончиться бедой? Дескрипторная таблица даёт возможность ограничить запись или выполнение кода в отдельных областях RAM. Дескрипторные таблицы появились как часть аппаратной защиты памяти вместе с 286 процессором.
Как это работает? После выхода из 16-битного режима процессор больше не принимает адреса в формате сегмент:смещение. Если попытаетесь так сделать - получите исключение GPT (General Protection Fault). Вместо этого в сегментном регистре процессор ожидает получить смещение дескриптора внутри ДТ. При этом для операций над этим сегментом будут действовать правила, указанные в дескрипторе. Например, попытка обратиться к памяти за пределами сегмента или запись в защищённый от записи сегмент будут вызывать исключения (кстати, про обработку исключений поговорим позже, пока давайте примем, что это фатальная ошибка, которая приведёт к остановке программы).
Кроме глобальной таблицы дескрипторов существуют ещё локальные (ЛДТ), TSS и таблицы дескрипторов прерываний (IDT). Для того, чтобы наша ОС могла начать работу, обязательно наличие только двух таблиц: GDT и IDT. Давайте теперь взглянем на GDT поподробнее. Скажу сразу, зрелище будет не очень приятное. Но начнём с лёгкого. Так как GDT - часть аппаратной схемы защиты памяти, у неё есть свой регистр: GDTR. Это 48-битный регистр, 4 байта которого предназначены для смещения GDT, а 2 - для её размера. Таким образом, GDT не может быть больше 65536 байтов в размере. Размер записи в GDT - 8 байтов, значит,< таблица может иметь максимум 8192 дескриптора. Зная всё это, хорошим тоном было бы сразу зарезервировать 64К под GDT, но в моей архитектуре ОС создаёт свои таблицы, так что сейчас я обойдусь минимумом. Минимум в данном случае - 3 дескриптора. Нулевой, сегмент кода и сегмент данных. Зачем отдельно выделять нулевой дескриптор? Дело в том, что обращение к нему в GDT приводит к, вы угадали, исключению. Это тоже своего рода мера предосторожности.
А теперь время взглянуть на структуру дескриптора. И тут, увы, наследие тяжёлого прошлого во всей красе. Ради обратной совместимости в кодом для старых процессоров дескриптор GDT превратили в кашу.
Первые два байта - это первые 16 битов границы сегмента.
Следующие три байта - первые 24 бита основания сегмента.
Следующий байт - параметры доступа. Рассмотрим ниже.
Следующий байт совмещает в себе биты 16-19 границы и флаги. Об этом тоже подробнее ниже.
Ну и последний байт - биты 24-31 основания.
Неудобно? Не то слово. Когда будем писать ядро - обязательно замутим процедуру для комфортной работы с этим месивом. К счастью, сейчас у нас статичная структура всего из трёх сегментов, так что заполнить можно и вручную. Создадим и подключим модуль GDT.inc. Как это сделать, мы рассматривали в прошлый раз. И добавим в него вот такую таблицу:

Это и есть наша GDT, ничего ужасного. Значения в нулевом дескрипторе для нас не важны, а вот остальные давайте рассмотрим подробнее. У нас есть два дескриптора: один - для кода, другой - для данных. Оба начинаются с 0 и занимают FFFFF*4Kib = 4Gib. Фактически это значит, что, начав работать, ОС сможет использовать всю память по своему усмотрению. Давайте теперь разберём параметры доступа и флаги.
Бит 1 - флаг чтения/записи. Его значение различается для сегментов кода и данных.
Для сегментов кода установленный флаг означает, что чтение разрешено.
Запись в сегменты кода запрещена всегда.
Для сегментов данных установленный флаг означает, что разрешена запись. Чтение из сегментов данных
Таким образом, значение параметров доступа 10010010b даёт нам вот что: это сегмент данных, запись в него разрешена, сегмент растёт вверх, уровень привилегий - ring0. А теперь флаги. Биты 0-3 здесь заняты границей сегмента, не обращаем на них внимания.
Бит 4 зарезервирован и должен быть равен 0.
Бит 5 указывает на 64-битный сегмент. Так как мы пока переходим в 32-битный, должен быть равен 0.
Бит 6 указывает на 32-битный сегмент. Наш выбор, устанавливаем в 1.
Бит 7 - гранулярность. Если равен 0, то значение границы сегмента используется как
Окей, теперь у нас есть GDT. Но как указать системе, что её нужно использовать? Процессор ведь не дурак, сам искать не станет. Всё просто, джентльмены из IBM в кои-то веки о нас позаботились. При помощи специальной ассемблерной команды lgdt (load GDT) мы можем передать в регистр GDTR линейный адрес таблицы и её размер. Для этого добавим перед GDT такую структуру:
60ТК_р1:г:
dы 00171п	;Размер таблицы - 1 (23 байта).
dd 00000000И	;Абсолютный адрес таблицы.,программирование,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,OSDev,Операционная система,ассемблер,разработка,длиннопост
Размер таблицы мы уже знаем, а вот адрес придётся посчитать, так что пока оставим 0 и напишем процедуру инициализации GDT:
init_GDT ргос
init_GDT endp
push eax pushfd
xor eax,eax
mov ax,offset GDT
add eax,00000500h
mov dword ptr [GDTR_ptr+2],eax
cli
lgdt pword ptr GDTR_ptr sti
popfd pop eax ret,программирование,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,OSDev,Операционная система,ассемблер
На случай, если тут не всё очевидно, поясню. Мы помещаем в EAX смещение GDT относительно сегмента, а потом добавляем адрес сегмента*16. Это и есть линейный адрес, сохраняем его в структуре. После этого отключаем прерывания, передаём структуру процессору командой lgdt и включаем прерывания обратно. По идее прерывания можно не трогать, так как в 16-битном режиме GDT не используется, но я перестраховщик.
Собственно, на этом всё. Добавьте вызов init_GDT в конец загрузчика перед cli и дело в шляпе. Сегодня без картинки, но вот вам котик.
Чистая дискета: https://drive.google.com/file/d/1Bold4ds8oEruHQ7fJZKHglVo7A2Vc5MR/view?
Предыдущие части:

История видеоигр, часть 20: Sea Wolf (1976)

,Игры,старые игры,игровой автомат,обзор

Имя компании Namco, наверное, знакомо всем моим читателям. Тем, кто постарше -- как создательницы Splatterhouse и Tekken, тем, кто помоложе -- по серии Souls, правда, уже в составе холдинга Bandai Namco. А вот самым первым продуктом Namco, по словам её основателя Масайи Накамуры, был электромеханический игровой автомат Periscope 1965 года. В нём на фоне изображавшего океан задника проплывали вырезанные из картона корабли, которые игрок, глядя в перископ, должен был сбивать торпедами. Траектории торпед обозначались световыми полосками, а попадания -- звуковым эффектом. Игра имела успех, и, кто бы мог подумать, у неё возникли подражатели. Например, Midway выпустила очень похожую игру под названием Sea Devil. Мой блог -- про видеоигры, и всё вышенаписанное меня бы не касалось, если бы Midway к 76 году не решила проапгрейдить последний до полноценного электронного автомата с дисплеем. Так появилась Sea Wolf. В ней точно так же по экрану, изображающему морскую гладь, проплывают военные корабли и катера, а невидимый и неуязвимый игрок разит их торпедами. Попадания вознаграждаются очками, а наивысший счёт - главная и единственная цель игры. Наводятся торпеды при помощи прицела, который перемещается влево и вправо вдоль линии горизонта. Торпеды плывут медленно, так что стрелять приходится с упреждением.

TIME SCORE 2H 1200,Игры,старые игры,игровой автомат,обзор
После каждых четырёх торпед некоторое время уходит на перезарядку.

Разные типы судов приносят разное количество очков. Руководствоваться стоит одним общим принципом: чем цель меньше,  тем больше очков она стоит. Исключение -- мины, которые не приносят очков и только осложняют жизнь игроку, перекрывая линию огня. Чем больше счёт -- тем больше мин появляется на экране.


TIME SCORE 20	3100,Игры,старые игры,игровой автомат,обзор

Графика в игре симпатична. Цели хорошо читаются и различаются между собой. Анимации практически нет, за исключением колышущихся волн, но удовольствие это не портит. Звуковое сопровождение ограничивается попискиванием радара и звуком моторов проносящихся по экрану катеров.

Здесь мы собираем самые интересные картинки, арты, комиксы, мемасики по теме (+61 постов - )