PDP-1: перезагрузка

 

Встретил на гф топик, где заданный вопрос заставил меня вернуться к древнему компьютеру. Вопрос касался ввода текста и печати сообщений, но я ничего про это не знал. Лет 8 назад я публиковал статью о разработке и эмуляции в сообществе было-Hype. Статья была невнятной и неполной, а сейчас многое изменилось.

Я использовал эмулятор simh v1.4b, в котором была включена эмуляция экрана. Сейчас я нашел simh v4 - сборник исполняемых файлов. Уже в старой версии была добавлена возможность исполнения команд при запуске: нужно создать файл simh.ini и ввести команды:

set dpy enable включить дисплей

attach ptr hex.rim подключить образ ленты hex.rim

boot ptr загрузка и запуск программы

Для отладки можно добавить break 100.


Еще один эмулятор классической игры SpaceWar! - это MAME/MESS


Способ запуска игры можно описать как "через задницу", но игра запускалась - были ролики на Ютуб.

Сейчас в версии 0.270 добавлена возможность загрузки .rim, включен отладчик, но мне так и не удалось загрузить файл, эмулятор невнятно ругался и ничего не работало. Если верить скриншотам, эмуляция шла на отлично:


Более того, сборник ROM находился неизвестно где, нашел заблокированный архив на web.archive.org. Камрады с чятика подсказали, что для скачки нужна регистрация - заработало! Приложил архивы ко всем файлам - ни один копираст не докопается.

Теперь разработка. Мое знание опкодов касалось книги PDP1_Handbook_Oct63, но сейчас появился отличный сайт с эмулятором и списком опкодов. В архив добавлен документ, т.к. сайт иногда уходит в кому.

В интернетах можно найти пример печати текста. Всегда интересно разобраться с программой, а не скомпилировать, запустить, и почувствовать себя великим программистом.

hello   
/ above: title line - was punched in human readable letters on paper tape
/ below: location specifier - told assembler what address to assemble to
100/
lup, lac i ptr / load ac from address stored in pointer
cli / clear io register
lu2, rcl 6s / rotate combined ac + io reg 6 bits to the left
/ left 6 bits in ac move into right 6 bits of io reg
tyo / type out character in 6 right-most bits of io reg
sza / skip next instr if accumulator is zero
jmp lu2 / otherwise do next character in current word
idx ptr / increment pointer to next word in message
sas end / skip next instr if pointer passes the end of message
jmp lup / otherwise do next word in message
hlt / halt machine
ptr, msg / pointer to current word in message
msg, text "hello, world" / 3 6-bit fiodec chars packed into each 18-bit word
end, .          / sentinel for end of message
start 100 / tells assembler where program starts

lac i ptr здесь используется косвенная адресация или AC=peek(peek(ptr))

rcl 6s сдвиг влево регистров AC и IO. 6s в исходнике описана как { DEFFIX, "6s",     0000077 } - это тоже 6 единиц.

tyo  печать значения правых 6 бит в IO. Не очень ясно, что печатается, если значение 0.

sza Выполнение опкода  jmp lu2 пропущено, если AC=0(опкод группы skip).

jmp lu2

idx ptr Увеличить указатель на 1, переход к следующему символу. AC=peek(ptr)+1

sas end Еще один skip-опкод: следующая инструкция будет пропущена, если  AC равен адресу end/

jmp lup

hlt  остановка выполнения.


Ввод с клавиатуры я решил написать сам, не ища опубликованные исходники. Почитал упомянутую книгу Handbook и получил результат. Потом я изменил программу, чтобы ввод останавливался при нажатии Enter(или CR).

hello
100/
bg,
cla
clf 1 /clear Program flag 0-7 holds at 0-7
sbf,
szf 1
jmp gk
jmp sbf
gk,
tyi /IO=code, right 6 bits
dio chk /stupid lai for PDP-1D
law 77
sad chk /skip if AC=m(cr)
hlt /stop execution
tyo /output
jmp bg
chk,0
end, .
start 100

Здесь до опроса нажатия клавиши сбрасывается Flag 1, он устанавливается при нажатии клавиши. Выход из цикла - szf 1, в регистре IO хранится код клавиши и выводится на консоль эмулятора(вангую, что в реале печатается на бумаге). После печати код проверяется на значение 77o или CR и в случае совпадения останавливается выполнение.

Интересно, что после кодинга я нашел похожий пример в описании Macro 1, но синтаксис отличается от того, что было под рукой.

Теперь еще одно - печать числа в восьмиричной системе исчисления:

plot
100/
 law 1234
 jda opt
 hlt
opt,0
 dap opx
 law i 6
 dac occ
 cli
opc, lac opt
 ral 3s
 dac opt
 and v7
 sza i
 law 20
 rcr 9s
 rcr 9s
 tyo
 isp occ
 jmp opc
opx, jmp .
occ, 0
v7, 7
end, .
start 100

 law 1234 загрузка значения AC=1234octal
 jda opt выполняется как dac opt jsp oct+1

 dap opx На момент выполнения AC=адресу для своего рода возврата из "процедуры" AC= адрес ниже, чем jda opt.
 law i 6 Загрузка в AC отрицательного числа -6. Увидел похожий прием в примерах - это счетчик цикла
 dac occ Сохранить счетчик в occ

opc, lac opt Загрузка числа
 ral 3s Циклический сдвиг AC влево
 dac opt Сохранить значение

 and v7 AC=AC&peek(v7) или AC=AC&7 получить три бита числа
 sza i Снова skip - пропустить следующий опкод, если AC=0
 law 20 Ели AC=0, то загрузка кода символа "0"
 rcr 9s  Пара инструкций  - это обмен  AC и IO
 rcr 9s см. исходный текст SpaceWar, где встречается макрос swap.
 tyo печать кода в IO, код это правые 6 бит.

 isp occ Index and skip if result is positive Счетчик увеличивается от -6 до 0 и при AC=0 будет пропущен переход  jmp opc
 jmp opc
opx, jmp . это возврат из "процедуры"

Описание кода слегка утомляет, поэтому не буду описывать мои попытки нарисовать на экране. Вместо старого алгоритма получилась просто фигня, названная displayhack.

На этом я закончу свой рассказ, упомянутые файлы тут.

А самая красивая программа snowflake в simh так и не работает.


Дополнение. похоже, что я не совсем понял, как работает код. В исходнике печати восьмиричного числа пара инструкций rcr 9s работают как обмен регистров AC и IO.

Печать текста  выглядит еще интереснее. Посмотрел на сгенерированный листинг:

   17 00113 706543      msg, text "hello, world" / 3 6-bit fiodec chars packed into each 18-bit word
      00114 434633
      00115 002646
      00116 514364
   18 00117 000117      end, .          / sentinel for end of message

Преобразовал 18-битное слово:
706543o=111000110101100011
0b111000=70 H
0b110101=65 E
0b100011=43 L
Получилось упаковка текста - 6 бит*3=18 бит. Если посчитать, то текст помещается в 4 18-битных слова. 3*4=12 символов. Цикл в lu2 это и есть распаковка данных в IO и печать.

Комментарии