Предполагается, что читатель знаком с работой на программируемом микрокалькуляторе МК-61 (далее просто ПМК), т.е. с документированными возможностями.
Целью использования недокументированных возможностей чаще всего
является сокращение длины программы (самый "узкий" ресурс),
реже - высвобождение дополнительных регистров памяти.
Большинство таких возможностей реализуется или используется только в
контексте программного режима.
Иногда такие возможности используются для получения видео изображения,
которое невозможно создать обычным способом.
Также знание недокументированных возможностей поможет понять, почему
иногда программа ведет себя не совсем так, или совсем не так, как ожидалось.
В данной документе не рассматривается получение и исследование сверхчисел (число, порядок которого лежит вне диапазона -99..99) и пустышек (число, начинающееся с цифры F), которые исторически называется Еггогологией. Но из указанной Еггогологии взяты термины ЕГГ0Га и 3Г0ГГа, и кое-где используются для получения нестандартных чисел или результатов.
Далее для обозначения старших шестнадцатеричных цифр используется
стандартное обозначение ABCDEF, при этом в примерах отображения
используются вид цифр ПМК.
Например, число 8.FEDBCA9 (как инверсия числа 8.0123456), будет
выглядеть как 8. EГCL·9 .
Следует понимать, что в ПМК шестнадцатеричные только цифры, но не числа,
то есть ПМК по-прежнему считает каждый разряд как десятичный.
Т.е. число FF, которое в обычном случае равно 255,
будет интерпретироваться как F × 10 + F, т.е. 150 + 15 = 165.
Дробные числа или числа с большим порядком обычно записываются в научной нотации обычно как ±m.mmmmmmmE±pp. Чтобы не путать знак экспоненты с шестнадцатеричной цифрой E далее такие числа будем указывать в форме ±m.mmmmmmm^±pp (^ вместо E), причем знак порядка будем указывать всегда, а для надежности и точку (десятичный разделить) для мантиссы. Например, 1.23^-02 будет выглядеть как 1.23 -02.
Т.к. ПМК может использовать ненормализованные числа, то это важно отличать, пример: ненормализованное 0.005^-03 это 0.005 -03, а нормализованное - это 5.^-06 5. -06. Ненормализованные числа в тексте так и будут записываться с ведущими нулями.
Документированное значение для адресов программы - 105 ячеек. Фактически всё немного сложнее. Напомним, что адресация ячеек использует только две цифры, поэтому для адресов более 99 используются шестнадцатеричные цифры.
Адрес формальный | Адрес фактический | Доп. адрес |
---|---|---|
00 | 00 | |
01 | 01 | |
… | ||
99 | 99 | |
A0 | A0 | |
A1 | A1 | |
A2 | A2 | |
A3 | A3 | |
A4 | A4 | |
A5 | 00 | |
A6 | 01 | |
A7 | 02 | |
A8 | 03 | |
A9 | 04 | |
B0 | 05 | |
B1 | 06 | |
B2 | 00 | |
B3 | 01 | |
B4 | 02 | |
B5 | 03 | |
B6 | 04 | |
B7 | 05 | |
B8 | 06 | |
B9 | 07 | |
C0 | 08 | |
C1 | 09 | |
C2 | 10 | |
C3 | 11 | |
C4 | 12 | |
C5 | 13 | |
C6 | 14 | |
C7 | 15 | |
C8 | 16 | |
C9 | 17 | |
D0 | 18 | |
D1 | 19 | |
D2 | 20 | |
D3 | 21 | |
D4 | 22 | |
D5 | 23 | |
D6 | 24 | |
D7 | 25 | |
D8 | 26 | |
D9 | 27 | |
E0 | 28 | |
E1 | 29 | |
E2 | 30 | |
E3 | 31 | |
E4 | 32 | |
E5 | 33 | |
E6 | 34 | |
E7 | 35 | |
E8 | 36 | |
E9 | 37 | |
F0 | 38 | |
F1 | 39 | |
F2 | 40 | |
F3 | 41 | |
F4 | 42 | |
F5 | 43 | |
F6 | 44 | |
F7 | 45 | |
F8 | 46 | |
F9 | 47 | |
FA | 48 | 01 |
FB | 49 | 02 |
FC | 50 | 03 |
FD | 51 | 04 |
FE | 52 | 05 |
FF | 53 | 06 |
Первый цикл короткий - адреса A5-B1, которые отображаются на адреса 00-06.
Второй цикл длинный - адреса B2-F9, которые отображаются на адреса 00-37. Причем адреса C0-F9 являются темными, это значит в режиме ввода программы по этим адресам сами команды не отображаются. Например, если выполнить БП; 1; 0; FПРГ, и по адресам 10-12 ввести команды КНОП; КНОП; КНОП, то мы увидим 54 54 54 13. Но если затем перейти на адрес C5 (FАВТ; БП; c5; FПРГ), то получим C5, хотя фактически этот будет тот же программный код.
На супертемные адреса FA-FF можно перейти только косвенно, т.к.
впрямую адрес, начинающийся с цифры F не вбить.
Впрочем, некоторые нестандартные последовательности команд в ручном
режиме, описанные в конце приложения,
позволяют вставлять команды,
начинающиеся с F. А также для владельцев МК-52 есть штатный способ
сделать "or" команд с отдельного модуля, с получением кодов с цифрой F.
Так вот, особенность супер темных адресов такая, что после перехода на
него выполняется только одна(!) команда, а затем идет переход на
дополнительный адрес, который указан в последней колонке.
Если в адресе перехода вторая цифра тоже шестнадцатеричная, то как указано в предисловии, она прибавляется как есть (E=14 и т.п.). Таким образом переход на адреса 9F (90 + 15) или A5 (100 + 5) или AC (100 + 12 = 110 + 2 = B2) или B2, будет эквивалентно переходу на адрес 00, только потом порядок команд будет отличаться.
Если сама команда двойная, т.е. с адресом перехода (например, БП; Fx<0; FL1), и при этом идет "разрыв" адресации, то вторая часть команды (адрес перехода) считывается из нового места. Т.е. если по адресу 06 стоит команда БП, то в обычном потоке адрес перехода будет по адресу 07, но если попали сюда как на адрес B1, то адрес перехода будет по адресу 00.
Документированная возможность - регистр, используемый для косвенной адресации, содержит только целое неотрицательное число. Фактически значением его может быть любое число. Пусть число, которое содержится в регистре, который используется для косвенной адресации, записано в виде ±M1.M2M3M4M5M6M7M8^±P1P2, где M - цифры мантиссы, P - цифры порядка. При этом предполагается, что всегда 8 цифр мантиссы, просто последние могут быть нулевыми и обычно не отображаются.
При косвенном обращение сначала исходное число преобразуется по правилам, описанным ниже, а затем записывается обратно (для команд FL0…FL3 иногда нет). При этом всегда(!) цифры M7M8 преобразованного числа содержат адрес перехода (или номер регистра). Обратите внимание, что преобразованное число не нормализуется перед сохранением обратно, тем самым могут быть числа с ведущими нулями. Обычно это используется или для генерации специальных изображений или в комбинации с другими недокументированными операциями, которым важны только цифры мантиссы (пример в разделе Таинственный регистр X2).
В этом случае к числу слева дописывается 7 - P2 нулей (только если P2 < 7), "выталкивая" остальную часть мантиссы право. Например, если P2 = 4, то допишется 3 нуля, т.е. число будет в виде 0.00M1M2M3M4M5^+P17.
Пример: 123. , что представляется как 1.23^+02, перейдет в 0.0000123^+07, т.е. 00000123.В этом случае к числу слева дописывается P2 - 3 нулей (если P2 < 3, то проверяется, что P1 не ноль, и если так, то вычисляется 1P2 - 3, и только если и оно меньше нуля, то ничего не делается), "выталкивая" остальную часть мантиссы право. Например, если P2 = 5, то допишется 2 нуля, т.е. число будет в виде 0.0M1M2M3M4M5M6^-P13. Или если P2 = 0, а P1 = 1, допишется 7 нулей (10-3), т.е. число будет в виде 0.000000M1^-03.
Пример: 1.23 -02 не измениться, т.к. 02 < 3, т.е. адрес перехода будет равен нулю (M7M8).В этом случае все как указано выше, только дописываются не нули, а девятки. Знак самого числа сохраняется.
Пример: -123. перейдет в -99999123. , т.е. адрес перехода = 23.Тут нужно сделать замечание. Есть предположение, что девятки не случайны, а равны 10 - 1, где 10 это знак минус (как шестнадцатеричная цифра A). Для проверки такого предположения возьмем нестандартное число, где вместо знака стоит другая цифра. Для этого используем сверхчисла, которые называются 0С-оборотнями (потому что в результате они дают ноль, но оставляют "хвост" в регистре c). Это числа порядка 1.^400. А "хвост" как раз примечателен тем, что на месте знака образуется цифра.
Сx; ВП; 5; 0; Fx2; Fx2; Fx2; П→xc; ВП; 7; x→П9.
Мы увидим "хвост" 2E. , где двойка стоит на месте знака. Нажав /-/ получим девятку, впрочем, можно получить сходным образом и другие цифры, кроме единицы, если программно умножить сверхчисло на 2…8 по программе:00.КНОП; 01.1; 02.ВП; 03.5; 04.0; 05.Fx2; 06.Fx2; 07.Fx2; 08.×; 09.x→П9. 10.П→x9; 11.П→xc; 12.ВП; 13.7; 14.С/П.
Так вот, возвращаясь к первому примеру, при косвенной адресации: КП→x9; П→x9. Двойка уменьшается на единицу и становится 21111111E. . Аналогично для других чисел, например, если для 9E. (которое после /-/) выполнить косвенную адресацию, то получим 28888888E. .
Раз уж затронули необычные числа, упомяну еще курьез. По указанной выше программе получим (для X=7) 8E. . Если теперь нажать КЗН (81. ) и взять квадрат(!) этого числа Fx2, то неожиданно получим -1. .
Теперь рассмотрим ситуацию, когда само значение регистра еще и меняется как описано в документации. Напомню, что изменения происходят не при любом косвенном обращении, а только когда точно вычисляется значение адреса для перехода: для условных операторов - только когда условие не выполнилось.
Для них преобразованное(!) значение предварительно уменьшается на 1 без(!) учета порядка и знака. В расчет берутся только (но все) цифры мантиссы. При этом если был ноль, то преобразуется в -99999999. Из того, что знак не учитывается выходит, что для отрицательных чисел получается увеличение числа (в арифметическом смысле), а не уменьшение. Примеры:
123. ,
что представляется как 1.23^02, перейдет в 0.0000123^+07,
затем уменьшится до 0.0000122^+07 и превратиться в
00000122. ,
т.е. адрес перехода = 22.
1.23 -02,
что представляется как 1.2300000^-02 уменьшится до 1.2299999^-02
превратиться в 1.2299999-02,
т.е. адрес перехода = 99.
-123. ,
что представляется как -1.23^+02, преобразуется в -9.9999123^+07,
затем уменьшится до -9.9999122^+07, превратиться в
-99999122. ,
т.е. адрес перехода = 22.
-1.2345678-08, преобразуется в -9.9999123^-03,
затем уменьшится до -9.9999122^-03, превратиться в
-9.9999122-03, т.е. адрес перехода = 22.
Обратите внимание, что при уменьшении до нуля порядок числа сохраняется. Например, если исходно было 1.^-20, то после преобразования будет 0.0000001^-13, а после уменьшения станет 0.0000000^-13, т.е. 0. -13. Правда такой ноль, кроме необычного вида, в операциях ничем от простого нуля не отличается. Но в сочетании с другими недокументированными возможностями, которые могут объединить мантиссу и порядок от разных чисел (см. Таинственный регистр X2) это может пригодится. Таким же образом из 1.^90 получается 0. 97.
Для них преобразованное значение предварительно увеличивается на 1, так же без учета порядка и знака. При этом если было ±99999999 (все девятки), то преобразуется в ноль. Примеры:
123. ,
что представляется как 1.23^+02, перейдет в 0.0000123^+07, затем
увеличится до 0.0000124^+07 и превратиться в
00000124. , т
.е. адрес перехода = 24.
1.23 -02,
что представляется как 1.2300000^-02, увеличится до
1.2300001^+02 превратиться в
1.2300001-02, т.е. адрес перехода = 01.
-123. ,
что представляется как -1.23^+02 преобразуется в -9.9999123^+07,
затем увеличится до -9.9999124^+07, превратиться в
-99999124. ,
т.е. адрес перехода = 24.
-1.2345678-08, преобразуется в -9.9999123^-03,
затем увеличится до -9.9999124^-03, превратиться в
-9.9999124-03, т.е. адрес перехода = 24.
Для них действуют те же правила, что для регистров R0…R3, только перед уменьшением проверяется, что полученное число не равно единице (опять же без учета знака и порядка). В случае если получится единица, происходит завершение цикла и преобразованное значение НЕ записывается обратно в регистр. Примеры:
123. ,
что представляется как 1.23^+02, перейдет в 0.0000123^+07,
затем уменьшится до 0.0000122^+07 и превратиться в
00000122. ,
цикл продолжается.
1.23 -02,
что представляется как 1.2300000^-02 уменьшится до 1.2299999^-02
превратиться в 1.2299999-02,
цикл продолжается.
1.23 ,
преобразуется в 00000001, равно единице - цикл завершиться, а
исходное число не измениться, т.е. останется
1.23
1.23 -10,
что представляется как 1.23^-10, преобразуется до 0.0000001^-03,
равно единице - цикл завершиться, а исходное число не измениться.
Проверить "на единицу" исходное можно через регистры R7…Re. Если после КП→xR; П→xR на экране мантисса будет 00000001 (и где-то точка), то значит цикл прервется на исходном числе.
В этом случае уменьшение идет сначала шестнадцатеричной цифры до 9, а затем как обычно. При уменьшении ниже 0 идет заем (уменьшение на 1 предыдущего, возможно так же шестнадцатеричного), при это текущая цифра будет не F, а как обычно 9.
Переход так же будет на шестнадцатеричный адрес, но с этим мы уже знакомы (см. Программное адресное пространство).
К сожалению, если идет увеличение, то число с шестнадцатеричными цифрами предварительно подвергается нормализации (справа налево, смысл как указано во введении): буквы считаются как двузначные, с переносом лишней единицы в предыдущий разряд. Например, число 9AE и +1 будет так: E=14, значит последняя цифра 4, + 1 = 5 и 1 «в уме»; затем A = 10, + 1 в уме, будет 11, т.е. 1 и 1 в уме; далее 9 + 1 = 10. Итого 1015. Это значит, что косвенная адресация через R4…R6 всегда уберет шестнадцатеричные цифры из числа. При этом нестандартный знак числа (см. замечание в варианте для отрицательных чисел) остается.
На самом деле без Еггогологии можно получить только 1.^±HH, используя оператор F10x.
Для чисел вида 1.^+0H (одна цифра) никаких преобразований не производится, а значит адрес перехода всегда будет нулевым. Если идет увеличение/уменьшение (через выбор регистра), то мантисса увеличивается/уменьшается в соответствии с правилами выше, а знак и порядок не меняется. Пример: если в регистр R0 записать 1. 0E и выполнить КП→x0, то результатом станет уменьшение мантиссы 100000000 до 09999999, т.е. 0.9999999 0E.
Для двузначных чисел порядка ситуация сложнее. Возьмем, для примера 1.^+B0 ( 1. L0). Кстати, 1.^+AA будет автоматически переведено в 1.^+B0, как и 1.^+0A будет автоматически переведено в 1.^+10 (чем-то именно A не нравиться, для остальных цифр такого не замечено, хотя и тут можно обмануть, если для 1^.-LL набрать ВП 55). Так вот, по сути это число равно 1.^+110. Как видно, вместо знака порядка используется цифра, но для ПМК это тоже, что и знак, поэтому будет применено правило для отрицательных порядков, то есть допишется слева 7(10-3) нулей, что приведет к 0.0000001^117. Т.е. в качестве адреса перехода будет 01, но само число из регистра уже лучше не извлекать, т.к. оно будет сверхчислом и на экране отображаться как ЕГГ0Г.
Если у двузначных чисел порядок отрицательный, то срабатывает правило "сумма=160", это значит, что внутренне порядок меняется на число по модулю 160. Для примера, возьмем 1.^-C0 ( 1. -C0). Это эквивалентно 1.^-120, а по правилу =160, как 1.^+40. Используя правило преобразования для чисел больше единицы, получаем, что число будет преобразовано в 0.0000001^+47, соответственно адрес перехода 01, а полученное после косвенной адресации число можно просто извлечь из регистра: будет 0.0000001 47 Для сравнения, если взять 1.^-C1, то правило =160, приведет к 1.^+39, что не поддается преобразованию (9 > 7), а значит само число после косвенной адресации не измениться и останется 1. -C1 (адрес переход равен 00). Да, к этому число можно прибавить 0, чтобы увидеть 1. 39.
Если идет увеличение/уменьшение (через выбор регистра), то мантисса увеличивается/уменьшается в соответствии с правилами выше.
Кстати, в связи с этим вспомнилась еще одна не документированность. Возьмем число 1.^-E0 ( 1. -E0):
8; 8; В↑; 6; 6; К∨ /-/; К{x} ВП; 2; F10x.
Получилось число как 1.^-140 или по правилу 160 = 1.^20. Теперь если убрать еще 20 из порядка:ВП; 2; 0; /-/; x→П9.
то мы получим очень интересное число 1.^-00 ( 1. -00).
У этой единицы есть замечательные способности:Если косвенное значение представляет не адрес, а номер регистра, то его номер также определятся двумя последними числами, разбиваясь на два варианта, когда первая цифра значения ноль, и когда нет:
Первая ноль | Первая НЕ ноль |
---|---|
00→R0 | #0→Ra |
01→R1 | #1→Rb |
02→R2 | #2→Rc |
03→R3 | #3→Rd |
04→R4 | #4→Re |
05→R5 | #5→R0 |
06→R6 | #6→R0 |
07→R7 | #7→R1 |
08→R8 | #8→R2 |
09→R9 | #9→R3 |
0A→Ra | #A→R4 |
0B→Rb | #B→R5 |
0C→Rc | #C→R6 |
0D→Rd | #D→R7 |
0E→Re | #E→R8 |
0F→R0 | #F→R9 |
Вот пример использования знаний косвенной адресации для номеров регистров: программа заполняет регистры R0…R3 инвертированными случайными числами, при этом используется только один регистр R0.
00.Сx; 01.x→П0; 02.КСЧ; 03.КИНВ; 04.Кx→П0; 05.П→x0; 06.Fx⩾0; 07.02; 08.С/П
Еще практических советов. Уменьшение шестнадцатеричной цифры для БЗ-34 было единственным способом из цифры E получить остальные цифры. В МК-61 с приходом бинарных операций стало проще, но т.к. первая цифра все еще не во власти этих операций, то через косвенную адресацию получается быстрее, чем через дробную часть и ввод порядка.
Особенность не уменьшения числа по окончании цикла, с учетом того, что фактически число может сильно отличаться от единицы (см. пример выше) позволяет использовать команды FLx для быстрой проверки значения регистра с одновременным переходом при удаче/неудаче.
Такой регистр действительно не упоминается в документации. В действительности этот тот регистр, содержимое которого отображается на дисплее при остановке ПМК. Поэтому его иногда называют экранным регистром.
В документации указано, что таковым является регистр X, но это не так. В режиме вычислений после каждой команды содержимое регистра X сразу же копируется в X2. И поэтому разницы между ними нет. При таком копировании производятся дополнительные проверки (например, на предмет переполнения). Видимо в целях экономии в программном режиме при выполнении большинства(!) команд такое копировании не производится. Более того (а это и есть самое полезное), некоторые команды позволяют сделать и обратное копирование.
Для начала опишем список команд, которые копируют содержимое X в X2 (после выполнения, конечно). Назовем такие команды как X2-влияющие.
Безусловно выполняют копирование X→X2 следующие команды:Остальные операторы НЕ копируют X в X2. А это, например, означает, что регистр X во время таких операций может содержать сверхчисло (больше или равно 10100). При этом, конечно, нужно учитывать, чтобы команда сам по себе не делала такую проверку на переполнение. Например, F10x делает проверку параметра перед выполнением, а Fx2 - нет.
Для пояснения рассмотрим такую программу:
00.КНОП; 01.5; 02.0; 03.F10x; 04.Fx2; 05.Fx=0; 06.07; 07.Сx; 08.С/П.
Можно увидеть, что на шаге 04 в регистре X возникает сверхчисло. Причем оператор 05.Fx=0 не выполняется, т.е. идет переход на адрес 07 (как адрес перехода). В соответствии с таблицей выше, в этом случае копирование X в X2 не происходит, а значит, ошибки не должно возникать. В чем легко убедиться, запустив программ.
Но если в программе заменить оператор на 05.Fx≠0, то условие уже будет выполняться, и будет выполняться копирование X в X2, которое приведет к ошибке. Что также проверяется запуском программы.
Все это не имело бы практического смысла, если бы не было команд, которые делают обратное копирование X2→X.
Данная команда в программном режиме восстанавливает в регистре X значение X2 (за исключением случая, когда идет обычный ввод числа, например, 1.23, в этом случае поведение полностью соответствует документации и эквивалентно режиму вычислений). При этом содержимое стека не меняется. Также команда ничего не делает после X2-влияющей команды, но это и логично.
На практике такую команду чаще всего используют для экономии регистров,
реже - когда требуется ввод без изменения стека.
Пример: проверка битового флага и если его нет, то его установка.
Пусть в R9 храниться некое число для работы с битами, а в R1 бит
для проверки, тогда следующий фрагмент сначала сделает проверку
доступности бита, а при недоступности, сделает его установку:
00.П→x9; 01.П→x1; 02.К∧; 03.К{x}; 04.Fx≠0; 05.77; …; 77..; 78.К∨; 79.x→П9.
Получается, что оператор "точка" по адресу 77 восстановит R1 в X без сдвига стека, что позволит сразу выполнить бинарную операцию. Обычно само значение из R1 "вычислено", а не храниться в регистре. Обратите внимание, что выбрана не X2-влияющая проверка с переходом.
Еще пример - использовать оператора точка как входной параметр подпрограммы, т.е. эта команда стоит первым оператором. В этом случае, например, после ввода пользователя (после команды С/П, которая всегда копирует X в X2), сначала выполняются некие вычисления, не затрагивающие X2 (в том числе сохранение этих результатов в регистры), а затем вызов подпрограммы, которая таким образом "видит" ввод пользователя.
Для этой команды существуют несколько условий и правил восстановления. Обращаю внимание, что после X2-влияющей команды она ведет себя как обычно (как документировано). Причем, даже если она используется нестандартно (и выполнит некое восстановление), то все равно после ее выполнения ввод цифр будет восприниматься как ввод порядка.
Пример. Пусть нам нужно обработать ввод выбора пользователя для перемещения в некоем 3-ном лабиринте. Обычно используются клавиши 2,4,6,8,±5 (что соответствует направлению движения). Рассмотрим такую последовательность (в предположении, что выбор пользователя храниться в регистре R9 и значение 0 имеет еще какой-то дополнительный смысл):
00.П→x9; 01.2; 02.÷; 03.Fx≠0 04.77; 05.Fπ; 06.+; 07.x→П9; 08.ВП; 09.Fx=0 10.55; 11.КП→x9
Что здесь происходит? На шаге два мы получим одно из чисел 1, 2, 3, 4, ±2.5 или 0. Условным оператором мы не только отсекаем вариант с нулем, но делаем X→X2. Далее к полученному числу сразу прибавляем π и сохраняем в R9 для дальнейшей косвенной адресации (пусть в R4…R7 хранятся коэффициенты умножения для выполнения движения). Обращаю внимание, что тут использованы не X2-влияющие команды. После команды ВП мы "восстановим" в X то значение, что было после деления на шаге 02, только без первой цифры, т.е. ноль (для 1…4) или ±0.5, чтобы использовать это для последующего ветвления программы (умножение на коэффициент деления, а для ±0.5 можно будет взять знак числа и т.д.). Без использования ВП потребовалось бы использовать или дополнительный регистр, или дополнительные команды по манипуляции со стеком.
Дополнительные условия. Если содержимое X2 равно нулю, то будет восстановлена единица.
Если в момент восстановления (выполнения команды
ВП) содержимое регистра X меньше нуля,
то вместо удаления первой цифры у числа X2,
она будет заменена на 9.
Например, после выполнения программы
00.Fπ; 01./-/; 02.Fπ; 03.×; 04.x→П9; 05.ВП; 06.С/П
на экране будет -9.1415926 в R9 будет -π×π (команда по адресу 01 X2-влияющая).
Рассмотри последовательность КНОП; ВП. При этом КНОП взята как более нейтральная, могут быть и другие не X2-влияющие команды, назовем это первой командой. Тут важно учитывать:
Пример:
00.Fπ; 01.КИНВ; 02.КЗН; 03.FВx; 04.⟷; 05.КНОП 06.ВП; 07.С/П.
В результате получим 1.ELE·6Г9 . Тут дробная часть понятна - это инверсия числа π которое сохраняется X2-влияющей командой по адресу 03. А интересна тут цифра 1 на первом месте, которая появилась в стеке по команде КЗН. Именно она подставляется вместо восьмерки при восстановлении.
Интересно, что первой цифрой может быть и шестнадцатеричная, тем самым можно получить то, что в режиме вычислений трудно сделать. Например,
00.КНОП 01.9; 02.F1/x; 03.5; 04.×; 05.КИНВ; 06./-/; 07.К{x}; 08.КНОП 09.ВП; 10./-/; 11.9; 12.9; 13.С/П.
Будет 10(!) "минусов". --.--------99. Тут X2-влияющая команда по адресу 06 фиксирует 8.AAAAAAA (заодно добавляя минус), а оператор дробной части оставляет на первом месте тоже цифру A. Все это последовательность с ВП успешно объединяет, а порядок -99 в конце дописывается для красоты.
Еще пример. Пусть нужна подпрограмма, которая из первой цифры (целой части) дробного числа в регистре X делает букву. Например, 1→E, 2→D,... 4→A. В режиме вычислений для этого подошла бы последовательность
00.1; 01.0; 02.+; 03.КИНВ; 04.К{x}; 05.ВП; 06.1; 07.К[x].
С учетом специфики выполнения команды ВП в программном режиме нужно еще скопировать X→X2 после команды К{x}, т.е.
00.1; 01.0; 02.+; 03.КИНВ; 04.К{x}; 05.В↑; 06.ВП; 07.1; 08.К[x].
Это +1 команда, к тому же стек будет испорчен. Рассмотрим
00.1; 01.F10x; 02.+; 03.КИНВ; 04.К{x}; 05.КНОП; 06.ВП.
Здесь, благодаря восстановлению единицы (шаг 0), на шаге 06, останется только одна шестнадцатеричная цифра. Так недокументированная последовательность сделала подпрограмму короче на два шага.
Нужно понимать, что если число в X2 не нормализовано, то все равно заменяется первая цифра. Воспользуемся знаниями косвенной адресации:
00.КНОП; 01.4; 02.x→П7; 03.КБП7; 04.П→x7; 05.КНОП; 06.ВП; 07.С/П.
В данном случае 4 после косвенного перехода становится 00000004, а значит замена первой цифры приведет к 40000004. , в чем легко убедиться, запустив программу.
Теперь рассмотрим особенности. Если число X до начала последовательности было нулем, то вместо первой цифры устанавливается ноль. Обычно это значит, что число будет без первой цифры (начальный ноль не значащий). Но если он уже и так там был (например, число - это результат косвенной адресации с ведущими нулями), тогда никаких полезных действий не будет. Впрочем, иногда, именно это различие можно использовать, чтобы узнать, выполнялась ли косвенная адресация или нет.
Если X2 нулевое, то тут как бы проявляется известный трюк с ВП, который 0 превращает в 1. В данном случае первая цифра числа X увеличивается на 1. Вот фрагмент:
00.КНОП; 01.0; 03.⟷; 04.КНОП; 05.ВП; 06.С/П.
Если на вход этой программе дать 5, то выдаст 6. А если 9, то выдаст...A(!), потом аналогично B, C, D, E, F. Впрочем, последнее лучше тут же заменить на 0 и прочистить стек - пустышки коварны (во всяком случае, если на вход передать F, то программа однозначно будет перекручена во что-то неузнаваемое).
Еще пример:
00.F1/x; 01.КНОП; 02.ВП; 03.С/П.
На вход -9. , на выходе первая цифра от обратной величины -1. .
В этом случае также идет восстановление X2, но при этом первая цифра меняется на 7. Знак и порядок сохраняются. Аналогично тому, что и ранее, если число в X2 не нормализовано, то все равно идет замена первой цифры, оставляя остальные как есть. В случае X2=0 число будет восстановлено как 8.
Для условных переходов это работает только если идет переход на адрес. Если же условие выполняется (т.е. без перехода), то поведение будет как указано выше (фактически команда будет проигнорирована). Если первая после перехода не ВП, то тоже по правилам для ВП ранее. Пример.
00.КНОП; 01.1; 02.0; 03.x→П8; 04.Fx2; 05.КБП8; … 10.ВП; 11.С/П.
После остановке на экране будет 70. , т.е. восстановлено X2=10 (вместо 100), и первая цифра заменена на 7.
Еще пример.
00.КНОП; 01.Fπ; 02.6; 03.+; 04.x→П9; 05.КП→x9; 06.П→x9; 07.Fπ; 08.КБП9; 09.ВП; 10.С/П.
По уже указанными правилам будет 70000009. . Если заменить команду по адресу 08 на Кx=09, то ничего не измениться, т.к. условие не выполниться и будет переход. Но если заменить на Кx≠09, то уже условие выполниться, перехода не будет, и сработает старое правило, т.е. будет использована первая цифра числа в X, т.е. 30000009. .
00.КНОП; 01.0; 02.⟷; 03.КНОП; 04.ВП; 05.ВП; 06.С/П.
На вход 6, на выходе D(6+7=13). На вход D, на выходе B(13+14=27=16+11).
Обычно команда . восстанавливает в X значение X2, но ВП тоже пытается восстановить X2, но 1-ю цифру берет ту, что было за 2 хода в до нее в X. Рассмотрим
00.КНОП; 01.1; 02.5; 03.Fx2; 04.Fx2; 05.КНОП; 06..; 07.ВП; 08.С/П.
Результат 55, потому что 152=225, 2252=50625 и «выигрывает» ВП, который восстановит X2(15), с первой цифрой 5. Если убрать второй КНОП, то результат будет 25 (2 от 225), т. е. второй x2 будет проигнорирован, как будто команда «.» отодвинула ВП вглубь. Если поменять KНОП и «.» местами, то будет 15, т. к. «.» восстановит 15 и уже с ним работает ВП, как в ручном режиме. Другой пример:
00.КНОП; 01.1; 02.5; 03.Fπ; 04.x→П9; 05.КНОП; 06..; 07.ВП; 08.С/П.
Здесь «.» отодвигает до хП9, т.е. восстановится 15 с цифрой 3, т.е. 35. Если убрать КНОП, то поведение будет как у хП9;ВП, т.е. 15 без первой цифры (5)".
Причиной нестандартного поведения команд ВП и . является то, что подпрограммы ввода числа в ПМК, в том числе его порядка, работают напрямую с регистром X2.
Рассмотрим пример, который затрагивает другую команду ввода числа:01.B↑; 02.ВП; 03.2; 04./-/; 05.С/П.
Очевидно, что тут произойдет деление на 100. Этот код ведет себя в соответствии с документацией. Немного дополним его:01.B↑; 02.ВП; 03.2; 04.Fπ; 05.Fx2; 06.Fx2; 07./-/; 08.С/П.
Если посмотреть, выглядит так, что после ввода порядка (умножения на 100) мы берем еще π, два раза возводим его в квадрат, а затем делаем отрицательным. На самом деле это фрагмент делает то же, что и первый! Операции ввода числа работают с X2, в промежутке все команды - не X2-влияющие, они, конечно, будут выполнены, но результат будет отброшен при выполнении команды /-/, которая продолжит ввод числа (в данном случае порядка) в регистре X2. То, что промежуточные команды выполнялись, можно узнать по содержимому стека - в регистре Y будет входное значение, умноженное на 100, а в X1 - квадрат от π.Еще пример. Известно (хотя не документировано), что для запрета ввода точки при вводе порядка, сочетание команд ВП и . генерит ошибку. Более того, в отличии от остальных способов получения ошибки он отличается тем, что
Но это сочетание также работает с X2, т.е. игнорирует все не X2-влияющие команды. В связи с этим становится ясно, что следующий фрагмент:
01.B↑; 02.Fx2; 03.ВП; 04.Fx2; 05.Fx2; 06..; 07.Fx2; 08.С/П.
остановится по ошибке уже на команде . (т.е. следующим для исполнения будет адрес 07) и успеет возвести в квадрат только дважды (первый квадрат, как не X2-влияющий будет отброшен по команде ВП).Речь идет об обычных арифметических операциях, но в ситуации, когда операнд представляет собой число, содержащее шестнадцатеричные цифры A..E (F убрано из рассмотрения как опасное, по крайней мере в качестве первой цифры).
Для простоты рассмотрим ситуации, когда число состоит из одной шестнадцатеричной цифры. Далее будем ее обозначать буквой H. Для более ясного порядка операндов будем использовать стандартные обозначения X и Y.
Y∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 |
1 | 1 | 2 | 3 | 4 | 5 |
2 | 2 | 3 | 4 | 5 | 0 |
3 | 3 | 4 | 5 | 0 | 1 |
4 | 4 | 5 | 0 | 1 | 2 |
5 | 5 | 0 | 1 | 2 | 3 |
6 | 0 | 1 | 2 | 3 | 4 |
7 | 1 | 2 | 3 | 4 | 5 |
8 | 2 | 3 | 4 | 5 | 6 |
9 | 3 | 4 | 5 | 6 | 7 |
A | 4 | 5 | 6 | 7 | 8 |
B | 5 | 6 | 7 | 8 | 9 |
C | 6 | 7 | 8 | 9 | 10 |
D | 7 | 8 | 9 | 10 | 11 |
E | 8 | 9 | 10 | 11 | 12 |
10 | 10 | 21 | 22 | 24 | 24 |
11 | 21 | 22 | 23 | 24 | 25 |
12 | 22 | 23 | 24 | 25 | 26 |
13 | 23 | 24 | 25 | 26 | 27 |
14 | 24 | 25 | 26 | 27 | 28 |
15 | 25 | 26 | 27 | 28 | 29 |
16 | 26 | 27 | 28 | 29 | 30 |
17 | 27 | 28 | 29 | 30 | 31 |
18 | 28 | 29 | 30 | 31 | 32 |
19 | 29 | 30 | 31 | 32 | 33 |
1A | 30 | 31 | 32 | 33 | 34 |
1B | 31 | 32 | 33 | 34 | 35 |
1C | 32 | 33 | 34 | 35 | 20 |
1D | 33 | 34 | 35 | 20 | 21 |
1E | 34 | 35 | 20 | 21 | 22 |
1F | 35 | 20 | 21 | 22 | 23 |
Вычисление идет как шестнадцатеричное, остаток по модулю 16, а от
него берется последняя цифра (чтобы осталась одна).
X = ((X + Y) mod 16) mod 10.
Если Y двузначное, то уже две цифры X = ((X + Y) mod 256)
mod 100, а значит получается как обычное сложение, если число
двузначное.
Если Y дробное, то целая часть как выше, а дробная сохраняется.
В этом случае осуществляется обычное сложение, только результат нормализуется. Например, 0 + A = 10; 9 + E = 23. В случае шестнадцатеричного X см. таблицу выше.
Y∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | -10 | -1 | -2 | -3 | -4 |
1 | -9 | -10 | -1 | -2 | -3 |
2 | -8 | -9 | -10 | -1 | -2 |
3 | -7 | -8 | -9 | -10 | -1 |
4 | -6 | -7 | -8 | -9 | -10 |
5 | -5 | -6 | -7 | -8 | -9 |
6 | -4 | -5 | -6 | -7 | -8 |
7 | -3 | -4 | -5 | -6 | -7 |
8 | -2 | -3 | -4 | -5 | -6 |
9 | -1 | -2 | -3 | -4 | -5 |
A | 0 | -1 | -2 | -3 | -4 |
B | 1 | 0 | -1 | -2 | -3 |
C | 2 | 1 | 0 | -1 | -2 |
D | 3 | 2 | 1 | 0 | -1 |
E | 4 | 3 | 2 | 1 | 0 |
10 | 0 | 15 | 14 | 13 | 12 |
11 | 1 | 16 | 15 | 14 | 13 |
12 | 2 | 17 | 16 | 15 | 14 |
13 | 3 | 18 | 17 | 16 | 15 |
14 | 4 | 19 | 18 | 17 | 16 |
15 | 5 | 20 | 19 | 18 | 17 |
16 | 6 | 21 | 20 | 19 | 18 |
17 | 7 | 22 | 21 | 20 | 19 |
18 | 8 | 23 | 22 | 21 | 20 |
19 | 9 | 24 | 23 | 22 | 21 |
1A | 0 | 25 | 24 | 23 | 22 |
1B | 1 | 10 | 25 | 24 | 23 |
1C | 2 | 11 | 10 | 25 | 24 |
1D | 3 | 12 | 11 | 10 | 25 |
1E | 4 | 13 | 12 | 11 | 10 |
1F | 5 | 14 | 13 | 12 | 11 |
Выглядит похоже на X = (Y - X) mod 16, но не всегда понятно, когда
берется заем, а когда нет. Число A какое-то особенное для двузначных.
Для трех- и выше значных повторяется как для двузначных,
т.е. 100 - A = 90; 100 - B = 105,..., 109 - E = 111.
X∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | 10 | 11 | 12 | 13 | 14 |
1 | 9 | 0 | 1 | 2 | 3 |
2 | 8 | 9 | 0 | 1 | 2 |
3 | 7 | 8 | 9 | 0 | 1 |
4 | 6 | 7 | 8 | 9 | 0 |
5 | 5 | 6 | 7 | 8 | 9 |
6 | 4 | 5 | 6 | 7 | 8 |
7 | 3 | 4 | 5 | 6 | 7 |
8 | 2 | 3 | 4 | 5 | 6 |
9 | 1 | 2 | 3 | 4 | 5 |
A | 0 | 1 | 2 | 3 | 4 |
B | -1 | 0 | 1 | 2 | 3 |
C | -2 | -1 | 0 | 1 | 2 |
D | -3 | -2 | -1 | 0 | 1 |
E | -4 | -3 | -2 | -1 | 0 |
10 | 0 | 1 | 2 | 3 | 4 |
11 | -1 | 0 | 1 | 2 | 3 |
12 | -2 | -1 | 0 | 1 | 2 |
13 | -3 | -2 | -1 | 0 | 1 |
14 | -4 | -3 | -2 | -1 | 0 |
15 | -5 | -4 | -3 | -2 | -1 |
16 | -6 | -5 | -4 | -3 | -2 |
17 | -7 | -6 | -5 | -4 | -3 |
18 | -8 | -7 | -6 | -5 | -4 |
19 | -9 | -8 | -7 | -6 | -5 |
1A | -10 | -9 | -8 | -7 | -6 |
1B | -1 | -10 | -9 | -8 | -7 |
1C | -2 | -1 | -10 | -9 | -8 |
1D | -3 | -2 | -1 | -10 | -9 |
1E | -4 | -3 | -2 | -1 | -10 |
1F | -5 | -4 | -3 | -2 | -1 |
Нарушения обычного вычитания только для нескольких чисел (ниже и
правее, начиная с 1;B, для двузначных начиная с 1B;A).
Кстати, особенность: когда из шестнадцатеричного числа вычитается
его десятичный аналог, то ноль, на самом деле, ненормализованный
(т.е. из 4-х значного будет 0000), и это можно использовать
для получения нуля в любой степени. Пример (в ручном режиме):
И вот мы получили 0. 99.
Y∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 |
1 | 10 | 10 | 10 | 10 | 0 |
2 | 20 | 20 | 20 | 20 | 0 |
3 | 30 | 30 | 30 | 30 | 0 |
4 | 40 | 40 | 40 | 40 | 0 |
5 | 50 | 50 | 50 | 50 | 0 |
6 | 60 | 60 | 60 | 60 | 0 |
7 | 70 | 70 | 70 | 70 | 0 |
8 | 80 | 80 | 80 | 80 | 0 |
9 | 90 | 90 | 90 | 90 | 0 |
A | 00 | 00 | 00 | 00 | 0 |
B | 10 | 10 | 10 | 10 | 0 |
C | 20 | 20 | 20 | 20 | 0 |
D | 30 | 30 | 30 | 30 | 0 |
E | 40 | 40 | 40 | 40 | 0 |
10 | 100 | 100 | 100 | 100 | 0 |
11 | 110 | 110 | 110 | 110 | 0 |
12 | 120 | 120 | 120 | 120 | 0 |
13 | 130 | 130 | 130 | 130 | 0 |
14 | 140 | 140 | 140 | 140 | 0 |
15 | 150 | 150 | 150 | 150 | 0 |
16 | 160 | 160 | 160 | 160 | 0 |
17 | 170 | 170 | 170 | 170 | 0 |
18 | 180 | 180 | 180 | 180 | 0 |
19 | 190 | 190 | 190 | 190 | 0 |
1A | 200 | 200 | 200 | 200 | 0 |
1B | 210 | 210 | 210 | 210 | 0 |
1C | 220 | 220 | 220 | 220 | 0 |
1D | 230 | 230 | 230 | 230 | 0 |
1E | 240 | 240 | 240 | 240 | 0 |
1F | 250 | 250 | 250 | 250 | 0 |
Поразительное однообразие, почти все ведут себя как 10. Не ясно, чем
так отличается E, но ноль получается и для многозначных чисел.
Для двузначных соответственно, т. е. C × 20 = 200, но E × 20 = 0.
X∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | 2 | 3 | 4 |
2 | 4 | 6 | 8 | 10 | 12 |
3 | 4 | 1 | 4 | 23 | 10 |
4 | 8 | 2 | 0 | 20 | 24 |
5 | 50 | 11 | 32 | 53 | 42 |
6 | 0 | 22 | 44 | 50 | 40 |
7 | 10 | 33 | 40 | 63 | 54 |
8 | 20 | 44 | 52 | 60 | 68 |
9 | 30 | 55 | 64 | 73 | 82 |
A | 00 | 10 | 20 | 30 | 40 |
B | 00 | 10 | 20 | 30 | 40 |
C | 00 | 10 | 20 | 30 | 40 |
D | 00 | 10 | 20 | 30 | 40 |
E | 0 | 0 | 0 | 0 | 0 |
10 | 00 | 10 | 20 | 30 | 40 |
11 | 10 | 21 | 32 | 43 | 54 |
12 | 04 | 16 | 28 | 40 | 52 |
13 | 14 | 11 | 24 | 53 | 50 |
14 | 08 | 22 | 20 | 50 | 4 |
15 | 990 | 021 | 052 | 923 | 922 |
16 | 000 | 032 | 904 | 920 | 920 |
17 | 010 | 043 | 900 | 933 | 934 |
18 | 020 | 054 | 912 | 930 | 948 |
19 | 030 | 905 | 924 | 943 | 962 |
1A | 940 | 960 | 980 | 0 | 20 |
1B | 940 | 960 | 980 | 0 | 20 |
1C | 940 | 960 | 980 | 0 | 20 |
1D | 940 | 960 | 980 | 0 | 20 |
1E | 00 | 10 | 20 | 30 | 40 |
1F | 00 | 10 | 20 | 30 | 40 |
Тут уже трудно поддается логике. На практике автор как-то использовал D. С одной стороны, это изображение, с другой - коэффициент 10 (см. таблицу H × Y), а самое основное - это проверка битового сдвига. При умножении дробной части, содержащей 1, 2, 4, 8 (как бы биты) возможен выход за диапазон, т.е. 0.5 или 1.6. Так вот, при умножении на D по указанной таблице, результат получался из одной цифры, если все нормально, или из двух, при выходе за диапазон.
Для двухзначных результат бывает ненормализованным: обратите внимание на ведущие нули в некоторых случаях. На этом фоне 14 × E выглядит как белая ворона.
X∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | ЕГГ0Г | ЕГГ0Г | ЕГГ0Г | ЕГГ0Г | ЕГГ0Г |
1 | 0 | 1 | 2 | 3 | 4 |
2 | 5 | 5.5 | 6 | 6.5 | 7 |
3 | 3.3333333 | 3.6666666 | 4 | 4.3333333 | 4.6666666 |
4 | 2.5 | 2.75 | 3 | 3.25 | 3.5 |
5 | 2 | 2.2 | 2.4 | 2.6 | 2.8 |
6 | 1.6666666 | 1.8333333 | 2 | 2.1666666 | 2.3333333 |
7 | 1.4285714 | 1.5714285 | 1.7142857 | 1.8571428 | 2 |
8 | 1.25 | 1.375 | 1.5 | 1.625 | 1.75 |
9 | 1.1111111 | 1.2222222 | 1.3333333 | 1.4444444 | 1.5555555 |
A | 1 | 1.1 | 1.2 | 1.3 | 1.4 |
B | 8.4444443^-01 | 1 | 1.2525252 | 1.3434343 | 1.4343434 |
C | ЕГГ0Г | ЕГГ0Г | 1 | 1.23 | 1.3 |
D | 4.^-01 | 6.^-01 | 8.^-01 | 1 | 1.2 |
E | 5.2929292^-01 | 2.2929292^-01 | 5.2929292^-0 | 8.2929292^-01 | 1 |
10 | 0.^-01 | 1.^-01 | 2.^-01 | 3.^-01 | 4.^-01 |
11 | 9.090909^-01 | 0.^-01 | 0.9090909^-01 | 1.8181818-01 | 2.7272727-01 |
12 | 8.3333333^-01 | 9.1666666^-01 | 0.^-01 | 0.8333333-01 | 1.6666666-01 |
13 | 7.6923076^-01 | 8.4615384^-01 | 9.2307692^-01 | 0.^-01 | 0.7692307-01 |
14 | 7.1428571^-01 | 7.8571428^-01 | 8.5714285^-01 | 9.2857142-01 | 0.^-01 |
15 | 6.6666666^-01 | 7.3333333^-01 | 8.^-01 | 8.6666666-01 | 9.3333333-01 |
16 | 6.25^-01 | 6.875^-01 | 7.5^-01 | 8.125-01 | 8.75-01 |
17 | 5.8823529^-01 | 6.4705882^-01 | 7.0588235^-01 | 7.6470588-01 | 8.2352941-01 |
18 | 5.5555555^-01 | 6.1111111^-01 | 6.6666666^-01 | 7.2222222-01 | 7.7777777-01 |
19 | 5.2631578^-01 | 5.7894736^-01 | 6.3157894^-01 | 6.8421052-01 | 7.368421-01 |
1A | 5.^-01 | 5.1^-01 | 6.3157894-01 | 6.1^-01 | 7.^-01 |
1B | 5.0330001^-01 | 6.^-01 | 7.0001032-01 | 7.8330001-01 | 8.2330001-01 |
1C | 6.^-01 | 7.0003809^-01 | 7.8100038-01 | 8.1810003-01 | 8.100038-01 |
1D | 7.0005899^-01 | 7.9000589^-01 | 8.^-01 | 9.0005899-01 | 9.9000589-01 |
1E | 5.64^-01 | 5.90002^-01 | 6.4^-01 | 6.60002-01 | 7.24-01 |
1F | 5.4000299^-01 | 5.3223099^-01 | 6.153223-01 | 7.0002999-01 | 7.7000299-01 |
Здесь кроме случая X = 1, и некоторых двузначных это обычное
деление нормализованного шестнадцатеричного числа. Пример:
E ÷ 7 = 14 ÷ 7 = 2.
Правда и здесь встречаются ненормализованные числа
(пример D ÷ 12, или C ÷ 11, которое на порядок меньше,
чем A ÷ 11).
Встречаются и ненормализованные нули, пример: С ÷ 12, и
выглядит, как указано:
0. -01.
Впрочем, в косвенной адресации мы
уже встречались с подобными нулями.
Y∖H | A | B | C | D | E |
---|---|---|---|---|---|
0 | 9.090909^-01 | 9.9099099^-01 | 4.4444443 | 9.9099099^-01 | 9.9099099^-01 |
1 | ЕГГ0Г | 9.099099^-01 | 9.9099099^-01 | 9.9099099^-01 | 9.9099099^-01 |
2 | ЕГГ0Г | 8.4444443^-01 | 9.099099^-01 | 9.9099099^-01 | 9.9099099^-01 |
3 | ЕГГ0Г | 6.4444443^-01 | ЕГГ0Г | 9.099099^-01 | 9.9099099^-01 |
4 | ЕГГ0Г | 4.4444443^-01 | ЕГГ0Г | 8.^-01 | 9.099099^-01 |
5 | ЕГГ0Г | 2.4444443^-01 | ЕГГ0Г | 0.^-01 | 2.929292^-02 |
6 | ЕГГ0Г | 6.4444443^-01 | ЕГГ0Г | 2.^-01 | 3.2929292^-01 |
7 | ЕГГ0Г | 4.4444443^-01 | ЕГГ0Г | 4.^-01 | 6.2929292^-01 |
8 | ЕГГ0Г | 2.4444443^-01 | ЕГГ0Г | 0 | 9.2929292^-01 |
9 | ЕГГ0Г | 0.4444443^-01 | ЕГГ0Г | 2.^-01 | 2.2929292^-01 |
A | 1 | 8.4444443^-01 | ЕГГ0Г | 4.^-01 | 5.2929292^-01 |
B | 1.1 | 1 | ЕГГ0Г | 6.^-01 | 2.2929292^-01 |
C | 1.2 | 1.2525252 | 1 | 8.^-01 | 5.2929292^-01 |
D | 1.3 | 1.3434343 | 1.23 | 1 | 8.2929292^-01 |
E | 1.4 | 1.4343434 | 1.3 | 1.2 | 1 |
10 | ЕГГ0Г | 9.09909 | 9.9099099 | 9.9099099 | 9.9099099 |
11 | ЕГГ0Г | 9.099099 | ЕГГ0Г | 9.8 | 9.0292929 |
12 | ЕГГ0Г | 9.099099 | ЕГГ0Г | 0 | 9.3292929 |
13 | ЕГГ0Г | 9.099099 | ЕГГ0Г | 0.2 | 9.6292929 |
14 | ЕГГ0Г | 9.099099 | ЕГГ0Г | 0.4 | 9.9292929 |
15 | ЕГГ0Г | 9 | ЕГГ0Г | 9 | 0.22922929 |
16 | ЕГГ0Г | 9.2525252 | ЕГГ0Г | 9.2 | 0.5292929 |
17 | ЕГГ0Г | 9.3434343 | ЕГГ0Г | 9.4 | 9.2292929 |
18 | ЕГГ0Г | 9.4343434 | ЕГГ0Г | 9.6 | 9.5292929 |
19 | ЕГГ0Г | 9.5252525 | ЕГГ0Г | 9.8 | 9.8292929 |
1A | ЕГГ0Г | 9.6 | 9.9909909 | 9.9909909 | 9.9909909 |
1B | ЕГГ0Г | 9.8525252 | 9.9909909 | 9.9909909 | 9.9909909 |
1C | ЕГГ0Г | 9.9434343 | 9.9909909 | 9.9909909 | 9.9909909 |
1D | ЕГГ0Г | 0.0343434 | 9.9909909 | 9.9909909 | 9.9909909 |
1E | ЕГГ0Г | 9.099099 | 9.9909909 | 9.9909909 | 9.9909909 |
1F | ЕГГ0Г | 7.4444443 | 9.9909909 | 9.9909909 | 9.9909909 |
Тут логики не наблюдается. Очень похожие, но разные числа:
9.099099^-01 и 9.9099099^-01 (или 9.099099 и 9.9099099).
Также интересно, что ноль разделить на A вовсе не ноль.
А самое главное, тут не просто ЕГГ0Г, а плохой ЕГГ0Г,
который ранее не встречался. После его появления ПМК в дальнейшем
(до выключения ПМК) при выполнении многих операций
всегда выдает ЕГГ0Г. Вот список таких операций.
Ошибка возникает как в режиме вычислений, так и в программном режиме. Интересно, что F↻ выдает ошибку, а FВx - нет.
H∖F(H) | A | B | B | D | E |
---|---|---|---|---|---|
Fx2 | 00 | 10 | 20 | 30 | 0 |
F√ | 3.1622776 | 3.3166247 | 3.4641016 | 3.6055512 | 3.7416573 |
F1/x | ЕГГ0Г | 9.099099^-01 | 9.9099099^-01 | 9.9099099^-01 | 9.9099099^-01 |
Fex | 22026.467 | 59874.133 | 162754.78 | 442413.37 | 1202604.3 |
F10x | 1.^+10 | 1.^+0L | 1.^+0C | 1.^+0Г | 1.^+0E |
Flg | 1 | 41.823681 | 42.40274 | 42.816354 | 43.126564 |
Fln | 2.3025851 | 96.302585 | 97.635918 | 98.588299 | 99.302585 |
Fx1 | 10 | 6.6631773^+41 | 2.5277867^+42 | 6.551706^+42 | 1.3383338^+43 |
К[x] | 0 | 1 | 2 | 3 | 4 |
Про более редкие операции переводов градусов/часов сказано в приложении по командам.
Практическое применение - это получение нестандартного результата (как отличие от обычной цифры). Или получение нестандартной последовательности. Или как был приведен выше по умножению.
Случаи с двойными и более шестнадцатеричными числами (в том числе в дробной части) не рассмотрены, как очень редко встречающиеся. Там тоже можно построить подобные таблицы, но проще посмотреть результат под конкретное число.
Здесь рассмотрены некоторые способы, которые позволяют оптимизировать программу. Большинство трюков - это использование документированных возможностей, но возможно необычным образом. Ясно, что все варианты не рассмотреть, а лишь несколько для демонстрации, как нужно нестандартно подходить к вопросу оптимизации.
Иногда ради такой "подгонки" делают перестроение программы: перемешивание независимых кусков программы, располагая их по разным адресам. Сюда же относится пример из журнала ТМ №9 за 1985:
60.Fx<0; 61.61; 62.Fx⩾0; 63.63; 64.С/П.
Когда перед остановкой выводилось содержимое нужного регистра при проверке условия. Экономия на том, что адрес перехода совпадает с командой извлечения из регистра.00.Fx≠0; 01.06; 02.П→x9; 03.1; 04.+; 05.x→П9.
Решение с удалением условия:00.КЗН; 01.П→x9; 02.+; 03.x→П9.
В случае, если X может быть и отрицательным, будет чуть длиннее, но все равно короче прямого решения:00.КЗН; 01.К|x|; 02.П→x9; 03.+; 04.x→П9.
00.КП→x2; 01.БП; 02.77.
которое, кстати, портит стек (и для исправления может потребоваться еще команда), сделать:00.FL2; 01.77.
Разумеется, счетчик должен не кончаться или сразу после этого кода идет проверка по его окончанию. При это для "бесконечности" счетчика иногда делают его отрицательным, если в конце вычислений важна только разница между началом и концом.00.К{x}; 01.0; 02.FВx; 03.К[x]; 04.ПП; 05.07; 06.⟷; 07.…; 20.+; 21.В/О.
Что здесь происходит? Сначала подготавливается на будущее дробная часть, затем заталкивается ноль и оставляется целая часть. Далее вызывается часть кода (часть кода текущей подпрограммы), которая делает обработку над целой частью, а в конце делает сложение (с нулем, в данном случае). Затем целая и дробная часть меняются местами и код повторяется, причем в конце сложение уже делает объединение, а затем возврат. Где экономия? Если бы мы вызывали обрабатываемую часть по очереди, то нам все равно потребовалось бы разделять на целую и дробную часть, обрабатывать по очереди, а затем объединять сложением. Для этого потребовались бы все те же команды, кроме 0. Но при это пришлось бы делать дважды ПП, а это две команды. Заменив на одну команду 0 и вызвав свой "хвост", мы сэкономили одну команду.Список всех 256 команд ПМК с дополнительным комментарием, в случае наличия недокументированных возможностей.
Код | Вид | Название | Комментарий |
---|---|---|---|
00…09 | 0…9 | Ввод числа |
Ввод цифр идет даже через границу С/П. Т.е. если в начале программы
идет цифра(ы), а перед ее запуском то же ввод, то он продолжиться.
То же касается и разделителя ".", он может как заканчиваться ввод в
режиме вычислений, так и начинаться в программе - будет воспринят
как разделитель целой и дробной части.
Второе нажатие "." для разделения разрядов игнорируется. Если предыдущая команда была не ввод цифры или разделителя, а так же не команда В↑, то предварительно осуществляется сдвиг стека под новое число. |
0A | . | Разделитель целой и дробной части | Кроме того, что указано выше см. Таинственный регистр X2. |
0B | /-/ | Смена знака | Cм. Таинственный регистр X2. |
0C | ВП | Ввод порядка |
Если X=0, то заменяется на 1. Для многих чисел ввод порядка означает
умножение на 10 в соответствующей степени.
После ввода порядка можно еще раз нажать ВП и ввести число - в этом случае порядки складываются. Знак порядка можно ввести как сразу после ВП, так и после ввода цифр. Также см. Таинственный регистр X2. |
0D | Сx | Сброс X в ноль | Важное свойство - стек не двигается. |
0E | В↑ | Сдвиг стека | Если последующая команда - ввод числа, то будет ввод в X, если извлечение из памяти или π - то стек еще раз сдвинется. |
0F | FВx | Полный сдвиг, включая X1 | Единственная документированная и вводимая команда с цифрой F в коде. |
10 | + | Сложение | Результат проверяется только при X→X2, т.е. можно использовать для получения сверхчисел. |
11 | - | Вычитание | Результат проверяется только при X→X2, т.е. можно использовать для получения сверхчисел. |
12 | × | Умножение | Результат проверяется при X→X2, т.е. можно использовать для получения сверхчисел. |
13 | ÷ | Деление | Ошибка деления на ноль возникает безусловно, остальное при X→X2. |
14 | ⟷ | Обмен X и Y | |
15 | F10x | Возведение в степень числа 10 | Аргументы на переполнение порядка (сверхчисло) проверяется безусловно, а не при X→X2. |
16 | Fex | Возведение в степень числа e (экспонента) | Аргументы на переполнение порядка (сверхчисло) проверяется безусловно, а не при X→X2. |
17 | Flg | Десятичный логарифм | Аргументы (должно быть больше нуля) проверяется безусловно, а не при X→X2. |
18 | Fln | Натуральный (экспоненциальный) логарифм | Аргументы (должно быть больше нуля) проверяется безусловно, а не при X→X2. |
19 | Fsin-1 | Арксинус | Аргументы (должно |X|⩽1) проверяется безусловно, а не при X→X2. Для ГРД/Г и X=0 выводит X=00. |
1A | Fcos-1 | Арккосинус | Аргументы (должно |X|⩽1) проверяется безусловно, а не при X→X2. arccos(1) для ГРД/Г дает ненормализованное 00. |
1B | Ftg-1 | Арктангенс | |
1C | Fsin | Синус | |
1D | Fcos | Косинус | |
1E | Ftg | Тангенс | Аргументы проверяется безусловно, а не при X→X2. При X=π/2+n×π будет ошибка. |
1F | Пустой оператор | Обычным образом такую команду не ввести. | |
20 | Fπ | Число π (пи) | Кроме того, что сдвигает стек (это документировано), также копирует предыдущее X в X1, как арифметическая операция (а это нет). |
21 | F√ | Квадратный корень | Аргументы проверяется безусловно (X⩾0), а не при X→X2. |
21 | Fx2 | Возведение в квадрат | Результат проверяется при X→X2, т.е. можно использовать для получения сверхчисел. |
23 | F1/x | Обратная величина | Ошибка деления на ноль возникает безусловно, остальное - при X→X2. |
24 | Fxy | Возведение в степень. |
Ошибка, если X=0. Не принимает отрицательный X, даже когда это
математически допустимо.
Ошибка переполнения возникает безусловно, а не при X→X2. Стек не сокращается, в отличие от обычных арифметических операций. Т.е. число в Y остается на месте (позволяя еще раз возвести в ту же степень). |
25 | F↻ | Подтягивание стека | |
26 | К°→′ | Перевод угловых или временных величин из обычной формы в часть целой | Целая часть не меняется, даже если шестнадцатеричное число. |
27 | К− | Ошибка | ЕГГ0Г. |
28 | К× | Ошибка | ЕГГ0Г. |
29 | К÷ | Ошибка | ЕГГ0Г. |
2A | К°→′" | Перевод угловых или временных величин из обычной формы в часть целой, включая секунды | Целая часть не меняется, даже если шестнадцатеричное число. |
2B…2E | Ошибка | ЕГГ0Г. | |
2F | Пустой оператор | Обычным образом такую команду не ввести. | |
30 | К°←′" | Перевод угловых или временных величин из части целого в обычную форму, включая секунды | Если целая часть шестнадцатеричное число - оно преобразуется, как в операциях с шестнадцатеричными цифрами. |
31 | К∣x∣ | Модуль | |
32 | КЗН | Знак числа | Отрицательный ноль преобразуется в ноль. |
33 | К°←′ | Перевод угловых или временных величин из части целого в обычную форму | Если целая часть шестнадцатеричное число - оно преобразуется, как в операциях с шестнадцатеричными цифрами. |
34 | К[x] | Целая часть |
Простое отбрасывание дробной части, а не математическое нахождение
целой части, т.е. [-1.2] = -1, а не -2.
Если число целое и шестнадцатеричное - оно преобразуется, как в операциях с шестнадцатеричными цифрами. Если есть дробная часть, то целая шестнадцатеричная часть остается без изменений. |
35 | К{x} | Дробная часть |
Простое отбрасывание целой части, а не математическое нахождение
целой части, т.е. {-1.2} = -0.2, а не 0.8.
Может получится отрицательный ноль (в сравнениях идет как отрицательное число и как ноль). Шестнадцатеричные числа сохраняются. |
36 | Кmax | Максимум |
Ноль исключение - самое большое число.
Обмена X и Y не происходит, т.е. либо X и Y после операции совпадают (в Y был максимум), либо остаются как есть (кроме копирования X в X1). |
37 | К∧ | Логическое умножение (AND) |
В логических операциях первая цифра заменяется на 8, а над
остальными цифрами мантиссы проводится побитовая операция, как с
шестнадцатеричными числами. Порядок и знак числа значения не имеют.
Исходное число может даже быть пустышкой. Результат всегда в
форме 8.HHHHHHH
Стек для двухоперандных операций не сокращается, что позволяет выполнить операцию еще раз. |
38 | К∨ | Логическое сложение (OR) | |
39 | К⊕ | Логическое исключающее или (XOR) | |
3A | КИНВ | Логическая инверсия (NOT) | |
3B | КСЧ | Случайное число |
Ноль и единица не бывает. Датчик не очень хороший, часто
циклится (от применения команд с К).
Сбросить на начало (как при включении) можно с помощью операции Kmax, когда аргумент нулевой. |
3C | Ошибка | ЕГГ0Г. | |
3D | То же, что и команда К°→′" (код 2A) | ||
3E | Копирование Y в X (а X→X1) | Те же действия, что и пара команд F↻; В↑, только не X2-влияющая. | |
3F | Пустой оператор | Обычным образом такую команду не ввести. | |
40…4E | x→ПR | Сохранение X в регистр R0…Re | |
4F | Сохранение X в регистр R0 | Обычным образом такую команду не ввести. | |
50 | С/П | Стоп/пуск | |
51 | БП | Безусловный переход | |
52 | В/О | Возврат обратно |
Стек обратных адресов подпрограмм из 5 ячеек, вначале нулевых.
При возврате из подпрограммы берется значение верхней ячейки
стека + 1 (для определения точки возврата) и стек сдвигается,
поэтому сразу после включения ПМК команда В/О эквивалентна БП 01.
Но если стек адресов возврата заполнился до конца, то последний
адрес стека начинает копироваться и В/О будет на этот адрес + 1.
Для зануления стека, в этом случае, удобнее воспользоваться
знаниями Еггогологии и вызвать нулевого 3Г0ГГа:
Cx; В↑; ÷; ВП; Fx2; Cx; Fx2. Последняя команда прибивает пустышку в X1. |
53 | ПП | Вызов подпрограммы | Переход на адрес подпрограммы, указанный следующей командой. Этот адрес запоминается в стеке обратных адресов (см. В/О). |
54 | КНОП | Пустой оператор | |
55 | К1 | Пустой оператор | |
56 | К2 | Пустой оператор | |
57 | Fx≠0 | Если не ноль | Пропуск адреса перехода, и выполнение следующей после него команды, если X не равно нулю (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
58 | FL2 | Цикл по регистру R2 | |
59 | Fx⩾0 | Если больше или равно нулю | Пропуск адреса перехода, и выполнение следующей после него команды, если X больше или равно нулю (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
5A | FL3 | Цикл по регистру R3 | |
5B | FL1 | Цикл по регистру R1 | |
5C | Fx<0 | Если меньше нуля | Пропуск адреса перехода, и выполнение следующей после него команды, если X меньше нуля (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
5D | FL0 | Цикл по регистру R0 | |
5E | Fx=0 | Если равно нулю | Пропуск адреса перехода, и выполнение следующей после него команды, если X равно нулю (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
5F | Зависание |
Визуально воспринимается как зависание. ПМК не реагирует на ввод
команд и ничего не отображает.
Обычным образом такую команду не ввести. |
|
60…6E | П→xR | Извлечение из регистра R0…Re в X | |
6F | Извлечение из регистра R0 в X | Обычным образом такую команду не ввести. | |
70…7E | Кx≠0R | Косвенный условный переход при равенстве, адрес перехода в регистре R0…Re | Пропуск адреса перехода, и выполнение следующей после него команды, если X не равно нулю (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
7F | Косвенный условный переход при равенстве, адрес перехода в регистре R0 |
Пропуск адреса перехода, и выполнение следующей после него команды,
если X не равно нулю (в большинстве языков обычно наоборот, переход
на адрес при выполнении условия).
Обычным образом такую команду не ввести. |
|
80…8E | КБПR | Косвенный безусловный переход на адрес перехода в регистре R0…Re | |
8F | Косвенный безусловный переход на адрес перехода в регистре R0 | Обычным образом такую команду не ввести. | |
90…9E | Кx⩾0R | Косвенный условный переход если больше или равно нулю, адрес перехода в регистре R0…Re | Пропуск адреса перехода, и выполнение следующей после него команды, если X больше или равно нулю (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
9F | Косвенный условный переход если больше или равно нулю, адрес перехода в регистре R0 |
Пропуск адреса перехода, и выполнение следующей после него команды,
если X больше или равно нулю (в большинстве языков обычно наоборот,
переход на адрес при выполнении условия).
Обычным образом такую команду не ввести. |
|
A0…AE | КППR | Косвенный вызов подпрограммы по адресу в регистре R0…Re | |
AF | Косвенный вызов подпрограммы по адресу в регистре R0 | Обычным образом такую команду не ввести. | |
B0…BE | Кx→ПR | Косвенное сохранение X в регистр, по номеру в регистре R0…Re | |
BF | Косвенное сохранение X в регистр, по номеру в регистре R0 | Обычным образом такую команду не ввести. | |
C0…CE | Кx<0R | Косвенный условный переход если меньше нуля, адрес перехода в регистре R0…Re | Пропуск адреса перехода, и выполнение следующей после него команды, если X меньше нуля (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
CF | Косвенный условный переход если меньше нуля, адрес перехода в регистре R0 |
Пропуск адреса перехода, и выполнение следующей после него команды,
если X меньше нуля (в большинстве языков обычно наоборот, переход
на адрес при выполнении условия).
Обычным образом такую команду не ввести. |
|
D0…DE | КП→xR | Косвенное извлечение X из регистра, по номеру в регистре R0…Re | |
DF | Косвенное извлечение X из регистра, по номеру в регистре R0 | Обычным образом такую команду не ввести. | |
E0…EE | Кx=0R | освенный условный переход если равно нулю, адрес перехода в регистре R0…Re | Пропуск адреса перехода, и выполнение следующей после него команды, если X равно нулю (в большинстве языков обычно наоборот, переход на адрес при выполнении условия). |
EF | Косвенный условный переход если равно нулю, адрес перехода в регистре R0 |
Пропуск адреса перехода, и выполнение следующей после него команды,
если X равно нулю (в большинстве языков обычно наоборот, переход на
адрес при выполнении условия).
Обычным образом такую команду не ввести. |
|
F0…FF | Пустой оператор | Обычным образом такую команду не ввести. |
Некоторые команды с цифрой F в коде можно получить тоже недокументированным образом.
Команды F0…FE получаются по адресам 30…44 после выполнения в режиме вычислений пары команд В/О; КППR (R=0…e), которые переведут ПМК в режим ввода программы и вставят код с цифрой F. При этом в регистрах R0…R3 должно быть число в диапазоне -3 < X < 3. Если там будет другое (например, ноль, как после включения ПМК), то для R1…R3 вставится F0…F2 по адресу 30…32, а для R0 это будет самый быстрый способ получить 3Г0ГГа (причем перехода в режим ввода программы не произойдет).
Команду FF можно получить по адресам 50…59 с помощью ЕГГ0Га. Пусть N=0…9. Сначала сохраним ЕГГ0Га:
1; ВП; 5; 0; Fx2; x→П9; Сx.
Затем выполним:
П→x9; ВП; N; .; N; Fавт.
По адресу 5N запишется код FF.
Не забудьте, что, вызывая "монстров" вы портите стек возврата из процедур. Лучше после таких манипуляций его почистить (указано как примечание для команды В/О).