1 org 100h 2 mov ax, 0x0013 3 int 0x10 ; Видеорежим 320x200x256 4 push 0xA000 5 pop es ; Сегмент ES = 0xA000 видеобуфера 6 pop cx ; CX=0, SP=0 7 inc bx ; BX = любое число для генератора 8R: xadd bx, dx ; Генератор псевдослучайного числа 9 mov ax, bx 10 and al, 0x0F 11 or al, 0x10 ; Цвет AL = [16 до 31] шкала серого 12 and ah, ah 13 cmovne ax, sp ; Если AH <> 0, AX = SP [=0] 14 stosb ; Нарисовать звезду 15 loop R ; Повторить 16 int 0x16 ; Ожидание нажатия клавиши 17 int 0x20 ; Выход из программыСуть программы в том, что она выдает на экран звездное небо, используя стандартный видеорежим BIOS, 320x200x256. Для установления этого видеорежима нужно вызвать шестнадцатое прерывание с параметрами AH = 0, AL = видеорежим, в данное случае AL = 19 = 0x13 [hex]
После чего в регистр ES записывается значение 0xA000, которое есть сегмент, указывающий на видеопамять. Инструкция "POP CX" в стандарных com-программах будет означать то, что в CX обязательно окажется 0, ибо при инициализации система непременно записывает в стек 16-ти разрядный 0. Инструкция "INC BX" сменяет на единицу регистр BX... По сути, это нужно лишь для того, чтобы запустить генератор чисел для построения звездного неба. Если в регистрах BX и DX одновременно окажутся 0, программа выдаст лишь черный экран.
Мнемоника "XADD BX, DX" означает следующее:
- Вначале BX и DX обмениваются местами [XCHG BX, DX]
- После чего BX складывается с DX: BX = BX + DX [ADD BX, DX]
1BX = 1, DX = 0: BX = 0, DX = 1: BX = 1 2BX = 1, DX = 1: BX = 1, DX = 1: BX = 2 3BX = 2, DX = 1: BX = 1, DX = 2: BX = 3 4BX = 3, DX = 2: BX = 2, DX = 3: BX = 5 5...Получается ряд чисел, последовательно хранящихся в регистре BX:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55...
Который является ничем иным, как "Числами Фибоначчи". Эффект псевдослучайных чисел получается тогда, когда есть некий "ограничитель", в данном случае ограничителем является разрядность регистров BX и DX. Если попробовать сложить два предыдущих числа Фибоначчи, например, 28657 и 46368, не превышающих допустимого предела значении 16-ти битных регистров (макс.знач. = 65535) получился число 75025. Если откинуть старший, 17-й бит, который, в данном случае, просто перейдет в флаг процессора Carry Flag = 1, получится число 9489. Можно допустить, это число получилось "непредсказуемо", и оно теперь будет новой "базой генерации". Сложив его с предыдущим, 46368, начнется новый цикл чисел. Постоянно находясь в замкнутом цикле, числа, "крутясь, как белка в колесе", будут очень похожи на случайные. Хотя это не так... Почему? Например, можно предположить, что последовательность случайна
1 2 4 7 9 4 3 2 1 5 6 1 2 4 7 9 4 3 2 1 5 6 1 2 4 7 9 4 3 2 1 5 6 1 2 4 7 9 4 3Теперь разложим в другом порядке:
1 2 4 7 9 4 3 2 1 5 6 1 2 4 7 9 4 3 2 1 5 6 1 2 4 7 9 4 3 2 1 5 6 1 2 4 7 9 4 3 . . . .Теперь же последовательность далеко не случайна. Мы видим, что здесь есть легко проглядывающийся цикл. Так же и в генераторе "случайных чисел" с помощью чисел Фибоначчи, где действительно случайными можно называть первые 24 числа, а остальными назвать произодными от них. Два фактора - постоянно меняющаяся база генерации в 65536x65536 значении и раскладка в 320 столбцов дает необходимый элемент случайности, достаточный для генерации звездного неба. Лирическое отступление: вот как я пристроил знаменитые числа Фибоначчи! :) Как называется, нашел им новое применение среди огромного числа его бывших применений
Дальше все просто. В регистр AX переписывается "случайное" значение BX, "отрезаются" лишние биты, оставляя только первые 4, а потом "дописывая" в 5-й бит единицу. Таким образом, в AL получается число в диапазоне от 16 до 31, что соответствует 16-ти градациям серого цвета из стандартной палитры цветов. Потом проверяется, есть ли 0 в регистре AH. Дело в том, что при генерации числа регистр BX редко оказывается со значением, меньшим, чем 256. Потому, если в регистре такое значение, а это говорит о том, что в старшем его байте BH = 0, то в точке под номером DI [регистр, в котором записана текущая позиция на экране] будет "звезда".
Инструкция CMOVNE AX, SP работает только начиная с 686 модели процессора. Она работает следующим образом:
- Если флаг ZF (Zero Flag) = 1, то инструкция не выполняется
- Иначе AX = SP
Инструкция STOSB записывает значение регистра AL по адресу ES:DI и увеличивает значение DI на единицу. Первоначальное значение DI не имеет значения, ибо оно все равно за 65536 раз увеличения на единицу примет все возможные свои значения. Инструкция LOOP производит повтор блока генерации звездного неба ровно 65536 раз.
INT 0x16 - это системное BIOS-прерывание, обслуживающее клавиатуру. Чтобы установить в ожидание приема символа, необходимо, чтобы в регистре AH = 0, что произойдет в любом случае, потому что если в AH будет не 0, то выполнится CMOVNE и запишет в AX = 0, и в AH тоже будет 0. Если же в AH = 0, то в AL = [16..31], но это значения не имеет. Команда INT 0x20 завершает выполнение COM-файла, причем только COM, а не EXE.