nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore: printf - числа с фиксированной точкой

Целые числа - это хорошо, но совсем шикарно было бы выдать параметры взаимного расположения объектов в понятных человеку числах, дальность - в метрах, углы - в градусах.

Первый шаг к этому - научиться ставить запятую между любыми десятичными разрядами. Это было бы предельно просто, используй мы в любом случае 5 цифр, но более заковыристо, если мы не хотим лишних нулей, однако обязаны их поставить перед запятой и после запятой.

Настолько заковыристо, что добавляет нашей процедуре ещё 4 слова (8 байт), а потом ещё 2 слова, чтобы чуть высвободить регистры, итого укладываемся ровно в 30 слов (60 байт), при этом новая процедура может и не рисовать запятую вовсе, т.е держать и старую, и новую нам не требуется. Любой каприз (со знаком, без знака, запятую ставить, и если ставить, то где) - всё здесь, в 30 словах :)




Вот он, код:
	;отобразить число со знаком из ACC, причём поставить запятую в позицию, заданную в регистре i.
	;меняет регистры k,Y,Acc
	;(j мы сохраняем в стек, чтобы вернуть как было)
	;после выполнения k=0
	SignedFixed proc
				SUB		0
				JGE		UnsignedFixed
				UART0		'-'
				ABS		Acc
	SignedFixed endp
	UnsignedFixed proc
				[SP++]		j
				k		4	;номер обрабатываемого разряда
				[SP+1]		-1	;-1 означает, что значащих цифр ещё не было, 0-что появились
				Y		BCDtable				
	@@start:		JNik		@@skipP0	;уже здесь нужно проверить на запятую, поскольку её наличие заставит поставить нолик!
				[SP+1]		0			;указали на значимость :)
	@@skipP0:		j		[SP+1]
	@@sub:			SUB		[Y+k]
				j++		0
				JGE		@@sub
				ADD		[Y+k]
				jLOOP		@@proceed	;прыжка не будет, если [SP+1]=-1 и текущий разряд нулевой
				kloop		@@start
				[SP+1]		0
	@@proceed:		[SP]		Acc		;сейчас аккумулятор пригодится...
				Acc		'0'
				ADD		j
				SUB		[SP+1]
				UART0		Acc
				[SP+1]		0
				Acc		[SP]
				JNik		@@finish
				UART0		','
	@@finish:		kloop		@@start	
				j		[--SP]
				JMP		[--SP]
	UnsignedFixed endp	


Чтобы эта штука заработала, мы ввели новую, недостающую команду в QuatCore: JNik (Jump if Not i=k). У нас была "непарная" Jik (Jump if i=k), и свободная ячейка, которая ждала своего часа:



Три соседа что-то делают, но я пока не вполне разбирался, что именно :) Дело в том, что инкременты i++, j++, k++ реализованы немножко "лениво", не проверяя DestAddr[3], и циклы тоже сделаны "лениво", не проверяя DestAddr[2], в итоге мы получим некую комбинацию циклов и инкремента, но кто кого пересилит - нужно разбираться. Может, даже что-то полезное из этого выйдет. А вот четвёртая ячейка вполне нас устраивает, ячейка ijk безобидная, тем более что мы её ещё ни разу не включали (у нас есть параметр ijkEnabled, выставив ноль мы отключаем логику, отвечающую за команды ijk в Src и Dest и экономим 24 ЛЭ, но заносить все-все индексные регистры в стек, когда припрёт, будет тем ещё геморроем)

Чтобы добавить команду JNik, мы поменяли строку в модуле QuatCorePCrelJumpDecision. Было так:
wire DoJik	 = isLOOP & (DestAddr[1:0] == 2'b11) & iEQk;


а стало так:
wire DoJik	 = isLOOP & (DestAddr[1:0] == 2'b11) & (iEQk ^ DestAddr[2]);


Как результат, процессор "растолстел" на 1 ЛЭ, в текущих настройках - от 482 ЛЭ до 483 ЛЭ (сюда же входит и мигающий с частотой 1 Гц светодиод, который нас успокаивает). Не страшно.

Изначально команда Jik вводилась для работы с треугольными и симметричными матрицами, и пока дожидается своего часа.

Смысл такой: в регистре i мы задаём позицию запятой, причём позиция отсчитывается так:

 4,3210

где запятая находится сейчас - это позиция 4.

Итак, регистр i может принимать любое значение. От 0 до 4 означает, что мы поставим запятую в нужном месте, от 5 и выше - запятой попросту не будет.

Когда мы начинаем обрабатывать очередной разряд (метка @@start:), первым же делом проверяем - не тот ли это разряд, где стоит запятая? И если это он, то объявляем его "значащим", т.е даже если там ноль, он всё равно отобразится.

Теперь мы точно знаем, что на данной итерации дойдём до метки @@proceed: и отобразим цифру, стоящую перед запятой. Далее повторно проверяем позицию - и отображаем саму запятую. Вот, собственно, и вся хитрость. Добавилось два условных перехода и две команды - объявить цифру "значащей" и непосредственно нарисовать запятую. Ах да, ещё у нас регистр i сменился на j (поскольку i занят), но теперь и цикл в процедуре main организовывать стало не на чем, поэтому мы всё-таки добавили [SP++] j в начале процедуры (занести значение в стек) и j [--SP] в конце (вытолкнуть из стека).

Вот тестовая программа:
.rodata
	OurString Int16 'С','т','е','п','е','н','и',' ','м','и','н','у','с',' ','д','в','о','й','к','и',':',13,10,-32768
	ZeroStr Int16 13,10,'Н','о','л','и','к',':'
	CRLF Int16 13,10,-32768
	BCDtable dw 1,10,100,1000,10000
	MinusOne Int16 -32768
.data
	Stack	dw	?,?,?,?
.code
	main proc
				SP		Stack
				Z		MinusOne
				i		5
		@@outer:	X		OurString
				CALL 		print
				j		15
				ACC		1
		@@start:	C		Acc
				CALL		SignedFixed	;очередное число
				UART0		9
				Acc		C
				C		[Z+k]
				MUL		Acc
				DIV2S		1
				ADD		Acc		;сделали умножение на -2 в итоге...
				jLOOP		@@start
				X		ZeroStr
				CALL		print
				Acc		0
				CALL		SignedFixed ;хотим проверить, "голый" ноль вообще отобразится?
				UART0		13	;CR
				UART0		10	;LF
				iLOOP		@@outer					
		@@endless: 	JMP 		@@endless
	main endp
	
	;X указывает на начало строки текста
	;конец обозначается отрицательным числом (у нас НЕТ ФЛАГА НУЛЯ!!!)
	;меняет значение Acc, остальные регистры остаются как есть
	print proc
				[SP++]	i
				i		0
		@@start:	ABS		[X+i]	;хитрая проверка на -32768
				JO		@@finish
				UART0	Acc
				i++		0
				JMP		@@start
		@@finish:	i		[--SP]
				JMP		[--SP]
	print endp

Поскольку теперь мы используем 2 индексных регистра в основной программе, а третий ожидаем нулевым после вызова всех процедур, то мы сделали print более аккуратным. Также мы догадались, что перенос строки выводить удобнее "ручками", чем вызывать целый print, а уж один символ табуляции - тем более.

Мы рисуем одну и ту же числовую последовательность, но с разным расположением запятой. Сначала её вообще нет (позиция 5), затем мы делаем 4 знака после запятой, 3 знака и т.д.

Я вижу одну проблему: ноль "с фиксированной запятой". Мы неявно полагаем, что после запятой рано или поздно должны появиться значащие разряды, поэтому рисуем ноль перед запятой, затем запятую, затем ещё нули - и вдруг оказывается, что там и нет ничего. Возможно, что вместо
0,0000 нам следовало бы выдавать просто 0.

Чтобы этого достичь, нам нужно теперь уже в наиболее явном виде рассмотреть отдельный случай нуля, и если это он - выдать 0 и побыстрее вернуться! Правда, проверки на равенство как таковой у нас нет (как и флага нуля, вот это совпадение :)), для работы с зашумлёнными "реальными данными" она не так уж нужна. Её можно "эмулировать", взяв модуль разности и вычитая единичку. Если результат отрицательный - значит равны! Но возможно, это и перебор, меня и так устраивает.


Poll #2098687 Ровно ноль

Как должен отображаться ноль в числах с фикс. запятой?

0
5(71.4%)
0,0000 (повторяя количество разрядов после запятой)
2(28.6%)
Tags: ПЛИС, математика, программки, работа, странные девайсы
Subscribe

  • Я создал монстра!

    Вот нормальная счастливая пара разъёмов ОНЦ-БС-1-10/14-Р12-2-В и ОНЦ-БС-1-10/14-В1-2-В: У розетки кроме основного выступа, отмечающего "верх",…

  • Нахождение двух самых отдалённых точек

    Пока компьютер долго и упорно мучал симуляцию, я пытался написать на ассемблере алгоритм захвата на ближней дистанции. А сейчас на этом коде можно…

  • Слишком общительный счётчик

    Вчера я чуть поторопился отсинтезировать проект,параметры не поменял: RomWidth = 8 вместо 7, RamWidth = 9 вместо 8, и ещё EnableByteAccess=1, чтобы…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 5 comments