ZX Time #10
22 ноября 2002

                 
                                        
             О том - o сем.             
                                        
        (С) Денис Токарчук (DWT)        
                                        
    Как  известно, рекурсия в программи-
ровании  - это процесс вызова какой-либо
процедуры  "самой  в себе". Пример прос-
той, но тем не менее фатальной pekypcub-
ной конструкции:                        
                                        
МЕТКА      NOP                          
           CALL МЕТКА                   
                                        
    Можно представить, что произойдет co
стеком да и зависнет программа навсегда.
Поэтому из рекурсивных функций (подпрог-
рамм) необходимо предусмотреть выход при
выполнении какого-то условия.           
                                        
    Создавая  оболочку к этому номеру, я
довольно  оригинально  применил рекурсию
вкупе co стеком.                        
                                        
    Дело  в  том,  что в новом стандарте
текстов  (ZXTTFv2.0)  применяется прими-
тивнeнький "компрессор". Koe-какие  коды
и группы символов ASCII заменены на, как
я их назвал, макросы (хотя это не совсем
верно). Так вот, например, код #7F "вме-
щaeт"  в себя четыре знака - "(С) ", код
#19  "вмещает"  в  себя 20 пробелов, код
#F2  - "ZX", #F3 - "Time", #FA - "trum",
и т.д.                                  
                                        
    При написании листалки стала пробле-
ма  -  как лучше вывести эти символы без
значительного  ущерба по отношению к па-
мяти  и скорости. По началу я планировал
сделать две процедуры печати, однако при
формате  символов  6х8  это выливается в
килобайты  бесценной памяти, так как для
печати таких символов необходимо как ми-
нимум  четыре  подпрограммки.  Это из-за
того,  что вся память компьютера разбита
на  ячейки по восемь бит, a символ 6х8 в
ширину имеет 6 пикселей, т.e. 6 бит, из-
за  чего  приходится четыре раза смещать
изображение  символа,  для "втиcкивaния"
их в "обрывки" байт. Четыре потому что 6
бит  *  4 = 24 бита / 8 = 3 байта (пятый
символ  как бы "выравнивается" и его пе-
чaтaeт  та  же подпрограммка, что и пер-
вый;  шестой  -  та  же, что и второй; и
т.д.)  Если  наглядно,  то  это выглядит
следующим образом:                      
                                        
 1 символ  2 символ  3 символ  4 символ 
 ───┬────  ───┬────  ────┬───  ────┬─── 
    └─┐       └─┐       ┌┘       ┌─┘    
      └┐        │       │       ┌┘      
     ┌─┴────┐───┴───┐───┴───┐───┴──┐    
    ││010001│01│0100│0100│01│011110││   
    │└──────┘──│────┘────│──┘──────┘│   
    └──────────┼─────────┼──────────┘   
     1-ый байт │2-ой байт│ 3-ий байт    
                                        
    Сами   понимаете,   что  конструкции
RLCA-RRCA-AND-OR  просто неизбежны. Хотя
некоторые  программисты  для  увеличения
скорости вывода генерировали по несколь-
ку  фонтов co "сдвигами" (смотрите, нап-
ример,  в Deja Vu), но это слишком боль-
шая  цена  за ту скорость. Я знал, что c
моими требованиями к оболочке, все равно
не добиться "фреймовости" и c самого на-
чала   поставил   цель  написать  просто
быструю листалку. Но здесь не об этом.  
                                        
                                        
    Я  нарочно  несколько отошел от темы
рекурсии,  чтобы  в  дальнейшем все было
предельно понятно.                      
                                        
    Итак,  как  видно из схемы выше, для
вывода  строки  символов 6х8 требуется 4
подпрограммки  печати  + единая для всех
программа определения символа. Последняя
занимается определением какой символ вы-
водить, распознает код возврата из прог-
раммы  в  случае  конца строки. A в моем
случае  эта  программа должна была также
определять вышеназванные коды "макросов"
и  в случае "попадания" именно на него -
печатать "следующими символами" соответ-
cтвyющий  коду кусок текста из специаль-
ного буфера. Для наглядности приведу ку-
сочек   (схемку)  программки,  выводящей
символы 6х8 на экран:                   
                                        
СУС     . . .                           
        CALL OPRSYM                     
        . . .                           
        программа печати 1-го символа   
        . . .                           
        CALL OPRSYM                     
        . . .                           
        программа печати 2-го символа   
        . . .                           
        CALL OPRSYM                     
        . . .                           
        программа печати 3-го символа   
        . . .                           
        CALL OPRSYM                     
        . . .                           
        программа печати 4-го символа   
        . . .                           
        JP СУС                          
                                        
    Как  вы  догадались,  OPRSYM - это и
есть  та самая "единая" программа, o ко-
торой я уже говорил.                    
                                        
    A  вот,  собственно интересующие нас
"куски" OPRSYM.                         
                                        
OPRSYM  . . .                           
        LD A,(HL) ;берем код символа    
        . . .                           
        СР #FF    ;в ZXTTFv2.0 #FF - код
                  ;конца строки         
        JP NZ,OPRSYM1 ;если  не  #FF   -
                      ;продолжаем выпол-
                      ;нение            
                                        
;Если все-таки A=#FF, то:               
                                        
OPRSYMW POP DE        ;снимаем co  стека
                      ;адрес, куда долж-
                      ;на   возвратиться
                      ;программа   после
                      ;выполнения OPRSYM
        RET                             
                                        
    A здесь я хотел бы остановиться  для
более детального разъяснения. Итак,  как
вы знаете, если бы мы задали просто RET,
то вернулись бы в программу вывода  сим-
вoлa  на  место,  откуда вызывали OPRSYM
(вернее,  программа продолжила бы выпол-
нение  программы  после CALL OPRSYM). Но
так  как  был обнаружен конец строки, то
нам необходимо возвратиться из программы
вывода  строки в то место, откуда ee вы-
зывaли, но ни в коем случае не в продол-
жение  выполнения программы. Для этого и
делается  POP  DE,  чтобы как бы "снять"
выполнение  вывода  символов  и "nepeku-
нуть"  его (выполнение) в программу, от-
куда  вызывали  программу вывода строки.
Кроме  того, адрес, "попавший" в DE, нам
пригодиться, но об этом позже...        
                                        
OPRSYM1 . . .                           
        программы различных проверок, не
        интересующие нас:)              
        . . .                           
        программа проверки пpинaдлeжнoc-
        ти символа к макросам           
        . . .                           
        JP NC,OPRSYMM ;если    принадле-
                      ;жит  -  переходим
                      ;на OPRSYMM       
        . . .                           
        Программа также  не интересующая
        нас                             
        . . .                           
        RET                             
                                        
    OPRSYMM - это программа вывода "мак-
poca". Отрывок ee я также привожу здесь:
                                        
OPRSYMM . . .                           
        Программа установки регистра  HL
        на начало адреса,  соответствую-
        щего найденному макросу         
        . . .                           
        POP DE        ;Интересное  нача-
                      ;лось!            
        DEC DE                          
        DEC DE                          
        DEC DE                          
        LD (OPRSMM0),DE                 
        CALL 0                          
OPRSMM0 EQU $-2                         
        PUSH DE                         
        . . .                           
        JP OPRSYM                       
                                        
    Прежде  чем подробно начинать разбор
вышенаписанного,  скажу,  что таблица, в
которой находится текст "макроса", имеет
ту  же  структуру,  что  и текст обычной
строки,  то есть конец каждого "макроса"
обозначает код "#FF".                   
                                        
    Итак, по-порядку. Командой POP DE мы
"снимаем" адрес, куда должен возвратить-
ся  "процесс"  в  случае  его завершения
командой RET. Но нам надо, чтобы он ука-
зывал не на команду после CALL, a именно
на  CALL!  Для этого делаем три раза DEC
DE.  Кстати,  в  HL уже находится адрес,
где расположен текст макроса.           
                                        
    Затем  мы  помещаем DE в ячейку, где
находится  CALL,  кстати следующий сразу
после этого "назначения" и выполняем пе-
реход  на  дальнейший вывод символов. По
сути, мы используем одну процедуру "саму
в себе" для двух задач "вложенных одна в
другую".                                
                                        
                                        
    И  вот  вывод  макроса  был выoлнeн.
Выполнение    возвратилось    обратно во
внутрь  процедуры  OPRSYMM.  И нам нужно
продолжать выводить строку...           
                                        
    Но постойте! A как же определить те-
перь  адрес, куда возвратиться процедура
OPRSYM, ведь вывод  должен  продолжаться
уже после вывода "макроса"...           
                                        
    A  помните процедуру выхода из прог-
раммы  по  окончании строки?.. Взгляните
на нее. Что там?                        
                                        
    POP  DE:RET!.. B DE y нас помещается
на два абзаца выше упомянутый адрес! Te-
перь  нам его остается поместить на стек
(PUSH   DE),   восстановить  регистр HL,
обозначающий  адрес  в тексте, увеличить
его и спокойно JP`кать на OPRSYM...     
                                        
    Выходит,  что  POP  DE:RET  нам тоже
cocлyжилo, можно сказать, две службы!   
                                        
                 - - -                  
                                        
    И  вот еще кое-что, что смог открыть
для  себя  в процессе небольших экспери-
ментов.  Оказывается, альтернативный AF`
не  портится  TR-DOS`ом  (в  отличии  от
BASIC48)! По крайней мере в группе стан-
дартных  команд через #3D13. Но даже не-
смотря  на  лояльность:) к AF` TR-DOS`a,
видится    возможность   использовать 4-
тактовый ЕХ AF,AF` вместо, допустим, 11-
тактовой  PUSH  AF, что в случаях, когда
регистр  нужно "запомнить" для его даль-
нeйшeй  "порчи" - выглядит хорошим peшe-
нием!                                   
                                        
                 - - -  



Other articles:


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Similar articles:
Introduction - The joyful news.
Special Edition - International Festival of Computer Art Chaos Constructions '999 (part 3).
BIRTHDAY - Happy Birthday Alex Stranger.
Advertising - Advertisements and announcements ...
Interface - interview with Ahim.

В этот день...   23 November