;адаптация игры "ПИТОН" РАДИО 4'87 с.18-19
        .model  small
        assume  cs:@code,ds:@code,ss:@code
        .code
        org     100h
ВЕРХ:   jmp     short   СТАРТ
;-------------------------------
H_KP    equ     4h              ;ст.байт адреса нижн. границы буфера
В_КР    equ     9h              ;ст.байт адреса верхн. границы буфера
BLEBO   equ     4bh             ;код клавиши "ВЛЕВО"
BPRABO  equ     4dh             ;код клавиши "ВПРАВО"
BHIZ    equ     50h             ;код клавиши "ВНИЗ"
ВВЕРХ   equ     48h             ;код клавиши "ВВЕРХ"
LINEO   equ     0b800h  ; (0b000h для монохром. дисплея)
СТРО    db      '*** ПИТОН     ***',0dh,0ah
        db      'СКОРОСТЬ (1..3) ? ',l$'
СТР2    db      'ДЛИНА—000','$'
СТРЗ    db      '<ENTER> - ИГРА, <ESC> - ВЫХОД ','$'
GOLOBA  dw      4 О Oh          ;адрес указателя головы
ХВОСТ   dw      ?               ;адрес указателя хвоста
DLIN    db      ?               ;величина удлинения питона
TIK     dw      ?               ;величина задержки
РОСТ    db      ?               ;счетчик роста хвоста
СТРК    db      ?               ;номер строки курсора
;-------------------------------
СТАРТ:  call    CLSCR    ;вызов процедуры очистки экрана
        mov     ah,09h   ;запись в ah,dx параметров
        lea     dx,CTP0  ;вызова функции DOS для вывода на экран сообщения
        int     21h      ;с помощью int 21h
М10:    mov     ah,08h   ;в ah - код функции для ввода с клавиатуры
        int     21h      ;вызов функции DOS для ввода значения скорости в а1
        cmp     al,31h          ;введенный символ должен лежать
        jb      M10             ;в диапазоне    от 31h
        cmp     al,33h          ;               до 33h,
        ja      M10             ;иначе - повторить ввод
        and     al,00001111b    ;перевод кода символа в двоичный код
        cbw                     ;преобразование байта в слово (ах)
        mov     ТIК,ах          ;задержка (в тиках таймера)
;настройка второго канала таймера для генерации
;случайных чисел 0-77 (4dh)
        mov     al,0b6h         ;запись управляющего байта (b6h)
        out     43h,al          ;в порт 43h для установки режима 3 канала 2
        mov     al,4dh          ;загружаем регистр счетчика таймера
        out     42h,al          ;сначала младший байт, затем
        mov     al,0h           ;старший байт
        out     42h,al
;включение второго канала таймера
        in      al,61h          ;чтение значения из порта 61h
        or      al,01h          ;включение нулевого бита разрешает работу канала
        out     61h,al          ;выдача нового значения в порт 61h
СТАРТ 1: mov    РОСТ,0          ;в начале игры обнулим яч. РОСТ
        mov     ah,02           ;в ah - код функции  BIOS для установки курсора
        mov     bh,00           ;страница О видеопамяти
        mov     dh,23           ;номер строки курсора (23)
        mov     dl,45           ;номер позиции курсора в строке (45)
        int     10h             ;вызов прерывания BIOS
        mov     ah,09h          ;выдача запроса для начала игры
        lea     dx,CTP3         ;в dx - адрес начала запроса
        int     21h             ;вызов функции DOS
        mov     ah,0 Oh ;в ah - код функции BIOS для ввода кода нажатой клавиши
        int     16h     ;вызов прерывания BIOS
        cmp     ax,011bh        ; нажата ESC ? (код Ollbh)
        jnz     GAME            ;нет - переход на игру
;выключение второго канала таймера (подготовка к завершению программы)
in      al,61h          ;чтение порта 61h
and     a1,0feh         ;обнуляем нулевой бит для выключения канала
out     61h,al          ;запись нового значения в порт 61h
;завершение программы
        mov     ax,4c00h         ;вызов функции 4с с кодом завершения OOh
        int     21h              ;для завершения работы программы
;--------------------------------
GAME:   call    CLSCR            ;очистка экрана
        mov     di,0             ;Рисование левого борта
        mov    а1,'+'            ;выводимый символ - в а1
        mov     сх,24            ;число строк - в сх
СНОВА:  mov     es:[di],al       ;запись символа в экранную область
        add     di,160           ;прибавим смещение
        loop    СНОВА            ;переход на начало цикла
        mov     сх,80            ;Рисование нижнего борта
СНОВА1: mov     es:[di],al
        add     di,2             ;прибавим смещение
        loop    CHOBA1
        mov     di,0             ;Рисование верхнего борта
        mov     сх,79            ;число выводимых символов - в сх
СНОВА2: mov     es:[di],al
        add     di,2             ;прибавим смещение
        loop    CHOBA2
        mov     cx,24            ;Рисование правого борта
СНОВАЗ: mov     es:[di],al       ;запишем символ в экранную область
        add     di,160           ;прибавим смещение
        loop    СНОВАЗ           ;повторим цикл
        mov     ah,02            ;в ah - код функции BIOS для установки курсора
        mov     bh,00            ;страница 0 видеопамяти
        mov     dh,00            ;номер строки курсора (0)
        mov     dl,62            ;номер позиции курсора в строке (62)
        int     10h              ;прерывание BIOS
        mov     ah,09h           ;функция DOS - выдача сообщения
        lea     dx,CTP2          ;адрес сообщения о длине питона - в dx
        int     21h              ;вызов функции DOS
        mov     dx,3760          ;позиция старта питона
        mov     сх,-160          ;начальное направление - вверх
        push    dx               ;запомнить адрес головы питона
        push    сх               ;и направление движения в стеке
        mov     ax,GOLOBA        ;установить адрес указателя хвоста =
        mov     ХВОСТ,ах         ; = адресу указателя головы (длина=0)
        jmp     Ml               ;вход в основной цикл программы
                                 ;и вывод очередного кролика
CYCLE:  mov     ah,01h           ;опрос клавиатуры
        int     16h              ;проверка на нажатие клавиши
        jz      НЕТ              ;нет нажатых клавиш - переход
        mov     ah,00h           ;получить скан-код
        int     16h              ;прерывание BIOS
        mov     al,ah            ;запишем код в а1
        jmp     KOD
НЕТ:    хоr     ах,ах            нет нажатых клавиш - обнулить ах
KOD:    mov     сх,-2            смещение для движения влево
        cmp     al,BLEBO         если нажата стрелка влево,
        jz      МО               сменить направление
        mov     сх,2             смещение для движения вправо
        cmp     al,BPRABO
        jz      МО
        mov     сх,-160         ;смещение для движения вверх
        cmp     a1,ВВЕРХ
        jz      МО
        mov     сх,160          ;смещение для движения вниз
        cmp     a1,BHIZ
        jz      МО
        pop     si
        xchg    si,cx            ;восстановить старое напр.движения
        push    si
МО:     pop     bx               ;извлечь из стека лишнее слово
        pop     dx               ;извлечь из стека адрес головы
        add     dx,cx            ;вычислить следующий адрес головы
        push    dx               ;запомнить адрес головы
        push    cx               ;и направление движения в стеке
        mov     di,dx            ;считать код по адресу головы
        mov     al,es:[di]       ;и записать в а1
        mov     byte ptr es:[di],'0'    ;отобразить новое положение головы
        mov     bx,GOLOBA               ;загрузить адрес указателя головы
        inc     bx                      ;переместить указатель
        inc     bx                      ;головы на 2 байта
        cmp     bh,B_KP          ;верх.граница кольцевого буфера достигнута?
        jnz     $+4              ;нет - переход
        mov     bh,H_KP          ;если да, установить нижнюю границу
        mov     GOLOBA,bx        ;запомнить указатель
        mov     [bx],dx          ;записать адрес нового положения головы
        call    T_DELAY          ;задержка
        cmp     al,' '           ;путь свободен ?
        je      M3               ;да - переход
        cmp     al,0dbh          ;наполз на кролика ?
        je      M7               ;да - переход
        mov     GOLOBA,400h      ;нет, конец игры.Указатель головы -
        jmp     CTAPT1           ;- на начало буфера и переход на новую игру
M7:     mov     al,DLIN          ;инициализация счетчика
        mov     РОСТ,а1          ;роста длины питона
Ml:     call    RND_NUM          ;получим поз.курсора Х (столбец)
        add     bl,01            ;Х=Х+1
        mov     dl,bl            ;запомним поз.курсора Х в dl
        call    RND_NUM          ;получим случайное число
М23:    cmp     bl,17h           ;сравним с числом 23 (17h)
        jb      DALEE            ;если <= 23,выйдем
        sub     bl,17h           ;иначе - вычтем 23
        ja      M23              ;повторим, пока не будет <= 23
DALEE:  add     bl,01h           ;строка Y=Y+l
        mov     CTPK,bl          ;запомним поз.курсора Y (строка)
        shr     bl,1             ;разделим на два
        add     bl,l             ;добавим 1
        mov     DLIN,bl          ;и запишем в DLIN
        mov     ah,02   ;функция BIOS - установка курсора в поз. X,Y
        mov     bh,00   ;страница 0 видеопамяти
        mov     dh,CTPK ;запишем номер строки в dh, dl(столбец) уже установлен
        int     10h     ;вызов прерывания BIOS
        mov     ah,08            ;функция-в регистр а1 код символа в поз.курсора
        mov     bh,00            ;страница 0 видеопамяти
        int     10h              ;вызов прерывания BIOS
        cmp     al,20h           ;сравним с пробелом
        jne     Ml               ;позиция занята,выберем новую
        mov     ah,0ah           ;функция BIOS - вывод символа в поз.курсора
        mov     al,0dbh          ;выводимый символ - "кролик" (0dbh)
        mov     bh,00            ;страница О видеопамяти
        mov     сх,01            ;выводим один символ
        int     10h              ;вызов прерывания BIOS
M3:     dec     РОСТ             ;уменьшить счетчик роста.
        cmp     РОСТ,0           ;если был О,
        js      M5               ;поджать хвост
;увеличение длины питона
        mov     di,142                   адрес младшей цифры длины питона - в di
М4:     inc     byte ptr es:[di]         увеличить цифру в сообщении
        cmp     byte ptr es:[di],3ah     цифра > 9 ?
        jz      $+5                      да
        jmp     CYCLE                    нет - следующий цикл
        mov     byte ptr es:[di],30h     записать цифру "О" (30h)
        sub     di,2                     ;вычислить адрес след.цифры
        jmp     M4                       ;увеличить след.цифру
М5:     inc     РОСТ                     ;восстановить 00h в счетчике роста
;поджимание хвоста
        mov     bx,ХВОСТ                 ;загрузить указатель хвоста
        mov     dx,[bx]                  ;загрузить адрес хвоста
        inc     bx                       ;переместить
        inc     bx                       ;указатель хвоста.
        cmp     bh,B_KP                  ;если достигнут конец
        jnz     $+4                      ;кольцевого буфера,
        mov     bh,H_KP                  ;установить адрес на его начало
        mov     ХВОСТ,bx                 ;запомнить указатель хвоста
        mov     di,dx                    ;стереть последний символ
        mov    byte ptr es:[di],' '      ;питона на экране
        jmp     CYCLE                    ;следующий цикл
;------------------------------
;процедура очистки экрана
CLSCR   рrос
        mov     ax,LINEO         ;сегментный адрес начала памяти
        mov     es,ax            ;дисплея в ES
        mov     di,0             ;начальное смещение
        mov     а1,' '           ;выводимый символ - пробел
        mov     ah,07h           ;атрибут 07h - белые символы на черном фоне
        mov     сх,2000          ;число символов - в сх
        cld                      ;направление - вперед
rep     stosw                    ;вывести символы
        ret
CLSCR   endp
;--------------------------------
;величина задержки в тиках таймера
;(за 1 секунду таймер тикает 18.2 раза)
T_DELAY proc
        push    ax               ;сохраним ax в стеке
        mov     ah,OOh           ;вызов функции OOh для чтения содержимого
        int     lah              ;счетчика таймера в dx
        mov     bx,dx            ;запишем младший байт счетчика в bx
        add     bx,TIK           ;добавим задержку
DELAY:  int     lah              ;прерывание BIOS - чтение счетчика таймера
        cmp     dx,bx            ;время истекло ?
        jne     DELAY            ;нет - продолжим задержку
        pop     ax               ;да, восстановим ах из стека
        ret                      ;и выйдем
T_DELAY endp
;--------------------------------
;выдача случайного числа
RND_NUM proc
        mov     al,86h          ;выдаем команду для фиксирования текущего
        out     43h,al          ;значения регистра второго канала таймера
        in      al,42h          ;запишем младший байт счетчика
        mov    b1,а1            ;(случайное число) в регистр b1
        in      al,42h          ;читаем старший байт счетчика
        ret
RND_NUM endp
        end     ВЕРХ