Реальный код.
Предлагаю вам перевод цикла The EightThirtyTwo ISA Part 7: Some real-life code… автора Alastair M. Robinson.
В прошлый раз я сказал, что продемонстрирую реальный код для этого нового ISA. Я уже предоставил очень простой код “Hello World”, и теперь я нахожусь в медленном, кропотливом процессе реализации CPU для запуска этого кода, и исследую удивительный мир моделирования HDL по ходу! Но в то же время, безусловно, лучший способ почувствовать, насколько хорошо работает набор инструкций - это написать для него фактический код и посмотреть, с какими проблемами и ограничениями вы сталкиваетесь.
Один из проектов, с которым я недавно игрался содержал f32c MIPS-совместимое CPU ядро, которое весьмя приятное и очень быстрое, когда запущено из блочной RAM. Я сделал fork, чтобы исправить незначительную ошибку инициализации, которая актуальна только в том случае, если вы хотите, чтобы начальное значение PC было чем-то отличным от нуля. Однако с 32-битным набором инструкций MIPS это ядро имеет довольно плохую плотность кода, и мне пришлось использовать больше блочной оперативной памяти, чем я хотел только для загрузочного кода.
Я смог уменьшить загрузочный код SD-карты до 4 КБ, используя сжатие LZ4, и, таким образом, нуждался в легком коде декомпрессора LZ4 и я наткнулся на github проект Arnaud Carré’s lz4-68k, который включает в себя декомпрессоры на ассемблере 68K, самый маленький из которых имеет длину всего 74 байта.
Поскольку я знаком с ассемблером 68K и тоже могу нащупать свой путь сквозь ассемблер MIPS, я портировал эту подпрограмму в MIPS, и хотя она работает, ее размер составляет около 204 байт благодаря 32-битным кодовым словам. Кто-то, конечно, с лучшим знанием ассемблера MIPS вполне может сделать ее меньше.
Во всяком случае, уже однажды портировал эту подпрограмму, она кажется идеальной для перевода на ассемблер EightThirtyTwo, так что это то, что я сделал!
Эта программа распаковывает некоторые (безголовочные) данные LZ4 и выводит их в UART, или, вернее, в окно shell, при использовании 832Sim (на основе моего более раннего проекта ZPUSim). Код выглядит так (собран с использованием ассемблера GNU и кучки #defines в файле include, так как, как упоминалось в предыдущем посте, мне лень писать ассемблер...):
#include "assembler.pp" start: // Инициализация стека li IMW2(0x1000) li IMW1(0x1000) li IMW0(0x1000) mr r6 // Инициализировать указатели на источник и приемник li IMW1(compressed-start) li IMW0(compressed-start) mr r0 li IMW1(decompressed-start) li IMW0(decompressed-start) mr r1 mr r2 // Вызов подпрограммы распаковки li IMW0(PCREL(lz4_depack)) add r7 li 0 stbinc r1 // Записать нуль-терминатор. // Теперь пишем распакованный буфер в UART. li IMW1(decompressed-start) li IMW0(decompressed-start) mr r1 li IMW1(0xffffffc0) // регистры UART li IMW0(0xffffffc0) mr r0 .txwait: li IMW1(0x100) // Флаг готовности TX li IMW0(0x100) mr r2 ld r0 and r2 cond EQ li IMW0(PCREL(.txwait)) add r7 ldbinc r1 cond NEQ st r0 li IMW0(PCREL(.txwait)) add r7 cond NEX // отключить симуляцию // r0 запакованный буфер // r1 указатель на буфер распаковки // r2 конец запакованного буфера lz4_depack: stdec r6 li PCREL(.tokenLoop) add r7 .lenOffset: ldbinc r0 mr r3 li 8 ror r3 ldbinc r0 or r3 li 24 ror r3 mt r4 mr r5 mt r1 mr r4 mt r3 sub r4 li IMW1(PCREL(.readLen-1)) li IMW0(PCREL(.readLen)) add r7 li 4 add r5 .copy: ldbinc r4 stbinc r1 li 1 sub r5 cond NEQ li IMW0(PCREL(.copy)) add r7 .tokenLoop: ldbinc r0 mr r4 mr r5 li 15 and r4 li 4 shr r5 cond EQ li IMW1(PCREL(.lenOffset-1)) li IMW0(PCREL(.lenOffset)) add r7 li IMW0(PCREL(.readLen)) add r7 .litCopy: ldbinc r0 stbinc r1 li 1 sub r5 cond NEQ li IMW0(PCREL(.litCopy)) add r7 mt r2 cmp r0 cond SGT li IMW1(PCREL(.lenOffset-1)) li IMW0(PCREL(.lenOffset)) add r7 .over: ldinc r6 mr r7 .readLen: stdec r6 li 15 cmp r5 cond NEQ li IMW0(PCREL(.readEnd)) add r7 .readLoop: ldbinc r0 mr r3 add r5 li IMW1(255) li IMW0(255) xor r3 cond EQ li IMW0(PCREL(.readLoop)) add r7 .readEnd: ldinc r6 mr r7 compressed: .incbin "compressed.lz4" decompressed: .fill 550,1,-1 // Зарезервировать место для декомпрессируемых данных
Я был приятно удивлен тем, насколько удобно использовать этот набор инструкций – очевидно, у него есть свои причуды, но я не чувствовал, что слишком сильно борюсь с его ограничениями. Но учитывая, что моя главная причина для изучения этого-оценить плотность кода, как он складывается против 68k и MIPS? Ответ “на удивление хорошо”! Оригинал 68k составлял 74 байта, портированная на MIPS-204 байта, а версия EightThirtyTwo - всего 72 байта, превосходя даже оригинал 68k!
Адрес для контактов : imax9@narod.ru
Если вам понравились мои работы и вы желаете поддержать сайт - сделайте дотацию.
При копировании статьи – обязательна ссылка на авторство и источник. Без разрешения автора копирование запрещено.
© Максим Ильин 2022г.