Imax9
NEWS   ARTICLES   MINIMIG   FILES   ABOUT

Реальный код.

Предлагаю вам перевод цикла 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г.

Яндекс.Метрика