Код (Text): Число регистров - 2^k (k должно быть не менее 3, наверное :) (к ним также относится счётчик команд ip) Архитектура - трехадресная Число команд - одна единственная (условное вычитание) Число флагов - один (f - флаг переполнения) Разрядность машинного слова - 2+3*k Максимально адресуемое число ячеек памяти - 2^(2+3*k) Все обрабатываемые числа считаются положительными, -1=11...1111 - максимальное число Формат команды: тип 00 R1 R2 R3 - csub R1,R2,R3 if f=0 then R1:=R2-R3,f:=R3>R2 else f=0 01 R1 R2 R3 - csub R1,R2,[R3] if f=0 then R1:=R2-[R3],f:=[R3]>R2 else f=0 10 R1 R2 R3 - csub R1,[R2],R3 if f=0 then R1:=[R2]-R3,f:=R3>[R2] else f=0 11 R1 R2 R3 - csub [R1],R2,R3 if f=0 then [R1]:=R2-R3,f:=R3>R2 else f=0 R1,R2,R3 - поля размером k бит, вся команда занимает одну ячейку памяти Основной цикл: Проверить флаг если f=0 1) считать команду из памяти 2) если тип команды 01 или 10 - считать операнд из памяти 3) вычислить разность и установить флаг при переполнении 4) записать результат в приёмник и увеличить счетчик команд если он(ip) не является приёмником данной команды иначе сбросить флаг перейти к началу При включении питания или поступлении сигнала сброса все регистры и флаг переполнения обнуляются, и начинает выполняться команда расположеная по нулевому адресу. Примеры(предполагается что в начале флаг переполнения сброшен) 1) обнуление регистра csub r,r,r; ;r=0 2) загрузка в регистр единицы csub r,r,r ;r=0 csub r,ip,r ;r=$(адрес этой команды) csub r,ip,r ;r=1 3) сложение регистров a и b csub r,r,r ;r=0 csub b,r,b ;b=-b csub r,r,r ;при b>0 эта команда не выполяется csub a,a,b ;a=a-(-b) csub r,r,r ;при -b>a эта команда не выполяется 4) проверка числа на 0 csub z,z,z csub z,z,r csub <- эта команда будет выполнена если r=0 5) загрузка из памяти csub z,z,z csub r,[m],z 6) сохранение в памяти csub z,z,z csub [m],r,z 7) деление числа в регистре a на число в регистре b, с записью частного в регистр d и остатка в регистр a #1 csub z,z,z ;z=0 #2 csub m,ip,z ;m=$ #3 csub m,m,ip ;m=-1 #4 csub z,z,z ;никогда не исполняется #5 csub d,d,d ;d=0 #6 csub l,ip,m ;l=адрес следующей команды ;начало цикла #7 csub d,d,m ;d=d-(-1) не исполняется при первом проходе цикла #8 сsub z,z,z ;исполняется только при первом прохорде #9 csub a,a,b ;a=a-b #10 csub ip,l,z ;ip=l-z, то есть переход в начало цикла если в предыдущей ;команде не произошло переполнение ;далее довычисление остатка #11 csub z,z,b ;z=z-b #12 csub z,z,z ;на результат предыдущей команды не влияет #13 csub a,a,b ;формирование остатка Работа алгоритма: пусть a=7 и b=3 #1 z=0 f=0 #2 m=2 f=0 #3 m=-1 f=1 -1=11...1111 #5 d=0 f=0 #6 l=7 f=1 #8 z=0 f=0 #9 a=4 f=0 #10 ip=7 f=0 #7 d=1 f=1 #9 a=1 f=0 #10 ip=7 f=0 #7 d=2 f=1 частное уже вычислено #9 a=-2 f=1 -2=11...1110 #11 z=-3 f=1 -3=11...1101 #13 a=1 f=0 остаток тоже вычислен Вот такие идеи иногда приходят в голову ... Нужно будет сделать эмулятор ...
Супер!!! (я пока пошёл за бутылкой, что бы разобраться . Вот только как загружать данные в регистры безо всяких трюков ? если k = 4, то разрядность слова будет 14 бит. (это не совсем удобно при эмуляции на x86, т.к. 2 бита будут пропадать) Может быть увеличить чуть-чуть слово, расширив поле команды добавив ещё один бит? этот бит будет безусловно прибавляться к IP перед шагом 2 и можно будет хранить непосредственные операнды за кодом команды, адресуясь к ним по IP.
S_T_A_S_ Зато если k = 10 ... Код (Text): Добавлять для этого непосредственный операнд не нужно: если в регистрах есть 0 и -1(если нет, придется их получить) csub rc,ip,-1 ;f=1, адрес этой команды не должен быть равен -1 const ;эта ячейка пропускается csub rc,[rc],0 ;rc=const или можно делать так если констант нужно много: #0 csub rz,rz,rz ;rz=0 #1 csub r1,ip,rz ;r1=1 #2 csub rc,rz,r1 ;rс=-1=11...1111, f=1 #3 const_start ;адрес последней ячейки области констант #4 csub rc,ip,r1 ;rс=3 #5 csub rc,[rc],rz ;rс=const_start ;константы #const_start-N constN constN-1 ... const1 #const_start const0 rz - регистр содержащий 0 r1 - регистр содержащий 1 rc - указатель на блок констант rx - некоторый регистр ну а там где нужна константа пишем csub rx,[rc],rz ;загружаем константу csub rc,rc,r1 ;переходим к следующей константе далее вместо регистров с константами будет просто записано их(констант) значение Для циклов перед списком констант размещается указатель(перед входом в цикл адрес этой ячейки хранится в регистре rc) на начало области констант цикла, также этот указатель нужно будет поместить последней константой цикла чтобы он перезагружася при повторных итерациях. L: csub rc,[rc],0 ;установка указателя на первую константу цикла ... csub rx,[rc],0 ;загрузка первой константы csub rc,rc,1 ... csub rx,[rc],0 ;загрузка последней константы ;скорее всего равной метке L csub rc,rc,1 ... csub <- тут должна быть проверка условия csub ip,L,0 ;переход в начало цикла csub rc,rc,1 ;пропуск указателя на начало констант цикла Для цикла использующего константы A,B и C соответствующий участок списка констант будет выглядеть так: #E P C B #P A #S P Такой список будем представлять так: S:{P,P:{A,B,C,P}},E:... вложенные скобки не обязательны, а служат для лучшего отражения связи со структурой программы В начале r2=S указывает на ячейку содержащую P Первая команда цикла загружает в r2 P, то есть адрес константы A В конце интерации r2 снова указывает на ячейку содержащую P Если переход будет выполнен, то в r2 будет загружено P иначе мы выходим из цикла и делаем r2 равным E, то есть началу следующего блока констант Размышления о вызовах процедур привели к выводу, что стоит малость изменить архитектуру: отобразить регистры на адресное пространство, чтобы без самомодифицирующегося кода можно было реализовать процедуры типа "сохранить регистры с R3 по R5 в области памяти на которую указывает R2". Теперь карта памяти будет выглядеть так: 0: R0, он же ip, то есть счетчик команд 1: R1 - первый регистр 2: R2 ... 2^k-1: Rn - последний регистр 2^k: - нулевая ячейка памяти ;) 2^k+1: - первая ячейка памяти ... 2^(2+k*3): - последняя ячейка памяти А выполнение программы будет начинаться с адреса 2^k. Еще несколько примеров(предполагается что константы уже есть в регистрах) -1=max=11...1111 8) побитовое отрицание csub r,max,r ;r=11...1111-r 9) проверка старшего бита r и переход на метку L если бит сброшен csub t,max,r ;t=~r=max-r csub t,t,r ;t=~r-r=max-r-r=max-2r=~2r csub ip,L,0 ;переход на метку L 10) проверка старшего бита r и переход на метку L если бит установлен csub t,-1,r ;t=~r=max-r csub t,r,t ;t=r-~r=r-(max-r) csub ip,L,0 ;переход на метку L 11) циклический сдвиг влево(от младших бит к старшим) csub t,-1,r ;t=~r=max-r csub r,r,t ;r=r-~r=r-(max-r)=2r+1 csub r,r,1 ;вычесть 1 если старший бит r был сброшен объединив несколько циклических сдвигов и один из пунктов 9) или 10) можно получить переход по состоянию любого бита 12) логический сдвиг влево csub t,-1,r ;t=~r=max-r csub r,r,t ;r=r-~r=r-(max-r)=2r+1 nop ;например csub rz,rz,rz где rz=0 перед выполнением csub r,r,1 ;r=2r+1-1=2r _DEN_ После защиты как раз месяц останется 8)
EvilsInterrupt Для начала ассемблер и програмный эмулятор с интерфейсом типа TD, а потом можно подумать о железе(ПЛИС например) и высокоуровневых языках. _DEN_ Конечно можно, по другому его и не сделать.
Black_mirror > Это хорошая мысль. Может быть принять за IP на R0, а RN, а при "инициализации" процессора заполнять каждый регистр значением его номера? Так получим некоторые нужные константы (0, 1) и заодно счётчик команд будет нуказывать на нужное место в памяти.
Между регистрами и ячейками памяти принципиальной разницы нет. Можно одно убрать, например регистры. А память поделить на три области, константы, память и порты. Правда команды чуток рабухнут.....